pi-claude-code-ui 1.0.91 → 1.0.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@
3
3
  > [!IMPORTANT]
4
4
  > **1.0.69 — package rename (permanent).** Canonical npm name is now [`pi-claude-code-ui`](https://www.npmjs.com/package/pi-claude-code-ui). `pi-claude-style-tools` is legacy and will not receive further releases. Install with `pi install npm:pi-claude-code-ui` or `npm i pi-claude-code-ui`.
5
5
 
6
+ ## 1.0.92 — 2026-10-24
7
+
8
+ ### Changed
9
+
10
+ - **Terminal colors are on by default** - on pi 1.4 or newer, pi now follows the terminal's palette (Ghostty, Rex, ...) without `/cc-theme terminal on`. The first start switches to pi's `system` theme, saves the theme you had as `terminalThemePrevious`, and says so; `/cc-theme terminal off` (or `"terminalTheme": false`) restores that theme and keeps it. `pi --use-theme <name>` still wins for that run, and older pi versions are left alone.
11
+ - **Branch connectors have one rule and no settings** - `├ └ │` are always a faint mark at a fixed 2:1 contrast against the terminal background, which keeps them visible on dark, light, and tinted themes. The `/cc-tools branch` command and the `toolBranchColorMode` / `toolBranchRgbGray` settings are removed; existing values are ignored. On pi before 1.4 the contrast is taken against black or white (rgb 72 on dark themes, as before).
12
+
6
13
  ## 1.0.91 — 2026-10-24
7
14
 
8
15
  ### Added
package/README.md CHANGED
@@ -28,8 +28,9 @@ Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots,
28
28
  - **Subagent completion notifications** restyled to match the same Claude-style tool rows
29
29
  - **RTK rewrite integration** that folds rewrite notices into the bash tool row with a muted `(RTK)` badge and expanded-only rewrite details
30
30
  - **Transparent tool backgrounds** in `transparent` or `border` mode
31
- - **Terminal colors** — `/cc-theme terminal on` makes pi's theme follow the terminal's own palette (Ghostty, Rex, kitty, WezTerm, …), including switches between two dark or two light themes
32
- - **Theme-adaptive palette** — borders, branch connectors, dim text, spinner accent, diff highlighting, and diff backgrounds automatically follow the active pi theme (set `themeAdaptive: false` to keep the fixed Claude-style palette)
31
+ - **Terminal colors** (default, pi 1.4+) — pi's theme follows the terminal's own palette (Ghostty, Rex, kitty, WezTerm, …), including switches between two dark or two light themes; `/cc-theme terminal off` keeps a theme of your choice
32
+ - **Theme-adaptive palette** — dim text, spinner accent, diff highlighting, and diff backgrounds automatically follow the active pi theme (set `themeAdaptive: false` to keep the fixed Claude-style palette)
33
+ - **Faint branch connectors** — `├ └ │` keep the same faint contrast on every terminal background, dark or light
33
34
  - **Transparent edit/write diffs** tinted with the theme's own diff colors
34
35
  - **Grouped consecutive tool calls** with single-row summaries for repeated targets and per-tool glance rows for mixed work (set `groupToolCalls: false` to disable)
35
36
  - **Extra detail toggle** with `Ctrl+Shift+O`, increasing expanded preview caps without making the default view heavy
@@ -59,7 +60,7 @@ Set in `.pi/settings.json` or the active Pi agent settings file (`$PI_CODING_AGE
59
60
  "diffCollapsedLines": 24,
60
61
  "diffLayout": "auto",
61
62
  "themeAdaptive": true,
62
- "terminalTheme": false
63
+ "terminalTheme": true
63
64
  }
64
65
  ```
65
66
 
@@ -71,8 +72,6 @@ When `themeAdaptive` is `true` (default), the following colors are derived from
71
72
 
72
73
  | Element | Derived from |
73
74
  |---------|--------------|
74
- | Branch connectors (`├`, `└`, `│`) | terminal background blended toward its text color to a 2:1 contrast (rgb 72 on a near-black terminal); `/cc-tools branch theme` uses `dim` → `muted` → `borderMuted` instead, `/cc-tools branch <0-255>` an exact gray |
75
- | User box, tool rules, code fences, thinking rule | the same blend at a 5.1:1 contrast (rgb ~136 on a near-black terminal) |
76
75
  | "✻ Turn took Ns" line (final message only, with session total + turn count) | `muted` |
77
76
  | Expanded thinking-block text and `∴` marker | `muted` |
78
77
  | Diff add/remove accents | `toolDiffAdded` / `toolDiffRemoved` |
@@ -82,7 +81,9 @@ When `themeAdaptive` is `true` (default), the following colors are derived from
82
81
  | Spinner verb text (`Working…`) | `borderAccent` (fallback: `accent`) |
83
82
  | Spinner status text | `muted` |
84
83
 
85
- These need pi 1.4 or newer, which exposes the theme's concrete colors. On older pi versions branch connectors stay rgb 72, diffs use `github-dark` / `github-light`, and diff tints are mixed against `toolSuccessBg`.
84
+ These need pi 1.4 or newer, which exposes the theme's concrete colors. On older pi versions diffs use `github-dark` / `github-light`, and diff tints are mixed against `toolSuccessBg`.
85
+
86
+ Branch connectors (`├`, `└`, `│`) and outlines (user box, tool rules, code fences, thinking rule) are neutral marks with nothing to configure. They blend the terminal's background toward its text color until they reach a fixed contrast: 2:1 for connectors (rgb 72 on a near-black terminal) and 5.1:1 for outlines (rgb ~136), so connectors stay faint but visible on dark, light, and tinted themes alike. On pi versions before 1.4, which don't report the terminal's colors, the same contrasts are taken against black or white.
86
87
 
87
88
  User-supplied `diffTheme` presets and `diffColors` overrides always win over theme-derived defaults. File-type icons (e.g. `ts`, `py`, `rs`) keep their language-identity colors and are not theme-derived.
88
89
 
@@ -92,13 +93,13 @@ On `/resume`, `/new`, or `/fork`, tool chrome is rebound from the **current** pi
92
93
 
93
94
  ### Terminal colors (Ghostty, Rex, …)
94
95
 
95
- `/cc-theme terminal on` switches pi to its built-in `system` theme, which builds every pi color from the palette the terminal reports, and keeps it there on every start. The theme you had before is saved as `terminalThemePrevious`; `/cc-theme terminal off` restores it.
96
+ On by default: at startup the extension switches pi to its built-in `system` theme, which builds every pi color from the palette the terminal reports, and keeps it there on every start. The theme you had before is saved as `terminalThemePrevious`; `/cc-theme terminal off` (or `"terminalTheme": false`) restores it and keeps it. `pi --use-theme <name>` still picks the theme for that run, and on pi versions without the `system` theme (before 1.4) nothing changes.
96
97
 
97
98
  Pi already rebuilds the `system` theme when the terminal switches between light and dark. Terminals send no notification when you switch between two themes of the same kind, for example from one dark Rex theme to another, so this extension also asks the terminal for its color scheme whenever the terminal window regains focus (you went to the terminal's settings and came back). Pi then re-reads the palette and repaints only if the colors changed. The query is answered by Ghostty, Rex, and other terminals that support color scheme reports; in regular (non-fullscreen) mode the extension turns on focus reporting itself and keeps the focus events out of the editor.
98
99
 
99
100
  ```text
100
101
  /cc-theme terminal on # follow the terminal's colors
101
- /cc-theme terminal off # go back to the previous theme
102
+ /cc-theme terminal off # go back to the previous theme and keep it
102
103
  /cc-theme terminal status # show the current state
103
104
  ```
104
105
 
@@ -17,6 +17,6 @@
17
17
  "diffCollapsedLines": 24,
18
18
  "showTruncationHints": false,
19
19
  "themeAdaptive": true,
20
- "terminalTheme": false,
20
+ "terminalTheme": true,
21
21
  "diffColors": {}
22
22
  }
@@ -95,13 +95,11 @@ const RESET = "\x1b[0m";
95
95
  const TRANSPARENT_BG = "\x1b[49m";
96
96
  const TRANSPARENT_RESET = `${RESET}${TRANSPARENT_BG}`;
97
97
 
98
- // User/code box borders and thinking/thought text: branch color + OUTLINE_CHROME_BRIGHTEN.
99
- // Branch ├└│ stay at `currentToolBranchAnsi` (see syncOutlineChromeFromBranch).
98
+ // User/code box borders and thinking/thought text use the outline chrome color, branch
99
+ // connectors ├└│ the fainter branch color (see chromeAnsi / syncOutlineChromeFromBranch).
100
100
  let BORDER_COLOR = "\x1b[38;5;238m";
101
101
  let CODE_BLOCK_LANG_FG = "\x1b[38;2;95;95;95m";
102
102
  const CHROME_ITALIC = "\x1b[3m";
103
- /** Lift outline chrome above branch connectors so boxes and thought read brighter. */
104
- const OUTLINE_CHROME_BRIGHTEN = 64;
105
103
  const ANSI_RE = /\x1b\[[0-9;]*m/g;
106
104
  const ANSI_PRESENT_RE = /\x1b\[[0-9;]*m/;
107
105
  const PATCH_FLAG = Symbol.for("pi-claude-style-tools:patched-container-render");
@@ -157,10 +155,11 @@ interface SettingsFile {
157
155
  */
158
156
  themeAdaptive?: boolean;
159
157
  /**
160
- * When true, pi uses its built-in `system` theme, which takes every color
161
- * from the terminal's own palette (Ghostty, Rex, ...), and re-reads that
162
- * palette when the terminal regains focus so same-kind theme switches
163
- * (dark to dark) are picked up too. Toggle with `/cc-theme terminal`.
158
+ * On by default (pi >= 1.4): pi uses its built-in `system` theme, which takes
159
+ * every color from the terminal's own palette (Ghostty, Rex, ...), and
160
+ * re-reads that palette when the terminal regains focus so same-kind theme
161
+ * switches (dark to dark) are picked up too. Set to false, or run
162
+ * `/cc-theme terminal off`, to keep the theme chosen in pi's /settings.
164
163
  */
165
164
  terminalTheme?: boolean;
166
165
  /** Pi theme that was active before `terminalTheme` was turned on; restored when it is turned off. */
@@ -185,14 +184,6 @@ interface SettingsFile {
185
184
  * renders expanded, like stock pi.
186
185
  */
187
186
  thinkingMode?: "live" | "full";
188
- /** Exact gray level 0–255 for ├ └ │ in `fixed` mode. Unset: a fixed-contrast blend of the terminal background and text. */
189
- toolBranchRgbGray?: number;
190
- /**
191
- * `fixed` (default): the terminal background blended toward its text color
192
- * to a fixed 2:1 contrast (rgb 72 on a #161616 terminal), or exactly
193
- * `toolBranchRgbGray` when set. `theme`: the theme's dim → muted → borderMuted color.
194
- */
195
- toolBranchColorMode?: "theme" | "fixed";
196
187
  }
197
188
 
198
189
  let _settingsCache: { value: SettingsFile; timestamp: number } | null = null;
@@ -1615,7 +1606,7 @@ const TOOL_EXECUTION_PATCH_FLAG = Symbol.for("pi-claude-style-tools:patched-tool
1615
1606
 
1616
1607
  // Rendered-output cache for assistant/user/custom message components.
1617
1608
  // Keyed by (width, branch visual epoch, tool background mode). The epoch changes on
1618
- // theme / /cc-tools branch / /cc-theme rebinds; the mode is included because subagent
1609
+ // theme and /cc-theme rebinds; the mode is included because subagent
1619
1610
  // (custom-message) framing follows `toolBackgroundMode` via frameToolLikeLines. This avoids
1620
1611
  // re-running the per-line ANSI stripping (applyTerminalCopyZones, normalizeLeadingCheckGlyph,
1621
1612
  // border boxing) on every scroll/expand re-render — the dominant CPU cost on long chats,
@@ -4260,8 +4251,14 @@ function themeAdaptiveEnabled(): boolean {
4260
4251
  // terminalTheme: pi follows the terminal's own palette (Ghostty, Rex, ...).
4261
4252
  // ---------------------------------------------------------------------------
4262
4253
 
4254
+ /** On by default; `terminalTheme: false` (or `/cc-theme terminal off`) turns it off. */
4263
4255
  function terminalThemeEnabled(): boolean {
4264
- return readSettings().terminalTheme === true;
4256
+ return readSettings().terminalTheme !== false;
4257
+ }
4258
+
4259
+ /** `pi --use-theme <name>` picks the theme for this run. */
4260
+ function themeChosenOnCommandLine(): boolean {
4261
+ return process.argv.some((arg) => arg === "--use-theme" || arg.startsWith("--use-theme="));
4265
4262
  }
4266
4263
 
4267
4264
  /** Pi's saved `theme` setting (may be a "light/dark" pair), from pi's own settings file. */
@@ -4275,11 +4272,16 @@ function readPiThemeSetting(): string | undefined {
4275
4272
  }
4276
4273
  }
4277
4274
 
4278
- /** Switch pi to its terminal-palette theme and keep it in sync. Returns false if unavailable. */
4279
- function applyTerminalTheme(ctx: any, options: { announce: boolean }): boolean {
4275
+ /**
4276
+ * Switch pi to its terminal-palette theme and keep it in sync. Returns false if unavailable.
4277
+ * `reportUnavailable` is off for the default-on startup path, so older pi versions stay quiet.
4278
+ */
4279
+ function applyTerminalTheme(ctx: any, options: { announce: boolean; reportUnavailable: boolean }): boolean {
4280
4280
  if (!ctx?.hasUI) return false;
4281
4281
  if (!systemThemeAvailable(ctx.ui)) {
4282
- ctx.ui.notify("Terminal colors need pi's built-in `system` theme (pi 1.4 or newer).", "error");
4282
+ if (options.reportUnavailable) {
4283
+ ctx.ui.notify("Terminal colors need pi's built-in `system` theme (pi 1.4 or newer).", "error");
4284
+ }
4283
4285
  return false;
4284
4286
  }
4285
4287
  const current = activeThemeName(ctx.ui);
@@ -4293,7 +4295,14 @@ function applyTerminalTheme(ctx: any, options: { announce: boolean }): boolean {
4293
4295
  return false;
4294
4296
  }
4295
4297
  if (options.announce) {
4296
- ctx.ui.notify(`Theme follows the terminal's colors (was "${previous ?? current}"). /cc-theme terminal off to switch back.`, "info");
4298
+ // Pi renders a resumed session's transcript right after session_start; post the
4299
+ // notice on the next tick so it lands below that history instead of above it.
4300
+ const message = `Theme follows the terminal's colors (was "${previous ?? current}"). /cc-theme terminal off switches back and keeps it.`;
4301
+ unrefTimer(setTimeout(() => {
4302
+ try {
4303
+ ctx.ui.notify(message, "info");
4304
+ } catch { /* session replaced in the meantime */ }
4305
+ }, 0));
4297
4306
  }
4298
4307
  }
4299
4308
  startFocusResync(ctx.ui, terminalThemeEnabled);
@@ -4358,88 +4367,53 @@ let FG_DEL = "\x1b[38;2;200;100;100m";
4358
4367
  let FG_DIM = "\x1b[38;2;80;80;80m";
4359
4368
  let FG_LNUM = "\x1b[38;2;100;100;100m";
4360
4369
  let FG_RULE = "\x1b[38;2;50;50;50m";
4361
- // Tool branch connectors (├ └ │). The default ("fixed" mode without an explicit gray) blends the
4362
- // terminal's background toward its text color until it has the contrast rgb 72 has on a #161616
4363
- // terminal (2.0:1), so connectors keep that weight on light and tinted themes and take on the
4364
- // theme's tint. Outline chrome (user box, code fences, thinking rule) does the same at the
4365
- // contrast of rgb 136 (5.1:1). Gray 72 / +64 is the fallback when pi does not expose concrete
4366
- // theme colors (pi < 1.4) or themeAdaptive is off.
4367
- const DEFAULT_TOOL_BRANCH_GRAY = 72;
4368
- const BRANCH_CHROME_CONTRAST = 2.0;
4369
- const OUTLINE_CHROME_CONTRAST = 5.1;
4370
-
4371
- // Branch connectors resolve their color on every rendered row, so keep the solved blends.
4372
- const _chromeBlendCache = new Map<string, string>();
4373
-
4374
- /** Terminal background blended toward its text color to `contrast`, or null without concrete colors. */
4375
- function themeChromeBlend(theme: any, contrast: number): string | null {
4376
- if (!theme || !themeAdaptiveEnabled()) return null;
4377
- const defaults = terminalDefaultColors(theme);
4378
- if (!defaults) return null;
4379
- const { background: bg, foreground: fg } = defaults;
4380
- const mode = typeof theme.getColorMode === "function" ? theme.getColorMode() : "";
4381
- const key = `${contrast}|${bg.r},${bg.g},${bg.b}|${fg.r},${fg.g},${fg.b}|${mode}`;
4382
- let ansi = _chromeBlendCache.get(key);
4383
- if (!ansi) {
4384
- if (_chromeBlendCache.size > 32) _chromeBlendCache.clear();
4385
- ansi = rgbFgAnsi(theme, blendForContrast(bg, fg, contrast));
4386
- _chromeBlendCache.set(key, ansi);
4387
- }
4388
- return ansi;
4389
- }
4390
-
4391
- function hasExplicitToolBranchGray(): boolean {
4392
- const raw = readSettings().toolBranchRgbGray;
4393
- return typeof raw === "number" && Number.isFinite(raw);
4394
- }
4370
+ // Branch connectors (├ └ │) and outline chrome (user box, code fences, thinking rule) are neutral
4371
+ // marks drawn at a fixed contrast against the terminal background: the background blended toward
4372
+ // the terminal's text color until it reaches 2:1 for connectors (rgb 72 on a #161616 terminal) and
4373
+ // 5.1:1 for outlines (rgb ~136). That keeps connectors faint but visible on dark, light, and tinted
4374
+ // themes alike, with nothing to configure. When pi does not report the terminal's colors (pi < 1.4),
4375
+ // the same contrasts are taken against plain black or white, per the theme's appearance.
4376
+ type ChromeKind = "branch" | "outline";
4377
+ const CHROME_CONTRAST: Record<ChromeKind, number> = { branch: 2.0, outline: 5.1 };
4378
+ const DARK_FALLBACK_GRAY: Record<ChromeKind, number> = { branch: 72, outline: 136 };
4379
+ const WHITE_RGB = { r: 255, g: 255, b: 255 };
4380
+ const BLACK_RGB = { r: 0, g: 0, b: 0 };
4395
4381
 
4396
4382
  function toolBranchRgbAnsi(gray: number): string {
4397
4383
  const g = Math.max(0, Math.min(255, Math.round(gray)));
4398
4384
  return `\x1b[38;2;${g};${g};${g}m`;
4399
4385
  }
4400
4386
 
4401
- function ansiRgbBrightenedBy(ansi: string, delta: number): string | null {
4402
- const rgb = parseAnsiRgb(ansi);
4403
- if (!rgb) return null;
4404
- const bump = (c: number) => Math.max(0, Math.min(255, Math.round(c + delta)));
4405
- return `\x1b[38;2;${bump(rgb.r)};${bump(rgb.g)};${bump(rgb.b)}m`;
4406
- }
4387
+ // Connectors resolve their color on every rendered row, so keep the solved blends.
4388
+ const _chromeAnsiCache = new Map<string, string>();
4407
4389
 
4408
- /** Outline chrome always stands out more than the branch; never falls back to identical branch ANSI. */
4409
- function outlineChromeAnsiFromBranch(theme?: any): string {
4410
- const t = theme ?? _toolBranchThemeHint;
4411
- if (toolBranchColorModeFixed() && !hasExplicitToolBranchGray()) {
4412
- const blend = themeChromeBlend(t, OUTLINE_CHROME_CONTRAST);
4413
- if (blend) return blend;
4414
- }
4415
- const branch = currentToolBranchAnsi(t);
4416
- // Toward the text color: brighter on dark themes, darker on light ones.
4417
- const towardText = t && isLightThemeBackground(t) ? -OUTLINE_CHROME_BRIGHTEN : OUTLINE_CHROME_BRIGHTEN;
4418
- const fromBranch = ansiRgbBrightenedBy(branch, towardText);
4419
- if (fromBranch) return fromBranch;
4420
- let gray = DEFAULT_TOOL_BRANCH_GRAY;
4421
- if (toolBranchColorModeFixed()) {
4422
- gray = getConfiguredToolBranchGray();
4423
- } else if (t) {
4424
- const hint = safeFgAnsi(t, "dim") ?? safeFgAnsi(t, "muted") ?? safeFgAnsi(t, "borderMuted");
4425
- const rgb = hint ? parseAnsiRgb(hint) : null;
4426
- if (rgb) gray = Math.round((rgb.r + rgb.g + rgb.b) / 3);
4390
+ /** Chrome color for `kind` on the current terminal background. */
4391
+ function chromeAnsi(theme: any, kind: ChromeKind): string {
4392
+ const defaults = theme ? terminalDefaultColors(theme) : null;
4393
+ const light = !defaults && !!theme && isLightThemeBackground(theme);
4394
+ const mode = typeof theme?.getColorMode === "function" ? theme.getColorMode() : "truecolor";
4395
+ const key = defaults
4396
+ ? `${kind}|${rgbToHex(defaults.background)}|${rgbToHex(defaults.foreground)}|${mode}`
4397
+ : `${kind}|${light ? "light" : "dark"}|${mode}`;
4398
+ let ansi = _chromeAnsiCache.get(key);
4399
+ if (!ansi) {
4400
+ const contrast = CHROME_CONTRAST[kind];
4401
+ const gray = DARK_FALLBACK_GRAY[kind];
4402
+ const rgb = defaults
4403
+ ? blendForContrast(defaults.background, defaults.foreground, contrast)
4404
+ : light
4405
+ ? blendForContrast(WHITE_RGB, BLACK_RGB, contrast)
4406
+ : { r: gray, g: gray, b: gray };
4407
+ if (_chromeAnsiCache.size > 32) _chromeAnsiCache.clear();
4408
+ ansi = rgbFgAnsi(theme, rgb);
4409
+ _chromeAnsiCache.set(key, ansi);
4427
4410
  }
4428
- return toolBranchRgbAnsi(Math.min(255, gray + OUTLINE_CHROME_BRIGHTEN));
4429
- }
4430
-
4431
- function getConfiguredToolBranchGray(): number {
4432
- const raw = readSettings().toolBranchRgbGray;
4433
- return typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.min(255, Math.round(raw))) : DEFAULT_TOOL_BRANCH_GRAY;
4434
- }
4435
-
4436
- function toolBranchColorModeFixed(): boolean {
4437
- return readSettings().toolBranchColorMode !== "theme";
4411
+ return ansi;
4438
4412
  }
4439
4413
 
4440
4414
  function toolBranchRenderCacheKey(): string {
4441
4415
  // The escape itself, not stripAnsi(TOOL_RULE) (which is always ""), so a new color is a new key.
4442
- return `${toolBranchColorModeFixed() ? "fixed" : "theme"}:${TOOL_RULE}`;
4416
+ return TOOL_RULE;
4443
4417
  }
4444
4418
 
4445
4419
  let _toolBranchVisualEpoch = 0;
@@ -4448,51 +4422,17 @@ function bumpToolBranchVisualEpoch(): void {
4448
4422
  _toolBranchVisualEpoch++;
4449
4423
  }
4450
4424
 
4451
- /** On light panels, theme dim/muted can be nearly white — pull chrome toward mid-gray. */
4452
- function attenuateChromeAnsi(ansi: string, theme: any): string {
4453
- const rgb = parseAnsiRgb(ansi);
4454
- if (!rgb) return ansi;
4455
- if (!isLightThemeBackground(theme)) return ansi;
4456
- const lum = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
4457
- // Already quiet enough on light backgrounds.
4458
- if (lum <= 145) return ansi;
4459
- const target = 118;
4460
- const t = Math.min(1, (lum - 130) / 110);
4461
- const mix = (c: number) => Math.round(c + (target - c) * t);
4462
- return `\x1b[38;2;${mix(rgb.r)};${mix(rgb.g)};${mix(rgb.b)}m`;
4463
- }
4464
-
4465
- /** Shared outline chrome: user box, tool rules, code fences, branch connectors. */
4466
- function resolveThemeChromeFg(theme: any): string | null {
4467
- if (!theme || !themeAdaptiveEnabled()) return null;
4468
- const dim = safeFgAnsi(theme, "dim");
4469
- const muted = safeFgAnsi(theme, "muted");
4470
- const borderMuted = safeFgAnsi(theme, "borderMuted");
4471
- const thinking = safeFgAnsi(theme, "thinkingText");
4472
- const raw = dim ?? muted ?? borderMuted ?? thinking;
4473
- return raw ? attenuateChromeAnsi(raw, theme) : null;
4474
- }
4475
-
4476
- /** Resolve ├ └ │ color from settings + theme on every use (not a stale global). */
4425
+ /** Last theme seen, for call sites that draw connectors without a theme at hand. */
4477
4426
  let _toolBranchThemeHint: any;
4478
4427
 
4428
+ /** ├ └ │ color for the current terminal background (resolved on every use, not a stale global). */
4479
4429
  function currentToolBranchAnsi(theme?: any): string {
4480
- const t = theme ?? _toolBranchThemeHint;
4481
- if (toolBranchColorModeFixed()) {
4482
- if (!hasExplicitToolBranchGray()) {
4483
- const blend = themeChromeBlend(t, BRANCH_CHROME_CONTRAST);
4484
- if (blend) return blend;
4485
- }
4486
- return toolBranchRgbAnsi(getConfiguredToolBranchGray());
4487
- }
4488
- const chrome = t ? resolveThemeChromeFg(t) : null;
4489
- if (chrome) return chrome;
4490
- return toolBranchRgbAnsi(getConfiguredToolBranchGray());
4430
+ return chromeAnsi(theme ?? _toolBranchThemeHint, "branch");
4491
4431
  }
4492
4432
 
4493
- /** User box, code fences, thinking/thought: branch + OUTLINE_CHROME_BRIGHTEN (never same as branch). */
4433
+ /** User box, code fences, thinking/thought: the outline chrome color (stronger than connectors). */
4494
4434
  function syncOutlineChromeFromBranch(theme?: any): void {
4495
- const outline = outlineChromeAnsiFromBranch(theme);
4435
+ const outline = chromeAnsi(theme ?? _toolBranchThemeHint, "outline");
4496
4436
  const prevBorder = BORDER_COLOR;
4497
4437
  BORDER_COLOR = outline;
4498
4438
  WORKED_LINE_FG = outline;
@@ -4556,7 +4496,7 @@ function refreshAllToolBranchVisuals(ctx: any): void {
4556
4496
  invalidateThemePaletteCache();
4557
4497
  applyToolBackgroundMode(ctx?.ui?.theme);
4558
4498
  applyToolBranchColor(ctx?.ui?.theme);
4559
- bumpToolBranchVisualEpoch(); // always bust ToolText + container caches after /cc-tools branch
4499
+ bumpToolBranchVisualEpoch(); // always bust ToolText + container caches
4560
4500
  // Tool rows recompute branch markup on next render (liveBranchDisplay + cache bust).
4561
4501
  if (ctx?.hasUI) {
4562
4502
  try {
@@ -4597,7 +4537,7 @@ function scheduleDeferredChromeRebind(ctx: any, delayMs = 0): void {
4597
4537
  unrefTimer(timer);
4598
4538
  }
4599
4539
 
4600
- let TOOL_RULE = toolBranchRgbAnsi(DEFAULT_TOOL_BRANCH_GRAY);
4540
+ let TOOL_RULE = toolBranchRgbAnsi(DARK_FALLBACK_GRAY.branch);
4601
4541
  let FG_SAFE_MUTED = "\x1b[38;2;139;148;158m";
4602
4542
  let FG_STRIPE = "\x1b[38;2;40;40;40m";
4603
4543
 
@@ -4781,7 +4721,7 @@ const _claudeStyleDefaults = {
4781
4721
  BORDER_COLOR: "\x1b[38;5;238m",
4782
4722
  WORKED_LINE_FG: "\x1b[38;2;140;140;140m",
4783
4723
  CODE_BLOCK_LANG_FG: "\x1b[38;2;95;95;95m",
4784
- TOOL_RULE: toolBranchRgbAnsi(DEFAULT_TOOL_BRANCH_GRAY),
4724
+ TOOL_RULE: toolBranchRgbAnsi(DARK_FALLBACK_GRAY.branch),
4785
4725
  FG_DIM: "\x1b[38;2;80;80;80m",
4786
4726
  FG_LNUM: "\x1b[38;2;100;100;100m",
4787
4727
  FG_RULE: "\x1b[38;2;50;50;50m",
@@ -7278,7 +7218,7 @@ export default function (pi: ExtensionAPI) {
7278
7218
  // /cc-tools command — control tool chrome, grouping, and detail level.
7279
7219
  const TOOL_MODES = ["outlines", "transparent", "default"] as const;
7280
7220
  const TOOL_BOOL_MODES = ["on", "off", "toggle", "status"] as const;
7281
- const TOOL_SUBCOMMANDS = [...TOOL_MODES, "group", "detail", "thinking", "branch", "status"] as const;
7221
+ const TOOL_SUBCOMMANDS = [...TOOL_MODES, "group", "detail", "thinking", "status"] as const;
7282
7222
  const booleanMode = (raw: string | undefined, current: boolean): boolean | "status" | undefined => {
7283
7223
  const mode = raw || "toggle";
7284
7224
  if (mode === "on") return true;
@@ -7289,24 +7229,11 @@ export default function (pi: ExtensionAPI) {
7289
7229
  };
7290
7230
  const notifyToolStatus = (ctx: any): void => {
7291
7231
  if (!ctx.hasUI) return;
7292
- const branchMode = toolBranchColorModeFixed() ? "fixed" : "theme";
7293
- const branchGray = getConfiguredToolBranchGray();
7294
- const theme = ctx.ui?.theme;
7295
- const chromeHint = branchMode === "theme" && theme
7296
- ? (resolveThemeChromeFg(theme) ? " (attenuated on light themes)" : " (fallback gray if theme keys missing)")
7297
- : "";
7298
- const branchLine = branchMode === "theme"
7299
- ? `Branch color: theme${chromeHint}`
7300
- : hasExplicitToolBranchGray() || !themeChromeBlend(theme, BRANCH_CHROME_CONTRAST)
7301
- ? `Branch color: fixed rgb(${branchGray})`
7302
- : "Branch color: fixed 2:1 contrast on the terminal background (rgb 72 on near-black)";
7303
7232
  ctx.ui.notify([
7304
7233
  `Tool style: ${toolBackgroundMode}`,
7305
7234
  `Tool grouping: ${toolGroupingEnabled() ? "on" : "off"}`,
7306
7235
  `Thinking: ${getThinkingMode()}`,
7307
7236
  `Extra detail: ${extraToolOutputExpanded ? "on" : "off"} (${rawKeyHint("ctrl+shift+o", "toggle")})`,
7308
- branchLine,
7309
- ` /cc-tools branch <0-255> | theme | fixed | reset`,
7310
7237
  ].join("\n"), "info");
7311
7238
  };
7312
7239
  pi.registerCommand("cc-tools", {
@@ -7324,20 +7251,12 @@ export default function (pi: ExtensionAPI) {
7324
7251
  m === "group" ? "Toggle grouped adjacent/concurrent tool rows"
7325
7252
  : m === "thinking" ? "Thinking display: live (default) or full"
7326
7253
  : m === "detail" ? "Toggle Ctrl+Shift+O extra-detail mode"
7327
- : m === "branch" ? "├ └ │ gray (0-255), theme, fixed, or reset"
7328
7254
  : m === "status" ? "Show tool UI settings"
7329
7255
  : m === "outlines" ? "Horizontal rules around each tool (default)"
7330
7256
  : m === "transparent" ? "No borders or backgrounds"
7331
7257
  : "Pi built-in tool backgrounds",
7332
7258
  }));
7333
7259
  }
7334
- if (first === "branch") {
7335
- const second = parts[1] ?? "";
7336
- const opts = ["theme", "fixed", "reset", "status"];
7337
- return opts
7338
- .filter((o) => o.startsWith(second))
7339
- .map((o) => ({ value: `branch ${o}`, label: o, description: "Branch connector color" }));
7340
- }
7341
7260
  if (first === "thinking") {
7342
7261
  const second = parts[1] ?? "";
7343
7262
  return ["live", "full", "status"]
@@ -7379,44 +7298,6 @@ export default function (pi: ExtensionAPI) {
7379
7298
  return;
7380
7299
  }
7381
7300
 
7382
- if (sub === "branch") {
7383
- const arg = parts[1] ?? "status";
7384
- if (arg === "status" || !arg) {
7385
- notifyToolStatus(ctx);
7386
- return;
7387
- }
7388
- if (arg === "reset") {
7389
- writeSettingsKey("toolBranchRgbGray", undefined);
7390
- writeSettingsKey("toolBranchColorMode", undefined);
7391
- if (ctx.hasUI) refreshAllToolBranchVisuals(ctx);
7392
- if (ctx.hasUI) ctx.ui.notify("Branch color → default (fixed contrast on the terminal background)", "info");
7393
- return;
7394
- }
7395
- if (arg === "theme") {
7396
- writeSettingsKey("toolBranchColorMode", "theme");
7397
- if (ctx.hasUI) refreshAllToolBranchVisuals(ctx);
7398
- if (ctx.hasUI) ctx.ui.notify("Branch color → follow pi theme (dim/muted)", "info");
7399
- return;
7400
- }
7401
- if (arg === "fixed") {
7402
- writeSettingsKey("toolBranchColorMode", "fixed");
7403
- if (ctx.hasUI) refreshAllToolBranchVisuals(ctx);
7404
- if (ctx.hasUI) ctx.ui.notify(hasExplicitToolBranchGray()
7405
- ? `Branch color → fixed rgb(${getConfiguredToolBranchGray()})`
7406
- : "Branch color → fixed contrast on the terminal background", "info");
7407
- return;
7408
- }
7409
- const gray = Number.parseInt(arg, 10);
7410
- if (!Number.isFinite(gray) || gray < 0 || gray > 255) {
7411
- if (ctx.hasUI) ctx.ui.notify("Usage: /cc-tools branch <0-255> | theme | fixed | reset", "error");
7412
- return;
7413
- }
7414
- writeSettingsKey("toolBranchRgbGray", gray);
7415
- writeSettingsKey("toolBranchColorMode", "fixed");
7416
- if (ctx.hasUI) refreshAllToolBranchVisuals(ctx);
7417
- if (ctx.hasUI) ctx.ui.notify(`Branch color → fixed rgb(${gray})`, "info");
7418
- return;
7419
- }
7420
7301
 
7421
7302
  if (sub === "thinking") {
7422
7303
  const arg = (parts[1] ?? "status").toLowerCase();
@@ -7478,8 +7359,9 @@ export default function (pi: ExtensionAPI) {
7478
7359
  if (!ctx.hasUI) return;
7479
7360
  const name = activeThemeName(ctx.ui) ?? "unknown";
7480
7361
  const previous = readSettings().terminalThemePrevious;
7362
+ const isDefault = readSettings().terminalTheme === undefined;
7481
7363
  ctx.ui.notify([
7482
- `Terminal colors: ${current ? "on" : "off"} (pi theme "${name}")`,
7364
+ `Terminal colors: ${current ? "on" : "off"}${isDefault ? " (default)" : ""} (pi theme "${name}")`,
7483
7365
  current && previous ? ` /cc-theme terminal off restores "${previous}"` : "",
7484
7366
  current && name !== SYSTEM_THEME_NAME ? " pi is not using the system theme right now; /cc-theme terminal on switches it back" : "",
7485
7367
  ].filter(Boolean).join("\n"), "info");
@@ -7494,15 +7376,17 @@ export default function (pi: ExtensionAPI) {
7494
7376
  return;
7495
7377
  }
7496
7378
  if (next) {
7379
+ const prior = readSettings().terminalTheme;
7497
7380
  writeSettingsKey("terminalTheme", true);
7498
- if (!applyTerminalTheme(ctx, { announce: false })) {
7499
- writeSettingsKey("terminalTheme", undefined);
7381
+ if (!applyTerminalTheme(ctx, { announce: false, reportUnavailable: true })) {
7382
+ writeSettingsKey("terminalTheme", prior);
7500
7383
  return;
7501
7384
  }
7502
7385
  requestTerminalColorScheme();
7503
7386
  if (ctx.hasUI) ctx.ui.notify("Terminal colors: on — pi's theme now follows the terminal (Ghostty, Rex, ...)", "info");
7504
7387
  } else {
7505
- writeSettingsKey("terminalTheme", undefined);
7388
+ // Explicit false: unset means on (the default).
7389
+ writeSettingsKey("terminalTheme", false);
7506
7390
  const restored = restorePreviousTheme(ctx);
7507
7391
  if (ctx.hasUI) {
7508
7392
  ctx.ui.notify(restored
@@ -7561,11 +7445,10 @@ export default function (pi: ExtensionAPI) {
7561
7445
  const statusKey = settings.spinnerStatusColor || "muted";
7562
7446
  const verbAnsi = safeFgAnsi(theme, verbKey) ?? safeFgAnsi(theme, "accent");
7563
7447
  const statusAnsi = safeFgAnsi(theme, statusKey) ?? safeFgAnsi(theme, "muted");
7564
- const chromePreview = resolveThemeChromeFg(theme);
7565
7448
  // Print a short preview of what we derived.
7566
7449
  const preview = [
7567
- `chrome : ${chromePreview ? `${chromePreview}─┌ User ├─\x1b[39m` : "(unchanged)"}`,
7568
- ` (user box, tool rules, branches)`,
7450
+ `outlines : ${chromeAnsi(theme, "outline")}╭─ User ──\x1b[39m (user box, code fences)`,
7451
+ `connectors : ${chromeAnsi(theme, "branch")}├ └ │\x1b[39m (faint, fixed contrast on the terminal background)`,
7569
7452
  `muted text : ${safeFgAnsi(theme, "muted") ? `${safeFgAnsi(theme, "muted")}example dim text\x1b[39m` : "(unchanged)"}`,
7570
7453
  `diff add : ${safeFgAnsi(theme, "toolDiffAdded") ? `${safeFgAnsi(theme, "toolDiffAdded")}+ added line\x1b[39m` : "(unchanged)"}`,
7571
7454
  `diff del : ${safeFgAnsi(theme, "toolDiffRemoved") ? `${safeFgAnsi(theme, "toolDiffRemoved")}- removed line\x1b[39m` : "(unchanged)"}`,
@@ -7708,8 +7591,11 @@ export default function (pi: ExtensionAPI) {
7708
7591
  clearRtkRewriteState();
7709
7592
  if (!ctx.hasUI) return;
7710
7593
  patchUiNotifications(ctx.ui);
7711
- // terminalTheme: switch to (and stay on) the theme built from the terminal's palette.
7712
- if (terminalThemeEnabled()) applyTerminalTheme(ctx, { announce: true });
7594
+ // terminalTheme (on by default): switch to, and stay on, the theme built from the
7595
+ // terminal's palette. `pi --use-theme <name>` still wins for that run.
7596
+ if (terminalThemeEnabled() && !(themeChosenOnCommandLine() && activeThemeName(ctx.ui) !== SYSTEM_THEME_NAME)) {
7597
+ applyTerminalTheme(ctx, { announce: true, reportUnavailable: readSettings().terminalTheme === true });
7598
+ }
7713
7599
  // Session switch (/resume, /new) can leave tool chrome from the previous
7714
7600
  // theme; rebind from ctx.ui.theme (other extensions may setTheme in the
7715
7601
  // same tick — deferred passes pick up the final theme without coupling).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-claude-code-ui",
3
- "version": "1.0.91",
3
+ "version": "1.0.92",
4
4
  "description": "Claude Code-style grouped tool rows for pi with Ctrl+O/Ctrl+Shift+O previews, math rendering, and consistent built-in, MCP, and custom tool rendering",
5
5
  "keywords": [
6
6
  "pi-package",