@hheei/pi-ext-ui 0.8.1 → 0.8.3
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/dist/extension.js +5 -2
- package/dist/host-transcript-patch.d.ts +1 -1
- package/dist/host-transcript-patch.js +13 -15
- package/dist/native-tools.js +2 -1
- package/dist/nested-calls-view.d.ts +1 -1
- package/dist/nested-calls-view.js +6 -9
- package/dist/payload-parser.d.ts +0 -1
- package/dist/payload-parser.js +1 -3
- package/dist/renderer.d.ts +2 -5
- package/dist/renderer.js +35 -20
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +22 -0
- package/dist/tool-view.js +1 -3
- package/package.json +6 -6
package/dist/extension.js
CHANGED
|
@@ -14,6 +14,9 @@ export default function (pi) {
|
|
|
14
14
|
key: "@hheei/pi-ext-ui",
|
|
15
15
|
async start(context) {
|
|
16
16
|
let patch;
|
|
17
|
+
// The host owns this setting; a message view that draws its own shell applies the same
|
|
18
|
+
// value the host applies to its own rows, including the default when it is unset.
|
|
19
|
+
const outputPad = () => (context.pi.getSettings().outputPad === 0 ? 0 : 1);
|
|
17
20
|
const apply = (enabled) => {
|
|
18
21
|
if (context.extension.mode !== "tui")
|
|
19
22
|
return;
|
|
@@ -26,7 +29,7 @@ export default function (pi) {
|
|
|
26
29
|
if (patch !== undefined)
|
|
27
30
|
return;
|
|
28
31
|
try {
|
|
29
|
-
patch = installHostToolGroups(context.extension, () => runtime.backgrounds);
|
|
32
|
+
patch = installHostToolGroups(context.extension, () => runtime.backgrounds, outputPad);
|
|
30
33
|
redrawGroups = patch.requestRender;
|
|
31
34
|
}
|
|
32
35
|
catch (error) {
|
|
@@ -70,7 +73,7 @@ export default function (pi) {
|
|
|
70
73
|
});
|
|
71
74
|
const toolRenderers = registerExtensionHook(pi, toolRendererPoint, createOptionalToolRendererFactory(pi, runtime));
|
|
72
75
|
context.resources.add("tool-renderer", () => toolRenderers.dispose());
|
|
73
|
-
const handle = registerExtensionHook(context.pi, messageViewPoint, createMessageView);
|
|
76
|
+
const handle = registerExtensionHook(context.pi, messageViewPoint, (spec) => createMessageView({ ...spec, outputPad: spec.outputPad ?? outputPad() }));
|
|
74
77
|
context.resources.add("message-view", () => handle.dispose());
|
|
75
78
|
try {
|
|
76
79
|
const state = await provider.storage.load({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
/** Private to pi-ext-ui: two process hooks, scoped by the actual host UI facade. */
|
|
3
|
-
export declare function installHostToolGroups(context: ExtensionContext, background: () => boolean): {
|
|
3
|
+
export declare function installHostToolGroups(context: ExtensionContext, background: () => boolean, outputPad: () => number): {
|
|
4
4
|
dispose(): void;
|
|
5
5
|
requestRender(): void;
|
|
6
6
|
beginRun(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AssistantMessageComponent, InteractiveMode, ToolExecutionComponent,
|
|
1
|
+
import { AssistantMessageComponent, InteractiveMode, ToolExecutionComponent, } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Container, stripTerminalSequences, Text } from "@earendil-works/pi-tui";
|
|
3
3
|
import { MessageView } from "./message-view.js";
|
|
4
4
|
import { memberParts, TailUnit, ToolGroupView, } from "./tool-group-view.js";
|
|
@@ -7,8 +7,8 @@ const SEAMS = ["getRegisteredToolDefinition", "showExtensionNotify"];
|
|
|
7
7
|
function record(value) {
|
|
8
8
|
return typeof value === "object" && value !== null;
|
|
9
9
|
}
|
|
10
|
-
// Audited Pi >=1.0
|
|
11
|
-
// All host-private access stays here
|
|
10
|
+
// Audited Pi >=1.5.0 compiles these TS-private members to ordinary JS properties.
|
|
11
|
+
// All host-private access stays here.
|
|
12
12
|
function readHost(value) {
|
|
13
13
|
if (!record(value))
|
|
14
14
|
throw new Error("Invalid InteractiveMode instance");
|
|
@@ -147,12 +147,14 @@ function noticeMember(component) {
|
|
|
147
147
|
* semantic color. The group draws these rows once, so a plain notice has no expansion of its own.
|
|
148
148
|
*/
|
|
149
149
|
class NotifyView {
|
|
150
|
-
#text = new Text("",
|
|
150
|
+
#text = new Text("", 0, 0);
|
|
151
151
|
#ui;
|
|
152
|
+
#outputPad;
|
|
152
153
|
#message;
|
|
153
154
|
#type;
|
|
154
|
-
constructor(message, type, ui) {
|
|
155
|
+
constructor(message, type, ui, outputPad) {
|
|
155
156
|
this.#ui = ui;
|
|
157
|
+
this.#outputPad = outputPad;
|
|
156
158
|
this.#message = message;
|
|
157
159
|
this.#type = type;
|
|
158
160
|
}
|
|
@@ -166,6 +168,8 @@ class NotifyView {
|
|
|
166
168
|
}
|
|
167
169
|
/** The notice body as it appears both in the transcript and inside the activity group. */
|
|
168
170
|
rows(width) {
|
|
171
|
+
// The pad setting can change while a notice is on screen, so it is read per render.
|
|
172
|
+
this.#text.setPaddingX(this.#outputPad());
|
|
169
173
|
// info is the terminal's own readable text; a warning or error keeps its status token and
|
|
170
174
|
// its written prefix, so the level never depends on color alone.
|
|
171
175
|
this.#text.setText(this.#ui.theme.fg(this.#type === "info" ? "text" : this.#type, this.#content));
|
|
@@ -182,7 +186,7 @@ class NotifyView {
|
|
|
182
186
|
this.#text.invalidate();
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
|
-
function attach(host, run, background) {
|
|
189
|
+
function attach(host, run, background, outputPad) {
|
|
186
190
|
const chat = host.chat;
|
|
187
191
|
const original = {
|
|
188
192
|
render: chat.render,
|
|
@@ -399,7 +403,7 @@ function attach(host, run, background) {
|
|
|
399
403
|
notify(message, type) {
|
|
400
404
|
if (!active)
|
|
401
405
|
return false;
|
|
402
|
-
chat.addChild(new NotifyView(message, type, host.ui));
|
|
406
|
+
chat.addChild(new NotifyView(message, type, host.ui, outputPad));
|
|
403
407
|
host.requestRender();
|
|
404
408
|
return true;
|
|
405
409
|
},
|
|
@@ -513,13 +517,7 @@ function patchState() {
|
|
|
513
517
|
return state;
|
|
514
518
|
}
|
|
515
519
|
/** Private to pi-ext-ui: two process hooks, scoped by the actual host UI facade. */
|
|
516
|
-
export function installHostToolGroups(context, background) {
|
|
517
|
-
const parts = VERSION.split(".").map(Number);
|
|
518
|
-
const [major = 0, minor = 0, patch = 0] = parts;
|
|
519
|
-
if (parts.length !== 3 ||
|
|
520
|
-
!parts.every(Number.isInteger) ||
|
|
521
|
-
!(major > 1 || (major === 1 && (minor > 0 || (minor === 0 && patch >= 1)))))
|
|
522
|
-
throw new Error(`Tool grouping requires Pi >=1.0.1; running ${VERSION}`);
|
|
520
|
+
export function installHostToolGroups(context, background, outputPad) {
|
|
523
521
|
const state = patchState();
|
|
524
522
|
const ui = context.ui;
|
|
525
523
|
if (state.owners.has(ui))
|
|
@@ -545,7 +543,7 @@ export function installHostToolGroups(context, background) {
|
|
|
545
543
|
ui,
|
|
546
544
|
hosts: new Map(),
|
|
547
545
|
failed: new WeakSet(),
|
|
548
|
-
attach: (host) => attach(host, run, background),
|
|
546
|
+
attach: (host) => attach(host, run, background, outputPad),
|
|
549
547
|
};
|
|
550
548
|
state.owners.set(ui, owner);
|
|
551
549
|
// Reload rebuilds history before session_start; reconnect already discovered instances.
|
package/dist/native-tools.js
CHANGED
|
@@ -3,8 +3,9 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { getLanguageFromPath, } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { getCapabilities, hyperlink, stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
6
|
+
import { isRecord } from "@hheei/pi-ext-core";
|
|
6
7
|
import { diffStats } from "./diff-view.js";
|
|
7
|
-
import { formatTimeoutFact,
|
|
8
|
+
import { formatTimeoutFact, parseCallArgs } from "./payload-parser.js";
|
|
8
9
|
/**
|
|
9
10
|
* The single owner of Pi's own tool vocabulary in this UI. Concrete tool packages state their own
|
|
10
11
|
* semantics through ext-core's presentation point; this module is the explicit adapter for the tools
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Theme, type ToolRenderers } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type Component, type TuiMouseEvent, type TuiMouseEventResult } from "@earendil-works/pi-tui";
|
|
3
|
-
import type
|
|
3
|
+
import { type ToolPresentationProvider } from "@hheei/pi-ext-core";
|
|
4
4
|
import type { NestedCallRecord, ToolRenderContext } from "./renderer.js";
|
|
5
5
|
import { type Collapsible } from "./tool-view.js";
|
|
6
6
|
import type { CapturedToolCall } from "./tracker.js";
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { copyToClipboard as copyToSystemClipboard, getLanguageFromPath, highlightCode, } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
3
|
+
import { isRecord } from "@hheei/pi-ext-core";
|
|
3
4
|
import { extractChildToolCopyText, inlinePresentation, nativeRequestCopy } from "./native-tools.js";
|
|
5
|
+
import { formatDurationMs } from "./payload-parser.js";
|
|
4
6
|
import { formatToolHeaderText, TOOL_SPINNER_INTERVAL_MS, ToolView, toContentRows, toolSpinnerFrame, transitionFoldLevel, } from "./tool-view.js";
|
|
5
7
|
function truncate(text, width) {
|
|
6
8
|
return truncateToWidth(text, Math.max(1, width));
|
|
7
9
|
}
|
|
8
|
-
function isRecord(value) {
|
|
9
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
-
}
|
|
11
10
|
/**
|
|
12
11
|
* A call without a final result is still running: its row carries the live spinner frame. A persisted
|
|
13
12
|
* record states its own status, so a recovered board never animates a call that already ended.
|
|
@@ -27,11 +26,6 @@ function copyText(text, request) {
|
|
|
27
26
|
}
|
|
28
27
|
return request + stripTerminalSequences(text.slice(request.length));
|
|
29
28
|
}
|
|
30
|
-
function formatDuration(ms) {
|
|
31
|
-
if (ms === undefined || !Number.isFinite(ms))
|
|
32
|
-
return "";
|
|
33
|
-
return ms < 1000 ? `${String(Math.round(ms))}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
34
|
-
}
|
|
35
29
|
export const NESTED_CALLS_EXPANDED_KEY = "__pi_ext_ui_nested_expanded__";
|
|
36
30
|
export const NESTED_CALLS_COPIED_KEY = "__pi_ext_ui_nested_copied__";
|
|
37
31
|
export class NestedCallsView {
|
|
@@ -140,6 +134,9 @@ export class NestedCallsView {
|
|
|
140
134
|
lastComponent: undefined,
|
|
141
135
|
args: "args" in call ? call.args : undefined,
|
|
142
136
|
toolCallId,
|
|
137
|
+
// The container frame already indented these rows by the output pad; a child body that
|
|
138
|
+
// applied it again would count the same setting twice.
|
|
139
|
+
outputPad: 0,
|
|
143
140
|
durationMs: call.durationMs,
|
|
144
141
|
isError: Boolean(call.isError || ("status" in call && call.status === "error")),
|
|
145
142
|
isPartial: false,
|
|
@@ -261,7 +258,7 @@ export class NestedCallsView {
|
|
|
261
258
|
title: toolName,
|
|
262
259
|
summary: childSummary,
|
|
263
260
|
...(ownSummary === undefined || ownSummary === "" ? {} : { summaryStyled: true }),
|
|
264
|
-
meta:
|
|
261
|
+
meta: formatDurationMs(call.durationMs),
|
|
265
262
|
}, theme);
|
|
266
263
|
const isCopied = this.#copiedIndex === i;
|
|
267
264
|
const copyIcon = this.#failedIndex === i
|
package/dist/payload-parser.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export interface NestedCallRecord {
|
|
|
20
20
|
readonly cost?: number | undefined;
|
|
21
21
|
readonly error?: string | undefined;
|
|
22
22
|
}
|
|
23
|
-
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
24
23
|
export declare function estimateStreamingTokens(args: unknown): number;
|
|
25
24
|
export declare function formatDurationMs(ms: number | undefined): string;
|
|
26
25
|
export declare function formatCostUsd(cost: number | undefined): string;
|
package/dist/payload-parser.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
+
import { isRecord } from "@hheei/pi-ext-core";
|
|
1
2
|
export const RESOLVED_CALLS_KEY = "__piExtUiResolvedCalls";
|
|
2
|
-
export function isRecord(value) {
|
|
3
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
3
|
export function estimateStreamingTokens(args) {
|
|
6
4
|
if (args === undefined || args === null)
|
|
7
5
|
return 0;
|
package/dist/renderer.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { type ExtensionAPI, type ToolRendererResolver, type ToolRenderers } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { ToolUiRuntime } from "./runtime.js";
|
|
3
|
-
/**
|
|
4
|
-
export type ToolRenderContext = Parameters<NonNullable<ToolRenderers["renderCall"]>>[2]
|
|
5
|
-
readonly durationMs?: number | undefined;
|
|
6
|
-
readonly outputPad?: number | undefined;
|
|
7
|
-
};
|
|
3
|
+
/** Everything the host hands a tool renderer for one call. */
|
|
4
|
+
export type ToolRenderContext = Parameters<NonNullable<ToolRenderers["renderCall"]>>[2];
|
|
8
5
|
/**
|
|
9
6
|
* Upstream tools whose call and result views pi-ext-ui still frames itself. Concrete tool packages
|
|
10
7
|
* own their own presentation through ext-core's optional renderer point, so their names never
|
package/dist/renderer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { highlightCode, } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
3
|
-
import { agentResultText, getRegisteredToolPresentationProvider, getRegisteredToolRenderers, getToolPresentationProvider, hasOptionalToolRenderers, } from "@hheei/pi-ext-core";
|
|
3
|
+
import { agentResultText, getRegisteredToolPresentationProvider, getRegisteredToolRenderers, getToolPresentationProvider, hasOptionalToolRenderers, isRecord, } from "@hheei/pi-ext-core";
|
|
4
4
|
import { DiffView } from "./diff-view.js";
|
|
5
5
|
import { NestedCallsView } from "./nested-calls-view.js";
|
|
6
6
|
import { formatToolHeaderText, ToolView, } from "./tool-view.js";
|
|
@@ -11,7 +11,7 @@ import { formatToolHeaderText, ToolView, } from "./tool-view.js";
|
|
|
11
11
|
*/
|
|
12
12
|
export const SPECIALIZED_TOOL_RENDERERS = ["codemode"];
|
|
13
13
|
import { inlinePresentation, nativeHeaderSummary, nativeHeavyRequest, nativeOutputLanguage, nativeRequestBlock, nativeResultText, nativeTimeoutFact, } from "./native-tools.js";
|
|
14
|
-
import { callStatsParts, estimateStreamingTokens, extractNestedCalls, formatCostUsd, formatDurationMs,
|
|
14
|
+
import { callStatsParts, estimateStreamingTokens, extractNestedCalls, formatCostUsd, formatDurationMs, RESOLVED_CALLS_KEY, resolveContainerStatus, } from "./payload-parser.js";
|
|
15
15
|
export { extractChildToolCopyText, inlinePresentation } from "./native-tools.js";
|
|
16
16
|
export { estimateStreamingTokens, } from "./payload-parser.js";
|
|
17
17
|
const SPECIALIZED_TOOL_SET = new Set(SPECIALIZED_TOOL_RENDERERS);
|
|
@@ -415,6 +415,9 @@ function renderGenericResult(params) {
|
|
|
415
415
|
const upstreamContext = {
|
|
416
416
|
...context,
|
|
417
417
|
lastComponent: lastUpstreamComponent,
|
|
418
|
+
// This frame indents the rows it embeds, so the renderer it wraps must not indent
|
|
419
|
+
// them again: the pad is applied once, by the outermost shell.
|
|
420
|
+
outputPad: 0,
|
|
418
421
|
};
|
|
419
422
|
const comp = originalRenderResult(result, effectiveOptions, theme, upstreamContext);
|
|
420
423
|
lastUpstreamComponent = comp;
|
|
@@ -486,19 +489,34 @@ function renderOwnedPresentation(params) {
|
|
|
486
489
|
const isError = context.isError || result?.isError === true;
|
|
487
490
|
const pending = result === undefined || context.isPartial;
|
|
488
491
|
const isDiff = !isError && !pending && presentation.kind === "diff";
|
|
492
|
+
// A container's children are facts: the provider states the ones it finished, and the tracker
|
|
493
|
+
// already holds the one still running, so the row can show work the tool has not reported yet.
|
|
494
|
+
const captured = runtime.tracker.getCaptured(context.toolCallId) ??
|
|
495
|
+
(result !== undefined &&
|
|
496
|
+
isRecord(result.details) &&
|
|
497
|
+
Array.isArray(result.details.__piExtUiCapturedCalls)
|
|
498
|
+
? result.details.__piExtUiCapturedCalls
|
|
499
|
+
: undefined);
|
|
500
|
+
const ownCalls = presentation.children === undefined
|
|
501
|
+
? extractNestedCalls(result)
|
|
502
|
+
: presentationChildCalls(presentation.children);
|
|
503
|
+
const childCalls = withSessionArgs(captured ?? ownCalls, runtime);
|
|
489
504
|
// The tool owns its status: a container says "none" for a child's failure and a partial patch
|
|
490
505
|
// says "warn" even though the host marks the result failed. Only a provider without an opinion
|
|
491
506
|
// falls back to the frame's own reading of the execution.
|
|
492
507
|
const status = presentation.status ??
|
|
493
508
|
(isError
|
|
494
509
|
? "error"
|
|
495
|
-
:
|
|
496
|
-
|
|
497
|
-
? "running"
|
|
498
|
-
: "streaming"
|
|
499
|
-
: isDiff
|
|
510
|
+
: // Child rows carry the work, so the container's own glyph is the fold pin only.
|
|
511
|
+
childCalls.length > 0
|
|
500
512
|
? "none"
|
|
501
|
-
:
|
|
513
|
+
: pending
|
|
514
|
+
? context.argsComplete
|
|
515
|
+
? "running"
|
|
516
|
+
: "streaming"
|
|
517
|
+
: isDiff
|
|
518
|
+
? "none"
|
|
519
|
+
: "ok");
|
|
502
520
|
const railToken = isError ? "error" : pending ? "accent" : "success";
|
|
503
521
|
const output = isError && result !== undefined
|
|
504
522
|
? agentResultText(result)
|
|
@@ -563,18 +581,6 @@ function renderOwnedPresentation(params) {
|
|
|
563
581
|
if (presentation.collapsedRows !== undefined) {
|
|
564
582
|
return presentation.collapsedRows;
|
|
565
583
|
}
|
|
566
|
-
const captured = result !== undefined
|
|
567
|
-
? (runtime.tracker.getCaptured(context.toolCallId) ??
|
|
568
|
-
(isRecord(result.details) && Array.isArray(result.details.__piExtUiCapturedCalls)
|
|
569
|
-
? result.details.__piExtUiCapturedCalls
|
|
570
|
-
: undefined))
|
|
571
|
-
: undefined;
|
|
572
|
-
const nestedCalls = extractNestedCalls(result);
|
|
573
|
-
// The owner's own child facts win over parsing another package's details.
|
|
574
|
-
const ownCalls = presentation.children === undefined
|
|
575
|
-
? nestedCalls
|
|
576
|
-
: presentationChildCalls(presentation.children);
|
|
577
|
-
const childCalls = withSessionArgs(captured ?? ownCalls, runtime);
|
|
578
584
|
if (childCalls.length > 0) {
|
|
579
585
|
return new NestedCallsView({
|
|
580
586
|
calls: childCalls,
|
|
@@ -659,6 +665,7 @@ function createRendererResolver(runtime, options, resolvePresentation, resolveNa
|
|
|
659
665
|
return {
|
|
660
666
|
renderShell: "self",
|
|
661
667
|
renderCall(args, theme, context) {
|
|
668
|
+
runtime.rememberRow(context.toolCallId, context.invalidate);
|
|
662
669
|
if (provider !== undefined) {
|
|
663
670
|
const callContext = { ...context, args };
|
|
664
671
|
const view = renderOwnedPresentation({
|
|
@@ -722,6 +729,7 @@ function createRendererResolver(runtime, options, resolvePresentation, resolveNa
|
|
|
722
729
|
return view;
|
|
723
730
|
},
|
|
724
731
|
renderResult(result, renderOptions, theme, context) {
|
|
732
|
+
runtime.rememberRow(context.toolCallId, context.invalidate);
|
|
725
733
|
const resultContext = {
|
|
726
734
|
...context,
|
|
727
735
|
expanded: renderOptions.expanded,
|
|
@@ -787,9 +795,16 @@ export function registerCollapsibleToolRenderer(pi, runtime, options) {
|
|
|
787
795
|
const disposers = [
|
|
788
796
|
pi.on("tool_execution_start", (event) => {
|
|
789
797
|
runtime.tracker.recordStart(event);
|
|
798
|
+
// A nested call has no row of its own until the container redraws, so the container is
|
|
799
|
+
// rebuilt the moment the call starts rather than when its result arrives.
|
|
800
|
+
runtime.repaintParent(event.parentToolCallId);
|
|
790
801
|
}),
|
|
791
802
|
pi.on("tool_execution_end", (event) => {
|
|
792
803
|
runtime.tracker.recordEnd(event);
|
|
804
|
+
if (event.parentToolCallId === undefined)
|
|
805
|
+
runtime.forgetRow(event.toolCallId);
|
|
806
|
+
else
|
|
807
|
+
runtime.repaintParent(event.parentToolCallId);
|
|
793
808
|
}),
|
|
794
809
|
pi.on("tool_call", (event) => {
|
|
795
810
|
runtime.tracker.recordResolved(event);
|
package/dist/runtime.d.ts
CHANGED
|
@@ -5,9 +5,14 @@ import { NestedCallArgsIndex, ToolExecutionTracker } from "./tracker.js";
|
|
|
5
5
|
* restart, cleanup or settings change in one cannot reach the other.
|
|
6
6
|
*/
|
|
7
7
|
export declare class ToolUiRuntime {
|
|
8
|
+
#private;
|
|
8
9
|
readonly tracker: ToolExecutionTracker;
|
|
9
10
|
readonly nestedCalls: NestedCallArgsIndex;
|
|
10
11
|
/** Pi-native per-tool backgrounds are opt-in; the default is the quiet transparent row. */
|
|
11
12
|
backgrounds: boolean;
|
|
13
|
+
rememberRow(toolCallId: string, invalidate: () => void): void;
|
|
14
|
+
/** The host rebuilds the row from fresh facts, so its child board shows the call that started. */
|
|
15
|
+
repaintParent(parentToolCallId: string | undefined): void;
|
|
16
|
+
forgetRow(toolCallId: string): void;
|
|
12
17
|
clear(): void;
|
|
13
18
|
}
|
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { NestedCallArgsIndex, ToolExecutionTracker } from "./tracker.js";
|
|
2
|
+
/** Bounded like the capture table: only rows the session can still repaint are worth keeping. */
|
|
3
|
+
const MAX_LIVE_ROWS = 200;
|
|
2
4
|
/**
|
|
3
5
|
* The mutable state one extension runtime owns: nested-call captures, the session-branch argument
|
|
4
6
|
* index, and the tool background preference. Two Pi runtimes in one process never share it, so a
|
|
@@ -7,10 +9,30 @@ import { NestedCallArgsIndex, ToolExecutionTracker } from "./tracker.js";
|
|
|
7
9
|
export class ToolUiRuntime {
|
|
8
10
|
tracker = new ToolExecutionTracker();
|
|
9
11
|
nestedCalls = new NestedCallArgsIndex();
|
|
12
|
+
/** A container row's own invalidator, so a live nested call can repaint the row that made it. */
|
|
13
|
+
#invalidators = new Map();
|
|
10
14
|
/** Pi-native per-tool backgrounds are opt-in; the default is the quiet transparent row. */
|
|
11
15
|
backgrounds = false;
|
|
16
|
+
rememberRow(toolCallId, invalidate) {
|
|
17
|
+
if (!this.#invalidators.has(toolCallId) && this.#invalidators.size >= MAX_LIVE_ROWS) {
|
|
18
|
+
const oldest = this.#invalidators.keys().next().value;
|
|
19
|
+
if (oldest !== undefined)
|
|
20
|
+
this.#invalidators.delete(oldest);
|
|
21
|
+
}
|
|
22
|
+
this.#invalidators.set(toolCallId, invalidate);
|
|
23
|
+
}
|
|
24
|
+
/** The host rebuilds the row from fresh facts, so its child board shows the call that started. */
|
|
25
|
+
repaintParent(parentToolCallId) {
|
|
26
|
+
if (parentToolCallId === undefined)
|
|
27
|
+
return;
|
|
28
|
+
this.#invalidators.get(parentToolCallId)?.();
|
|
29
|
+
}
|
|
30
|
+
forgetRow(toolCallId) {
|
|
31
|
+
this.#invalidators.delete(toolCallId);
|
|
32
|
+
}
|
|
12
33
|
clear() {
|
|
13
34
|
this.tracker.clear();
|
|
14
35
|
this.nestedCalls.setSource(undefined);
|
|
36
|
+
this.#invalidators.clear();
|
|
15
37
|
}
|
|
16
38
|
}
|
package/dist/tool-view.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import { copyToClipboard as copyToSystemClipboard, } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
4
|
+
import { isRecord } from "@hheei/pi-ext-core";
|
|
4
5
|
import { surfaceBlock } from "./background.js";
|
|
5
6
|
/** Pi native tool surfaces: a failure outranks work in progress, which outranks a settled tool. */
|
|
6
7
|
function nativeSurface(status) {
|
|
@@ -28,9 +29,6 @@ const DEFAULT_MAX_SECTION_LINES = 9;
|
|
|
28
29
|
export const MAX_LEVEL2_SECTION_LINES = 500;
|
|
29
30
|
export const MAX_LEVEL2_BYTES = 64 * 1024;
|
|
30
31
|
const DEFAULT_COPY_FEEDBACK_MS = 1200;
|
|
31
|
-
function isRecord(value) {
|
|
32
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
-
}
|
|
34
32
|
export function transitionFoldLevel(current, ctrlKey) {
|
|
35
33
|
if (current === 0) {
|
|
36
34
|
return ctrlKey ? 2 : 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hheei/pi-ext-ui",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Collapsible tool rendering frame and tool renderer pilot for Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@hheei/pi-ext-core": "^0.8.
|
|
38
|
+
"@hheei/pi-ext-core": "^0.8.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@earendil-works/pi-coding-agent": ">=1.0
|
|
42
|
-
"@earendil-works/pi-tui": ">=1.0
|
|
41
|
+
"@earendil-works/pi-coding-agent": ">=1.5.0",
|
|
42
|
+
"@earendil-works/pi-tui": ">=1.5.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@earendil-works/pi-coding-agent": "^1.
|
|
46
|
-
"@earendil-works/pi-tui": "^1.
|
|
45
|
+
"@earendil-works/pi-coding-agent": "^1.5.0",
|
|
46
|
+
"@earendil-works/pi-tui": "^1.5.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "node -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json"
|