pi-claude-code-ui 1.0.69 → 1.0.70
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 +14 -0
- package/README.md +11 -0
- package/extensions/index.ts +145 -39
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
> [!IMPORTANT]
|
|
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
|
+
|
|
6
|
+
## 1.0.70 — 2026-07-17
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **Live tool status dots blink again while commands stream** — partial tool rows re-arm the blink timer from both the call header and the live preview path, and `tool_execution_update` keeps the 15s stale watchdog from killing blink mid-bash.
|
|
11
|
+
- **Interrupted / resumed tools no longer blink forever** — only tools with `executionStarted` during a live agent run count as pending. History partials (resume, compaction, `/tree`, aborted runs without a toolResult) settle to a static green/dim dot and clear blink timers on `session_start`.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **No more `Running...` status row** — the blinking `●` on the tool heading is the only in-flight indicator. Live non-empty line count moves to the heading as muted `(N lines)`; the body shows only the output tail while streaming.
|
|
16
|
+
|
|
3
17
|
## 1.0.69 — 2026-07-17
|
|
4
18
|
|
|
5
19
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# pi-claude-code-ui
|
|
2
2
|
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> **Package renamed in 1.0.69.** This project is now published as [`pi-claude-code-ui`](https://www.npmjs.com/package/pi-claude-code-ui) (was `pi-claude-style-tools`).
|
|
5
|
+
>
|
|
6
|
+
> Install / migrate:
|
|
7
|
+
> ```bash
|
|
8
|
+
> pi install npm:pi-claude-code-ui
|
|
9
|
+
> # or
|
|
10
|
+
> npm i pi-claude-code-ui
|
|
11
|
+
> ```
|
|
12
|
+
> See [CHANGELOG 1.0.69](./CHANGELOG.md#1069--2026-07-17) for the full release notes (status dots, bare branch connectors, and more).
|
|
13
|
+
|
|
3
14
|
Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots, branch connectors, file icons, and configurable output modes.
|
|
4
15
|
|
|
5
16
|
## Features
|
package/extensions/index.ts
CHANGED
|
@@ -483,7 +483,13 @@ type ToolStatus = "pending" | "success" | "error";
|
|
|
483
483
|
function getToolStatusForGroup(tool: any): ToolStatus {
|
|
484
484
|
if (tool?.result?.isError) return "error";
|
|
485
485
|
if (tool?.result && tool?.isPartial !== true) return "success";
|
|
486
|
-
|
|
486
|
+
// Only in-flight tools that actually started this agent run count as pending.
|
|
487
|
+
// History rows reconstructed without a matching toolResult stay isPartial=true
|
|
488
|
+
// forever; treating them as pending made interrupted tools blink again on resume.
|
|
489
|
+
if (tool?.isPartial === true && tool?.executionStarted === true && currentAgentWorkStartMs !== undefined) {
|
|
490
|
+
return "pending";
|
|
491
|
+
}
|
|
492
|
+
return "success";
|
|
487
493
|
}
|
|
488
494
|
|
|
489
495
|
let TOOL_STATUS_SUCCESS = "\x1b[32m";
|
|
@@ -2117,6 +2123,28 @@ function patchToolExecutionBackgroundSync(): void {
|
|
|
2117
2123
|
proto[TOOL_BG_PATCH_FLAG] = true;
|
|
2118
2124
|
}
|
|
2119
2125
|
|
|
2126
|
+
function syncLiveToolRenderState(component: any): void {
|
|
2127
|
+
// updateDisplay paints call header BEFORE result. Pre-seed status + live line
|
|
2128
|
+
// count so the header's blinking ● and `(N lines)` trail stay in sync with
|
|
2129
|
+
// the partial result that is about to render underneath.
|
|
2130
|
+
const state = component?.rendererState;
|
|
2131
|
+
if (!state || typeof state !== "object") return;
|
|
2132
|
+
const ctxLike = {
|
|
2133
|
+
state,
|
|
2134
|
+
isPartial: component?.isPartial === true,
|
|
2135
|
+
executionStarted: component?.executionStarted === true,
|
|
2136
|
+
isError: component?.result?.isError === true,
|
|
2137
|
+
};
|
|
2138
|
+
syncToolCallStatus(ctxLike);
|
|
2139
|
+
if (component?.isPartial === true && component?.result) {
|
|
2140
|
+
const raw = getTextContent(component.result).replace(/\r\n/g, "\n").trimEnd();
|
|
2141
|
+
// tailLimit=0 → count-only (no line materialization) so huge bash tails stay cheap.
|
|
2142
|
+
state._liveLineCount = collectNonEmptyLines(raw, 0).total;
|
|
2143
|
+
} else if (component?.isPartial !== true) {
|
|
2144
|
+
delete state._liveLineCount;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2120
2148
|
function patchToolRenderCacheInvalidation(): void {
|
|
2121
2149
|
const proto = ToolExecutionComponent.prototype as any;
|
|
2122
2150
|
if (proto[TOOL_CACHE_PATCH_FLAG]) return;
|
|
@@ -2138,6 +2166,9 @@ function patchToolRenderCacheInvalidation(): void {
|
|
|
2138
2166
|
if (typeof original !== "function") continue;
|
|
2139
2167
|
proto[method] = function patchedToolMutation(...args: any[]) {
|
|
2140
2168
|
clearToolRenderCache(this);
|
|
2169
|
+
if (method === "updateDisplay" || method === "updateResult" || method === "invalidate") {
|
|
2170
|
+
syncLiveToolRenderState(this);
|
|
2171
|
+
}
|
|
2141
2172
|
const result = original.apply(this, args);
|
|
2142
2173
|
clearToolRenderCache(this);
|
|
2143
2174
|
return result;
|
|
@@ -2240,31 +2271,50 @@ function isBlinkOn(): boolean {
|
|
|
2240
2271
|
return Math.floor(Date.now() / 500) % 2 === 0;
|
|
2241
2272
|
}
|
|
2242
2273
|
|
|
2243
|
-
function toolHeader(tool: string, summary: string, theme: Theme, prefix = ""): string {
|
|
2274
|
+
function toolHeader(tool: string, summary: string, theme: Theme, prefix = "", trailing = ""): string {
|
|
2244
2275
|
applyThemePaletteIfNeeded(theme);
|
|
2245
2276
|
const label = theme.fg("toolTitle", theme.bold(tool));
|
|
2246
|
-
|
|
2247
|
-
|
|
2277
|
+
const body = summary
|
|
2278
|
+
? `${label} ${WRAP_MARK}${theme.fg("accent", summary)}`
|
|
2279
|
+
: label;
|
|
2280
|
+
return trailing ? `${prefix}${body}${trailing}` : `${prefix}${body}`;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
function liveLineCountTrailing(ctx: any, theme: Theme): string {
|
|
2284
|
+
if (ctx?.isPartial !== true) return "";
|
|
2285
|
+
const count = ctx?.state?._liveLineCount;
|
|
2286
|
+
if (typeof count !== "number" || !Number.isFinite(count) || count <= 0) return "";
|
|
2287
|
+
return ` ${theme.fg("muted", `(${lineCountLabel(count)})`)}`;
|
|
2248
2288
|
}
|
|
2249
2289
|
|
|
2250
|
-
function setToolStatus(ctx: any, status: "pending" | "success" | "error"): void {
|
|
2251
|
-
ctx.state._toolStatus = status;
|
|
2290
|
+
function setToolStatus(ctx: any, status: "pending" | "success" | "error" | "idle"): void {
|
|
2291
|
+
if (ctx?.state) ctx.state._toolStatus = status;
|
|
2252
2292
|
}
|
|
2253
2293
|
|
|
2254
2294
|
function syncToolCallStatus(ctx: any): void {
|
|
2255
2295
|
if (ctx?.isPartial) {
|
|
2256
|
-
//
|
|
2257
|
-
//
|
|
2258
|
-
//
|
|
2259
|
-
//
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2296
|
+
// Blink only for tools that actually started in the current agent run.
|
|
2297
|
+
// History rebuilds (resume, compaction, /tree) leave unmatched tool calls
|
|
2298
|
+
// with isPartial=true forever and never set executionStarted — those must
|
|
2299
|
+
// render settled, not keep a pending blink alive across sessions.
|
|
2300
|
+
const agentLive = currentAgentWorkStartMs !== undefined;
|
|
2301
|
+
const started = ctx?.executionStarted === true;
|
|
2302
|
+
if (agentLive && started) {
|
|
2303
|
+
setToolStatus(ctx, "pending");
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
if (agentLive && !started) {
|
|
2307
|
+
// Args still streaming before tool_execution_start — static, no blink.
|
|
2308
|
+
setToolStatus(ctx, "idle");
|
|
2309
|
+
clearBlinkTimer(ctx);
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
setToolStatus(ctx, "success");
|
|
2313
|
+
clearBlinkTimer(ctx);
|
|
2265
2314
|
return;
|
|
2266
2315
|
}
|
|
2267
2316
|
setToolStatus(ctx, ctx.isError ? "error" : "success");
|
|
2317
|
+
clearBlinkTimer(ctx);
|
|
2268
2318
|
}
|
|
2269
2319
|
|
|
2270
2320
|
function shouldRevealCallArgs(ctx: any): boolean {
|
|
@@ -2480,9 +2530,10 @@ function getWriteWasNewFile(ctx: any, cwd: string, filePath: string, reveal = sh
|
|
|
2480
2530
|
}
|
|
2481
2531
|
|
|
2482
2532
|
function toolStatusDot(ctx: any, theme: Theme): string {
|
|
2483
|
-
const status = ctx.state?._toolStatus as "pending" | "success" | "error" | undefined;
|
|
2533
|
+
const status = ctx.state?._toolStatus as "pending" | "success" | "error" | "idle" | undefined;
|
|
2484
2534
|
if (status === "success") return `${themeStatusDot(theme, "success")} `;
|
|
2485
2535
|
if (status === "error") return `${themeStatusDot(theme, "error")} `;
|
|
2536
|
+
if (status === "idle") return `${themeStatusDot(theme, "dim")} `;
|
|
2486
2537
|
return `${blinkDot(ctx, theme)} `;
|
|
2487
2538
|
}
|
|
2488
2539
|
|
|
@@ -2651,6 +2702,11 @@ function pendingToolChromeColor(theme: Theme): "dim" | "muted" | "thinkingText"
|
|
|
2651
2702
|
}
|
|
2652
2703
|
|
|
2653
2704
|
function blinkDot(ctx: any, theme: Theme): string {
|
|
2705
|
+
// Only true in-flight tools arm the blink timer. Idle partials (args still
|
|
2706
|
+
// streaming, or history rows left isPartial without a result) stay static.
|
|
2707
|
+
if (ctx?.state?._toolStatus !== "pending") {
|
|
2708
|
+
return themeStatusDot(theme, "dim");
|
|
2709
|
+
}
|
|
2654
2710
|
setupBlinkTimer(ctx);
|
|
2655
2711
|
const key = getBlinkKey(ctx);
|
|
2656
2712
|
// Claude Code: solid filled circle that either shows or fully disappears —
|
|
@@ -4724,6 +4780,10 @@ function registerThinkingLabels(pi: ExtensionAPI): void {
|
|
|
4724
4780
|
// message history, so /new starts fresh while /resume picks up past prompts
|
|
4725
4781
|
// and the original session start time. Resetting here is what lets /new
|
|
4726
4782
|
// clear the totals (Math.min / Math.max seeding alone could never lower them).
|
|
4783
|
+
// Also drop the live-agent marker so history partials rebuilt during resume
|
|
4784
|
+
// never look "in flight" and re-arm blink timers.
|
|
4785
|
+
currentAgentWorkStartMs = undefined;
|
|
4786
|
+
currentAssistantMessageStartMs = undefined;
|
|
4727
4787
|
sessionStartMs = undefined;
|
|
4728
4788
|
userTurnCount = 0;
|
|
4729
4789
|
});
|
|
@@ -4847,7 +4907,10 @@ function renderGenericToolCall(name: string, args: any, theme: Theme, ctx: any):
|
|
|
4847
4907
|
ctx.state._openAiPatchFiles = [];
|
|
4848
4908
|
const sp = (path: string) => shortPath(ctx.cwd ?? process.cwd(), path);
|
|
4849
4909
|
const summary = stableCallSummary(ctx, "_callSummary", () => summarizeGenericToolCall(name, args, theme, sp));
|
|
4850
|
-
return makeText(
|
|
4910
|
+
return makeText(
|
|
4911
|
+
ctx.lastComponent,
|
|
4912
|
+
toolHeader(genericToolLabel(name), summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
4913
|
+
);
|
|
4851
4914
|
}
|
|
4852
4915
|
|
|
4853
4916
|
function renderGenericToolResult(name: string, result: any, options: any, theme: Theme, ctx: any): Text {
|
|
@@ -4903,17 +4966,20 @@ function lineCountLabel(count: number): string {
|
|
|
4903
4966
|
|
|
4904
4967
|
function runningPreviewBlock(
|
|
4905
4968
|
result: any,
|
|
4906
|
-
|
|
4969
|
+
_statusText: string,
|
|
4907
4970
|
expanded: boolean,
|
|
4908
4971
|
theme: Theme,
|
|
4909
4972
|
ctx: any,
|
|
4910
4973
|
options: { lines?: string[]; totalLineCount?: number; styleLine?: (line: string) => string; tail?: boolean } = {},
|
|
4911
4974
|
): string {
|
|
4912
|
-
|
|
4975
|
+
// Keep the header status dot blinking while partial output streams. Call/result
|
|
4976
|
+
// renderers share rendererState, so setupBlinkTimer here re-arms the same key
|
|
4977
|
+
// the call header uses — but only when this tool actually started executing.
|
|
4978
|
+
syncToolCallStatus(ctx);
|
|
4979
|
+
if (ctx?.state?._toolStatus === "pending") setupBlinkTimer(ctx);
|
|
4980
|
+
else clearBlinkTimer(ctx);
|
|
4981
|
+
|
|
4913
4982
|
const limit = liveToolPreviewLimit();
|
|
4914
|
-
if (!liveToolPreviewEnabled() || limit <= 0) {
|
|
4915
|
-
return withBranch(statusText, theme);
|
|
4916
|
-
}
|
|
4917
4983
|
let lines: string[];
|
|
4918
4984
|
let totalLineCount: number;
|
|
4919
4985
|
if (options.lines) {
|
|
@@ -4926,8 +4992,13 @@ function runningPreviewBlock(
|
|
|
4926
4992
|
lines = collected.lines;
|
|
4927
4993
|
totalLineCount = collected.total;
|
|
4928
4994
|
}
|
|
4929
|
-
|
|
4930
|
-
|
|
4995
|
+
// Line count lives on the tool heading (via liveLineCountTrailing); keep it in
|
|
4996
|
+
// renderer state so the next renderCall pass can pick it up.
|
|
4997
|
+
if (ctx?.state) ctx.state._liveLineCount = totalLineCount;
|
|
4998
|
+
|
|
4999
|
+
if (!liveToolPreviewEnabled() || limit <= 0 || totalLineCount === 0) {
|
|
5000
|
+
// No status row — the blinking ● on the header is the only running indicator.
|
|
5001
|
+
return "";
|
|
4931
5002
|
}
|
|
4932
5003
|
|
|
4933
5004
|
const styleLine = options.styleLine ?? ((line: string) => theme.fg("dim", line || " "));
|
|
@@ -4942,7 +5013,7 @@ function runningPreviewBlock(
|
|
|
4942
5013
|
if (options.tail && !expanded && totalLineCount > previewSource.length) {
|
|
4943
5014
|
preview = `${theme.fg("muted", `... (${totalLineCount - previewSource.length} earlier lines${toolOutputDetailHint(theme, expanded, true)})`)}\n${preview}`;
|
|
4944
5015
|
}
|
|
4945
|
-
return withBranch(
|
|
5016
|
+
return withBranch(preview, theme);
|
|
4946
5017
|
}
|
|
4947
5018
|
|
|
4948
5019
|
function buildPersistentBashPreview(lines: string[], theme: Theme): string {
|
|
@@ -5317,7 +5388,7 @@ function renderApplyPatchCall(args: any, theme: Theme, ctx: any, sp: (path: stri
|
|
|
5317
5388
|
syncToolCallStatus(ctx);
|
|
5318
5389
|
const patchText = getStringArg(args, "patchText", "patch_text");
|
|
5319
5390
|
const summary = stableCallSummary(ctx, "_callSummary", () => summarizeOpenAiToolCall("apply_patch", args, theme, sp));
|
|
5320
|
-
const hdr = toolHeader("Apply Patch", summary, theme, toolStatusDot(ctx, theme));
|
|
5391
|
+
const hdr = toolHeader("Apply Patch", summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme));
|
|
5321
5392
|
|
|
5322
5393
|
if (!ctx.argsComplete) return makeText(ctx.lastComponent, hdr);
|
|
5323
5394
|
const preview = getCachedApplyPatchPreview(patchText, sp, ctx);
|
|
@@ -6150,7 +6221,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6150
6221
|
const line =
|
|
6151
6222
|
theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m `) +
|
|
6152
6223
|
theme.fg("customMessageText", skillName);
|
|
6153
|
-
return makeText(ctx.lastComponent, `${toolStatusDot(ctx, theme)}${line}`);
|
|
6224
|
+
return makeText(ctx.lastComponent, `${toolStatusDot(ctx, theme)}${line}${liveLineCountTrailing(ctx, theme)}`);
|
|
6154
6225
|
}
|
|
6155
6226
|
const summary = stableCallSummary(ctx, "_callSummary", () => {
|
|
6156
6227
|
let value = sp(args.path ?? "");
|
|
@@ -6162,7 +6233,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6162
6233
|
}
|
|
6163
6234
|
return value;
|
|
6164
6235
|
});
|
|
6165
|
-
return makeText(
|
|
6236
|
+
return makeText(
|
|
6237
|
+
ctx.lastComponent,
|
|
6238
|
+
toolHeader("Read", summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
6239
|
+
);
|
|
6166
6240
|
},
|
|
6167
6241
|
renderResult(result, { expanded, isPartial }, theme, ctx) {
|
|
6168
6242
|
if (isPartial) {
|
|
@@ -6197,7 +6271,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6197
6271
|
const rewrite = ensureRtkRewriteForContext(ctx, args);
|
|
6198
6272
|
const summary = stableCallSummary(ctx, "_callSummary", () => summarizeText(args.command, 72));
|
|
6199
6273
|
const rtkBadge = rewrite ? theme.fg("muted", " (RTK)") : "";
|
|
6200
|
-
return makeText(
|
|
6274
|
+
return makeText(
|
|
6275
|
+
ctx.lastComponent,
|
|
6276
|
+
toolHeader("Bash", `${summary}${rtkBadge}`, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
6277
|
+
);
|
|
6201
6278
|
},
|
|
6202
6279
|
renderResult(result, { expanded, isPartial }, theme, ctx) {
|
|
6203
6280
|
const details = result.details as BashToolDetails | undefined;
|
|
@@ -6205,13 +6282,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
6205
6282
|
const output = result.content[0]?.type === "text" ? result.content[0].text : "";
|
|
6206
6283
|
if (isPartial) {
|
|
6207
6284
|
const preview = collectNonEmptyLines(output, expanded ? undefined : liveToolPreviewLimit());
|
|
6208
|
-
const running = runningPreviewBlock(result,
|
|
6285
|
+
const running = runningPreviewBlock(result, "", expanded, theme, ctx, {
|
|
6209
6286
|
lines: preview.lines,
|
|
6210
6287
|
totalLineCount: preview.total,
|
|
6211
6288
|
styleLine: (line) => theme.fg("dim", line),
|
|
6212
6289
|
tail: true,
|
|
6213
6290
|
});
|
|
6214
|
-
const withRewrite = expanded && rewrite
|
|
6291
|
+
const withRewrite = expanded && rewrite
|
|
6292
|
+
? [running, withBranch(formatRtkRewriteDetails(rewrite, theme), theme)].filter(Boolean).join("\n")
|
|
6293
|
+
: running;
|
|
6215
6294
|
return makeText(ctx.lastComponent, withRewrite);
|
|
6216
6295
|
}
|
|
6217
6296
|
// Collapsed: only keep the live-preview tail (or nothing). Expanded: full list.
|
|
@@ -6255,7 +6334,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6255
6334
|
if (args.path) value += ` in ${args.path}`;
|
|
6256
6335
|
return value;
|
|
6257
6336
|
});
|
|
6258
|
-
return makeText(
|
|
6337
|
+
return makeText(
|
|
6338
|
+
ctx.lastComponent,
|
|
6339
|
+
toolHeader("Grep", summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
6340
|
+
);
|
|
6259
6341
|
},
|
|
6260
6342
|
renderResult(result, { expanded, isPartial }, theme, ctx) {
|
|
6261
6343
|
if (isPartial) {
|
|
@@ -6292,7 +6374,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6292
6374
|
if (args.path) value += ` in ${args.path}`;
|
|
6293
6375
|
return value;
|
|
6294
6376
|
});
|
|
6295
|
-
return makeText(
|
|
6377
|
+
return makeText(
|
|
6378
|
+
ctx.lastComponent,
|
|
6379
|
+
toolHeader("Find", summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
6380
|
+
);
|
|
6296
6381
|
},
|
|
6297
6382
|
renderResult(result, { expanded, isPartial }, theme, ctx) {
|
|
6298
6383
|
if (isPartial) {
|
|
@@ -6336,7 +6421,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6336
6421
|
renderCall(args, theme, ctx) {
|
|
6337
6422
|
syncToolCallStatus(ctx);
|
|
6338
6423
|
const summary = stableCallSummary(ctx, "_callSummary", () => sp(args.path ?? "."));
|
|
6339
|
-
return makeText(
|
|
6424
|
+
return makeText(
|
|
6425
|
+
ctx.lastComponent,
|
|
6426
|
+
toolHeader("List", summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
6427
|
+
);
|
|
6340
6428
|
},
|
|
6341
6429
|
renderResult(result, { expanded, isPartial }, theme, ctx) {
|
|
6342
6430
|
if (isPartial) {
|
|
@@ -6411,12 +6499,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
6411
6499
|
const base = sp(fp);
|
|
6412
6500
|
return shouldRevealCallArgs(ctx) ? `${base} ${theme.fg("muted", `(${lineCount(args.content ?? "")} lines)`)}` : base;
|
|
6413
6501
|
}, revealSummary);
|
|
6414
|
-
const hdr = toolHeader(label, summary, theme, toolStatusDot(ctx, theme));
|
|
6502
|
+
const hdr = toolHeader(label, summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme));
|
|
6415
6503
|
return makeText(ctx.lastComponent, hdr);
|
|
6416
6504
|
},
|
|
6417
6505
|
renderResult(result, { expanded, isPartial }, theme, ctx) {
|
|
6418
6506
|
if (isPartial) {
|
|
6419
|
-
return makeText(ctx.lastComponent, runningPreviewBlock(result,
|
|
6507
|
+
return makeText(ctx.lastComponent, runningPreviewBlock(result, "", expanded, theme, ctx));
|
|
6420
6508
|
}
|
|
6421
6509
|
clearBlinkTimer(ctx);
|
|
6422
6510
|
setToolStatus(ctx, ctx.isError ? "error" : "success");
|
|
@@ -6534,7 +6622,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6534
6622
|
const revealSummary = shouldRevealCallArgs(ctx) || (!!fp && hasOwnArg(args, "edits"));
|
|
6535
6623
|
const summary = stableCallSummary(ctx, "_callSummary", () => shouldRevealCallArgs(ctx) && operations.length > 1 ? `${sp(fp)} ${theme.fg("muted", `(${operations.length} edits)`)}` : sp(fp), revealSummary);
|
|
6536
6624
|
syncToolCallStatus(ctx);
|
|
6537
|
-
const hdr = toolHeader("Edit", summary, theme, ` ${toolStatusDot(ctx, theme)}
|
|
6625
|
+
const hdr = toolHeader("Edit", summary, theme, ` ${toolStatusDot(ctx, theme)}`, liveLineCountTrailing(ctx, theme));
|
|
6538
6626
|
if (!(ctx.argsComplete && operations.length > 0)) return makeText(ctx.lastComponent, hdr);
|
|
6539
6627
|
const diffWidth = branchDiffWidth();
|
|
6540
6628
|
const key = `edit:${fp}:${hashText(operations.map((edit) => `${edit.oldText}\u0000${edit.newText}`).join("\u0001"))}:${diffWidth}:${ctx.expanded ? 1 : 0}`;
|
|
@@ -6620,7 +6708,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6620
6708
|
syncToolCallStatus(ctx);
|
|
6621
6709
|
ctx.state._openAiPatchFiles = [];
|
|
6622
6710
|
const summary = stableCallSummary(ctx, "_callSummary", () => summarizeOpenAiToolCall(name, args, theme, sp));
|
|
6623
|
-
return makeText(
|
|
6711
|
+
return makeText(
|
|
6712
|
+
ctx.lastComponent,
|
|
6713
|
+
toolHeader(label, summary, theme, toolStatusDot(ctx, theme), liveLineCountTrailing(ctx, theme)),
|
|
6714
|
+
);
|
|
6624
6715
|
},
|
|
6625
6716
|
renderResult(result: any, { expanded, isPartial }: any, theme: Theme, ctx: any) {
|
|
6626
6717
|
if (name === "apply_patch") return renderApplyPatchResult(result, isPartial, theme, ctx);
|
|
@@ -6683,6 +6774,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6683
6774
|
pi.on("message_start", async () => { markBlinkActivity(); });
|
|
6684
6775
|
pi.on("message_update", async () => { markBlinkActivity(); });
|
|
6685
6776
|
pi.on("tool_execution_start", async () => { markBlinkActivity(); });
|
|
6777
|
+
// Partial tool output is the main long-running signal (bash streams for minutes).
|
|
6778
|
+
// Without this, the 15s stale watchdog can kill blink while a quiet command runs.
|
|
6779
|
+
pi.on("tool_execution_update", async () => { markBlinkActivity(); });
|
|
6780
|
+
pi.on("tool_execution_end", async () => { markBlinkActivity(); });
|
|
6686
6781
|
pi.on("agent_end", async () => {
|
|
6687
6782
|
markBlinkActivity();
|
|
6688
6783
|
for (const entry of _blinkContexts.values()) {
|
|
@@ -6695,7 +6790,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6695
6790
|
}
|
|
6696
6791
|
});
|
|
6697
6792
|
|
|
6698
|
-
// Safety net: clear
|
|
6793
|
+
// Safety net: clear blink timers after a turn's tools finish.
|
|
6699
6794
|
// Keep Shiki hlCache across turns — it's an LRU (CACHE_LIMIT=48) and
|
|
6700
6795
|
// re-highlighting the same diffs every turn was a free long-chat tax.
|
|
6701
6796
|
// Full clear still happens on session_shutdown / theme rebind.
|
|
@@ -6709,6 +6804,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
6709
6804
|
_globalBlinkTimer = null;
|
|
6710
6805
|
}
|
|
6711
6806
|
});
|
|
6807
|
+
// Session rebuild (resume/reload/fork) must not leave history partials blinking.
|
|
6808
|
+
pi.on("session_start", async () => {
|
|
6809
|
+
for (const entry of _blinkContexts.values()) {
|
|
6810
|
+
entry.key._blinkActive = false;
|
|
6811
|
+
}
|
|
6812
|
+
_blinkContexts.clear();
|
|
6813
|
+
if (_globalBlinkTimer) {
|
|
6814
|
+
clearTimeout(_globalBlinkTimer);
|
|
6815
|
+
_globalBlinkTimer = null;
|
|
6816
|
+
}
|
|
6817
|
+
});
|
|
6712
6818
|
pi.on("session_shutdown", async () => {
|
|
6713
6819
|
for (const entry of _blinkContexts.values()) {
|
|
6714
6820
|
entry.key._blinkActive = false;
|
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.70",
|
|
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",
|