fix(image): use fixed image width

This commit is contained in:
Jan De Dobbeleer 2024-02-15 14:18:53 +01:00 committed by Jan De Dobbeleer
parent 59be189233
commit fece104e73
3 changed files with 97 additions and 117 deletions

View file

@ -12,11 +12,11 @@ import (
)
var (
author string
cursorPadding int
rPromptOffset int
bgColor string
outputImage string
author string
// cursorPadding int
// rPromptOffset int
bgColor string
outputImage string
)
// imageCmd represents the image command
@ -49,10 +49,12 @@ Exports the config to an image file using customized output options.`,
Run: func(_ *cobra.Command, _ []string) {
env := &platform.Shell{
CmdFlags: &platform.Flags{
Config: config,
Shell: shell.GENERIC,
Config: config,
Shell: shell.GENERIC,
TerminalWidth: 150,
},
}
env.Init()
defer env.Close()
cfg := engine.LoadConfig(env)
@ -80,12 +82,10 @@ Exports the config to an image file using customized output options.`,
prompt := eng.Primary()
imageCreator := &engine.ImageRenderer{
AnsiString: prompt,
Author: author,
CursorPadding: cursorPadding,
RPromptOffset: rPromptOffset,
BgColor: bgColor,
Ansi: writer,
AnsiString: prompt,
Author: author,
BgColor: bgColor,
Ansi: writer,
}
if outputImage != "" {
@ -108,8 +108,8 @@ Exports the config to an image file using customized output options.`,
func init() { //nolint:gochecknoinits
imageCmd.Flags().StringVar(&author, "author", "", "config author")
imageCmd.Flags().StringVar(&bgColor, "background-color", "", "image background color")
imageCmd.Flags().IntVar(&cursorPadding, "cursor-padding", 0, "prompt cursor padding")
imageCmd.Flags().IntVar(&rPromptOffset, "rprompt-offset", 0, "right prompt offset")
// imageCmd.Flags().IntVar(&cursorPadding, "cursor-padding", 0, "prompt cursor padding")
// imageCmd.Flags().IntVar(&rPromptOffset, "rprompt-offset", 0, "right prompt offset")
imageCmd.Flags().StringVarP(&outputImage, "output", "o", "", "image file (.png) to export to")
exportCmd.AddCommand(imageCmd)
}

View file

@ -339,6 +339,73 @@ func (ir *ImageRenderer) runeAdditionalWidth(r rune) int {
return 0
}
func (ir *ImageRenderer) cleanContent() {
// clean abundance of empty lines
ir.AnsiString = strings.Trim(ir.AnsiString, "\n")
ir.AnsiString = "\n" + ir.AnsiString
// clean string before render
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[m", "\x1b[0m")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[K", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[0J", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[27m", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b8", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\u2800", " ")
// cursor indication
saveCursorAnsi := "\x1b7"
if !strings.Contains(ir.AnsiString, saveCursorAnsi) {
ir.AnsiString += "_"
}
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, saveCursorAnsi, "_")
// replace rprompt with adding and mark right aligned blocks with a pointer
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[1000C", strings.Repeat(" ", ir.RPromptOffset))
// add watermarks
ir.AnsiString += "\n\n\x1b[1mohmyposh.dev\x1b[22m"
if len(ir.Author) > 0 {
createdBy := fmt.Sprintf(" by \x1b[1m%s\x1b[22m", ir.Author)
ir.AnsiString += createdBy
}
}
func (ir *ImageRenderer) measureContent() (width, height float64) {
linewidth := 145
linewidth += ir.additionalWidth()
tmpDrawer := &font.Drawer{Face: ir.regular}
advance := tmpDrawer.MeasureString(strings.Repeat(" ", linewidth))
width = float64(advance >> 6)
// height, lines times font height and line spacing
height = float64(len(strings.Split(ir.AnsiString, "\n"))) * ir.fontHeight() * ir.lineSpacing
return width, height
}
/*
additionalWidth returns the number of additional characters of width to allocate when drawing
for characters that are 2 wide. A standard character will return 0
Nerd Font glyphs will return 1, since most are double width
*/
func (ir *ImageRenderer) additionalWidth() int {
longest := 0
var longestLine string
for _, line := range strings.Split(ir.AnsiString, "\n") {
length := ir.lenWithoutANSI(line)
if length > longest {
longestLine = line
longest = length
}
}
var additionalWidth int
for _, rune := range longestLine {
additionalWidth += ir.runeAdditionalWidth(rune)
}
return additionalWidth
}
func (ir *ImageRenderer) lenWithoutANSI(text string) int {
if len(text) == 0 {
return 0
@ -363,57 +430,6 @@ func (ir *ImageRenderer) lenWithoutANSI(text string) int {
return length
}
func (ir *ImageRenderer) calculateWidth() int {
longest := 0
for _, line := range strings.Split(ir.AnsiString, "\n") {
length := ir.lenWithoutANSI(line)
if length > longest {
longest = length
}
}
return longest
}
func (ir *ImageRenderer) cleanContent() {
rPromptAnsi := "\x1b7\x1b[1000C"
hasRPrompt := strings.Contains(ir.AnsiString, rPromptAnsi)
// clean abundance of empty lines
ir.AnsiString = strings.Trim(ir.AnsiString, "\n")
ir.AnsiString = "\n" + ir.AnsiString
// clean string before render
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[m", "\x1b[0m")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[K", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[0J", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[27m", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[1F", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b8", "")
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\u2800", " ")
// replace rprompt with adding and mark right aligned blocks with a pointer
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, rPromptAnsi, fmt.Sprintf("_%s", strings.Repeat(" ", ir.CursorPadding)))
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[1000C", strings.Repeat(" ", ir.RPromptOffset))
if !hasRPrompt {
ir.AnsiString += fmt.Sprintf("_%s", strings.Repeat(" ", ir.CursorPadding))
}
// add watermarks
ir.AnsiString += "\n\n\x1b[1mohmyposh.dev\x1b[22m"
if len(ir.Author) > 0 {
createdBy := fmt.Sprintf(" by \x1b[1m%s\x1b[22m", ir.Author)
ir.AnsiString += createdBy
}
}
func (ir *ImageRenderer) measureContent() (width, height float64) {
// get the longest line
linewidth := ir.calculateWidth()
// width, taken from the longest line
tmpDrawer := &font.Drawer{Face: ir.regular}
advance := tmpDrawer.MeasureString(strings.Repeat(" ", linewidth))
width = float64(advance >> 6)
// height, lines times font height and line spacing
height = float64(len(strings.Split(ir.AnsiString, "\n"))) * ir.fontHeight() * ir.lineSpacing
return width, height
}
func (ir *ImageRenderer) SavePNG() error {
var f = func(value float64) float64 { return ir.factor * value }

View file

@ -8,10 +8,8 @@ const exec = util.promisify(require('child_process').exec);
const themesConfigDir = "./../themes";
const themesStaticDir = "./static/img/themes";
function newThemeConfig(rpromptOffset = 40, cursorPadding = 30, author = "", bgColor = "#151515") {
function newThemeConfig(author = "", bgColor = "#151515") {
var config = {
rpromptOffset: rpromptOffset,
cursorPadding: cursorPadding,
author: author,
bgColor: bgColor
};
@ -23,51 +21,18 @@ function isValidTheme(theme) {
}
let themeConfigOverrrides = new Map();
themeConfigOverrrides.set('agnoster.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('agnosterplus.omp.json', newThemeConfig(80));
themeConfigOverrrides.set('amro.omp.json', newThemeConfig(40, 100, 'AmRo', '#1C2029'));
themeConfigOverrrides.set('avit.omp.json', newThemeConfig(40, 80));
themeConfigOverrrides.set('blueish.omp.json', newThemeConfig(40, 100));
themeConfigOverrrides.set('cert.omp.json', newThemeConfig(40, 50));
themeConfigOverrrides.set('chips.omp.json', newThemeConfig(25, 30, 'CodexLink | v1.2.4, Single Width (07/11/2023) | https://github.com/CodexLink/chips.omp.json'));
themeConfigOverrrides.set('cinnamon.omp.json', newThemeConfig(40, 80));
themeConfigOverrrides.set('craver.omp.json', newThemeConfig(40, 80, 'Nick Craver', '#282c34'));
themeConfigOverrrides.set('darkblood.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('honukai.omp.json', newThemeConfig(20));
themeConfigOverrrides.set('hotstick.minimal.omp.json', newThemeConfig(40, 10));
themeConfigOverrrides.set('hunk.omp.json', newThemeConfig(40, 15, 'Paris Qian'));
themeConfigOverrrides.set('huvix.omp.json', newThemeConfig(40, 70));
themeConfigOverrrides.set('jandedobbeleer.omp.json', newThemeConfig(40, 15));
themeConfigOverrrides.set('kushal.omp.json', newThemeConfig(90, 30, 'Kushal-Chandar'));
themeConfigOverrrides.set('lambda.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('marcduiker.omp.json', newThemeConfig(0, 40));
themeConfigOverrrides.set('material.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('microverse-power.omp.json', newThemeConfig(40, 100));
themeConfigOverrrides.set('negligible.omp.json', newThemeConfig(10));
themeConfigOverrrides.set('night-owl.omp.json', newThemeConfig(40, 0, 'Mr-Vipi', '#011627'));
themeConfigOverrrides.set('paradox.omp.json', newThemeConfig(40, 100));
themeConfigOverrrides.set('powerlevel10k_classic.omp.json', newThemeConfig(10));
themeConfigOverrrides.set('powerlevel10k_lean.omp.json', newThemeConfig(80));
themeConfigOverrrides.set('powerline.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('pure.omp.json', newThemeConfig(40, 80));
themeConfigOverrrides.set('quick-term.omp.json', newThemeConfig(15, 0, 'SokLay'))
themeConfigOverrrides.set('remk.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('robbyrussell.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('slim.omp.json', newThemeConfig(10, 80));
themeConfigOverrrides.set('slimfat.omp.json', newThemeConfig(10, 93));
themeConfigOverrrides.set('space.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('spaceship.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('star.omp.json', newThemeConfig(40, 70));
themeConfigOverrrides.set('stelbent.minimal.omp.json', newThemeConfig(70));
themeConfigOverrrides.set('tonybaloney.omp.json', newThemeConfig(0, 40));
themeConfigOverrrides.set('unicorn.omp.json', newThemeConfig(0, 40));
themeConfigOverrrides.set('ys.omp.json', newThemeConfig(40, 100));
themeConfigOverrrides.set('zash.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('catppuccin.omp.json', newThemeConfig(40, 40, 'IrwinJuice', '#24273A'));
themeConfigOverrrides.set('catppuccin_latte.omp.json', newThemeConfig(40, 40, 'IrwinJuice', '#EFF1F5'));
themeConfigOverrrides.set('catppuccin_frappe.omp.json', newThemeConfig(40, 40, 'IrwinJuice', '#303446'));
themeConfigOverrrides.set('catppuccin_macchiato.omp.json', newThemeConfig(40, 40, 'IrwinJuice', '#24273A'));
themeConfigOverrrides.set('catppuccin_mocha.omp.json', newThemeConfig(40, 40, 'IrwinJuice', '#1E1E2E'));
themeConfigOverrrides.set('amro.omp.json', newThemeConfig('AmRo', '#1C2029'));
themeConfigOverrrides.set('chips.omp.json', newThemeConfig('CodexLink | v1.2.4, Single Width (07/11/2023) | https://github.com/CodexLink/chips.omp.json'));
themeConfigOverrrides.set('craver.omp.json', newThemeConfig('Nick Craver', '#282c34'));
themeConfigOverrrides.set('hunk.omp.json', newThemeConfig('Paris Qian'));
themeConfigOverrrides.set('kushal.omp.json', newThemeConfig('Kushal-Chandar'));
themeConfigOverrrides.set('night-owl.omp.json', newThemeConfig('Mr-Vipi', '#011627'));
themeConfigOverrrides.set('quick-term.omp.json', newThemeConfig('SokLay'))
themeConfigOverrrides.set('catppuccin.omp.json', newThemeConfig('IrwinJuice', '#24273A'));
themeConfigOverrrides.set('catppuccin_latte.omp.json', newThemeConfig('IrwinJuice', '#EFF1F5'));
themeConfigOverrrides.set('catppuccin_frappe.omp.json', newThemeConfig('IrwinJuice', '#303446'));
themeConfigOverrrides.set('catppuccin_macchiato.omp.json', newThemeConfig('IrwinJuice', '#24273A'));
themeConfigOverrrides.set('catppuccin_mocha.omp.json', newThemeConfig('IrwinJuice', '#1E1E2E'));
(async () => {
const themes = await fs.promises.readdir(themesConfigDir);
@ -85,10 +50,7 @@ themeConfigOverrrides.set('catppuccin_mocha.omp.json', newThemeConfig(40, 40, 'I
}
let poshCommand = `oh-my-posh config export image --config=${configPath}`;
poshCommand += ` --rprompt-offset=${config.rpromptOffset}`;
poshCommand += ` --cursor-padding=${config.cursorPadding}`;
poshCommand += ` --background-color=${config.bgColor}`;
poshCommand += ` --terminal-width=200`;
if (config.author !== '') {
poshCommand += ` --author="${config.author}"`;
}
@ -100,6 +62,8 @@ themeConfigOverrrides.set('catppuccin_mocha.omp.json', newThemeConfig(40, 40, 'I
continue;
}
console.info(`Exported ${theme}`);
const themeName = theme.slice(0, -9);
const image = themeName + '.png';
const toPath = path.join(themesStaticDir, image);