feat: export image of the current theme

This commit is contained in:
Jan De Dobbeleer 2021-04-04 20:28:41 +02:00 committed by Jan De Dobbeleer
parent e8f7eb7776
commit afb69b4229
51 changed files with 958 additions and 305 deletions

View file

@ -13,10 +13,20 @@ jobs:
uses: actions/checkout@v2.3.1
with:
persist-credentials: false
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Build
run: |
cd src
go build -o docs/oh-my-posh
cd ..
- name: Install and Build 🔧
run: |
cd docs
npm install
npm run themes
npm run build
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.2

3
.gitignore vendored
View file

@ -155,3 +155,6 @@ bin/
Output/
*.sha256
*.7z
# images
*.png

25
.vscode/launch.json vendored
View file

@ -1,7 +1,4 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
@ -42,6 +39,28 @@
"--shell=pwsh",
"--config=${workspaceRoot}/themes/jandedobbeleer.omp.json"
]
},
{
"name": "Export PNG",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceRoot}/src",
"args": [
"--export-png",
"--shell=shell",
"--rprompt-offset=40",
"--cursor-padding=15",
"--config=${workspaceRoot}/themes/jandedobbeleer.omp.json"
]
},
{
"type": "node",
"request": "launch",
"name": "Theme export",
"cwd": "${workspaceFolder}/docs",
"program": "${workspaceRoot}/docs/export_themes.js",
"console": "integratedTerminal"
}
]
}

54
docs/docs/share.mdx Normal file
View file

@ -0,0 +1,54 @@
---
id: share
title: Share
sidebar_label: 📸 Share
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
You can export your prompt to an image which you can share online. You have the ability to align it correctly and add your name for credits too.
:::warning
Some glyphs aren't rendered correctly, that's not you but the limitations of the renderer. Depending on your config, you might have to tweak
the output a little bit.
:::
<Tabs
defaultValue="powershell"
values={[
{ label: 'powershell', value: 'powershell', },
{ label: 'others', value: 'others', },
]
}>
<TabItem value="powershell">
You can make use of the `Export-PoshImage` function to export your current theme configuration.
```powershell
Export-PoshImage -CursorPadding 50
```
There are a couple of parameters you can use to tweak the image rendering:
- `CursorPadding`: spaces to add after the cursor indication (`_`)
- `RPromptOffset`: spaces to add **before** a block that's right aligned
- `Author`: the name of the creator, added after `https://ohmyposh.dev`
</TabItem>
<TabItem value="others">
The oh-my-posh executable has the `--export-png` switch to export your current theme configuration.
```powershell
oh-my-posh --config ~/.mytheme.omp.json --export-png --cursor-padding 50
```
There are a couple of additional switches you can use to tweak the image rendering:
- `--cursor-padding`: spaces to add after the cursor indication (`_`)
- `--rprompt-offset`: spaces to add **before** a block that's right aligned
- `--author`: the name of the creator, added after `https://ohmyposh.dev`
</TabItem>
</Tabs>

View file

@ -16,102 +16,6 @@ Once you're ready to swap to a theme, follow the steps described in [🚀Install
Themes with `minimal` in their names do not require a Nerd Font. Read about [🆎Fonts][fonts] for more information.
### [Agnoster]
[![Agnoster](/img/themes/agnoster.png)][Agnoster]
### [AgnosterPlus]
[![AgnosterPlus](/img/themes/agnosterplus.png)][AgnosterPlus]
### [Aliens]
[![Aliens](/img/themes/aliens.png)][Aliens]
### [Avit]
[![Avit](/img/themes/avit.png)][Avit]
### [DarkBlood]
[![DarkBlood](/img/themes/darkblood.png)][DarkBlood]
### [Emodipt]
[![Emodipt](/img/themes/emodipt.png)][Emodipt]
### [Fish]
[![Fish](/img/themes/fish.png)][Fish]
### [Honukai]
[![Honukai](/img/themes/honukai.png)][Honukai]
### [JanDeDobbeleer]
[![JanDeDobbeleer](/img/themes/jandedobbeleer.png)][JanDeDobbeleer]
### [Lambda]
[![Lambda](/img/themes/lambda.png)][Lambda]
### [Material]
[![Material](/img/themes/material.png)][Material]
### [ParaRussel]
[![ParaRussel](/img/themes/pararussel.png)][ParaRussel]
### [Powerlevel10k_Classic]
[![Powerlevel10k_Classic](/img/themes/powerlevel10k_classic.png)][Powerlevel10k_Classic]
### [Powerlevel10k_Lean]
[![Powerlevel10k_Lean](/img/themes/powerlevel10k_lean.png)][Powerlevel10k_Lean]
### [PowerLine]
[![PowerLine](/img/themes/powerline.png)][PowerLine]
### [RobbyRussel]
[![RobbyRussel](/img/themes/robbyrussel.png)][RobbyRussel]
### [Sorin]
[![Sorin](/img/themes/sorin.png)][Sorin]
### [Star]
[![Star](/img/themes/star.png)][Star]
### [Zash]
[![Zash](/img/themes/zash.png)][Zash]
[themes]: https://github.com/JanDeDobbeleer/oh-my-posh/tree/main/themes
[fonts]: /docs/fonts
[replace-you-existing-prompt]: /docs/installation#3-replace-your-existing-prompt
[Agnoster]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/agnoster.omp.json 'Agnoster'
[AgnosterPlus]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/agnosterplus.omp.json 'AgnosterPlus'
[Aliens]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/aliens.omp.json 'Aliens'
[Avit]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/avit.omp.json 'Avit'
[DarkBlood]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/darkblood.omp.json 'DarkBlood'
[Emodipt]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/emodipt.omp.json 'Emodipt'
[Fish]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/fish.omp.json 'Fish'
[Honukai]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/honukai.omp.json 'Honukai'
[JanDeDobbeleer]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/jandedobbeleer.omp.json 'JanDeDobbeleer'
[Lambda]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/lambda.omp.json 'Lambda'
[Material]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/material.omp.json 'Material'
[ParaRussel]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/ParaRussel.omp.json 'ParaRussel'
[Powerlevel10k_Classic]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/powerlevel10k_classic.omp.json 'Powerlevel10k_Classic'
[Powerlevel10k_Lean]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/powerlevel10k_lean.omp.json 'Powerlevel10k_Lean'
[PowerLine]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/powerline.omp.json 'PowerLine'
[RobbyRussel]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/robbyrussel.omp.json 'RobbyRussel'
[Sorin]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/sorin.omp.json 'Sorin'
[Star]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/star.omp.json 'Star'
[Zash]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/zash.omp.json 'Zash'

