pi-cc-extensions 1.0.14 → 1.0.15

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/README.en.md CHANGED
@@ -34,7 +34,7 @@ Run `/reload` after installation.
34
34
  | Context inspection | Usage breakdown and previews for the system prompt, memory, skills, tools definition, and messages | `/context` |
35
35
  | Session/Subagent references | Search and inject effective context from previous Sessions or existing SubAgents | `@` |
36
36
  | Status bar | Shows model, context, cache, cost, the conversation token total, and git; also works with `@narumitw/pi-usage` for live quota. Git/cache Nerd Font icons can be turned off | `/ccstyle` |
37
- | Theme | Bundled CC Dark and CC Light themes | `/settings` → Theme |
37
+ | Theme | Bundled themes: CC Dark, CC Light, CC Dark HC, CC Light HC | `/settings` → Theme |
38
38
 
39
39
  Renderer snapshots for `on` / `compact` modes: [default](./docs/tool-render-examples-default.md) · [compact](./docs/tool-render-examples-compact.md)
40
40
 
package/README.md CHANGED
@@ -34,7 +34,7 @@ pi install git:github.com/minuque/pi-cc-extensions
34
34
  | 上下文检查 | 查看上下文占用,并预览 System prompt、Memory、Skills、Tools definition 和消息内容 | `/context` |
35
35
  | Session/Subagent 引用 | 搜索并注入历史 Session 或现有 SubAgent 的有效上下文 | `@` |
36
36
  | 状态栏 | 显示:模型、上下文、缓存、费用、本次对话累计 token、git并适配 @narumitw/pi-usage 实时显示额度;git/缓存图标可关 Nerd Font | `/ccstyle` |
37
- | 主题 | 随包提供内置 CC Dark、CC Light 主题 | `/settings` → Theme |
37
+ | 主题 | 随包提供 4 个主题:CC Dark、CC Light、CC Dark HC、CC Light HC | `/settings` → Theme |
38
38
 
39
39
  `on` / `compact` 两种模式的渲染样例:[默认模式](./docs/tool-render-examples-default.md) · [紧凑模式](./docs/tool-render-examples-compact.md)
40
40
 
