@owlburtoe/pi-cc-tools 1.1.1 → 1.1.6
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.md +11 -10
- package/config/config.example.json +2 -2
- package/extensions/index.ts +275 -69
- package/extensions/message-chrome.ts +0 -11
- package/extensions/spinner.ts +0 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,15 +4,16 @@ Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots,
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Compact
|
|
7
|
+
- **Compact Claude Code-like tool rendering** for `read`, `bash`, `grep`, `find`, `ls`, `edit`, and `write`, including `⏺ Tool(args)` headers and `⎿` result rows
|
|
8
8
|
- **Semantic bash display** that renders common read-only shell one-liners like `nl -ba file | sed -n '1,200p'` as Claude Code-style `Read file (lines 1-200)` rows
|
|
9
|
+
- **Read-only inspection grouping** that collapses consecutive `read`/`grep`/`find`/`ls` rows into one Claude-style `Inspect` block, capped to five visible entries by default
|
|
9
10
|
- **Claude-style OpenAI tool rendering** for `apply_patch` plus common Pi/OpenAI-style tools like `webfetch`, `web_search`, `fetch_content`, task tools, and context tools
|
|
10
11
|
- **`apply_patch` diff previews** that render parsed file patches in the call phase, similar to `edit`/`write`
|
|
11
12
|
- **Adaptive edit/write diffs** with split or unified layouts, syntax highlighting, and inline word-level emphasis
|
|
12
13
|
- **Diff stat bar** with colored add/remove summary and hunk metadata
|
|
13
14
|
- **Progressive collapsed diff hints** that shorten on narrow terminals
|
|
14
15
|
- **Thinking labels** during streaming and final messages, with context sanitization
|
|
15
|
-
- **Claude-style transcript grammar controls** for assistant/thinking prefixes, message spacing, hidden thinking labels
|
|
16
|
+
- **Claude-style transcript grammar controls** for assistant/thinking prefixes, message spacing, and hidden thinking labels
|
|
16
17
|
- **MCP-aware rendering** with hidden, summary, and preview modes
|
|
17
18
|
- **Configurable output modes** for read, search, bash, and MCP results
|
|
18
19
|
- **Transparent tool backgrounds** in `transparent` or `border` mode
|
|
@@ -26,7 +27,7 @@ Set in `.pi/settings.json` or `~/.pi/settings.json`:
|
|
|
26
27
|
|
|
27
28
|
```json
|
|
28
29
|
{
|
|
29
|
-
"toolBackground": "
|
|
30
|
+
"toolBackground": "transparent",
|
|
30
31
|
"readOutputMode": "preview",
|
|
31
32
|
"searchOutputMode": "preview",
|
|
32
33
|
"mcpOutputMode": "preview",
|
|
@@ -35,6 +36,8 @@ Set in `.pi/settings.json` or `~/.pi/settings.json`:
|
|
|
35
36
|
"bashCollapsedLines": 10,
|
|
36
37
|
"bashStackConsecutive": true,
|
|
37
38
|
"bashSemanticDisplay": true,
|
|
39
|
+
"readOnlyToolGrouping": true,
|
|
40
|
+
"readOnlyToolGroupLimit": 5,
|
|
38
41
|
"diffCollapsedLines": 24,
|
|
39
42
|
"themeAdaptive": true,
|
|
40
43
|
"diffTheme": "github-dark",
|
|
@@ -45,8 +48,6 @@ Set in `.pi/settings.json` or `~/.pi/settings.json`:
|
|
|
45
48
|
"assistantPrefix": "●",
|
|
46
49
|
"thinkingPrefix": "✻",
|
|
47
50
|
"messageSpacing": "comfortable",
|
|
48
|
-
"workingTipEnabled": true,
|
|
49
|
-
"workingTipText": "Run /install-github-app to tag @claude right from your GitHub issues and PRs",
|
|
50
51
|
"hiddenThinkingLabel": "Pondering..."
|
|
51
52
|
}
|
|
52
53
|
```
|
|
@@ -113,13 +114,10 @@ Color selections are persisted as `spinnerColor` / `spinnerStatusColor` in `~/.p
|
|
|
113
114
|
/cc-message assistant-prefix ● # set assistant paragraph prefix
|
|
114
115
|
/cc-message thinking-prefix ✻ # set visible thinking prefix
|
|
115
116
|
/cc-message hidden-thinking-label Pondering...
|
|
116
|
-
/cc-message tip on # show active working tip line
|
|
117
|
-
/cc-message tip off # hide active working tip line
|
|
118
|
-
/cc-message tip text Run /help for tips # set active working tip text
|
|
119
117
|
/cc-message reset # restore message chrome defaults
|
|
120
118
|
```
|
|
121
119
|
|
|
122
|
-
`messageStyle: "claude"` trims leading/trailing blank render lines, collapses paragraph gaps, and aligns wrapped assistant/thinking lines under the message body, matching Claude Code's sparse transcript grammar. `messageStyle: "classic"` keeps the previous package behavior.
|
|
120
|
+
`messageStyle: "claude"` trims leading/trailing blank render lines, collapses paragraph gaps, and aligns wrapped assistant/thinking lines under the message body, matching Claude Code's sparse transcript grammar. `messageStyle: "classic"` keeps the previous package behavior.
|
|
123
121
|
|
|
124
122
|
### Tool background modes
|
|
125
123
|
|
|
@@ -127,7 +125,8 @@ Color selections are persisted as `spinnerColor` / `spinnerStatusColor` in `~/.p
|
|
|
127
125
|
|-------|----------|
|
|
128
126
|
| `default` | Standard Pi tool backgrounds |
|
|
129
127
|
| `transparent` | Transparent tool backgrounds |
|
|
130
|
-
| `border` | Transparent backgrounds with top/bottom border lines |
|
|
128
|
+
| `border` | Transparent backgrounds with top/bottom border lines (alias for `outlines`) |
|
|
129
|
+
| `outlines` | Transparent backgrounds with top/bottom border lines |
|
|
131
130
|
|
|
132
131
|
### Output modes
|
|
133
132
|
|
|
@@ -152,6 +151,7 @@ Color selections are persisted as `spinnerColor` / `spinnerStatusColor` in `~/.p
|
|
|
152
151
|
|---------|---------|-------------|
|
|
153
152
|
| `bashStackConsecutive` | `true` | Remove the extra synthetic spacer between adjacent bash tool rows so command bursts render as a tight stack |
|
|
154
153
|
| `bashSemanticDisplay` | `true` | Render common read-only shell file-inspection commands as semantic `Read` rows instead of raw `Bash` rows |
|
|
154
|
+
| `readOnlyToolGrouping` | `true` | Collapse adjacent read-only inspection tools into one `Inspect` block; mutating `write`/`edit`/`apply_patch` rows stay independent |
|
|
155
155
|
|
|
156
156
|
### Numeric settings
|
|
157
157
|
|
|
@@ -161,6 +161,7 @@ Color selections are persisted as `spinnerColor` / `spinnerStatusColor` in `~/.p
|
|
|
161
161
|
| `expandedPreviewMaxLines` | `4000` | Max lines when fully expanded |
|
|
162
162
|
| `bashCollapsedLines` | `10` | Lines for collapsed bash output |
|
|
163
163
|
| `diffCollapsedLines` | `24` | Diff lines before collapsing |
|
|
164
|
+
| `readOnlyToolGroupLimit` | `5` | Max inspection entries shown inside a grouped read-only tool block |
|
|
164
165
|
|
|
165
166
|
## Notes
|
|
166
167
|
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"bashCollapsedLines": 10,
|
|
10
10
|
"bashStackConsecutive": true,
|
|
11
11
|
"bashSemanticDisplay": true,
|
|
12
|
+
"readOnlyToolGrouping": true,
|
|
13
|
+
"readOnlyToolGroupLimit": 5,
|
|
12
14
|
"diffCollapsedLines": 24,
|
|
13
15
|
"showTruncationHints": false,
|
|
14
16
|
"themeAdaptive": true,
|
|
@@ -21,7 +23,5 @@
|
|
|
21
23
|
"assistantPrefix": "●",
|
|
22
24
|
"thinkingPrefix": "✻",
|
|
23
25
|
"messageSpacing": "comfortable",
|
|
24
|
-
"workingTipEnabled": true,
|
|
25
|
-
"workingTipText": "Run /install-github-app to tag @claude right from your GitHub issues and PRs",
|
|
26
26
|
"hiddenThinkingLabel": "Pondering..."
|
|
27
27
|
}
|
package/extensions/index.ts
CHANGED
|
@@ -71,7 +71,12 @@ const WRAP_MARK = "\uE000";
|
|
|
71
71
|
const KITTY_IMAGE_PREFIX = "\x1b_G";
|
|
72
72
|
const ITERM2_IMAGE_PREFIX = "\x1b]1337;File=";
|
|
73
73
|
|
|
74
|
-
let toolBackgroundMode: "default" | "transparent" | "outlines" = "
|
|
74
|
+
let toolBackgroundMode: "default" | "transparent" | "outlines" = "transparent";
|
|
75
|
+
|
|
76
|
+
const CLAUDE_TOOL_GLYPH = "⏺";
|
|
77
|
+
const CLAUDE_RESULT_GLYPH = "⎿";
|
|
78
|
+
const CLAUDE_RESULT_PREFIX = ` ${CLAUDE_RESULT_GLYPH} `;
|
|
79
|
+
const CLAUDE_RESULT_CONTINUATION = " ".repeat(CLAUDE_RESULT_PREFIX.length);
|
|
75
80
|
|
|
76
81
|
type SpinnerVerbMode = "append" | "replace";
|
|
77
82
|
|
|
@@ -88,6 +93,8 @@ interface SettingsFile {
|
|
|
88
93
|
bashStackConsecutive?: boolean;
|
|
89
94
|
/** When true (default), read-only shell file-inspection one-liners render as semantic Read rows instead of raw Bash rows. */
|
|
90
95
|
bashSemanticDisplay?: boolean;
|
|
96
|
+
readOnlyToolGrouping?: boolean;
|
|
97
|
+
readOnlyToolGroupLimit?: number;
|
|
91
98
|
showTruncationHints?: boolean;
|
|
92
99
|
diffCollapsedLines?: number;
|
|
93
100
|
diffTheme?: string;
|
|
@@ -127,10 +134,6 @@ interface SettingsFile {
|
|
|
127
134
|
thinkingPrefix?: string;
|
|
128
135
|
/** Blank-line normalization in assistant/thinking transcript blocks. */
|
|
129
136
|
messageSpacing?: MessageSpacing;
|
|
130
|
-
/** Whether the active working spinner may show a subordinate Claude-style tip line. */
|
|
131
|
-
workingTipEnabled?: boolean;
|
|
132
|
-
/** Custom subordinate tip line text for the active working spinner. */
|
|
133
|
-
workingTipText?: string;
|
|
134
137
|
/** Label shown when thinking blocks are hidden by the Pi UI. */
|
|
135
138
|
hiddenThinkingLabel?: string;
|
|
136
139
|
}
|
|
@@ -271,7 +274,7 @@ function syncToolBackgroundMode(): void {
|
|
|
271
274
|
const settings = readSettings();
|
|
272
275
|
// Backward compat: "border" was renamed to "outlines"
|
|
273
276
|
const raw = settings.toolBackground === "border" ? "outlines" : settings.toolBackground;
|
|
274
|
-
toolBackgroundMode = raw ?? "
|
|
277
|
+
toolBackgroundMode = raw ?? "transparent";
|
|
275
278
|
}
|
|
276
279
|
|
|
277
280
|
function setThemeBg(theme: unknown, key: string, value: string): void {
|
|
@@ -341,6 +344,253 @@ function shouldStackConsecutiveBash(): boolean {
|
|
|
341
344
|
return readSettings().bashStackConsecutive !== false;
|
|
342
345
|
}
|
|
343
346
|
|
|
347
|
+
function readOnlyToolGroupingEnabled(): boolean {
|
|
348
|
+
return readSettings().readOnlyToolGrouping !== false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function readOnlyToolGroupLimit(): number {
|
|
352
|
+
const value = readSettings().readOnlyToolGroupLimit;
|
|
353
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
354
|
+
? Math.max(1, Math.min(20, Math.floor(value)))
|
|
355
|
+
: 5;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function toolComponentRecord(value: unknown): Record<string, any> {
|
|
359
|
+
return value as Record<string, any>;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function componentCwd(value: unknown): string {
|
|
363
|
+
const cwd = toolComponentRecord(value).cwd;
|
|
364
|
+
return typeof cwd === "string" && cwd ? cwd : process.cwd();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function componentTextContent(value: unknown): string {
|
|
368
|
+
return getTextContent(toolComponentRecord(value).result);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function componentHasImageResult(value: unknown): boolean {
|
|
372
|
+
return !!getFirstImageBlock(toolComponentRecord(value).result);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function bashReadDisplayInfo(value: unknown): BashDisplayInfo | null {
|
|
376
|
+
const rec = toolComponentRecord(value);
|
|
377
|
+
if (rec.toolName !== "bash" || !bashSemanticDisplayEnabled()) return null;
|
|
378
|
+
return classifyBashCommandForDisplay(rec.args?.command ?? "");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function isReadOnlyInspectionToolExecution(value: unknown): boolean {
|
|
382
|
+
if (!readOnlyToolGroupingEnabled() || !isToolExecutionLike(value)) return false;
|
|
383
|
+
const rec = toolComponentRecord(value);
|
|
384
|
+
if (rec.expanded === true || componentHasImageResult(value)) return false;
|
|
385
|
+
if (rec.toolName === "read" || rec.toolName === "grep" || rec.toolName === "find" || rec.toolName === "ls") return true;
|
|
386
|
+
return !!bashReadDisplayInfo(value);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function hasConsecutiveReadOnlyInspectionToolChildren(children: unknown[]): boolean {
|
|
390
|
+
let previousWasInspect = false;
|
|
391
|
+
for (const child of children) {
|
|
392
|
+
const currentIsInspect = isReadOnlyInspectionToolExecution(child);
|
|
393
|
+
if (currentIsInspect && previousWasInspect) return true;
|
|
394
|
+
previousWasInspect = currentIsInspect;
|
|
395
|
+
}
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function plural(count: number, noun: string): string {
|
|
400
|
+
if (count === 1) return `1 ${noun}`;
|
|
401
|
+
const suffix = /(?:s|x|z|ch|sh)$/i.test(noun) ? "es" : "s";
|
|
402
|
+
return `${count} ${noun}${suffix}`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function textDataLines(text: string): string[] {
|
|
406
|
+
return text.split("\n").filter((line) => line.trim().length > 0 && !line.trimStart().startsWith("["));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function compactList(values: string[], limit: number): string {
|
|
410
|
+
if (values.length === 0) return "";
|
|
411
|
+
const shown = values.slice(0, limit);
|
|
412
|
+
const suffix = values.length > shown.length ? `, … +${values.length - shown.length}` : "";
|
|
413
|
+
return `${shown.join(", ")}${suffix}`;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function uniqueLeadingGrepFiles(text: string, limit: number): string[] {
|
|
417
|
+
const seen = new Set<string>();
|
|
418
|
+
const files: string[] = [];
|
|
419
|
+
for (const rawLine of text.split("\n")) {
|
|
420
|
+
const line = rawLine.trim();
|
|
421
|
+
if (!line || line.startsWith("[")) continue;
|
|
422
|
+
const match = /^(.+?)(?::\d+:|-\d+-)/.exec(line);
|
|
423
|
+
if (!match) continue;
|
|
424
|
+
const file = match[1];
|
|
425
|
+
if (seen.has(file)) continue;
|
|
426
|
+
seen.add(file);
|
|
427
|
+
files.push(file);
|
|
428
|
+
if (files.length >= limit + 1) break;
|
|
429
|
+
}
|
|
430
|
+
return files;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function formatOffsetLimit(args: any): string {
|
|
434
|
+
const parts: string[] = [];
|
|
435
|
+
if (args?.offset !== undefined && args?.offset !== null) parts.push(`offset=${args.offset}`);
|
|
436
|
+
if (args?.limit !== undefined && args?.limit !== null) parts.push(`limit=${args.limit}`);
|
|
437
|
+
return parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function readInspectionTarget(value: unknown): string {
|
|
441
|
+
const rec = toolComponentRecord(value);
|
|
442
|
+
return `${shortPath(componentCwd(value), rec.args?.path ?? "")}${formatOffsetLimit(rec.args)}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function grepInspectionTarget(value: unknown): string {
|
|
446
|
+
const rec = toolComponentRecord(value);
|
|
447
|
+
const pattern = summarizeText(rec.args?.pattern ?? "", 40);
|
|
448
|
+
const path = rec.args?.path ? ` in ${shortPath(componentCwd(value), rec.args.path)}` : "";
|
|
449
|
+
return `"${pattern}"${path}`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function findInspectionTarget(value: unknown): string {
|
|
453
|
+
const rec = toolComponentRecord(value);
|
|
454
|
+
const pattern = summarizeText(rec.args?.pattern ?? "", 40);
|
|
455
|
+
const path = rec.args?.path ? ` in ${shortPath(componentCwd(value), rec.args.path)}` : "";
|
|
456
|
+
return `"${pattern}"${path}`;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function listInspectionTarget(value: unknown): string {
|
|
460
|
+
const rec = toolComponentRecord(value);
|
|
461
|
+
return shortPath(componentCwd(value), rec.args?.path ?? ".");
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function readInspectionStatus(value: unknown): string {
|
|
465
|
+
const rec = toolComponentRecord(value);
|
|
466
|
+
if (!rec.result) return "Reading...";
|
|
467
|
+
if (rec.result?.isError) return "failed";
|
|
468
|
+
const text = componentTextContent(value);
|
|
469
|
+
const lines = text.length === 0 ? 0 : text.split("\n").length;
|
|
470
|
+
const suffix = rec.result?.details?.truncation?.truncated ? " (truncated)" : "";
|
|
471
|
+
return `${plural(lines, "line")} loaded${suffix}`;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function grepInspectionStatus(value: unknown): string {
|
|
475
|
+
const rec = toolComponentRecord(value);
|
|
476
|
+
if (!rec.result) return "Searching...";
|
|
477
|
+
if (rec.result?.isError) return "failed";
|
|
478
|
+
const text = componentTextContent(value).trim();
|
|
479
|
+
if (!text || /^No matches found\b/i.test(text)) return "no matches";
|
|
480
|
+
const lines = textDataLines(text);
|
|
481
|
+
let status = plural(lines.length, "match");
|
|
482
|
+
const files = uniqueLeadingGrepFiles(text, readOnlyToolGroupLimit());
|
|
483
|
+
if (files.length > 1) status += ` in ${compactList(files, readOnlyToolGroupLimit())}`;
|
|
484
|
+
if (rec.result?.details?.truncation?.truncated) status += " (truncated)";
|
|
485
|
+
return status;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function findInspectionStatus(value: unknown): string {
|
|
489
|
+
const rec = toolComponentRecord(value);
|
|
490
|
+
if (!rec.result) return "Finding...";
|
|
491
|
+
if (rec.result?.isError) return "failed";
|
|
492
|
+
const text = componentTextContent(value).trim();
|
|
493
|
+
if (!text || /^No files found\b/i.test(text)) return "no files";
|
|
494
|
+
const items = textDataLines(text);
|
|
495
|
+
let status = plural(items.length, "file");
|
|
496
|
+
if (items.length > 0) status += `: ${compactList(items, readOnlyToolGroupLimit())}`;
|
|
497
|
+
if (rec.result?.details?.truncation?.truncated) status += " (truncated)";
|
|
498
|
+
return status;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function listInspectionStatus(value: unknown): string {
|
|
502
|
+
const rec = toolComponentRecord(value);
|
|
503
|
+
if (!rec.result) return "Listing...";
|
|
504
|
+
if (rec.result?.isError) return "failed";
|
|
505
|
+
const text = componentTextContent(value).trim();
|
|
506
|
+
if (!text || /^empty directory\b/i.test(text)) return "empty directory";
|
|
507
|
+
const items = textDataLines(text);
|
|
508
|
+
let status = plural(items.length, "entry");
|
|
509
|
+
if (items.length > 0) status += `: ${compactList(items, readOnlyToolGroupLimit())}`;
|
|
510
|
+
return status;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function bashReadInspectionTarget(value: unknown): string {
|
|
514
|
+
const info = bashReadDisplayInfo(value);
|
|
515
|
+
if (!info) return "";
|
|
516
|
+
const range = info.rangeLabel ? ` (${info.rangeLabel})` : "";
|
|
517
|
+
return `${shortPath(componentCwd(value), info.path)}${range}`;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function bashReadInspectionStatus(value: unknown): string {
|
|
521
|
+
const rec = toolComponentRecord(value);
|
|
522
|
+
if (!rec.result) return "Reading...";
|
|
523
|
+
if (rec.result?.isError) return "failed";
|
|
524
|
+
const count = componentTextContent(value).split("\n").filter((line) => line.trim().length > 0).length;
|
|
525
|
+
return `${plural(count, "line")} read`;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function summarizeReadOnlyInspectionTool(value: unknown): string {
|
|
529
|
+
const rec = toolComponentRecord(value);
|
|
530
|
+
if (rec.toolName === "read") return `Read ${readInspectionTarget(value)} — ${readInspectionStatus(value)}`;
|
|
531
|
+
if (rec.toolName === "grep") return `Grep ${grepInspectionTarget(value)} — ${grepInspectionStatus(value)}`;
|
|
532
|
+
if (rec.toolName === "find") return `Find ${findInspectionTarget(value)} — ${findInspectionStatus(value)}`;
|
|
533
|
+
if (rec.toolName === "ls") return `List ${listInspectionTarget(value)} — ${listInspectionStatus(value)}`;
|
|
534
|
+
return `Read ${bashReadInspectionTarget(value)} — ${bashReadInspectionStatus(value)}`;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function renderInspectionGroup(group: unknown[], width: number): string[] {
|
|
538
|
+
syncToolBackgroundMode();
|
|
539
|
+
const limit = readOnlyToolGroupLimit();
|
|
540
|
+
const shown = group.slice(0, limit);
|
|
541
|
+
const remaining = group.length - shown.length;
|
|
542
|
+
const core: string[] = [` ${WRAP_MARK}${CLAUDE_TOOL_GLYPH} Inspect(${plural(group.length, "tool use")})`];
|
|
543
|
+
for (let index = 0; index < shown.length; index++) {
|
|
544
|
+
const prefix = index === 0 ? `${TOOL_RULE}${CLAUDE_RESULT_PREFIX}${TRANSPARENT_RESET}` : CLAUDE_RESULT_CONTINUATION;
|
|
545
|
+
core.push(`${prefix}${WRAP_MARK}${summarizeReadOnlyInspectionTool(shown[index])}`);
|
|
546
|
+
}
|
|
547
|
+
if (remaining > 0) {
|
|
548
|
+
const prefix = shown.length === 0 ? `${TOOL_RULE}${CLAUDE_RESULT_PREFIX}${TRANSPARENT_RESET}` : CLAUDE_RESULT_CONTINUATION;
|
|
549
|
+
core.push(`${prefix}${WRAP_MARK}… ${remaining} more inspection${remaining === 1 ? "" : "s"}`);
|
|
550
|
+
}
|
|
551
|
+
const renderedCore = core.flatMap((line) => wrapMarkedLine(line, width)).map((line) => padToWidth(line, width));
|
|
552
|
+
if (toolBackgroundMode === "outlines") return [" ".repeat(width), borderLine(width), ...renderedCore, borderLine(width)];
|
|
553
|
+
if (toolBackgroundMode === "transparent") return [" ".repeat(width), ...renderedCore];
|
|
554
|
+
return renderedCore;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function renderWithGroupedReadOnlyInspectionTools(container: any, width: number): string[] | null {
|
|
558
|
+
const children = Array.isArray(container?.children) ? container.children : null;
|
|
559
|
+
if (!children || !hasConsecutiveReadOnlyInspectionToolChildren(children)) return null;
|
|
560
|
+
|
|
561
|
+
const rendered: string[] = [];
|
|
562
|
+
let group: unknown[] = [];
|
|
563
|
+
let previousWasBash = false;
|
|
564
|
+
const flushGroup = () => {
|
|
565
|
+
if (group.length === 0) return;
|
|
566
|
+
if (group.length === 1) {
|
|
567
|
+
const child = group[0] as any;
|
|
568
|
+
const currentIsBash = isBashToolExecution(child);
|
|
569
|
+
const childLines = typeof child?.render === "function" ? child.render(width) : [];
|
|
570
|
+
rendered.push(...(currentIsBash && previousWasBash ? dropLeadingSpacerLine(childLines) : childLines));
|
|
571
|
+
previousWasBash = currentIsBash;
|
|
572
|
+
} else {
|
|
573
|
+
rendered.push(...renderInspectionGroup(group, width));
|
|
574
|
+
previousWasBash = false;
|
|
575
|
+
}
|
|
576
|
+
group = [];
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
for (const child of children) {
|
|
580
|
+
if (isReadOnlyInspectionToolExecution(child)) {
|
|
581
|
+
group.push(child);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
flushGroup();
|
|
585
|
+
const currentIsBash = isBashToolExecution(child);
|
|
586
|
+
const childLines = typeof child?.render === "function" ? child.render(width) : [];
|
|
587
|
+
rendered.push(...(currentIsBash && previousWasBash ? dropLeadingSpacerLine(childLines) : childLines));
|
|
588
|
+
previousWasBash = currentIsBash;
|
|
589
|
+
}
|
|
590
|
+
flushGroup();
|
|
591
|
+
return rendered;
|
|
592
|
+
}
|
|
593
|
+
|
|
344
594
|
function hasConsecutiveBashToolChildren(children: unknown[]): boolean {
|
|
345
595
|
let previousWasBash = false;
|
|
346
596
|
for (const child of children) {
|
|
@@ -402,6 +652,8 @@ function patchGlobalToolBorders(): void {
|
|
|
402
652
|
const originalRender = proto.render;
|
|
403
653
|
proto.render = function patchedContainerRender(width: number): string[] {
|
|
404
654
|
if (!isToolExecutionLike(this)) {
|
|
655
|
+
const grouped = renderWithGroupedReadOnlyInspectionTools(this, width);
|
|
656
|
+
if (grouped) return grouped;
|
|
405
657
|
const stacked = renderWithStackedConsecutiveBash(this, width);
|
|
406
658
|
if (stacked) return stacked;
|
|
407
659
|
}
|
|
@@ -1000,7 +1252,7 @@ function toolHeader(tool: string, summary: string, theme: Theme, prefix = ""): s
|
|
|
1000
1252
|
applyThemePaletteIfNeeded(theme);
|
|
1001
1253
|
const label = theme.fg("toolTitle", theme.bold(tool));
|
|
1002
1254
|
if (!summary) return `${prefix}${label}`;
|
|
1003
|
-
return `${prefix}${label} ${WRAP_MARK}${theme.fg("accent", summary)}`;
|
|
1255
|
+
return `${prefix}${label}${theme.fg("muted", "(")}${WRAP_MARK}${theme.fg("accent", summary)}${theme.fg("muted", ")")}`;
|
|
1004
1256
|
}
|
|
1005
1257
|
|
|
1006
1258
|
function setToolStatus(ctx: any, status: "pending" | "success" | "error"): void {
|
|
@@ -1064,8 +1316,8 @@ function getWriteWasNewFile(ctx: any, cwd: string, filePath: string, reveal = sh
|
|
|
1064
1316
|
|
|
1065
1317
|
function toolStatusDot(ctx: any, theme: Theme): string {
|
|
1066
1318
|
const status = ctx.state?._toolStatus as "pending" | "success" | "error" | undefined;
|
|
1067
|
-
if (status === "
|
|
1068
|
-
if (status === "
|
|
1319
|
+
if (status === "error") return `${theme.fg("error", CLAUDE_TOOL_GLYPH)} `;
|
|
1320
|
+
if (status === "success") return `${theme.fg("accent", CLAUDE_TOOL_GLYPH)} `;
|
|
1069
1321
|
return `${blinkDot(ctx, theme)} `;
|
|
1070
1322
|
}
|
|
1071
1323
|
|
|
@@ -1073,13 +1325,12 @@ function toolStatusDot(ctx: any, theme: Theme): string {
|
|
|
1073
1325
|
// Branch connector — visual tree from header to output
|
|
1074
1326
|
// ---------------------------------------------------------------------------
|
|
1075
1327
|
|
|
1076
|
-
function branchIndent(text: string,
|
|
1077
|
-
|
|
1078
|
-
return `${prefix}${WRAP_MARK}${text}`;
|
|
1328
|
+
function branchIndent(text: string, _continued = false): string {
|
|
1329
|
+
return `${CLAUDE_RESULT_CONTINUATION}${WRAP_MARK}${text}`;
|
|
1079
1330
|
}
|
|
1080
1331
|
|
|
1081
|
-
function branchLead(text: string,
|
|
1082
|
-
return `${TOOL_RULE}${
|
|
1332
|
+
function branchLead(text: string, _continued = false): string {
|
|
1333
|
+
return `${TOOL_RULE}${CLAUDE_RESULT_PREFIX}${TRANSPARENT_RESET}${WRAP_MARK}${text}`;
|
|
1083
1334
|
}
|
|
1084
1335
|
|
|
1085
1336
|
function withBranch(content: string, _theme: Theme, _isError = false, continued = false): string {
|
|
@@ -1092,20 +1343,11 @@ function withBranch(content: string, _theme: Theme, _isError = false, continued
|
|
|
1092
1343
|
}
|
|
1093
1344
|
|
|
1094
1345
|
function withFinalBranchBlock(content: string, theme: Theme, isError = false): string {
|
|
1095
|
-
|
|
1096
|
-
const lines = content.split("\n");
|
|
1097
|
-
const first = lines[0] ?? "";
|
|
1098
|
-
if (lines.length === 1) return branchLead(first, false);
|
|
1099
|
-
const middle = lines.slice(1, -1).map((line) => branchIndent(line, true));
|
|
1100
|
-
const last = lines[lines.length - 1] ?? "";
|
|
1101
|
-
return [branchLead(first, true), ...middle, branchLead(last, false)].join("\n");
|
|
1346
|
+
return withBranch(content, theme, isError);
|
|
1102
1347
|
}
|
|
1103
1348
|
|
|
1104
1349
|
function indentBranchBlock(block: string): string {
|
|
1105
|
-
return block
|
|
1106
|
-
.split("\n")
|
|
1107
|
-
.map((line) => (line ? ` ${line}` : line))
|
|
1108
|
-
.join("\n");
|
|
1350
|
+
return block;
|
|
1109
1351
|
}
|
|
1110
1352
|
|
|
1111
1353
|
// ---------------------------------------------------------------------------
|
|
@@ -1207,8 +1449,8 @@ function clearBlinkTimer(ctx: any): void {
|
|
|
1207
1449
|
function blinkDot(ctx: any, theme: Theme): string {
|
|
1208
1450
|
setupBlinkTimer(ctx);
|
|
1209
1451
|
const key = getBlinkKey(ctx);
|
|
1210
|
-
if (key?._blinkActive !== true) return theme.fg("muted",
|
|
1211
|
-
return _globalBlinkPhase ? theme.fg("
|
|
1452
|
+
if (key?._blinkActive !== true) return theme.fg("muted", CLAUDE_TOOL_GLYPH);
|
|
1453
|
+
return _globalBlinkPhase ? theme.fg("accent", CLAUDE_TOOL_GLYPH) : theme.fg("muted", CLAUDE_TOOL_GLYPH);
|
|
1212
1454
|
}
|
|
1213
1455
|
|
|
1214
1456
|
// ---------------------------------------------------------------------------
|
|
@@ -1294,6 +1536,8 @@ function markedContinuationPrefix(prefix: string): string {
|
|
|
1294
1536
|
if (branchMatch) {
|
|
1295
1537
|
return `${branchMatch[1]}${TOOL_RULE}│${TRANSPARENT_RESET} `;
|
|
1296
1538
|
}
|
|
1539
|
+
const resultMatch = /^(\s*)⎿\s+/.exec(plain);
|
|
1540
|
+
if (resultMatch) return " ".repeat(visibleWidth(prefix));
|
|
1297
1541
|
return " ".repeat(visibleWidth(prefix));
|
|
1298
1542
|
}
|
|
1299
1543
|
|
|
@@ -4247,7 +4491,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4247
4491
|
},
|
|
4248
4492
|
});
|
|
4249
4493
|
|
|
4250
|
-
const MESSAGE_COMMANDS = ["style", "spacing", "assistant-prefix", "thinking-prefix", "
|
|
4494
|
+
const MESSAGE_COMMANDS = ["style", "spacing", "assistant-prefix", "thinking-prefix", "hidden-thinking-label", "reset", "status"] as const;
|
|
4251
4495
|
pi.registerCommand("cc-message", {
|
|
4252
4496
|
description: "Configure Claude-style assistant/thinking transcript chrome",
|
|
4253
4497
|
getArgumentCompletions(prefix: string) {
|
|
@@ -4264,7 +4508,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
4264
4508
|
: c === "spacing" ? "Set blank-line rhythm: compact or comfortable"
|
|
4265
4509
|
: c === "assistant-prefix" ? "Set assistant paragraph prefix glyph/text"
|
|
4266
4510
|
: c === "thinking-prefix" ? "Set visible thinking prefix glyph/text"
|
|
4267
|
-
: c === "tip" ? "Enable, disable, or customize active working tip line"
|
|
4268
4511
|
: c === "hidden-thinking-label" ? "Set label shown when thinking is hidden"
|
|
4269
4512
|
: c === "reset" ? "Reset message chrome settings"
|
|
4270
4513
|
: "Show current message chrome settings",
|
|
@@ -4282,12 +4525,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
4282
4525
|
.filter((v) => v.startsWith(valuePrefix))
|
|
4283
4526
|
.map((v) => ({ value: `spacing ${v}`, label: v, description: v === "comfortable" ? "Preserve one blank line between paragraphs" : "Remove blank lines inside assistant/thinking blocks" }));
|
|
4284
4527
|
}
|
|
4285
|
-
if (parts[0] === "tip") {
|
|
4286
|
-
const valuePrefix = (parts[1] ?? "").toLowerCase();
|
|
4287
|
-
return ["on", "off", "text", "reset"]
|
|
4288
|
-
.filter((v) => v.startsWith(valuePrefix))
|
|
4289
|
-
.map((v) => ({ value: `tip ${v}`, label: v, description: v === "text" ? "Set custom active working tip text" : `${v} working tip line` }));
|
|
4290
|
-
}
|
|
4291
4528
|
return [];
|
|
4292
4529
|
},
|
|
4293
4530
|
async handler(args: string, ctx: any) {
|
|
@@ -4302,8 +4539,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
4302
4539
|
`Assistant prefix: ${current.assistantPrefix}`,
|
|
4303
4540
|
`Thinking prefix: ${current.thinkingPrefix}`,
|
|
4304
4541
|
`Spacing: ${current.messageSpacing}`,
|
|
4305
|
-
`Working tip: ${current.workingTipEnabled ? "on" : "off"}`,
|
|
4306
|
-
`Tip text: ${current.workingTipText}`,
|
|
4307
4542
|
`Hidden thinking label: ${current.hiddenThinkingLabel}`,
|
|
4308
4543
|
].join("\n"), "info");
|
|
4309
4544
|
};
|
|
@@ -4314,7 +4549,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4314
4549
|
}
|
|
4315
4550
|
|
|
4316
4551
|
if (sub === "reset") {
|
|
4317
|
-
for (const key of ["messageStyle", "assistantPrefix", "thinkingPrefix", "messageSpacing", "
|
|
4552
|
+
for (const key of ["messageStyle", "assistantPrefix", "thinkingPrefix", "messageSpacing", "hiddenThinkingLabel"]) {
|
|
4318
4553
|
writeSettingsKey(key, undefined);
|
|
4319
4554
|
}
|
|
4320
4555
|
bustSpinnerSettingsCache();
|
|
@@ -4330,8 +4565,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
4330
4565
|
return;
|
|
4331
4566
|
}
|
|
4332
4567
|
writeSettingsKey("messageStyle", value);
|
|
4333
|
-
writeSettingsKey("workingTipEnabled", value === "classic" ? false : undefined);
|
|
4334
|
-
bustSpinnerSettingsCache();
|
|
4335
4568
|
if (ctx.hasUI) ctx.ui.notify(`Message style → ${value}`, "info");
|
|
4336
4569
|
return;
|
|
4337
4570
|
}
|
|
@@ -4356,33 +4589,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
4356
4589
|
return;
|
|
4357
4590
|
}
|
|
4358
4591
|
|
|
4359
|
-
if (sub === "tip") {
|
|
4360
|
-
const action = (parts[1] ?? "").toLowerCase();
|
|
4361
|
-
if (action === "on" || action === "off") {
|
|
4362
|
-
writeSettingsKey("workingTipEnabled", action === "on");
|
|
4363
|
-
bustSpinnerSettingsCache();
|
|
4364
|
-
if (ctx.hasUI) ctx.ui.notify(`Working tip → ${action}`, "info");
|
|
4365
|
-
return;
|
|
4366
|
-
}
|
|
4367
|
-
if (action === "reset") {
|
|
4368
|
-
writeSettingsKey("workingTipEnabled", undefined);
|
|
4369
|
-
writeSettingsKey("workingTipText", undefined);
|
|
4370
|
-
bustSpinnerSettingsCache();
|
|
4371
|
-
if (ctx.hasUI) ctx.ui.notify("Working tip reset", "info");
|
|
4372
|
-
return;
|
|
4373
|
-
}
|
|
4374
|
-
if (action === "text") {
|
|
4375
|
-
const textArg = rawArgs.match(/^tip\s+text(?:\s+(.+))?$/i)?.[1] ?? "";
|
|
4376
|
-
const value = resolveMessageChromeSettings({ workingTipText: textArg }).workingTipText;
|
|
4377
|
-
writeSettingsKey("workingTipText", value);
|
|
4378
|
-
bustSpinnerSettingsCache();
|
|
4379
|
-
if (ctx.hasUI) ctx.ui.notify(`Working tip text → ${value}`, "info");
|
|
4380
|
-
return;
|
|
4381
|
-
}
|
|
4382
|
-
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message tip on|off|reset|text <text>", "error");
|
|
4383
|
-
return;
|
|
4384
|
-
}
|
|
4385
|
-
|
|
4386
4592
|
if (sub === "hidden-thinking-label") {
|
|
4387
4593
|
const textArg = rawArgs.match(/^hidden-thinking-label(?:\s+(.+))?$/i)?.[1] ?? "";
|
|
4388
4594
|
const value = resolveMessageChromeSettings({ hiddenThinkingLabel: textArg }).hiddenThinkingLabel;
|
|
@@ -4392,7 +4598,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4392
4598
|
return;
|
|
4393
4599
|
}
|
|
4394
4600
|
|
|
4395
|
-
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message style|spacing|assistant-prefix|thinking-prefix|
|
|
4601
|
+
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message style|spacing|assistant-prefix|thinking-prefix|hidden-thinking-label|reset|status", "error");
|
|
4396
4602
|
},
|
|
4397
4603
|
});
|
|
4398
4604
|
|
|
@@ -4815,7 +5021,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4815
5021
|
const revealSummary = shouldRevealCallArgs(ctx) || (!!fp && hasOwnArg(args, "edits"));
|
|
4816
5022
|
const summary = stableCallSummary(ctx, "_callSummary", () => shouldRevealCallArgs(ctx) && operations.length > 1 ? `${sp(fp)} ${theme.fg("muted", `(${operations.length} edits)`)}` : sp(fp), revealSummary);
|
|
4817
5023
|
syncToolCallStatus(ctx);
|
|
4818
|
-
const hdr = toolHeader("Edit", summary, theme,
|
|
5024
|
+
const hdr = toolHeader("Edit", summary, theme, toolStatusDot(ctx, theme));
|
|
4819
5025
|
if (!(ctx.argsComplete && operations.length > 0)) return makeText(ctx.lastComponent, hdr);
|
|
4820
5026
|
const diffWidth = branchDiffWidth();
|
|
4821
5027
|
const key = `edit:${fp}:${hashText(operations.map((edit) => `${edit.oldText}\u0000${edit.newText}`).join("\u0001"))}:${diffWidth}:${ctx.expanded ? 1 : 0}`;
|
|
@@ -6,8 +6,6 @@ export interface MessageChromeInput {
|
|
|
6
6
|
assistantPrefix?: unknown;
|
|
7
7
|
thinkingPrefix?: unknown;
|
|
8
8
|
messageSpacing?: unknown;
|
|
9
|
-
workingTipEnabled?: unknown;
|
|
10
|
-
workingTipText?: unknown;
|
|
11
9
|
hiddenThinkingLabel?: unknown;
|
|
12
10
|
}
|
|
13
11
|
|
|
@@ -16,8 +14,6 @@ export interface MessageChromeSettings {
|
|
|
16
14
|
assistantPrefix: string;
|
|
17
15
|
thinkingPrefix: string;
|
|
18
16
|
messageSpacing: MessageSpacing;
|
|
19
|
-
workingTipEnabled: boolean;
|
|
20
|
-
workingTipText: string;
|
|
21
17
|
hiddenThinkingLabel: string;
|
|
22
18
|
}
|
|
23
19
|
|
|
@@ -28,7 +24,6 @@ export interface TranscriptLineOptions {
|
|
|
28
24
|
visibleWidth?: (text: string) => number;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
export const DEFAULT_WORKING_TIP_TEXT = "Run /install-github-app to tag @claude right from your GitHub issues and PRs";
|
|
32
27
|
export const DEFAULT_HIDDEN_THINKING_LABEL = "Pondering...";
|
|
33
28
|
|
|
34
29
|
const DEFAULT_MESSAGE_CHROME: MessageChromeSettings = {
|
|
@@ -36,8 +31,6 @@ const DEFAULT_MESSAGE_CHROME: MessageChromeSettings = {
|
|
|
36
31
|
assistantPrefix: "●",
|
|
37
32
|
thinkingPrefix: "✻",
|
|
38
33
|
messageSpacing: "comfortable",
|
|
39
|
-
workingTipEnabled: true,
|
|
40
|
-
workingTipText: DEFAULT_WORKING_TIP_TEXT,
|
|
41
34
|
hiddenThinkingLabel: DEFAULT_HIDDEN_THINKING_LABEL,
|
|
42
35
|
};
|
|
43
36
|
|
|
@@ -45,7 +38,6 @@ const ANSI_ESCAPE_SEQUENCE_RE = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*
|
|
|
45
38
|
const ANSI_SGR_RE = /\x1b\[[0-9;]*m/g;
|
|
46
39
|
const CONTROL_CHARS_RE = /[\u0000-\u001F\u007F-\u009F]/g;
|
|
47
40
|
const MAX_PREFIX_GRAPHEMES = 8;
|
|
48
|
-
const MAX_TIP_GRAPHEMES = 160;
|
|
49
41
|
const MAX_LABEL_GRAPHEMES = 80;
|
|
50
42
|
|
|
51
43
|
function stripAnsi(text: string): string {
|
|
@@ -81,14 +73,11 @@ export function sanitizeMessagePrefix(value: unknown, fallback: string): string
|
|
|
81
73
|
export function resolveMessageChromeSettings(input: MessageChromeInput = {}): MessageChromeSettings {
|
|
82
74
|
const messageStyle: MessageStyle = input.messageStyle === "classic" ? "classic" : "claude";
|
|
83
75
|
const messageSpacing: MessageSpacing = input.messageSpacing === "compact" ? "compact" : "comfortable";
|
|
84
|
-
const defaultWorkingTipEnabled = messageStyle === "classic" ? false : DEFAULT_MESSAGE_CHROME.workingTipEnabled;
|
|
85
76
|
return {
|
|
86
77
|
messageStyle,
|
|
87
78
|
assistantPrefix: sanitizeMessagePrefix(input.assistantPrefix, DEFAULT_MESSAGE_CHROME.assistantPrefix),
|
|
88
79
|
thinkingPrefix: sanitizeMessagePrefix(input.thinkingPrefix, DEFAULT_MESSAGE_CHROME.thinkingPrefix),
|
|
89
80
|
messageSpacing,
|
|
90
|
-
workingTipEnabled: typeof input.workingTipEnabled === "boolean" ? input.workingTipEnabled : defaultWorkingTipEnabled,
|
|
91
|
-
workingTipText: sanitizeTextSetting(input.workingTipText, DEFAULT_MESSAGE_CHROME.workingTipText, MAX_TIP_GRAPHEMES),
|
|
92
81
|
hiddenThinkingLabel: sanitizeTextSetting(input.hiddenThinkingLabel, DEFAULT_MESSAGE_CHROME.hiddenThinkingLabel, MAX_LABEL_GRAPHEMES),
|
|
93
82
|
};
|
|
94
83
|
}
|
package/extensions/spinner.ts
CHANGED
|
@@ -3,8 +3,6 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { Loader } from "@mariozechner/pi-tui";
|
|
5
5
|
|
|
6
|
-
import { resolveMessageChromeSettings } from "./message-chrome.ts";
|
|
7
|
-
|
|
8
6
|
// ---------------------------------------------------------------------------
|
|
9
7
|
// Patch built-in Loader with Claude/OpenBrawd-style glyphs.
|
|
10
8
|
// Keep animation cadence constant so the spinner doesn't appear to slow down
|
|
@@ -28,8 +26,6 @@ interface SpinnerSettings {
|
|
|
28
26
|
verbColor: string;
|
|
29
27
|
statusColor: string;
|
|
30
28
|
verbs: readonly string[];
|
|
31
|
-
workingTipEnabled: boolean;
|
|
32
|
-
workingTipText: string;
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
let _spinnerSettingsCache: { value: SpinnerSettings; expires: number } | null = null;
|
|
@@ -100,9 +96,6 @@ function readSpinnerSettings(): SpinnerSettings {
|
|
|
100
96
|
let statusColor = "muted";
|
|
101
97
|
let customVerbs: string[] | null = null;
|
|
102
98
|
let verbMode: SpinnerVerbMode = "append";
|
|
103
|
-
let messageStyle: string | undefined;
|
|
104
|
-
let workingTipEnabled: boolean | undefined;
|
|
105
|
-
let workingTipText: string | undefined;
|
|
106
99
|
const paths = [`${process.cwd()}/.pi/settings.json`, `${process.env.HOME ?? ""}/.pi/settings.json`];
|
|
107
100
|
for (const p of paths) {
|
|
108
101
|
try {
|
|
@@ -115,20 +108,14 @@ function readSpinnerSettings(): SpinnerSettings {
|
|
|
115
108
|
if (typeof raw.spinnerStatusColor === "string" && raw.spinnerStatusColor.length > 0) statusColor = raw.spinnerStatusColor;
|
|
116
109
|
if (raw.spinnerVerbMode === "append" || raw.spinnerVerbMode === "replace") verbMode = raw.spinnerVerbMode;
|
|
117
110
|
if (Array.isArray(raw.spinnerVerbs)) customVerbs = sanitizeSpinnerVerbs(raw.spinnerVerbs);
|
|
118
|
-
if (typeof raw.messageStyle === "string") messageStyle = raw.messageStyle;
|
|
119
|
-
if (typeof raw.workingTipEnabled === "boolean") workingTipEnabled = raw.workingTipEnabled;
|
|
120
|
-
if (typeof raw.workingTipText === "string") workingTipText = raw.workingTipText;
|
|
121
111
|
}
|
|
122
112
|
} catch { /* ignore */ }
|
|
123
113
|
}
|
|
124
|
-
const messageChrome = resolveMessageChromeSettings({ messageStyle, workingTipEnabled, workingTipText });
|
|
125
114
|
const value: SpinnerSettings = {
|
|
126
115
|
adaptive,
|
|
127
116
|
verbColor,
|
|
128
117
|
statusColor,
|
|
129
118
|
verbs: resolveSpinnerVerbs(customVerbs, verbMode),
|
|
130
|
-
workingTipEnabled: messageChrome.workingTipEnabled,
|
|
131
|
-
workingTipText: messageChrome.workingTipText,
|
|
132
119
|
};
|
|
133
120
|
_spinnerSettingsCache = { value, expires: now + SPINNER_SETTINGS_TTL_MS };
|
|
134
121
|
return value;
|
|
@@ -567,10 +554,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
567
554
|
if (statusParts.length > 0) {
|
|
568
555
|
message += statusText(` (${statusParts.join(" · ")})`);
|
|
569
556
|
}
|
|
570
|
-
const { workingTipEnabled, workingTipText } = readSpinnerSettings();
|
|
571
|
-
if (workingTipEnabled && workingTipText) {
|
|
572
|
-
message += `\n${STATUS_DIM} └─ Tip: ${workingTipText}${RESET}`;
|
|
573
|
-
}
|
|
574
557
|
return message;
|
|
575
558
|
}
|
|
576
559
|
|
package/package.json
CHANGED