1223 Commits
Author SHA1 Message Date
allcontributors[bot]andJan De Dobbeleer f4f4a8ed6d docs: update website/docs/contributors.md [skip ci] 2026-08-09 10:17:19 +02:00
Ezequiel SalazarandJan De Dobbeleer 11b836f65d docs(battery): correct not_charging_icon description 2026-08-09 10:16:53 +02:00
allcontributors[bot]andJan De Dobbeleer 96d72fc996 docs: update website/docs/contributors.md [skip ci] 2026-08-08 17:25:06 +02:00
168ae17860 ci(website): validate docs build in pull requests
Catch broken Docusaurus links before website changes merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-05 15:44:47 +02:00
eda0185975 fix(cache): refresh serve daemon's cache from disk each render cycle (#7774)
* fix(cache): refresh serve daemon's cache from disk each render cycle

The `serve` daemon (used by streaming mode) loads the on-disk Session
and Device caches once at startup and only flushes them back on exit,
so a write from a separate one-shot process was invisible to the
running daemon until it restarted, and could even be clobbered by the
daemon's own stale copy on shutdown. This affected `oh-my-posh toggle`
(Session store) as well as `enable`/`disable` - e.g. `enable reload`,
which config.Get in prompt.New checks to bypass its own config cache
after an edit (Device store). Fixes #7758.

Add cache.Refresh(), which re-syncs the in-memory store from disk when
the file's mtime has advanced, merging entries by Timestamp so a value
the daemon has itself set more recently than the file always wins.
Call it for both stores at the start of every render cycle in
serve.go, and once more before close() persists a dirty store, closing
the shutdown clobber window too. Drop the Session-only guard on the
mtime bump in store.close() so a Device write reliably updates the
file's mtime on Windows as well (the mmap-backed write path doesn't do
this on its own).

Also documents the residual limitation (there's still a narrow window
where a write can land mid-cycle) in the streaming docs.

* fix(cache): reorder store fields to satisfy fieldalignment