102
docs/export_themes.js Normal file
View file

@ -0,0 +1,102 @@
//jshint esversion:8
//jshint node:true
const fs = require('fs');
const path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const themesConfigDir = "./../themes";
const themesStaticDir = "./static/img/themes";
function newThemeConfig(rpromptOffset = 40, cursorPadding = 30, author = "") {
var config = {
rpromptOffset: rpromptOffset,
cursorPadding: cursorPadding,
author: author
};
return config;
}
let themeConfigOverrrides = new Map();
themeConfigOverrrides.set('agnoster.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('agnosterplus.omp.json', newThemeConfig(80));
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('cinnamon.omp.json', newThemeConfig(40, 80));
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('huvix.omp.json', newThemeConfig(40, 70));
themeConfigOverrrides.set('jandedobbeleer.omp.json', newThemeConfig(40, 15));
themeConfigOverrrides.set('lambda.omp.json', newThemeConfig(40, 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('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('remk.omp.json', newThemeConfig(40, 40));
themeConfigOverrrides.set('robbyrussel.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('ys.omp.json', newThemeConfig(40, 100));
themeConfigOverrrides.set('zash.omp.json', newThemeConfig(40, 40));
(async () => {
const themes = await fs.promises.readdir(themesConfigDir);
let links = new Array();
for (const theme of themes) {
if (!theme.endsWith('.omp.json')) {
continue;
}
const configPath = path.join(themesConfigDir, theme);
let config = newThemeConfig();
if (themeConfigOverrrides.has(theme)) {
config = themeConfigOverrrides.get(theme);
}
let poshCommand = `oh-my-posh --config=${configPath} --shell shell --export-png`;
poshCommand += ` --rprompt-offset=${config.rpromptOffset}`;
poshCommand += ` --cursor-padding=${config.cursorPadding}`;
if (config.author !== '') {
poshCommand += ` --author=${config.author}`;
}
const { _, stderr } = await exec(poshCommand);
if (stderr !== '') {
console.error(`Unable to create image for ${theme}, please try manually`);
continue;
}
const image = theme.replace('.omp.json', '.png');
const toPath = path.join(themesStaticDir, image);
await fs.promises.rename(image, toPath);
const themeName = theme.replace('.omp.json', '');
const themeData = `
### [${themeName}]
[![${themeName}](/img/themes/${themeName}.png)][${themeName}]
`;
await fs.promises.appendFile('./docs/themes.md', themeData);
links.push(`[${themeName}]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/themes/${theme} '${themeName}'\n`);
}
for (const link of links) {
await fs.promises.appendFile('./docs/themes.md', link);
}
})();

View file

@ -5,7 +5,7 @@
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle"
"themes": "node export_themes.js"
},
"dependencies": {
"@docusaurus/core": "2.0.0-alpha.72",

View file

@ -3,7 +3,7 @@ module.exports = {
{
type: "category",
label: "Getting Started",
items: ["introduction", "upgrading", "installation", "configure", "themes", "fonts"],
items: ["introduction", "upgrading", "installation", "configure", "themes", "share", "fonts"],
},
{
type: "category",

0
docs/static/img/themes/.keep vendored Normal file
View file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View file

@ -3,8 +3,10 @@ package main
import (
"fmt"
"strings"
)
"golang.org/x/text/unicode/norm"
const (
ANSIRegex = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
)
type ansiFormats struct {
@ -44,7 +46,7 @@ func (a *ansiFormats) init(shell string) {
a.title = "%%{\x1b]0;%s\007%%}"
a.colorSingle = "%%{\x1b[%sm%%}%s%%{\x1b[0m%%}"
a.colorFull = "%%{\x1b[%sm\x1b[%sm%%}%s%%{\x1b[0m%%}"
a.colorTransparent = "%%{\x1b[%s;49m\x1b[7m%%}%s%%{\x1b[m\x1b[0m%%}"
a.colorTransparent = "%%{\x1b[%s;49m\x1b[7m%%}%s%%{\x1b[0m%%}"
a.escapeLeft = "%{"
a.escapeRight = "%}"
a.hyperlink = "%%{\x1b]8;;%s\x1b\\%%}%s%%{\x1b]8;;\x1b\\%%}"
@ -64,7 +66,7 @@ func (a *ansiFormats) init(shell string) {
a.title = "\\[\x1b]0;%s\007\\]"
a.colorSingle = "\\[\x1b[%sm\\]%s\\[\x1b[0m\\]"
a.colorFull = "\\[\x1b[%sm\x1b[%sm\\]%s\\[\x1b[0m\\]"
a.colorTransparent = "\\[\x1b[%s;49m\x1b[7m\\]%s\\[\x1b[m\x1b[0m\\]"
a.colorTransparent = "\\[\x1b[%s;49m\x1b[7m\\]%s\\[\x1b[0m\\]"
a.escapeLeft = "\\["
a.escapeRight = "\\]"
a.hyperlink = "\\[\x1b]8;;%s\x1b\\\\\\]%s\\[\x1b]8;;\x1b\\\\\\]"
@ -84,7 +86,7 @@ func (a *ansiFormats) init(shell string) {
a.title = "\x1b]0;%s\007"
a.colorSingle = "\x1b[%sm%s\x1b[0m"
a.colorFull = "\x1b[%sm\x1b[%sm%s\x1b[0m"
a.colorTransparent = "\x1b[%s;49m\x1b[7m%s\x1b[m\x1b[0m"
a.colorTransparent = "\x1b[%s;49m\x1b[7m%s\x1b[0m"
a.escapeLeft = ""
a.escapeRight = ""
a.hyperlink = "\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\"
@ -93,23 +95,28 @@ func (a *ansiFormats) init(shell string) {
a.italic = "\x1b[3m%s\x1b[23m"
a.underline = "\x1b[4m%s\x1b[24m"
a.strikethrough = "\x1b[9m%s\x1b[29m"
a.strikethrough = "\x1b[9m%s\x1b[29m"
}
}
func (a *ansiFormats) lenWithoutANSI(text string) int {
rANSI := "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
stripped := replaceAllString(rANSI, text, "")
if len(text) == 0 {
return 0
}
// replace hyperlinks
matches := findAllNamedRegexMatch(`(?P<STR>\x1b]8;;file:\/\/(.+)\x1b\\(?P<URL>.+)\x1b]8;;\x1b\\)`, text)
for _, match := range matches {
text = strings.ReplaceAll(text, match[STR], match[URL])
}
// replace console title
matches = findAllNamedRegexMatch(`(?P<STR>\x1b\]0;(.+)\007)`, text)
for _, match := range matches {
text = strings.ReplaceAll(text, match[STR], "")
}
stripped := replaceAllString(ANSIRegex, text, "")
stripped = strings.ReplaceAll(stripped, a.escapeLeft, "")
stripped = strings.ReplaceAll(stripped, a.escapeRight, "")
var i norm.Iter
i.InitString(norm.NFD, stripped)
var count int
for !i.Done() {
i.Next()
count++
}
return count
runeText := []rune(stripped)
return len(runeText)
}
func (a *ansiFormats) generateHyperlink(text string) string {

View file

@ -129,7 +129,7 @@ func (e *engine) setStringValues(segments []*Segment) {
}
}
func (e *engine) render() {
func (e *engine) render() string {
for _, block := range e.config.Blocks {
// if line break, append a line break
switch block.Type {
@ -161,19 +161,18 @@ func (e *engine) render() {
}
if !e.config.OSC99 {
e.print()
return
return e.print()
}
cwd := e.env.getcwd()
if e.env.isWsl() {
cwd, _ = e.env.runCommand("wslpath", "-m", cwd)
}
e.renderer.osc99(cwd)
e.print()
return e.print()
}
// debug will loop through your config file and output the timings for each segments
func (e *engine) debug() {
func (e *engine) debug() string {
var segmentTimings []SegmentTiming
largestSegmentNameLength := 0
e.renderer.write("\n\x1b[1mHere are the timings of segments in your prompt:\x1b[0m\n\n")
@ -236,19 +235,19 @@ func (e *engine) debug() {
segmentName := fmt.Sprintf("%s(%t)", segment.name, segment.enabled)
e.renderer.write(fmt.Sprintf("%-*s - %3d ms - %s\n", largestSegmentNameLength, segmentName, duration, segment.stringValue))
}
fmt.Print(e.renderer.string())
return e.renderer.string()
}
func (e *engine) print() {
func (e *engine) print() string {
switch e.env.getShellName() {
case zsh:
if *e.env.getArgs().Eval {
// escape double quotes contained in the prompt
fmt.Printf("PS1=\"%s\"", strings.ReplaceAll(e.renderer.string(), "\"", "\"\""))
fmt.Printf("\nRPROMPT=\"%s\"", e.rprompt)
return
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.renderer.string(), "\"", "\"\""))
prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt)
return prompt
}
case pwsh, powershell5, bash:
case pwsh, powershell5, bash, shelly:
if e.rprompt != "" {
e.renderer.saveCursorPosition()
e.renderer.carriageForward()
@ -257,7 +256,7 @@ func (e *engine) print() {
e.renderer.restoreCursorPosition()
}
}
fmt.Print(e.renderer.string())
return e.renderer.string()
}
func (e *engine) resetBlock() {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -11,7 +11,10 @@ require (
github.com/alecthomas/colour v0.1.0 // indirect
github.com/alecthomas/repr v0.0.0-20210301060118-828286944d6a // indirect
github.com/distatus/battery v0.10.0
github.com/esimov/stackblur-go v1.0.0
github.com/fogleman/gg v1.3.0
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.2.0 // indirect
github.com/gookit/color v1.3.8
github.com/gookit/config/v2 v2.0.22
@ -24,6 +27,7 @@ require (
github.com/stretchr/testify v1.7.0
github.com/tklauser/go-sysconf v0.3.5 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
golang.org/x/sys v0.0.0-20210324051608-47abb6519492
golang.org/x/text v0.3.5
howett.net/plist v0.0.0-20201203080718-1454fab16a06 // indirect

View file

@ -23,9 +23,15 @@ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJE
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/esimov/stackblur-go v1.0.0 h1:PuXWrQ16VIh6Di+tn3CpKAyzkIIECo9DPGVecrzyDGc=
github.com/esimov/stackblur-go v1.0.0/go.mod h1:a3zzeKuJKUpCcReHmEsuPaEnq42D2b/bHoCI8UjIuMY=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -101,6 +107,8 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=

444
src/image.go Normal file
View file

@ -0,0 +1,444 @@
// Copyright © 2020 The Homeport Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// https://github.com/homeport/termshot
package main
import (
_ "embed"
"fmt"
"math"
"strconv"
"strings"
"github.com/esimov/stackblur-go"
"github.com/fogleman/gg"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
)
const (
red = "#ED655A"
yellow = "#E1C04C"
green = "#71BD47"
// known ansi sequences
FG = "FG"
BG = "BG"
STR = "STR"
URL = "URL"
invertedColor = "inverted"
invertedColorSingle = "invertedsingle"
fullColor = "full"
foreground = "foreground"
reset = "reset"
bold = "bold"
boldReset = "boldr"
italic = "italic"
italicReset = "italicr"
underline = "underline"
underlineReset = "underliner"
strikethrough = "strikethrough"
strikethroughReset = "strikethroughr"
color16 = "color16"
left = "left"
osc99 = "osc99"
lineChange = "linechange"
title = "title"
link = "link"
)
//go:embed font/VictorMono-Bold.ttf
var victorMonoBold []byte
//go:embed font/VictorMono-Regular.ttf
var victorMonoRegular []byte
//go:embed font/VictorMono-Italic.ttf
var victorMonoItalic []byte
type RGB struct {
r int
g int
b int
}
func NewRGBColor(ansiColor string) *RGB {
colors := strings.Split(ansiColor, ";")
r, _ := strconv.Atoi(colors[0])
g, _ := strconv.Atoi(colors[1])
b, _ := strconv.Atoi(colors[2])
return &RGB{
r: r,
g: g,
b: b,
}
}
type ImageRenderer struct {
ansiString string
author string
formats *ansiFormats
factor float64
columns int
rows int
defaultForegroundColor *RGB
defaultBackgroundColor *RGB
shadowBaseColor string
shadowRadius uint8
shadowOffsetX float64
shadowOffsetY float64
padding float64
margin float64
regular font.Face
bold font.Face
italic font.Face
lineSpacing float64
// canvas switches
style string
backgroundColor *RGB
foregroundColor *RGB
ansiSequenceRegexMap map[string]string
rPromptOffset int
cursorPadding int
}
func (ir *ImageRenderer) init() {
f := 2.0
ir.cleanContent()
fontRegular, _ := truetype.Parse(victorMonoRegular)
fontBold, _ := truetype.Parse(victorMonoBold)
fontItalic, _ := truetype.Parse(victorMonoItalic)
fontFaceOptions := &truetype.Options{Size: f * 12, DPI: 144}
ir.defaultForegroundColor = &RGB{255, 255, 255}
ir.defaultBackgroundColor = &RGB{21, 21, 21}
ir.factor = f
ir.columns = 80
ir.rows = 25
ir.margin = f * 48
ir.padding = f * 24
ir.shadowBaseColor = "#10101066"
ir.shadowRadius = uint8(math.Min(f*16, 255))
ir.shadowOffsetX = f * 16
ir.shadowOffsetY = f * 16
ir.regular = truetype.NewFace(fontRegular, fontFaceOptions)
ir.bold = truetype.NewFace(fontBold, fontFaceOptions)
ir.italic = truetype.NewFace(fontItalic, fontFaceOptions)
ir.lineSpacing = 1.2
ir.ansiSequenceRegexMap = map[string]string{
invertedColor: `^(?P<STR>(\x1b\[38;2;(?P<BG>(\d+;?){3});49m){1}(\x1b\[7m))`,
invertedColorSingle: `^(?P<STR>\x1b\[(?P<BG>\d{2,3});49m\x1b\[7m)`,
fullColor: `^(?P<STR>(\x1b\[48;2;(?P<BG>(\d+;?){3})m)(\x1b\[38;2;(?P<FG>(\d+;?){3})m))`,
foreground: `^(?P<STR>(\x1b\[38;2;(?P<FG>(\d+;?){3})m))`,
reset: `^(?P<STR>\x1b\[0m)`,
bold: `^(?P<STR>\x1b\[1m)`,
boldReset: `^(?P<STR>\x1b\[22m)`,
italic: `^(?P<STR>\x1b\[3m)`,
italicReset: `^(?P<STR>\x1b\[23m)`,
underline: `^(?P<STR>\x1b\[4m)`,
underlineReset: `^(?P<STR>\x1b\[24m)`,
strikethrough: `^(?P<STR>\x1b\[9m)`,
strikethroughReset: `^(?P<STR>\x1b\[29m)`,
color16: `^(?P<STR>\x1b\[(?P<FG>\d{2,3})m)`,
left: `^(?P<STR>\x1b\[(\d{1,3})D)`,
osc99: `^(?P<STR>\x1b\]9;9;(.+)\x1b\\)`,
lineChange: `^(?P<STR>\x1b\[(\d)[FB])`,
title: `^(?P<STR>\x1b\]0;(.+)\007)`,
link: `^(?P<STR>\x1b]8;;file:\/\/(.+)\x1b\\(?P<URL>.+)\x1b]8;;\x1b\\)`,
}
}
func (ir *ImageRenderer) fontHeight() float64 {
return float64(ir.regular.Metrics().Height >> 6)
}
func (ir *ImageRenderer) calculateWidth() int {
longest := 0
for _, line := range strings.Split(ir.ansiString, "\n") {
length := ir.formats.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[1F", "")
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\x1b8", "")
// 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[1mhttps://ohmyposh.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(path string) error {
var f = func(value float64) float64 { return ir.factor * value }
var (
corner = f(6)
radius = f(9)
distance = f(25)
)
contentWidth, contentHeight := ir.measureContent()
// Make sure the output window is big enough in case no content or very few
// content will be rendered
contentWidth = math.Max(contentWidth, 3*distance+3*radius)
marginX, marginY := ir.margin, ir.margin
paddingX, paddingY := ir.padding, ir.padding
xOffset := marginX
yOffset := marginY
titleOffset := f(40)
width := contentWidth + 2*marginX + 2*paddingX
height := contentHeight + 2*marginY + 2*paddingY + titleOffset
dc := gg.NewContext(int(width), int(height))
xOffset -= ir.shadowOffsetX / 2
yOffset -= ir.shadowOffsetY / 2
bc := gg.NewContext(int(width), int(height))
bc.DrawRoundedRectangle(xOffset+ir.shadowOffsetX, yOffset+ir.shadowOffsetY, width-2*marginX, height-2*marginY, corner)
bc.SetHexColor(ir.shadowBaseColor)
bc.Fill()
var done = make(chan struct{}, ir.shadowRadius)
shadow := stackblur.Process(
bc.Image(),
uint32(width),
uint32(height),
uint32(ir.shadowRadius),
done,
)
<-done
dc.DrawImage(shadow, 0, 0)
// Draw rounded rectangle with outline and three button to produce the
// impression of a window with controls and a content area
dc.DrawRoundedRectangle(xOffset, yOffset, width-2*marginX, height-2*marginY, corner)
dc.SetHexColor("#151515")
dc.Fill()
dc.DrawRoundedRectangle(xOffset, yOffset, width-2*marginX, height-2*marginY, corner)
dc.SetHexColor("#404040")
dc.SetLineWidth(f(1))
dc.Stroke()
for i, color := range []string{red, yellow, green} {
dc.DrawCircle(xOffset+paddingX+float64(i)*distance+f(4), yOffset+paddingY+f(4), radius)
dc.SetHexColor(color)
dc.Fill()
}
// Apply the actual text into the prepared content area of the window
var x, y float64 = xOffset + paddingX, yOffset + paddingY + titleOffset + ir.fontHeight()
for len(ir.ansiString) != 0 {
if !ir.shouldPrint() {
continue
}
runes := []rune(ir.ansiString)
str := string(runes[0:1])
ir.ansiString = string(runes[1:])
switch ir.style {
case bold:
dc.SetFontFace(ir.bold)
case italic:
dc.SetFontFace(ir.italic)
default:
dc.SetFontFace(ir.regular)
}
w, h := dc.MeasureString(str)
if ir.backgroundColor != nil {
dc.SetRGB255(ir.backgroundColor.r, ir.backgroundColor.g, ir.backgroundColor.b)
dc.DrawRectangle(x, y-h, w, h+12)
dc.Fill()
}
if ir.foregroundColor != nil {
dc.SetRGB255(ir.foregroundColor.r, ir.foregroundColor.g, ir.foregroundColor.b)
} else {
dc.SetRGB255(ir.defaultForegroundColor.r, ir.defaultForegroundColor.g, ir.defaultForegroundColor.b)
}
if str == "\n" {
x = xOffset + paddingX
y += h * ir.lineSpacing
continue
}
dc.DrawString(str, x, y)
if ir.style == underline {
dc.DrawLine(x, y+f(4), x+w, y+f(4))
dc.SetLineWidth(f(1))
dc.Stroke()
}
x += w
}
return dc.SavePNG(path)
}
func (ir *ImageRenderer) shouldPrint() bool {
for sequence, regex := range ir.ansiSequenceRegexMap {
match := findNamedRegexMatch(regex, ir.ansiString)
if len(match) == 0 {
continue
}
ir.ansiString = strings.TrimPrefix(ir.ansiString, match[STR])
switch sequence {
case invertedColor:
ir.foregroundColor = ir.defaultBackgroundColor
ir.backgroundColor = NewRGBColor(match[BG])
return false
case invertedColorSingle:
ir.foregroundColor = ir.defaultBackgroundColor
color, _ := strconv.Atoi(match[BG])
color += 10
ir.setBase16Color(fmt.Sprint(color))
return false
case fullColor:
ir.foregroundColor = NewRGBColor(match[FG])
ir.backgroundColor = NewRGBColor(match[BG])
return false
case foreground:
ir.foregroundColor = NewRGBColor(match[FG])
return false
case reset:
ir.foregroundColor = ir.defaultForegroundColor
ir.backgroundColor = nil
return false
case bold, italic, underline:
ir.style = sequence
return false
case boldReset, italicReset, underlineReset:
ir.style = ""
return false
case strikethrough, strikethroughReset, left, osc99, lineChange, title:
return false
case color16:
ir.setBase16Color(match[FG])
return false
case link:
ir.ansiString = match[URL] + ir.ansiString
}
}
return true
}
func (ir *ImageRenderer) setBase16Color(colorStr string) {
color := ir.defaultForegroundColor
colorInt, err := strconv.Atoi(colorStr)
if err != nil {
ir.foregroundColor = color
}
switch colorInt {
case 30, 40: // Black
color = &RGB{1, 1, 1}
case 31, 41: // Red
color = &RGB{222, 56, 43}
case 32, 42: // Green
color = &RGB{57, 181, 74}
case 33, 43: // Yellow
color = &RGB{255, 199, 6}
case 34, 44: // Blue
color = &RGB{0, 111, 184}
case 35, 45: // Magenta
color = &RGB{118, 38, 113}
case 36, 46: // Cyan
color = &RGB{44, 181, 233}
case 37, 47: // White
color = &RGB{204, 204, 204}
case 90, 100: // Bright Black (Gray)
color = &RGB{128, 128, 128}
case 91, 101: // Bright Red
color = &RGB{255, 0, 0}
case 92, 102: // Bright Green
color = &RGB{0, 255, 0}
case 93, 103: // Bright Yellow
color = &RGB{255, 255, 0}
case 94, 104: // Bright Blue
color = &RGB{0, 0, 255}
case 95, 105: // Bright Magenta
color = &RGB{255, 0, 255}
case 96, 106: // Bright Cyan
color = &RGB{101, 194, 205}
case 97, 107: // Bright White
color = &RGB{255, 255, 255}
}
if colorInt < 40 || (colorInt >= 90 && colorInt < 100) {
ir.foregroundColor = color
return
}
ir.backgroundColor = color
}

40
src/image_test.go Normal file
View file

@ -0,0 +1,40 @@
package main
import (
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func runImageTest(content string) error {
poshImagePath := "ohmyposh.png"
file, err := ioutil.TempFile("", poshImagePath)
if err != nil {
return err
}
defer os.Remove(file.Name())
formats := &ansiFormats{}
formats.init(shelly)
image := &ImageRenderer{
ansiString: content,
formats: formats,
}
image.init()
err = image.SavePNG(poshImagePath)
return err
}
func TestStringImageFileWithText(t *testing.T) {
err := runImageTest("foobar")
assert.NoError(t, err)
}
func TestStringImageFileWithANSI(t *testing.T) {
prompt := ` oh-my-posh
 main ~4 -8 ?7 
 `
err := runImageTest(prompt)
assert.NoError(t, err)
}

View file

@ -115,3 +115,28 @@ function global:Export-PoshTheme {
$configString = @(&$omp --config="$config" --config-format="$Format" --print-config 2>&1)
[IO.File]::WriteAllLines($FilePath, $configString)
}
function global:Export-PoshImage {
param(
[Parameter(Mandatory = $false)]
[int]
$RPromptOffset = 40,
[Parameter(Mandatory = $false)]
[int]
$CursorPadding = 30,
[Parameter(Mandatory = $false)]
[string]
$Author
)
if ($Author) {
$Author = "--author=$Author"
}
$omp = "::OMP::"
$config = $global:PoshSettings.Theme
$cleanPWD = $PWD.ProviderPath.TrimEnd("\")
$cleanPSWD = $PWD.ToString().TrimEnd("\")
$standardOut = @(&$omp --config="$config" --pwd="$cleanPWD" --pswd="$cleanPSWD" --export-png --rprompt-offset="$RPromptOffset" --cursor-padding="$CursorPadding" $Author 2>&1)
$standardOut -join "`n"
}

View file

@ -33,6 +33,7 @@ const (
pwsh = "pwsh"
fish = "fish"
powershell5 = "powershell"
shelly = "shell"
)
type args struct {
@ -51,6 +52,10 @@ type args struct {
Eval *bool
Init *bool
PrintInit *bool
ExportPNG *bool
Author *string
CursorPadding *int
RPromptOffset *int
}
func main() {
@ -115,6 +120,22 @@ func main() {
"print-init",
false,
"Print the shell initialization script"),
ExportPNG: flag.Bool(
"export-png",
false,
"Create an image based on the current configuration"),
Author: flag.String(
"author",
"",
"Add the author to the exported image using --export-img"),
CursorPadding: flag.Int(
"cursor-padding",
30,
"Pad the cursor with x when using --export-img"),
RPromptOffset: flag.Int(
"rprompt-offset",
40,
"Offset the right prompt with x when using --export-img"),
}
flag.Parse()
env := &environment{}
@ -170,10 +191,27 @@ func main() {
}
if *args.Debug {
engine.debug()
fmt.Print(engine.debug())
return
}
engine.render()
prompt := engine.render()
if !*args.ExportPNG {
fmt.Print(prompt)
return
}
imageCreator := &ImageRenderer{
ansiString: prompt,
author: *args.Author,
cursorPadding: *args.CursorPadding,
rPromptOffset: *args.RPromptOffset,
formats: formats,
}
imageCreator.init()
match := findNamedRegexMatch(`.*(\/|\\)(?P<STR>.+).omp.(json|yaml|toml)`, *args.Config)
err := imageCreator.SavePNG(fmt.Sprintf("%s.png", match[STR]))
if err != nil {
fmt.Print(err.Error())
}
}
func initShell(shell, configFile string) string {

View file

@ -16,7 +16,7 @@
"user_info_separator": "",
"display_host": false,
"prefix": "",
"postfix": ""
"postfix": " "
}
},
{
@ -42,8 +42,8 @@
"properties": {
"branch_icon": "",
"display_status": false,
"prefix": "git(",
"postfix": ")"
"prefix": " git(",
"postfix": ") "
}
},
{
@ -55,8 +55,8 @@
"background": "lightMagenta",
"properties": {
"time_format": "15:04",
"prefix": "",
"postfix": ""
"prefix": " ",
"postfix": " "
}
}
]

View file

@ -1,74 +1,63 @@
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"final_space":true,
"osc99":true,
"console_title":true,
"console_title_style":"template",
"console_title_template":"{{.Folder}}{{if .Root}} :: root{{end}} :: {{.Shell}}",
"blocks":[
{
"type":"prompt",
"alignment":"left",
"horizontal_offset":0,
"vertical_offset":1,
"segments":[
{
"type": "root",
"style": "plain",
"foreground": "yellow",
"properties": {
"root_icon": ""
}
},
{
"type":"path",
"style":"diamond",
"powerline_symbol":"",
"invert_powerline":false,
"foreground":"black",
"foreground_templates":null,
"background":"lightBlue",
"background_templates":null,
"leading_diamond":"",
"trailing_diamond":"",
"properties":{
"style":"mixed"
}
},
{
"type":"git",
"style":"powerline",
"powerline_symbol":"",
"invert_powerline":false,
"foreground":"black",
"foreground_templates":null,
"background":"green",
"background_templates":null,
"leading_diamond":"",
"trailing_diamond":"",
"properties":{
"display_status_detail": true,
"branch_icon":" ",
"branch_identical_icon":"≡",
"branch_ahead_icon":"↑",
"branch_behind_icon":"↓",
"branch_gone":"≢",
"local_working_icon":"",
"local_staged_icon":"",
"stash_count_icon":"",
"commit_icon": "▷ ",
"tag_icon": "▶ ",
"rebase_icon": "Ɫ ",
"cherry_pick_icon": "✓ ",
"merge_icon": "◴ ",
"no_commits_icon": "[no commits]",
"status_separator_icon": " │",
"status_colors_enabled": true,
"color_background": true,
"local_changes_color": "yellow"
}
}
]
}
]
}
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"final_space": true,
"osc99": true,
"console_title": true,
"console_title_style": "template",
"console_title_template": "{{.Folder}}{{if .Root}} :: root{{end}} :: {{.Shell}}",
"blocks": [
{
"type": "prompt",
"alignment": "left",
"segments": [
{
"type": "root",
"style": "plain",
"foreground": "yellow",
"properties": {
"root_icon": ""
}
},
{
"type": "path",
"style": "diamond",
"foreground": "black",
"background": "lightBlue",
"leading_diamond": "",
"trailing_diamond": "",
"properties": {
"style": "mixed"
}
},
{
"type": "git",
"style": "powerline",
"powerline_symbol": "",
"foreground": "black",
"background": "green",
"properties": {
"display_status_detail": true,
"branch_icon": " ",
"branch_identical_icon": "≡",
"branch_ahead_icon": "↑",
"branch_behind_icon": "↓",
"branch_gone": "≢",
"local_working_icon": "",
"local_staged_icon": "",
"stash_count_icon": "",
"commit_icon": "▷ ",
"tag_icon": "▶ ",
"rebase_icon": "Ɫ ",
"cherry_pick_icon": "✓ ",
"merge_icon": "◴ ",
"no_commits_icon": "[no commits]",
"status_separator_icon": " │",
"status_colors_enabled": true,
"color_background": true,
"local_changes_color": "yellow"
}
}
]
}
]
}

View file

@ -32,6 +32,5 @@
}
]
}
],
"final_space": true
]
}

View file

@ -11,6 +11,7 @@
"foreground": "#ffffff",
"properties": {
"prefix": "",
"postfix": "",
"text": "<#C591E8>\u276F</><#69FF94>\u276F</>"
}
},
@ -52,6 +53,5 @@
}
]
}
],
"final_space": true
]
}

View file

@ -8,7 +8,10 @@
{
"type": "os",
"style": "plain",
"foreground": "#3A86FF"
"foreground": "#3A86FF",
"properties": {
"prefix": ""
}
},
{
"type": "session",
@ -101,6 +104,5 @@
}
]
}
],
"final_space": true
]
}

