feat(theme): add Pierre defaults#471
Conversation
Greptile SummaryThis PR adds Diffs.com's
Confidence Score: 4/5Safe to merge — the change is additive and well-tested, with no modifications to existing theme behavior. The implementation correctly threads Pierre themes through every data path (backgrounds, foregrounds, diff colors, config validation, selector lists) and the new color-remap bypass is exercised by an end-to-end test. The only concern is the indexOf("plastic") insertion-index lookup, which silently degrades to wrong ordering rather than failing loudly if the anchor theme is ever removed. src/ui/lib/shikiThemes.ts — the PIERRE_THEME_INSERTION_INDEX derivation via indexOf deserves a defensive check. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["BUNDLED_PIERRE_THEME_IDS\n['pierre-dark', 'pierre-light']"] --> C
B["BUNDLED_SHIKI_THEME_IDS\n(64 themes)"] --> C
C["BUNDLED_HIGHLIGHTER_THEME_IDS\n(merged, alphabetical via indexOf('plastic'))"] --> D
C --> E
C --> F
D["config.ts\nBUILT_IN_THEME_IDS\n(validation)"]
E["opentui/themes.ts\nHUNK_DIFF_THEME_NAMES\n(type + selector)"]
F["themes.ts\nTHEMES[]\nbuildShikiTheme per id\nsyntaxTheme = themeId"]
F --> G["resolveTheme()"]
G --> H["pierre.ts\nnormalizeHighlightedColor()"]
H -- "syntaxTheme starts with 'pierre-'" --> I["Return raw Pierre token color"]
H -- "other theme" --> J["Remap reserved Pierre colors\nto theme-safe syntax colors"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["BUNDLED_PIERRE_THEME_IDS\n['pierre-dark', 'pierre-light']"] --> C
B["BUNDLED_SHIKI_THEME_IDS\n(64 themes)"] --> C
C["BUNDLED_HIGHLIGHTER_THEME_IDS\n(merged, alphabetical via indexOf('plastic'))"] --> D
C --> E
C --> F
D["config.ts\nBUILT_IN_THEME_IDS\n(validation)"]
E["opentui/themes.ts\nHUNK_DIFF_THEME_NAMES\n(type + selector)"]
F["themes.ts\nTHEMES[]\nbuildShikiTheme per id\nsyntaxTheme = themeId"]
F --> G["resolveTheme()"]
G --> H["pierre.ts\nnormalizeHighlightedColor()"]
H -- "syntaxTheme starts with 'pierre-'" --> I["Return raw Pierre token color"]
H -- "other theme" --> J["Remap reserved Pierre colors\nto theme-safe syntax colors"]
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
src/ui/lib/shikiThemes.ts:76-82
**Fragile insertion-index via `indexOf`**
`BUNDLED_SHIKI_THEME_IDS.indexOf("plastic")` returns `-1` if `"plastic"` is ever removed from the Shiki list. `Array.prototype.slice` treats negative indices as offsets from the end, so the resulting `BUNDLED_HIGHLIGHTER_THEME_IDS` would silently place Pierre themes immediately before `"vitesse-light"` rather than at the correct alphabetical position — breaking the documented "alphabetized with Shiki ids" contract without any error or failing test. A runtime assertion (`if (PIERRE_THEME_INSERTION_INDEX === -1) throw new Error(...)`) or at least a comment noting the assumption would make the failure loud instead of silent.
Reviews (1): Last reviewed commit: "feat(theme): add Pierre defaults" | Re-trigger Greptile |
| const PIERRE_THEME_INSERTION_INDEX = BUNDLED_SHIKI_THEME_IDS.indexOf("plastic"); | ||
|
|
||
| export const BUNDLED_HIGHLIGHTER_THEME_IDS: readonly BundledHighlighterThemeId[] = [ | ||
| ...BUNDLED_SHIKI_THEME_IDS.slice(0, PIERRE_THEME_INSERTION_INDEX), | ||
| ...BUNDLED_PIERRE_THEME_IDS, | ||
| ...BUNDLED_SHIKI_THEME_IDS.slice(PIERRE_THEME_INSERTION_INDEX), | ||
| ]; |
There was a problem hiding this comment.
Fragile insertion-index via
indexOf
BUNDLED_SHIKI_THEME_IDS.indexOf("plastic") returns -1 if "plastic" is ever removed from the Shiki list. Array.prototype.slice treats negative indices as offsets from the end, so the resulting BUNDLED_HIGHLIGHTER_THEME_IDS would silently place Pierre themes immediately before "vitesse-light" rather than at the correct alphabetical position — breaking the documented "alphabetized with Shiki ids" contract without any error or failing test. A runtime assertion (if (PIERRE_THEME_INSERTION_INDEX === -1) throw new Error(...)) or at least a comment noting the assumption would make the failure loud instead of silent.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/lib/shikiThemes.ts
Line: 76-82
Comment:
**Fragile insertion-index via `indexOf`**
`BUNDLED_SHIKI_THEME_IDS.indexOf("plastic")` returns `-1` if `"plastic"` is ever removed from the Shiki list. `Array.prototype.slice` treats negative indices as offsets from the end, so the resulting `BUNDLED_HIGHLIGHTER_THEME_IDS` would silently place Pierre themes immediately before `"vitesse-light"` rather than at the correct alphabetical position — breaking the documented "alphabetized with Shiki ids" contract without any error or failing test. A runtime assertion (`if (PIERRE_THEME_INSERTION_INDEX === -1) throw new Error(...)`) or at least a comment noting the assumption would make the failure loud instead of silent.
How can I resolve this? If you propose a fix, please make it concise.
Summary
pierre-dark,pierre-light) as built-in selectable/configurable themes.Testing
bun run format:checkbun run typecheckbun test src/ui/themes.test.ts src/ui/diff/pierre.test.ts src/core/config.test.ts src/ui/lib/ui-lib.test.tsbun run test(failed on existing session integration timeout:session CLI integration > navigate works, and comment add only focuses the session when --focus is passed; rerunning that single test also timed out)This PR description was generated by Pi using OpenAI GPT-5 Codex (2026-06-21)