Adding mtime widened the struct's pointer-scannable prefix from 40 to
56 bytes (the time.Time's trailing *Location pointer landed after the
three trailing bools). Reorder so pointer-bearing fields lead and the
bools trail, matching what CI's fieldalignment check expects.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-05 07:08:04 +00:00
49a5524bd4 feat(config): add default_steady/default_blinking cursor styles
Adds two cursor_style values that only touch shape reset (CSI 0 SP q)
and blink mode (CSI ? 12 h/l), instead of selecting one of the fixed
DECSCUSR shapes. Some terminals - Windows Terminal among them - offer
cursor shapes with no DECSCUSR equivalent (vintage/thick underscore,
double underscore, empty box). Existing cursor_style values always
override those with block/underline/bar, but default_steady and
default_blinking let the terminal profile's own shape stand while
still controlling steady vs. blinking.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-05 07:59:36 +02:00
4813f30942 feat(config): support templates in cursor_style
Renders cursor_style through the trusted template engine before
emitting the DECSCUSR sequence, mirroring the existing pwd and
console_title_template config options. This lets the cursor shape
vary per render, e.g. by branching on .Env.POSH_VI_MODE (set by the
vimode segment's shell hooks) to change shape between vi modes,
without introducing a dedicated config field.

Closes #7767

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-05 07:59:36 +02:00
41ef31d0be feat(config): add native cursor_style option
Terminal control-rune stripping (edcf3c88, 16bf1387) closed an
injection vector but also blocked literal cursor-shape escapes users
previously put in templates (see discussion #7742). Render DECSCUSR
sequences natively instead, driven by an enum config value, the same
way iterm_features and pwd already expose trusted terminal features.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: afc824f2ed92
2026-08-04 08:41:23 +02:00
James MontemagnoandJan De Dobbeleer 413f96fb12 fix(blog): fix link formatting for configurator app 2026-08-04 08:41:23 +02:00
allcontributors[bot]andJan De Dobbeleer e859f2af5e docs: update website/docs/contributors.md [skip ci] 2026-08-04 08:00:55 +02:00
Jan De DobbeleerandJan De Dobbeleer 7e084f18de docs(blog): studio, native git and a leaner binary
Entire-Checkpoint: 69141334755a
2026-08-03 13:27:25 +02:00
allcontributors[bot]andJan De Dobbeleer 37abfcc9d8 docs: update website/docs/contributors.md [skip ci] 2026-08-03 07:50:33 +02:00
Adam RadoczandJan De Dobbeleer 0d6c8e5159 docs(dsc): working configuration and schema examples 2026-08-03 07:50:10 +02:00
GraviferandJan De Dobbeleer 734f7d4a38 docs(git): clarify main worktree compatibility
Entire-Checkpoint: ef231d69c9bf
2026-08-03 07:50:10 +02:00
GraviferandJan De Dobbeleer 3d8332fbf1 feat(git): expose main worktree path
Gravifer Tokyo demonstrates the PowerShell workaround this property replaces:
https://github.com/JanDeDobbeleer/oh-my-posh/discussions/7723

The helper resolves linked-worktree indirection, runs the porcelain query, and
caches the result for the shell session. This change moves the capability behind
lazy template access and shares the cache by common repository.
2026-08-03 07:50:10 +02:00
allcontributors[bot]andJan De Dobbeleer 5b7f28b825 docs: update website/docs/contributors.md [skip ci] 2026-08-03 07:21:00 +02:00
de36c8d2af feat(git): add native status engine
Spawning git for status costs 22-25ms in process startup alone on
Windows, roughly 70% of the segment's render time. Computing the same
porcelain v2 counts in-process (go-git plumbing for index, gitignore
and object access; custom stat-cache worktree scan with a cache-tree
fast path) removes that tax: 6ms vs 36ms on this repository.

Opt-in via the native_status option. Any unsupported repository shape
(sparse or split index, reftables, submodules) falls back to the git
CLI automatically. Parity with the exec path is enforced by tests that
compare both engines against real repositories, including merge
conflicts, linked worktrees, and index.skipHash zero trailers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:33:43 +02:00
bd14374ce8 fix(studio): use configurator nonce fragment
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-02 18:17:35 +02:00
allcontributors[bot]andJan De Dobbeleer fc579e0990 docs: update website/docs/contributors.md [skip ci] 2026-08-02 16:41:02 +02:00
477acf0fbe feat(studio): open config in configurator
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-02 16:40:20 +02:00
42b9f83fc4 fix(studio): drop stray text under segment preview
A leftover `)}` outside the JSX expression rendered as literal text
below every segment doc's sample preview.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: e8444e478272
2026-08-02 12:37:05 +02:00
c53ff0de67 feat(studio): rebuild config editor on CodeMirror 6
The hand-rolled completion engine could not complete YAML key
positions: its cursor-marker probe produced invalid YAML mid-document,
the parser folded the marker into the next line's key, and the popup
never opened. Instead of patching the probe, replace the editor
foundation: CodeMirror 6 provides the popup, gutter, hover, and
tooltip machinery natively.

codemirror-json-schema was evaluated for the schema features and
rejected: enum completion through $ref resolves to nothing, $ref
nodes with sibling keywords crash its resolver, and both its published
ESM and CJS builds are broken under strict module resolution. The
proven schema resolver from the previous engine stays, rewired as a
native completion/hover source that walks the lezer syntax tree
instead of scanning text - which makes mid-edit states (blank lines,
dangling keys, open strings) work in both JSON and YAML, including
per-segment-type options completion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: 9045963ce7e2
2026-08-02 12:37:05 +02:00
2002b84594 feat(studio): lightweight editor feature in JSON
Show a description tooltip on hover for completion options (real
prose text, or a "View documentation" link for URL-only schema
descriptions), and flip the completion popup above the caret when
there isn't enough viewport room below, clamping its max-height so
it always stays fully on-screen.

Also remove the entire.json repo hook, whose sh invocation errored
on this machine and blocked every gated tool call.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: da7937bafeeb
2026-08-01 11:21:47 +02:00
Jan De DobbeleerandJan De Dobbeleer 55e97cc3b1 docs: style code blocks accordingly
Entire-Checkpoint: 9214af96e44d
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer f6793cbedc fix(svg): change studio render based on mode
Entire-Checkpoint: 642cd14ce7ad
2026-07-30 19:44:41 +02:00
8528d35a7a feat(studio): add Download PNG button
Rasterizes the live prompt SVG to a PNG on a hidden canvas. The font
is embedded as a base64 data URI in the SVG before rasterizing, since
an Image() loaded from a blob URL cannot see the page's own
@font-face and would otherwise fall back to a generic monospace font.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: 0401007e6324
2026-07-30 19:44:41 +02:00
73bda6801d feat(website): switch homepage hero svg with site theme
The homepage hero rendered once, against the svg exporter's own fixed
dark canvas background, so it always looked like a dark-mode prompt
even when a reader had the site in light mode.

export_themes.mjs now renders the default config twice - once
unchanged for dark, once with --background-color=#ffffff (Infima's
own light background) for light - and hero.json carries both as svg/
svgLight. The homepage ships both renders in its static HTML and picks
between them with plain CSS keyed off Docusaurus's own
html[data-theme] attribute, the same convention custom.css's
--omp-card-background override already uses, so the switch is instant
and needs no client re-render.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: e4462dff49f1
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 4db87633d3 docs(themes): add Open in Studio button
Entire-Checkpoint: 10e4ab343956
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 267f672b96 fix(svg): render colors correctly
Entire-Checkpoint: f00095ed9b98
2026-07-30 19:44:41 +02:00
48d6685d85 feat(website): live-reload themes and wasm during local dev
`npm start` now runs scripts/dev.mjs instead of `docusaurus start`
directly. It wraps the dev server with watchers that keep generated
artifacts current without a manual regen + restart:

- a /themes edit re-renders just that theme and patches it into
  generated/themes.json, so <ThemeGallery/> hot-reloads it
- editing export_themes.mjs, font-metrics.mjs or segment_data.json
  falls back to a full `npm run themes`, since those can change
  every theme`s render
- editing a Go source file under src/ rebuilds the studio`s wasm
  module (`npm run wasm`)

`npm run build` and CI are unaffected: they already run these
scripts explicitly before building and never watch anything.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: 23a2e79bf51a
2026-07-30 19:44:41 +02:00
47f2228931 fix(data): render a segment the same with or without its writer
Recorded data replayed without a segment writer diverged from the same
data replayed with one, in four ways that only surfaced once both paths
rendered the bundled themes side by side:

- Colour templates resolved against the writer rather than the data, so
  every one of them fell back to the plain colour where no writer
  exists - 49 of 124 themes.
- A value that is not a struct lost its methods. battery.State is an
  int, and `.State.String` is what every battery theme switches on.
  Method results now travel in a tree of their own, which keeps the
  data itself writer-shaped: an entry whose "State" were an object no
  longer unmarshals into the writer.
- A field renamed by its json tag was recorded under its Go name alone,
  so encoding/json never matched it on the way back and terraform's
  version restored as nil. Both names are recorded now.
- `date` fell through to the wall clock for a timestamp arriving as a
  string, which is how a recorded time.Time always arrives.

The wasm build carries no zoneinfo, so its idea of local time was
whatever offset the host happened to be on and a gallery built in July
printed an hour later than the same gallery built in January. It
renders in UTC now, with recorded timestamps written to match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Entire-Checkpoint: 707298e5cc26
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer e4ac930b0c docs(website): hide the table of contents on the theme gallery
Entire-Checkpoint: e9f35f03135c
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 3b63012568 refactor(cli)!: give fonts a list and an install command
Choosing a font was a five-stage terminal UI: fetch the list, scroll it,
download, unzip, install. It pulled bubbletea, lipgloss, bubbles and a
fuzzy matcher for what amounted to a non-filtering list of names with
one highlighted - and Go runs a linked package's init before main
whether or not the subcommand was invoked, so everyone typing at a
prompt paid for it on every render.

A CLI already has a way to answer "which fonts are there": print them.

  oh-my-posh font list
  oh-my-posh font install Meslo

The list can be grepped, piped and scripted, none of which a picker
allows, and install is what it always was underneath - resolve, fetch,
unzip - now reporting on a status line and a progress bar.

Removes 20 modules including go-runewidth, whose package init built a
2.2MB lookup table nothing in the prompt path ever read. The
atotto/clipboard fork this repo carried goes with them: bubbletea was
the only thing that needed it.

Binary 17,330,176 to 15,981,056 bytes (-7.8%). Init, median of 11 runs,
39.3ms to 6.8ms - on every invocation, prompt renders included.

BREAKING CHANGE: `oh-my-posh font install` now requires a font name; run
`oh-my-posh font list` to see them. The interactive picker is gone, and
with it the --headless flag, which existed to skip it.

Entire-Checkpoint: ef231d69c9bf
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 0116798118 fix(config): render a data-only segment the way a writer would
Two things a segment lost when there was no writer to build it.

A segment with nothing recorded about it never turned on. Enabled was
only ever set from the writer, so with none the segment fell straight to
its fallback and drew nothing - including a text segment, which needs no
data at all and whose whole content is its template. Render already
settles this correctly for every other case, by asking whether the text
came out blank, so the answer is to let it: with no writer and nothing
recorded, the template decides.

Numbers arrived as the wrong type. JSON has one number type, so
unmarshalling into a map makes every number a float64 where the writer
struct would have produced whatever the field declared. sysinfo renders
`{{ round .PhysicalPercentUsed .Precision }}`, and round wants its
precision as an int, so a Precision of 2.0 failed a template that an int
2 rendered. Whole numbers are restored as ints, which is the guess that
still renders: a function taking a float widens an int, while one taking
an int rejects a float.

The fixture guard learns that a recorded entry is an envelope, and that
a key naming a method is not the typo it hunts for. azpwsh leaves the
fixture: it is not a registered segment type, though two bundled themes
still name it.

Entire-Checkpoint: 1478bb7e734f
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 5c83ffad5e feat(website): render the site's artifacts without spawning the CLI
The gallery spawns oh-my-posh once per theme and the segment previews
once per segment: 241 processes to draw pictures the studio already
draws in a browser tab, from the same render.SVG.

A Node module instantiates that same wasm once and renders every prompt
in-process. It also decides what the SVG encoder has to stay reachable
from: with this, nothing outside the module needs to draw one.

Two things it needed. An empty config now means the built-in default,
matching what config.Load does with no path, so the homepage can ask for
the prompt someone sees before configuring anything - there is no file
to point at and no bundled theme that matches it. And the data fixture
carries what a template calls as well as what it reads, re-recorded
through the writers so its curated values survive.

The generators still shell out to the CLI. Rendering all 124 themes
through the module leaves 16 whose templates fail against the fixture,
and that is not understood yet - the keys those templates name are all
present. Switching them over waits on that.

Entire-Checkpoint: b83e4917be12
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 79698031f6 feat(config): leave the segment implementations out of the browser build
The studio renders a config from recorded data. It has no filesystem, no
processes and no network, so every segment's detection finds nothing the
moment it runs - and yet all 118 implementations were linked into the
module, because a package-level map holds a constructor for each and an
init gob-registers them all. Both are reachable from any config parse,
so between them they pinned every segment package and everything those
pull in: cloud SDKs, HTTP clients, the lot. 4.2MB raw, 0.5MB brotli, for
code no visitor can reach.

The registry moves behind a build tag. The browser build constructs no
writer at all and renders from the recorded data instead, which carries
what a template reads - fields and method results alike. Seven places
assumed a writer exists; they now ask whether there is one, and the
rendered text has somewhere to live when there is not.

The website's own data file moves to the recorded envelope format, which
is what carries a segment's enabled state when there is no writer left
to ask.

Combined with the HTTP stack already gone from this build: 29,351,525
bytes to 21,857,604 raw, 4,675,591 to 3,640,973 brotli - 22% off what a
visitor downloads.

Entire-Checkpoint: da3ad3698634
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer b76e33e648 docs: remove Windows Store reference
The badge linked to a listing that is no longer the recommended way to
install on Windows; winget is. Removing it takes the two SVG assets
with it.

Entire-Checkpoint: fdf9cbbb3842
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 585f50ae88 feat(website): rebuild the docs around a catalog and a studio
The site asked visitors to read a wall of segment pages to find out
what oh-my-posh could do, and to install it before seeing a prompt.

The homepage now leads with the install command and a rendered prompt.
Every one of the 117 segments is in a searchable catalog generated from
the docs themselves, so it cannot drift from what ships, and each
segment page carries a preview rendered from committed sample data at
build time. The theme gallery inlines the SVGs from the previous
commits instead of loading screenshots.

The studio renders a config live in the browser through the wasm
module, and each segment page embeds the same editor: change the
sample config, watch the prompt redraw, then send the segment to the
studio to build a full theme out of it. Nothing is sent anywhere.

The docs move into one shell around all of it, on a single type and
spacing scale, with a filterable sidebar.

Entire-Checkpoint: dc6fc3bdbdf2
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 264f6d3e5f feat(render): render a config without the machine
Rendering a prompt meant going through the CLI: cobra flags, a terminal
UI, an environment that probes git and cloud tooling on every segment.
Nothing else could ask for a rendered prompt, and nothing could ask for
one that touches nothing.

A render package takes a config, data and options, and returns the run
stream. The CLI becomes one caller of it; a WebAssembly entry point
becomes another, which is what lets a browser render a prompt with no
server and no machine behind it.

DataOnly moves onto the environment rather than sitting beside it as a
flag the call sites had to remember to check. A data-only render still
derives what it can from the data it was given instead of reporting
every segment absent, so a recorded file renders the same prompt it
recorded.

The terminal UI leaves the core packages so the wasm build does not
pull a TUI toolkit into the browser bundle, and the retired PNG
renderer's image package and its dependencies go with it.

Entire-Checkpoint: 603a21cafec5
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 59e8d08d8b feat(cli)!: export a config to SVG instead of PNG
The PNG exporter rasterized the prompt itself: it re-parsed the ANSI
string, resolved fonts, drew glyphs, and blurred a drop shadow, all to
produce a bitmap that could not be searched, selected, restyled or
scaled. It also carried image and font dependencies for that one
command.

A new svg package encodes the run stream from the terminal writer
(see the previous commit) into a terminal window drawn as vectors:
rounded frame, traffic lights, and one <text> per styled run. Text
stays text.

Everything after the first cut is a rendering fix found by comparing
real themes against what a terminal shows: transparent runs painted as
a cutout rather than a hole, wide glyphs given the room they ink, the
grid taken from the font's own metrics instead of the retired
renderer's picture, template whitespace preserved, the Mono Nerd Font
variants asked for first so icons advance one cell, the cursor placed
where the shell leaves it rather than after the right prompt, short
hex colours resolved, and a segment background ending where its
separator does. The canvas is now the window itself, with no
transparent gutter around it.

BREAKING CHANGE: `oh-my-posh config export image` writes SVG only. The
`--author`, `--background-color`, `--cursor-padding`, `--rprompt-offset`
and `--settings` flags are gone, as is PNG output; a command line using
them now fails rather than quietly producing something different.

Entire-Checkpoint: f6059ae8893a
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 6de79cd3dc feat(terminal): record the prompt as styled runs
The prompt was only ever emitted as a finished ANSI string. Anything
that wanted to draw it some other way had to parse those escape
sequences back out, which is why the image exporter carried its own
half-interpreter of them.

The writer now records what it emits as a stream of styled runs: the
text, the cell count, and the colours a segment actually asked for
rather than the ones already resolved to escapes. Emitting ANSI is one
consumer of that stream; anything else can be another.

Getting there meant fixing what the string form had been hiding.
Hyperlink state leaked between writes, plain mode ignored hyperlinks,
and visible-cell counting disagreed with how the runes render, which
matters the moment something lays them out on a grid instead of letting
the terminal do it. Gradients now resolve only the colours they use.

A golden harness covers all 124 bundled themes against one shared,
sanitized --data fixture: a manifest of per-theme hashes so a change
touches one line, plus full ANSI blobs for a representative handful.
Recorded replay had to become hermetic first, or the fixture would not
have been reproducible.

Entire-Checkpoint: d27890fba3f4
2026-07-30 19:44:41 +02:00
Jan De DobbeleerandJan De Dobbeleer 928140efa3 refactor: strip restating comments across the codebase
Removed doc/inline comments that only paraphrased the name or next
line of code, across Go source, shell init scripts (bash, zsh, fish,
pwsh, nu, elvish, xonsh, yash, Clink lua), and website JS. Comments
encoding non-obvious workarounds, race conditions, or external bug
references were kept and trimmed to just that point.

Moves the minimal-comment rule from the golang skill into AGENTS.md
so it applies repo-wide instead of to Go only.

Entire-Checkpoint: 7a66b31a1ad7
2026-07-30 19:44:41 +02:00
GraviferandJan De Dobbeleer e84d617322 feat(jujutsu): expose change ID prefix and rest 2026-07-28 09:02:38 +02:00
Jan De Dobbeleer 273394c990 feat: add darken() and lighten() color shade functions
resolves #7725
2026-07-28 09:02:38 +02:00
allcontributors[bot]andJan De Dobbeleer e5d4d59e97 docs: update website/docs/contributors.md [skip ci] 2026-07-28 08:32:51 +02:00
GraviferandJan De Dobbeleer 224eb9a865 fix(path): support gitdir_format in linked worktrees 2026-07-28 08:32:19 +02:00
Jan De DobbeleerandJan De Dobbeleer ca2f764101 docs(git): steer posh-git users to source: pwsh, add FAQ
resolves #7718

Entire-Checkpoint: 631ff72e482a
2026-07-24 14:33:13 +02:00
Jan De DobbeleerandJan De Dobbeleer 5f2e723a6d feat(color): auto-shade single-color gradients with dark-gradient/light-gradient
A single-color gradient effect for segments too narrow for a real
two-stop gradient to render as anything but a solid color:
dark-gradient(#color) runs from that exact color to a darker
shade, light-gradient(#color) to a lighter one.

The shade drops (or raises) HCL lightness only, by a fraction of
the base color's own headroom toward the target end, keeping hue
exact and walking chroma down only as far as the sRGB gamut
forces - blending toward black/white directly pulls chroma down
with it, reading as the color going muddy rather than
deepening/brightening. The reference delta is tuned so a 3-cell
segment - the common case this exists for - lands its shade close
to a just-noticeable-difference rather than a hard color swap; a
width multiplier grows that delta for wider segments as a
saturating curve (not linear growth with a hard clamp - that hit
its clamp by ~15-20 cells and crushed every wider segment to the
exact same near-white/near-black color, which is what "the
gradient stops working on a larger segment" turned out to be),
so a very wide segment still shades further than a moderately
wide one while topping out at a moderate, recognizably-still-the-
same-hue color instead of washing out to white or black.

GradientLastForCells mirrors the same shading (for a given cell
count) on the raw stop text, so powerline separators, diamond
caps, and inline color-override edges (via a new
gradientRenderCells package var in the terminal writer) line up
with the color the body actually ends on for that segment's own
width. GradientLast (no cell count - used by parentBackground/
parentForeground crossing into another segment, and wherever
width isn't known) falls back to the narrowest, gentlest shade.

WithGradientStops rebuilds a gradient string keeping its own
prefix (linear-gradient, dark-gradient, or light-gradient), so
resolving a palette-referenced stop (dark-gradient(p:teal)) keeps
its darken semantics instead of silently becoming a plain
linear-gradient - whole-string palette resolution only ever
expanded a bare "p:name" that itself resolved to a gradient,
never a "p:name" used as one stop inside a gradient literal, so a
palette-referenced stop reached GradientLast unresolved and fell
back to the raw, unshaded base color.

linear-gradient itself goes back to requiring two or more stops;
the single-stop overload it briefly had is now dark-gradient's job.

Entire-Checkpoint: 85522027570b
2026-07-24 14:33:13 +02:00
allcontributors[bot]andJan De Dobbeleer bd6da667f4 docs: update website/docs/contributors.md [skip ci] 2026-07-23 12:38:55 +02:00
8f4f7c890c feat: add linear-gradient segment colors
Foreground and background accept linear-gradient(stop, stop, ...) with
hex or palette-reference stops, interpolated per visible cell in HCL
space and emitted as truecolor escapes (256-color fallback when the
terminal lacks truecolor support). Powerline separators, diamond caps,
and parentBackground/parentForeground collapse to the matching gradient
edge so adjacent glyphs connect with the correct color.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entire-Checkpoint: d84718082c10
2026-07-22 17:29:21 +02:00