feat(palettes): allow color reuse using palette

This commit is contained in:
Jan De Dobbeleer 2024-09-02 11:52:44 +02:00 committed by Jan De Dobbeleer
parent e5617c6659
commit b67756001f
5 changed files with 212 additions and 141 deletions

View file

@ -65,16 +65,31 @@ func (cfg *Config) getPalette() color.Palette {
if cfg.Palettes == nil { if cfg.Palettes == nil {
return cfg.Palette return cfg.Palette
} }
tmpl := &template.Text{ tmpl := &template.Text{
Template: cfg.Palettes.Template, Template: cfg.Palettes.Template,
Env: cfg.env, Env: cfg.env,
} }
if palette, err := tmpl.Render(); err == nil {
if p, ok := cfg.Palettes.List[palette]; ok { key, err := tmpl.Render()
return p if err != nil {
} return cfg.Palette
} }
return cfg.Palette
palette, ok := cfg.Palettes.List[key]
if !ok {
return cfg.Palette
}
for key, color := range cfg.Palette {
if _, ok := palette[key]; ok {
continue
}
palette[key] = color
}
return palette
} }
func (cfg *Config) Features() shell.Features { func (cfg *Config) Features() shell.Features {

View file

@ -17,6 +17,7 @@ func TestGetPalette(t *testing.T) {
"red": "#ff0000", "red": "#ff0000",
"blue": "#0000ff", "blue": "#0000ff",
} }
cases := []struct { cases := []struct {
Case string Case string
Palettes *color.Palettes Palettes *color.Palettes
@ -69,7 +70,26 @@ func TestGetPalette(t *testing.T) {
Case: "no palettes", Case: "no palettes",
ExpectedPalette: nil, ExpectedPalette: nil,
}, },
{
Case: "match, with override",
Palettes: &color.Palettes{
Template: "{{ .Shell }}",
List: map[string]color.Palette{
"bash": {
"red": "#ff0001",
"yellow": "#ffff00",
},
},
},
Palette: palette,
ExpectedPalette: color.Palette{
"red": "#ff0001",
"blue": "#0000ff",
"yellow": "#ffff00",
},
},
} }
for _, tc := range cases { for _, tc := range cases {
env := &mock.Environment{} env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{ env.On("TemplateCache").Return(&cache.Template{

View file

@ -16,6 +16,8 @@ using an old and new version of Oh My Posh at the same time. Who does that you m
using WSL on Windows, or VMs on any system while sharing the same configuration cross installation. using WSL on Windows, or VMs on any system while sharing the same configuration cross installation.
Not the main use-case, but also not exotic either. Not the main use-case, but also not exotic either.
<!-- truncate -->
## What happened? ## What happened?
In the beginning of the year, after what has been quite the struggle, I was finally able to deliver **the most important In the beginning of the year, after what has been quite the struggle, I was finally able to deliver **the most important

View file

@ -4,6 +4,8 @@ title: Colors
sidebar_label: Colors sidebar_label: Colors
--- ---
import Config from "@site/src/components/Config.js";
## Standard colors ## Standard colors
Oh My Posh supports multiple different color references, being: Oh My Posh supports multiple different color references, being:
@ -37,19 +39,19 @@ section in the documentation. The general template properties are listed [here][
The following sample is based on the [AWS Segment][aws]. The following sample is based on the [AWS Segment][aws].
```json <Config
{ data={{
"type": "aws", type: "aws",
"style": "powerline", style: "powerline",
"powerline_symbol": "\uE0B0", powerline_symbol: "\uE0B0",
"foreground": "#ffffff", foreground: "#ffffff",
"background": "#111111", background: "#111111",
"foreground_templates": [ foreground_templates: [
"{{if contains \"default\" .Profile}}#FFA400{{end}}", '{{if contains "default" .Profile}}#FFA400{{end}}',
"{{if contains \"jan\" .Profile}}#f1184c{{end}}" '{{if contains "jan" .Profile}}#f1184c{{end}}',
] ],
} }}
``` />
The logic is as follows: when `foreground_templates` contains an array, we will check every template line until there's The logic is as follows: when `foreground_templates` contains an array, we will check every template line until there's
one that returns a non-empty string. So, when the contents of `.Profile` contain the word `default`, the first template one that returns a non-empty string. So, when the contents of `.Profile` contain the word `default`, the first template
@ -66,53 +68,55 @@ Anything between the color start `<#FF479C>` and end `</>` will be colored accor
If you want to print a colored bracket that isn't the same as the segment's `foreground`, you can If you want to print a colored bracket that isn't the same as the segment's `foreground`, you can
do so like this: do so like this:
```json <Config
"template": "<#CB4B16>┏[</>", data={{
``` template: "<#CB4B16>┏[</>",
}}
/>
If you also wanted to change the background color in the previous command, you would do so like this: If you also wanted to change the background color in the previous command, you would do so like this:
```json <Config
"template": "<#CB4B16,#FFFFFF>┏[</>", data={{
``` template: "<#CB4B16,#FFFFFF>┏[</>",
}}
/>
To change _only_ the background color, just omit the first color from the above string: To change _only_ the background color, just omit the first color from the above string:
```json <Config
"template": "<,#FFFFFF>┏[</>", data={{
``` template: "<,#FFFFFF>┏[</>",
}}
/>
## Palette ## Palette
If your theme defined the Palette, you can use the _Palette reference_ `p:<palette key>` in places where the If your configuration defined the Palette, you can use the _Palette reference_ `p:<palette key>` in places where the
**Standard color** is expected. **Standard color** is expected.
### Defining a Palette ### Defining a Palette
Palette is a set of named **Standard colors**. To use a Palette, define a `"palette"` object Palette is a set of named **Standard colors**. To use a Palette, define a `"palette"` object
at the top level of your theme: at the top level of your configuration:
```json <Config
{ data={{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", palette: {
"palette": { "git-foreground": "#193549",
"git-foreground": "#193549", git: "#FFFB38",
"git": "#FFFB38", "git-modified": "#FF9248",
"git-modified": "#FF9248", "git-diverged": "#FF4500",
"git-diverged": "#FF4500", "git-ahead": "#B388FF",
"git-ahead": "#B388FF", "git-behind": "#B388FF",
"git-behind": "#B388FF", red: "#FF0000",
"red": "#FF0000", green: "#00FF00",
"green": "#00FF00", blue: "#0000FF",
"blue": "#0000FF", white: "#FFFFFF",
"white": "#FFFFFF", black: "#111111",
"black": "#111111"
}, },
"blocks": { }}
... />
}
}
```
Color names (palette keys) can have any string value, so be creative. Color names (palette keys) can have any string value, so be creative.
Color values, on the other hand, should adhere to the **Standard color** format. Color values, on the other hand, should adhere to the **Standard color** format.
@ -123,48 +127,47 @@ You can now use _Palette references_ in any [Segment's][segment] `foreground`, `
`background`, `background_templates` properties, and other config properties that expect **Standard color** value. `background`, `background_templates` properties, and other config properties that expect **Standard color** value.
_Palette reference_ format is `p:<palette key>`. Take a look at the [Git][git] segment using _Palette references_: _Palette reference_ format is `p:<palette key>`. Take a look at the [Git][git] segment using _Palette references_:
```json <Config
{ data={{
"type": "git", type: "git",
"style": "powerline", style: "powerline",
"powerline_symbol": "\uE0B0", powerline_symbol: "\uE0B0",
"foreground": "p:git-foreground", foreground: "p:git-foreground",
"background": "p:git", background: "p:git",
"background_templates": [ background_templates: [
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:git-modified{{ end }}", "{{ if or (.Working.Changed) (.Staging.Changed) }}p:git-modified{{ end }}",
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:git-diverged{{ end }}", "{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:git-diverged{{ end }}",
"{{ if gt .Ahead 0 }}p:git-ahead{{ end }}", "{{ if gt .Ahead 0 }}p:git-ahead{{ end }}",
"{{ if gt .Behind 0 }}p:git-behind{{ end }}" "{{ if gt .Behind 0 }}p:git-behind{{ end }}",
], ],
... }}
}, />
```
Having all of the colors defined in one place allows you to import existing color themes (usually with slight Having all of the colors defined in one place allows you to import existing color configurations (usually with slight
tweaking to adhere to the format), easily change colors of multiple segments at once, and have a more tweaking to adhere to the format), easily change colors of multiple segments at once, and have a more
organized theme overall. Be creative! organized configuration overall. Be creative!
### _Palette references_ and **Standard colors** ### _Palette references_ and **Standard colors**
Using Palette does not interfere with using **Standard colors** in your theme. You can still use **Standard colors** Using Palette does not interfere with using **Standard colors** in your configuration. You can still use **Standard colors**
everywhere. This can be useful if you want to use a specific color for a single segment element, or in a everywhere. This can be useful if you want to use a specific color for a single segment element, or in a
_Color override_ ([Battery segment][battery]): _Color override_ ([Battery segment][battery]):
```json <Config
{ data={{
"type": "battery", type: "battery",
"style": "powerline", style: "powerline",
"invert_powerline": true, invert_powerline: true,
"powerline_symbol": "\uE0B2", powerline_symbol: "\uE0B2",
"foreground": "p:white", foreground: "p:white",
"background": "p:black", background: "p:black",
"properties": { properties: {
"discharging_icon": "<#ffa500>-</> ", discharging_icon: "<#ffa500>-</> ",
"charging_icon": "+ ", charging_icon: "+ ",
"charged_icon": "* ", charged_icon: "* ",
} },
}, }}
``` />
### Handling of invalid references ### Handling of invalid references
@ -178,18 +181,16 @@ Palette allows for recursive _Palette reference_ resolution. You can use a _Pale
value in Palette. This allows you to define named colors, and use references to those colors as Palette values. value in Palette. This allows you to define named colors, and use references to those colors as Palette values.
For example, `p:foreground` and `p:background` will be correctly set to "#CAF0F80" and "#023E8A": For example, `p:foreground` and `p:background` will be correctly set to "#CAF0F80" and "#023E8A":
```json <Config
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", data={{
"palette": { palette: {
"light-blue": "#CAF0F8", "light-blue": "#CAF0F8",
"dark-blue": "#023E8A", "dark-blue": "#023E8A",
"foreground": "p:light-blue", foreground: "p:light-blue",
"background": "p:dark-blue" background: "p:dark-blue",
}, },
"blocks": { }}
... />
}
```
## Palettes ## Palettes
@ -199,67 +200,97 @@ runtime so your prompt can change at any time based on the outcome of the `templ
Take the following configuration: Take the following configuration:
```json <Config
{ data={{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", palettes: {
"palettes": { template: '{{ if eq .Shell "pwsh" }}latte{{ else }}frappe{{ end }}',
"template": "{{ if eq .Shell \"pwsh\" }}latte{{ else }}frappe{{ end }}", list: {
"list": { latte: {
"latte": { black: "#262B44",
"black": "#262B44", green: "#59C9A5",
"green": "#59C9A5", orange: "#F07623",
"orange": "#F07623", red: "#e64553",
"red": "#e64553", white: "#E0DEF4",
"white": "#E0DEF4", yellow: "#df8e1d",
"yellow": "#df8e1d", blue: "#7287fd",
"blue": "#7287fd" },
frappe: {
black: "#262B44",
green: "#59C9A5",
orange: "#F07623",
red: "#D81E5B",
white: "#E0DEF4",
yellow: "#F3AE35",
blue: "#4B95E9",
},
}, },
"frappe": { },
"black": "#262B44", }}
"green": "#59C9A5", />
"orange": "#F07623",
"red": "#D81E5B",
"white": "#E0DEF4",
"yellow": "#F3AE35",
"blue": "#4B95E9"
}
}
},
"blocks": [
...
]
}
```
In this case, when the shell is `pwsh`, the `latte` palette will be used, otherwise it uses the `frappe` palette. If you want, In this case, when the shell is `pwsh`, the `latte` palette will be used, otherwise it uses the `frappe` palette. If you want,
you could also add `frappe` as the default `palette`, given that one is used as a fallback when not match can be found based on what you could also add `frappe` as the default `palette`, given that one is used as a fallback when not match can be found based on what
the `template` resolves to. In case no match is available and no `palette` is defined, it will also fallback to `transparent` the `template` resolves to. In case no match is available and no `palette` is defined, it will also fallback to `transparent`
for any palette color reference in templates/colors. for any palette color reference in templates/colors.
If you want to avoid color duplication, you can use palettes in combination with the `palette` property. This way you can define
a color once and reuse it in multiple palettes. For example:
<Config
data={{
palette: {
black: "#262B44",
green: "#59C9A5",
orange: "#F07623",
},
palettes: {
template: '{{ if eq .Shell "pwsh" }}latte{{ else }}frappe{{ end }}',
list: {
latte: {
red: "#e64553",
white: "#E0DEF4",
yellow: "#df8e1d",
blue: "#7287fd",
},
frappe: {
red: "#D81E5B",
white: "#E0DEF4",
yellow: "#F3AE35",
blue: "#4B95E9",
},
},
},
}}
/>
:::info
If a color is defined in both palette and palettes, the palettes' resolved color will take precedence.
:::
## Cycle ## Cycle
When you want to display the same **sequence of colors** (background and foreground) regardless of which segments are active, you can When you want to display the same **sequence of colors** (background and foreground) regardless of which segments are active, you can
make use of the cycle property. This property is a list of colors which are used one after the other in a continuous loop. A defined make use of the cycle property. This property is a list of colors which are used one after the other in a continuous loop. A defined
cycle always gets precedence over everything else. cycle always gets precedence over everything else.
```json <Config
... data={{
"cycle": [ cycle: [
{ {
"background": "p:blue", background: "p:blue",
"foreground": "p:white" foreground: "p:white",
}, },
{ {
"background": "p:green", background: "p:green",
"foreground": "p:black" foreground: "p:black",
}, },
{ {
"background": "p:orange", background: "p:orange",
"foreground": "p:white" foreground: "p:white",
} },
], ],
... }}
``` />
[hexcolors]: https://htmlcolorcodes.com/color-chart/material-design-color-chart/ [hexcolors]: https://htmlcolorcodes.com/color-chart/material-design-color-chart/
[ansicolors]: https://htmlcolorcodes.com/color-chart/material-design-color-chart/ [ansicolors]: https://htmlcolorcodes.com/color-chart/material-design-color-chart/

View file

@ -168,6 +168,9 @@ module.exports = {
require.resolve('./src/css/custom.css') require.resolve('./src/css/custom.css')
], ],
}, },
blog: {
onInlineAuthors: 'ignore'
},
}, },
], ],
], ],