@hheei/pi-ext-ui 0.8.2 → 0.8.4
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/host-transcript-patch.js +5 -10
- package/dist/native-tools.js +2 -1
- package/dist/nested-calls-view.d.ts +1 -1
- package/dist/nested-calls-view.js +3 -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 +2 -2
- package/dist/tool-group-view.d.ts +1 -1
- package/dist/tool-group-view.js +16 -7
- package/dist/tool-view.js +1 -3
- package/package.json +6 -6
|
@@ -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");
|
|
@@ -287,7 +287,8 @@ function attach(host, run, background, outputPad) {
|
|
|
287
287
|
weight += 0.5;
|
|
288
288
|
}
|
|
289
289
|
if (first !== undefined && weight >= 2) {
|
|
290
|
-
const group = groups.get(first) ??
|
|
290
|
+
const group = groups.get(first) ??
|
|
291
|
+
new ToolGroupView(host.ui, host.requestRender, background, outputPad);
|
|
291
292
|
const boundaryTime = ended && boundary !== undefined
|
|
292
293
|
? (run.observed.get(boundary)?.at ?? performance.now())
|
|
293
294
|
: undefined;
|
|
@@ -518,12 +519,6 @@ function patchState() {
|
|
|
518
519
|
}
|
|
519
520
|
/** Private to pi-ext-ui: two process hooks, scoped by the actual host UI facade. */
|
|
520
521
|
export function installHostToolGroups(context, background, outputPad) {
|
|
521
|
-
const parts = VERSION.split(".").map(Number);
|
|
522
|
-
const [major = 0, minor = 0, patch = 0] = parts;
|
|
523
|
-
if (parts.length !== 3 ||
|
|
524
|
-
!parts.every(Number.isInteger) ||
|
|
525
|
-
!(major > 1 || (major === 1 && (minor > 0 || (minor === 0 && patch >= 1)))))
|
|
526
|
-
throw new Error(`Tool grouping requires Pi >=1.0.1; running ${VERSION}`);
|
|
527
522
|
const state = patchState();
|
|
528
523
|
const ui = context.ui;
|
|
529
524
|
if (state.owners.has(ui))
|
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 {
|
|
@@ -264,7 +258,7 @@ export class NestedCallsView {
|
|
|
264
258
|
title: toolName,
|
|
265
259
|
summary: childSummary,
|
|
266
260
|
...(ownSummary === undefined || ownSummary === "" ? {} : { summaryStyled: true }),
|
|
267
|
-
meta:
|
|
261
|
+
meta: formatDurationMs(call.durationMs),
|
|
268
262
|
}, theme);
|
|
269
263
|
const isCopied = this.#copiedIndex === i;
|
|
270
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);
|
|
@@ -70,7 +70,7 @@ export declare class TailUnit implements Component {
|
|
|
70
70
|
*/
|
|
71
71
|
export declare class ToolGroupView extends Container {
|
|
72
72
|
#private;
|
|
73
|
-
constructor(ui: ExtensionUIContext, redraw: () => void, background: () => boolean);
|
|
73
|
+
constructor(ui: ExtensionUIContext, redraw: () => void, background: () => boolean, outputPad: () => number);
|
|
74
74
|
/**
|
|
75
75
|
* Release the block's own lifetime. The projection drops a block when it is evicted, when
|
|
76
76
|
* grouping is disabled, or when the patch unloads; an idempotent dispose keeps that path honest,
|
package/dist/tool-group-view.js
CHANGED
|
@@ -157,6 +157,8 @@ export class ToolGroupView extends Container {
|
|
|
157
157
|
#redraw;
|
|
158
158
|
/** Pi-native block backgrounds are opt-in; while they are on, the fill replaces the rails. */
|
|
159
159
|
#background;
|
|
160
|
+
/** Pi's own output padding, so the frame lines up with the tool rows it wraps. */
|
|
161
|
+
#outputPad;
|
|
160
162
|
/** The stable clock for this block; restored history stays unknown until live content joins. */
|
|
161
163
|
#clock;
|
|
162
164
|
/** The block's own members, drawn inside the group. */
|
|
@@ -179,11 +181,12 @@ export class ToolGroupView extends Container {
|
|
|
179
181
|
// One extra colored row above and below: the block needs breathing room inside its background.
|
|
180
182
|
#box = new Box(0, 1);
|
|
181
183
|
#spacer = new Spacer(1);
|
|
182
|
-
constructor(ui, redraw, background) {
|
|
184
|
+
constructor(ui, redraw, background, outputPad) {
|
|
183
185
|
super();
|
|
184
186
|
this.#ui = ui;
|
|
185
187
|
this.#redraw = redraw;
|
|
186
188
|
this.#background = background;
|
|
189
|
+
this.#outputPad = outputPad;
|
|
187
190
|
this.#heading = new MouseRegion(this.#header, (event) => {
|
|
188
191
|
if (event.type !== "click" || event.button !== "left")
|
|
189
192
|
return undefined;
|
|
@@ -387,8 +390,13 @@ export class ToolGroupView extends Container {
|
|
|
387
390
|
const theme = this.#ui.theme;
|
|
388
391
|
const lines = [];
|
|
389
392
|
const hits = [];
|
|
393
|
+
// The frame is the block's own row; a tool row inside it pads itself, so only these rows take
|
|
394
|
+
// Pi's output padding.
|
|
395
|
+
const pad = Math.max(0, this.#outputPad());
|
|
396
|
+
const lead = " ".repeat(pad);
|
|
397
|
+
const inner = Math.max(0, width - pad * 2);
|
|
390
398
|
const push = (row, action = { kind: "none" }) => {
|
|
391
|
-
lines.push(fitRow(row
|
|
399
|
+
lines.push(fitRow(`${lead}${row}`, Math.max(0, width), "…"));
|
|
392
400
|
hits.push({ height: 1, action });
|
|
393
401
|
};
|
|
394
402
|
/** Rows that belong to one component keep one hit so its own geometry stays correct. */
|
|
@@ -404,7 +412,7 @@ export class ToolGroupView extends Container {
|
|
|
404
412
|
if (member.kind === "tool")
|
|
405
413
|
tools.push(index);
|
|
406
414
|
}
|
|
407
|
-
push(theme.fg("borderMuted", "─".repeat(
|
|
415
|
+
push(theme.fg("borderMuted", "─".repeat(inner)));
|
|
408
416
|
const notices = this.#noticeItems();
|
|
409
417
|
if (notices.length > 0) {
|
|
410
418
|
const kinds = new Map();
|
|
@@ -418,7 +426,7 @@ export class ToolGroupView extends Container {
|
|
|
418
426
|
category: "notices",
|
|
419
427
|
});
|
|
420
428
|
if (this.#category === "notices")
|
|
421
|
-
this.#renderNotices(notices,
|
|
429
|
+
this.#renderNotices(notices, inner, push);
|
|
422
430
|
}
|
|
423
431
|
const thinking = this.#thinkingItems();
|
|
424
432
|
if (thinking.length > 0) {
|
|
@@ -428,7 +436,7 @@ export class ToolGroupView extends Container {
|
|
|
428
436
|
category: "thinking",
|
|
429
437
|
});
|
|
430
438
|
if (this.#category === "thinking")
|
|
431
|
-
this.#renderThinking(thinking,
|
|
439
|
+
this.#renderThinking(thinking, inner, push);
|
|
432
440
|
}
|
|
433
441
|
// Names and header calls share one eligibility: only completed calls are counted. A tool that
|
|
434
442
|
// is still streaming or running stays listed and expandable, but adds no number.
|
|
@@ -461,7 +469,7 @@ export class ToolGroupView extends Container {
|
|
|
461
469
|
this.#renderTools(tools, width, push, pushBlock);
|
|
462
470
|
}
|
|
463
471
|
// The rails bracket the header, categories, and any opened detail; the footer sits below them.
|
|
464
|
-
push(theme.fg("borderMuted", "─".repeat(
|
|
472
|
+
push(theme.fg("borderMuted", "─".repeat(inner)));
|
|
465
473
|
// The footer is always the block's request and token totals: one row, never a tool status.
|
|
466
474
|
push(this.#footerRow());
|
|
467
475
|
this.#hits = hits;
|
|
@@ -698,7 +706,8 @@ export class ToolGroupView extends Container {
|
|
|
698
706
|
theme.fg("dim", elapsed),
|
|
699
707
|
theme.fg("dim", `${calls} ${calls === 1 ? "call" : "calls"}`),
|
|
700
708
|
].join(theme.fg("dim", " · "));
|
|
701
|
-
|
|
709
|
+
const pad = Math.max(0, this.#outputPad());
|
|
710
|
+
this.#header.setText(fitRow(`${" ".repeat(pad)}${theme.fg("dim", this.#renderedExpanded ? "▾" : "▸")} ${details}`, Math.max(0, width - pad * 2), "…"));
|
|
702
711
|
return super.render(width);
|
|
703
712
|
}
|
|
704
713
|
/**
|
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.4",
|
|
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"
|