View file

@ -19,6 +19,7 @@
"style": "plain",
"foreground": "#56B6C2",
"properties": {
"prefix": "",
"style": "folder"
}
},
@ -77,6 +78,5 @@
}
]
}
],
"final_space": true
]
}

View file

@ -1,91 +1,92 @@
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"console_title": true,
"console_title_style": "template",
"console_title_template": "{{if .Root}}(Admin){{end}} {{.Path}}",
"blocks": [
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"console_title": true,
"console_title_style": "template",
"console_title_template": "{{if .Root}}(Admin){{end}} {{.Path}}",
"blocks": [
{
"type": "newline"
},
{
"type": "prompt",
"alignment": "left",
"segments": [
{
"type":"newline"
"type": "root",
"style": "plain",
"foreground": "#BF616A",
"properties": {
"prefix": "",
"root_icon": "Admin"
}
},
{
"type":"prompt",
"alignment": "left",
"segments": [
{
"type":"root",
"style":"plain",
"foreground": "#BF616A",
"properties": {
"prefix": "",
"root_icon": "Admin"
}
},
{
"type":"path",
"style":"plain",
"foreground": "#81A1C1",
"properties": {
"prefix": "",
"style": "full"
}
}
]
},
{
"type":"prompt",
"alignment": "left",
"segments": [
{
"type":"git",
"style":"plain",
"foreground": "#6C6C6C",
"properties": {
"display_stash_count": true,
"display_status": true,
"display_status_detail": true,
"display_upstream_icon": true,
"branch_icon": "",
"github_icon": "",
"branch_ahead_icon": "<#88C0D0>\u21e1 </>",
"branch_behind_icon": "<#88C0D0>\u21e3 </>",
"local_working_icon": "<#FFAFD7>\u002a</>"
}
}
]
},
{
"type":"prompt",
"alignment": "left",
"segments": [
{
"type":"executiontime",
"style":"plain",
"foreground": "#A3BE8C",
"properties": {
"style": "austin"
}
}
]
},
{
"type":"newline"
},
{
"alignment": "left",
"type":"prompt",
"segments": [
{
"type":"exit",
"style":"plain",
"foreground": "#B48EAD",
"properties": {
"prefix": "\u276f",
"display_exit_code": false,
"error_color": "#BF616A",
"always_enabled": true
}
}
]
"type": "path",
"style": "plain",
"foreground": "#81A1C1",
"properties": {
"prefix": "",
"style": "full"
}
}
]
]
},
{
"type": "prompt",
"alignment": "left",
"segments": [
{
"type": "git",
"style": "plain",
"foreground": "#6C6C6C",
"properties": {
"prefix": "",
"display_stash_count": true,
"display_status": true,
"display_status_detail": true,
"display_upstream_icon": true,
"branch_icon": "",
"github_icon": "",
"branch_ahead_icon": "<#88C0D0>\u21e1 </>",
"branch_behind_icon": "<#88C0D0>\u21e3 </>",
"local_working_icon": "<#FFAFD7>\u002a</>"
}
}
]
},
{
"type": "prompt",
"alignment": "left",
"segments": [
{
"type": "executiontime",
"style": "plain",
"foreground": "#A3BE8C",
"properties": {
"style": "austin"
}
}
]
},
{
"type": "newline"
},
{
"alignment": "left",
"type": "prompt",
"segments": [
{
"type": "exit",
"style": "plain",
"foreground": "#B48EAD",
"properties": {
"prefix": "\u276f",
"display_exit_code": false,
"error_color": "#BF616A",
"always_enabled": true
}
}
]
}
]
}

View file

@ -13,6 +13,7 @@
"leading_diamond": "\uE0B6",
"trailing_diamond": "",
"properties": {
"prefix": "",
"display_host":false
}
},

View file

@ -1,3 +1,4 @@
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"blocks": [

View file

@ -33,7 +33,7 @@
"wsl": "\ue712",
"wsl_separator": " on ",
"windows": "\ue70f",
"postfix": "<#7a7a7a> \ue0b1</>"
"postfix": "<#7a7a7a> \uE0b1</>"
}
},
{
@ -177,7 +177,9 @@
"style": "plain",
"foreground": "#7a7a7a",
"properties": {
"text": "~#@\u276F"
"text": "~#@\u276F",
"prefix": "",
"postfix": ""
}
}
]

View file

@ -25,7 +25,8 @@
"style": "plain",
"foreground": "#0973C0",
"properties": {
"style": "full"
"style": "full",
"prefix": ""
}
},
{