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 +7 -0
- package/README.md +9 -8
- package/config/config.example.json +1 -1
- package/extensions/index.ts +89 -203
- package/package.json +1 -1
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**
|
|
32
|
-
- **Theme-adaptive 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":
|
|
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
|
|
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
|
-
|
|
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
|
|
package/extensions/index.ts
CHANGED
|
@@ -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
|
|
99
|
-
//
|
|
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
|
-
*
|
|
161
|
-
* from the terminal's own palette (Ghostty, Rex, ...), and
|
|
162
|
-
* palette when the terminal regains focus so same-kind theme
|
|
163
|
-
* (dark to dark) are picked up too.
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
4279
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
4362
|
-
//
|
|
4363
|
-
// terminal
|
|
4364
|
-
//
|
|
4365
|
-
//
|
|
4366
|
-
//
|
|
4367
|
-
|
|
4368
|
-
const
|
|
4369
|
-
const
|
|
4370
|
-
|
|
4371
|
-
|
|
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
|
-
|
|
4402
|
-
|
|
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
|
-
/**
|
|
4409
|
-
function
|
|
4410
|
-
const
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
|
|
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:
|
|
4433
|
+
/** User box, code fences, thinking/thought: the outline chrome color (stronger than connectors). */
|
|
4494
4434
|
function syncOutlineChromeFromBranch(theme?: any): void {
|
|
4495
|
-
const outline =
|
|
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
|
|
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(
|
|
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(
|
|
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", "
|
|
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",
|
|
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
|
-
|
|
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
|
-
`
|
|
7568
|
-
` (
|
|
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
|
|
7712
|
-
|
|
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.
|
|
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",
|