@@ -49,6 +49,7 @@ import {
49
49
  fullscreenLazyTui,
50
50
  hideScrollButton,
51
51
  dockScrollButtonActive,
52
+ isFullscreenAtBottom,
52
53
  restoreOfficialScrollToEnd,
53
54
  restoreNativeScrollIndicatorText,
54
55
  syncOfficialScrollToEnd,
@@ -457,9 +458,9 @@ function handleFullscreenToolClick(tui: any, packet: SgrMousePacket): boolean {
457
458
  // compact 摘要行自己记展开前的视口(收起时还原跟随),所以它先展开再停跟随:
458
459
  // 顺序反了快照会记下「未跟随」,收起时就交不回官方 follow。
459
460
  component.setExpanded(true);
460
- keepViewportOnExpand(tui);
461
+ keepViewportOnExpand(tui, hit);
461
462
  } else {
462
- keepViewportOnExpand(tui);
463
+ keepViewportOnExpand(tui, hit);
463
464
  component.setExpanded(true);
464
465
  }
465
466
  clearPendingCollapsePress();
@@ -509,13 +510,40 @@ function isSameExpandedRound(other: any, owner: any): boolean {
509
510
  }
510
511
 
511
512
  /**
512
- * 原地展开时停在当前滚动位置:transcript 跟随底部时,内容变高会把视口拖到底,
513
- * 点击的行和它上方的外层面板被顶出屏幕。展开前关掉跟随,点击行留在原位。
513
+ * 点击行在滚动视口里的位置:视口内行号与视口高度。布局树读不到时返回 null。
514
+ * leaf 不带 scrollView,视口是最近的带 scrollView 的祖先 box。
515
+ */
516
+ function clickedRowInViewport(
517
+ hit: { box: any; localRow: number } | null | undefined,
518
+ ): { y: number; height: number } | null {
519
+ if (!hit?.box) return null;
520
+ let scrollBox: any = null;
521
+ for (let current = hit.box; current; current = current.parent) {
522
+ if (current.scrollView) {
523
+ scrollBox = current;
524
+ break;
525
+ }
526
+ }
527
+ const rect = scrollBox?.rect;
528
+ const height = Number(rect?.height);
529
+ const rowY = Number(hit.box.rect?.y);
530
+ if (!rect || !(height > 0) || !Number.isFinite(rowY)) return null;
531
+ return { y: rowY + hit.localRow - Number(rect.y), height };
532
+ }
533
+
534
+ /**
535
+ * 原地展开时视口怎么走。transcript 跟随底部时,内容变高会把视口拖到底,
536
+ * 点击的行和它上方的外层面板被顶出屏幕:展开前关掉跟随,点击行留在原位。
514
537
  * compact 摘要行同样适用:它的展开入口在 setExpanded 之后调用本函数(见调用点)。
538
+ *
539
+ * 例外是点击行已经落在视口下半屏:它下面没有足够空间,冻结只会把展开出来的
540
+ * 内容全推到屏幕外(贴着底部输入框时最常见)。这种位置保持跟随,让新内容顶上来。
515
541
  */
516
- function keepViewportOnExpand(tui: any): void {
542
+ function keepViewportOnExpand(tui: any, hit?: { box: any; localRow: number } | null): void {
517
543
  const view = tui.getPrimaryScrollView?.();
518
544
  if (typeof view?.scrollTo !== "function" || typeof view.scrollTop !== "number") return;
545
+ const spot = clickedRowInViewport(hit);
546
+ if (spot && isFullscreenAtBottom(tui) && spot.y >= spot.height / 2) return;
519
547
  view.scrollTo(view.scrollTop, { disableFollow: true });
520
548
  // 已脱离底部跟随:dock 模式亮出回到底部按钮,新输出到来时仍有入口。
521
549
  if (view.isFollowingEnd === false && dockScrollButtonActive()) setScrollButtonVisible(true);
@@ -194,6 +194,67 @@ function shellDurationText(toolName: string, context: any, isPartial: boolean):
194
194
  return elapsed === undefined ? "" : formatToolDuration(elapsed);
195
195
  }
196
196
 
197
+ /**
198
+ * 运行中的展开视图:与结束后同一份 Input / Output。
199
+ * 还没输出时用 Pending… 占位:空正文会被视图画成 Done,像已经跑完了。
200
+ * shell 的已用时间挂到视图尾部;取值函数每帧重算,秒数才跟着动画 tick 走。
201
+ */
202
+ function renderRunningExpandedResult(toolName: string, result: any, context: any, theme: any): any {
203
+ const view = renderExpandedToolResult(
204
+ textFromResult(result) || "Pending…",
205
+ theme,
206
+ false,
207
+ context?.lastComponent,
208
+ context?.args,
209
+ context,
210
+ true,
211
+ );
212
+ if (isExpandedToolIoView(view) && SHELL_TOOL_NAMES.has(toolName)) {
213
+ view.setFooter(() => shellDurationText(toolName, context, true) || undefined);
214
+ }
215
+ return view;
216
+ }
217
+
218
+ /**
219
+ * 运行中的折叠行:` ↳ Pending… · 12.4s · click to show more`。
220
+ * 提示必须留在行尾:collapsedHintHitbox 只认行尾,没有它这行既点不动也看不出能点。
221
+ * 已用时间在 render() 内取,动画 tick 才能带上新值。
222
+ */
223
+ function renderRunningCollapsedResult(options: {
224
+ toolName: string;
225
+ context: any;
226
+ theme: any;
227
+ /** ask_user_question 的结果树用 1 格缩进,与它的 ├/└ 对齐。 */
228
+ prefix?: string;
229
+ }): any {
230
+ const { toolName, context, theme, prefix } = options;
231
+ const toolCallId = context?.toolCallId;
232
+ const hintPrefix = theme.fg("dim", " · ");
233
+ const hint = hintPrefix + theme.fg("dim", showMoreHintText());
234
+ const hoveredHint = hintPrefix + theme.fg("text", showMoreHintText());
235
+ let cachedWidth: number | undefined;
236
+ let cachedBody: string | undefined;
237
+ let cachedLine: string | undefined;
238
+ let cachedHoveredLine: string | undefined;
239
+ return {
240
+ render(width: number) {
241
+ const elapsed = shellDurationText(toolName, context, true);
242
+ const body = elapsed ? `Pending… · ${elapsed}` : "Pending…";
243
+ if (cachedLine === undefined || cachedWidth !== width || cachedBody !== body) {
244
+ cachedWidth = width;
245
+ cachedBody = body;
246
+ cachedLine = theme.fg("muted", renderCollapsedToolResultToWidth(body, hint, width, prefix));
247
+ cachedHoveredLine = theme.fg(
248
+ "muted",
249
+ renderCollapsedToolResultToWidth(body, hoveredHint, width, prefix),
250
+ );
251
+ }
252
+ return [isToolCallHovered(toolCallId) ? cachedHoveredLine! : cachedLine];
253
+ },
254
+ invalidate(): void {},
255
+ };
256
+ }
257
+
197
258
  /**
198
259
  * Agent 的 renderResult:与 pi-subagents 保持同样的分支,但收敛成 ccstyle 的 ↳ 行。
199
260
  * running/partial 转交原渲染器(只有它有 spinner frame 和 activity 文案);
@@ -454,9 +515,9 @@ export function createCcstyleTool(
454
515
  if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
455
516
  if (!options?.isPartial) setToolVisualState(context, isError ? "error" : "success");
456
517
  if (options?.isPartial) {
457
- // 折叠态 1 格缩进:与完成后的 ├/└ 对齐标题行的勾。
458
- const pending = expanded ? "↳ Pending…" : " ↳ Pending…";
459
- return new Text(theme.fg("muted", pending), 0, 0);
518
+ // 等待回答时也能展开看问题原文(Input 即 args.questions);折叠态 1 格缩进。
519
+ if (expanded) return renderRunningExpandedResult(toolName, result, context, theme);
520
+ return renderRunningCollapsedResult({ toolName, context, theme, prefix: " ↳ " });
460
521
  }
461
522
  if (expanded) {
462
523
  return renderExpandedToolResult(
@@ -479,21 +540,11 @@ export function createCcstyleTool(
479
540
  });
480
541
  }
481
542
  if (options?.isPartial) {
543
+ // 运行中也给和结束后同一份 Input / Output:命令全文与已产生的输出。
544
+ if (expanded) return renderRunningExpandedResult(toolName, result, context, theme);
482
545
  // 折叠态保持 3 格:与完成后的 ` ↳ …` 同列,工具跑完那行不横跳。
483
- // 展开态贴左(Box 提供 1 格 pad)。
484
- const pending = expanded ? "↳ Pending…" : " ↳ Pending…";
485
- if (!SHELL_TOOL_NAMES.has(toolName)) {
486
- return new Text(theme.fg("muted", pending), 0, 0);
487
- }
488
- // shell 运行中报已用时间:数字在 render() 内取,动画 tick 才能带上新值。
489
- return {
490
- render(width: number) {
491
- const elapsed = shellDurationText(toolName, context, true);
492
- const suffix = elapsed ? theme.fg("dim", ` · ${elapsed}`) : "";
493
- return [truncateToWidth(theme.fg("muted", pending) + suffix, width, "")];
494
- },
495
- invalidate(): void {},
496
- };
546
+ if (context?.state) context.state.ccstyleIoView = undefined;
547
+ return renderRunningCollapsedResult({ toolName, context, theme });
497
548
  }
498
549
 
499
550
  // shell:结果定型时定一次结束时间,这一行之后不再变。
@@ -26,6 +26,7 @@ import { mcpToolTitle } from "./mcp-title.ts";
26
26
  import { collapseHintText, isToolTuiFullscreen, showMoreHintText } from "./names.ts";
27
27
  import {
28
28
  contentRowRange,
29
+ hasVisibleText,
29
30
  imagePlacement,
30
31
  stripAnsi,
31
32
  stripBackgroundAnsi,
@@ -364,10 +365,17 @@ function expandedToolRows(tool: any, width: number): string[] {
364
365
  const cached = expandedToolRowsCache.get(paint);
365
366
  if (cached) return cached;
366
367
  const rendered = visibleLines(paint);
367
- if (rendered.length) {
368
- rendered[0] = stripLeadingStatusIcon(rendered[0])
368
+ // 首行的状态图标由组前缀给,子行里要剥掉。
369
+ const stripIcon = (line: string) =>
370
+ stripLeadingStatusIcon(line)
369
371
  .replace(/^ +/, "")
370
372
  .replace(/^((?:\x1b\[[0-?]*[ -/]*[@-~])*) +/, "$1");
373
+ // 剥完什么都没剩下(子卡首行只剩一个图标)就丢掉这一行,再对新的首行剥一次:
374
+ // 留着会多出一行只有 `├ ✓` 的空行,标题行也会带着图标和前缀叠成两个。
375
+ while (rendered.length) {
376
+ rendered[0] = stripIcon(rendered[0]);
377
+ if (hasVisibleText(rendered[0])) break;
378
+ rendered.shift();
371
379
  }
372
380
  const rows = rendered.length
373
381
  ? // 续行只剥外层 Box 的 1 格 left pad,保留 Input/Output 相对缩进
@@ -106,8 +106,11 @@ export class ExpandedToolIoView {
106
106
 
107
107
  /** flushLeft:贴左渲染(外层 Box 负责 1 格 pad);默认 false 保留 1 格前导空格。 */
108
108
  private flushLeft: boolean;
109
- /** 尾部附注(shell 的耗时)。挂到 Output 的 show-more 行上,没有那一行时单独成行。 */
110
- private footer: string | undefined;
109
+ /** 尾部附注(shell 的耗时)。挂到 Output 的 show-more 行上,没有那一行时单独成行。
110
+ * 传取值函数时每帧求值,值变了才重画:运行中的秒数要跟着动画 tick 走。 */
111
+ private footer: string | (() => string | undefined) | undefined;
112
+ /** 上一次求值结果,用来判断 footer 是否变化。 */
113
+ private footerText: string | undefined;
111
114
 
112
115
  constructor(
113
116
  theme: any,
@@ -224,7 +227,7 @@ export class ExpandedToolIoView {
224
227
  }
225
228
 
226
229
  /** 尾部附注(耗时等)。挂在 show-more 行上;无 show-more 行时单独成行。 */
227
- setFooter(text: string | undefined): void {
230
+ setFooter(text: string | (() => string | undefined) | undefined): void {
228
231
  if (this.footer === text) return;
229
232
  this.footer = text;
230
233
  this.invalidate();
@@ -268,6 +271,12 @@ export class ExpandedToolIoView {
268
271
  }
269
272
 
270
273
  render(width: number): string[] {
274
+ // footer 可以是取值函数(运行中的耗时),每帧重算;变了就丢掉行缓存。
275
+ const footer = typeof this.footer === "function" ? this.footer() : this.footer;
276
+ if (footer !== this.footerText) {
277
+ this.footerText = footer;
278
+ this.cachedLines = undefined;
279
+ }
271
280
  if (this.cachedLines !== undefined && this.cachedWidth === width) {
272
281
  return withIoViewMarkers(this, this.cachedLines);
273
282
  }
@@ -397,7 +406,7 @@ export class ExpandedToolIoView {
397
406
  limit: this.maxOutputLines,
398
407
  continued: false,
399
408
  section: "output",
400
- footerNote: this.footer,
409
+ footerNote: footer,
401
410
  });
402
411
  } else {
403
412
  this.truncated.output = outputWouldTruncate;
@@ -406,14 +415,14 @@ export class ExpandedToolIoView {
406
415
  limit: this.maxOutputLines,
407
416
  continued: false,
408
417
  section: "output",
409
- footerNote: this.footer,
418
+ footerNote: footer,
410
419
  });
411
420
  }
412
421
 
413
422
  // 耗时挂在 Output 的 show-more 行上(与折叠态一样,提示留在行尾);
414
423
  // 没截断就没有那一行,才单独成行。放在最后追加,前面记下的行号不受影响。
415
- if (this.footer && this.showMoreHeaderRows.output === undefined) {
416
- pushRailLine(theme.fg("muted", this.footer), false);
424
+ if (footer && this.showMoreHeaderRows.output === undefined) {
425
+ pushRailLine(theme.fg("muted", footer), false);
417
426
  }
418
427
 
419
428
  this.cachedWidth = width;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cc-extensions",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "license": "MIT",
5
5
  "description": "Claude Code-style TUI output with some personal touches and handy utilities.",
6
6
  "type": "module",
@@ -31,7 +31,9 @@
31
31
  ],
32
32
  "themes": [
33
33
  "./themes/cc-dark.json",
34
- "./themes/cc-light.json"
34
+ "./themes/cc-light.json",
35
+ "./themes/cc-dark-hc.json",
36
+ "./themes/cc-light-hc.json"
35
37
  ],
36
38
  "image": "https://raw.githubusercontent.com/minuque/pi-cc-extensions/main/assets/readme/preview.webp",
37
39
  "video": "https://github.com/minuque/pi-cc-extensions/releases/download/v0.9.4/demo.mp4"
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "https://pi.dev/schemas/theme.schema.json",
3
+ "name": "cc-dark-hc",
4
+ "appearance": "dark",
5
+ "vars": {
6
+ "fg": "#d6dde3",
7
+ "fgMuted": "#929eab",
8
+ "fgSubtle": "#768494",
9
+ "canvas": "#0d1117",
10
+ "canvasSubtle": "#1c2033",
11
+ "canvasInset": "#161a2b",
12
+ "border": "#1c2d44",
13
+ "borderMuted": "#16233a",
14
+ "blue": "#4f9eff",
15
+ "blueBright": "#79c0ff",
16
+ "green": "#37b34a",
17
+ "red": "#ff6e62",
18
+ "redSoft": "#ff7b72",
19
+ "yellow": "#cb9214",
20
+ "orange": "#f0883e",
21
+ "orangeSoft": "#ffa657",
22
+ "purple": "#be8fff",
23
+ "pink": "#f778ba",
24
+ "cyan": "#39c5cf",
25
+ "selection": "#1a4480",
26
+ "diffAddBg": "#0f2a24",
27
+ "diffDelBg": "#261418"
28
+ },
29
+ "colors": {
30
+ "accent": "blue",
31
+ "border": "border",
32
+ "borderAccent": "blue",
33
+ "borderMuted": "borderMuted",
34
+ "success": "green",
35
+ "error": "#ffc4bd",
36
+ "warning": "yellow",
37
+ "muted": "fgMuted",
38
+ "dim": "#71717a",
39
+ "text": "#f2f6fa",
40
+ "thinkingText": "#c6d0dc",
41
+ "selectedBg": "selection",
42
+ "scrollbarTrack": "borderMuted",
43
+ "scrollbarThumb": "#3a5274",
44
+ "searchMatchBg": "#1e1800",
45
+ "searchMatchText": "fg",
46
+ "userMessageBg": "#17375a",
47
+ "userMessageText": "#f2f6fa",
48
+ "customMessageBg": "borderMuted",
49
+ "customMessageText": "fgMuted",
50
+ "customMessageLabel": "blue",
51
+ "toolPendingBg": "#1a2534",
52
+ "toolSuccessBg": "#123c2b",
53
+ "toolErrorBg": "#4b1b21",
54
+ "toolTitle": "#f2f6fa",
55
+ "toolOutput": "#c6d0dc",
56
+ "mdHeading": "#b8ddff",
57
+ "mdLink": "#a3d0ff",
58
+ "mdLinkUrl": "#c6d0dc",
59
+ "mdCode": "#a5d6ff",
60
+ "mdCodeBlock": "#f2f6fa",
61
+ "mdCodeBlockBorder": "#6b95c7",
62
+ "mdQuote": "#c6d0dc",
63
+ "mdQuoteBorder": "#6b95c7",
64
+ "mdHr": "#6b95c7",
65
+ "mdListBullet": "#b8ddff",
66
+ "toolDiffAdded": "green",
67
+ "toolDiffRemoved": "red",
68
+ "toolDiffContext": "fgMuted",
69
+ "syntaxComment": "#c6d0dc",
70
+ "syntaxKeyword": "#ffc7c0",
71
+ "syntaxFunction": "#e0c9ff",
72
+ "syntaxVariable": "#ffc99b",
73
+ "syntaxString": "#a5d6ff",
74
+ "syntaxNumber": "#b8ddff",
75
+ "syntaxType": "#ffc98f",
76
+ "syntaxOperator": "#ffc7c0",
77
+ "syntaxPunctuation": "#f2f6fa",
78
+ "thinkingOff": "fgMuted",
79
+ "thinkingMinimal": "fgSubtle",
80
+ "thinkingLow": "blue",
81
+ "thinkingMedium": "blue",
82
+ "thinkingHigh": "blueBright",
83
+ "thinkingXhigh": "cyan",
84
+ "thinkingMax": "cyan",
85
+ "bashMode": "green"
86
+ },
87
+ "export": {
88
+ "pageBg": "#0a1424",
89
+ "cardBg": "canvasSubtle",
90
+ "infoBg": "#122544"
91
+ }
92
+ }
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "https://pi.dev/schemas/theme.schema.json",
3
+ "name": "cc-light-hc",
4
+ "appearance": "light",
5
+ "vars": {
6
+ "fg": "#1e252a",
7
+ "fgMuted": "#3e4a56",
8
+ "fgSubtle": "#46525e",
9
+ "canvas": "#ffffff",
10
+ "canvasSubtle": "#d6e1f1",
11
+ "canvasInset": "#e3ebf5",
12
+ "border": "#7391c2",
13
+ "borderMuted": "#a8bdd9",
14
+ "blue": "#0058ad",
15
+ "blueBright": "#004776",
16
+ "green": "#005a16",
17
+ "red": "#a81417",
18
+ "redSoft": "#9b1b1f",
19
+ "yellow": "#765300",
20
+ "orange": "#843f00",
21
+ "orangeSoft": "#663600",
22
+ "purple": "#65339c",
23
+ "pink": "#931662",
24
+ "cyan": "#005359",
25
+ "selection": "#c5d6ee",
26
+ "diffAddBg": "#c3dcd0",
27
+ "diffDelBg": "#f0d5d8"
28
+ },
29
+ "colors": {
30
+ "accent": "blue",
31
+ "border": "border",
32
+ "borderAccent": "blue",
33
+ "borderMuted": "borderMuted",
34
+ "success": "green",
35
+ "error": "#6f0307",
36
+ "warning": "yellow",
37
+ "muted": "fgMuted",
38
+ "dim": "#4e5966",
39
+ "text": "#0a0f14",
40
+ "thinkingText": "#262e36",
41
+ "selectedBg": "selection",
42
+ "scrollbarTrack": "borderMuted",
43
+ "scrollbarThumb": "#5d84b8",
44
+ "searchMatchBg": "#f3e4b0",
45
+ "searchMatchText": "fg",
46
+ "userMessageBg": "#8ecdfa",
47
+ "userMessageText": "#0a0f14",
48
+ "customMessageBg": "#c5d3e6",
49
+ "customMessageText": "fgMuted",
50
+ "customMessageLabel": "blue",
51
+ "toolPendingBg": "canvasSubtle",
52
+ "toolSuccessBg": "#8ce3b8",
53
+ "toolErrorBg": "#ffbdba",
54
+ "toolTitle": "#0a0f14",
55
+ "toolOutput": "#262e36",
56
+ "mdHeading": "#00294f",
57
+ "mdLink": "#003263",
58
+ "mdLinkUrl": "#262e36",
59
+ "mdCode": "#00294f",
60
+ "mdCodeBlock": "#0a0f14",
61
+ "mdCodeBlockBorder": "#2f4d75",
62
+ "mdQuote": "#262e36",
63
+ "mdQuoteBorder": "#4a6d9e",
64
+ "mdHr": "#2f4d75",
65
+ "mdListBullet": "#00294f",
66
+ "toolDiffAdded": "green",
67
+ "toolDiffRemoved": "red",
68
+ "toolDiffContext": "fgMuted",
69
+ "syntaxComment": "#262e36",
70
+ "syntaxKeyword": "#6f0307",
71
+ "syntaxFunction": "#3f1a66",
72
+ "syntaxVariable": "#552800",
73
+ "syntaxString": "#00294f",
74
+ "syntaxNumber": "#00294f",
75
+ "syntaxType": "#522500",
76
+ "syntaxOperator": "#6f0307",
77
+ "syntaxPunctuation": "#0a0f14",
78
+ "thinkingOff": "fgMuted",
79
+ "thinkingMinimal": "fgSubtle",
80
+ "thinkingLow": "blue",
81
+ "thinkingMedium": "blue",
82
+ "thinkingHigh": "blueBright",
83
+ "thinkingXhigh": "cyan",
84
+ "thinkingMax": "cyan",
85
+ "bashMode": "green"
86
+ },
87
+ "export": {
88
+ "pageBg": "#f4f7fb",
89
+ "cardBg": "canvasSubtle",
90
+ "infoBg": "#d0dff2"
91
+ }
92
+ }