@esso0428/pi-subagents 0.15.13 → 0.15.14
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 +6 -0
- package/dist/ui/ccstyle/tool-renderer.d.ts.map +1 -1
- package/dist/ui/ccstyle/tool-renderer.js +19 -7
- package/dist/ui/ccstyle/tool-renderer.js.map +1 -1
- package/dist/ui/ccstyle/tool-result.d.ts +23 -4
- package/dist/ui/ccstyle/tool-result.d.ts.map +1 -1
- package/dist/ui/ccstyle/tool-result.js +165 -28
- package/dist/ui/ccstyle/tool-result.js.map +1 -1
- package/dist/ui/conversation-timeline.d.ts +2 -0
- package/dist/ui/conversation-timeline.d.ts.map +1 -1
- package/dist/ui/conversation-timeline.js +37 -1
- package/dist/ui/conversation-timeline.js.map +1 -1
- package/dist/ui/conversation-viewer.d.ts.map +1 -1
- package/dist/ui/conversation-viewer.js +19 -23
- package/dist/ui/conversation-viewer.js.map +1 -1
- package/package.json +1 -1
- package/src/ui/ccstyle/tool-renderer.ts +20 -8
- package/src/ui/ccstyle/tool-result.ts +192 -28
- package/src/ui/conversation-timeline.ts +38 -1
- package/src/ui/conversation-viewer.ts +26 -25
- package/test/ui/conversation-diff.test.ts +19 -0
|
@@ -143,6 +143,8 @@ export class ConversationTimeline implements Component {
|
|
|
143
143
|
private focusedToolCallId: string | undefined;
|
|
144
144
|
private expandedTools = new Set<string>();
|
|
145
145
|
private hoveredToolCallId: string | undefined;
|
|
146
|
+
private hoveredIoView: any;
|
|
147
|
+
private hoveredIoSection: "input" | "output" | null = null;
|
|
146
148
|
/** The last tool clicked; used to make only clickCount=2 collapse it. */
|
|
147
149
|
private lastClickedToolCallId: string | undefined;
|
|
148
150
|
private lastClickedPoint: { x: number; y: number } | undefined;
|
|
@@ -167,6 +169,11 @@ export class ConversationTimeline implements Component {
|
|
|
167
169
|
const ids = new Set(this.blocks.filter((block) => block.kind === "tool" && block.toolCallId).map((block) => block.toolCallId!));
|
|
168
170
|
if (this.focusedToolCallId && !ids.has(this.focusedToolCallId)) this.focusedToolCallId = undefined;
|
|
169
171
|
if (this.hoveredToolCallId && !ids.has(this.hoveredToolCallId)) this.hoveredToolCallId = undefined;
|
|
172
|
+
if (this.hoveredIoView && typeof this.hoveredIoView.setHoveredSection === "function") {
|
|
173
|
+
this.hoveredIoView.setHoveredSection(null);
|
|
174
|
+
this.hoveredIoView = undefined;
|
|
175
|
+
this.hoveredIoSection = null;
|
|
176
|
+
}
|
|
170
177
|
if (this.lastClickedToolCallId && !ids.has(this.lastClickedToolCallId)) {
|
|
171
178
|
this.lastClickedToolCallId = undefined;
|
|
172
179
|
this.lastClickedPoint = undefined;
|
|
@@ -182,6 +189,11 @@ export class ConversationTimeline implements Component {
|
|
|
182
189
|
if (!show) {
|
|
183
190
|
this.focusedToolCallId = undefined;
|
|
184
191
|
this.hoveredToolCallId = undefined;
|
|
192
|
+
if (this.hoveredIoView && typeof this.hoveredIoView.setHoveredSection === "function") {
|
|
193
|
+
this.hoveredIoView.setHoveredSection(null);
|
|
194
|
+
}
|
|
195
|
+
this.hoveredIoView = undefined;
|
|
196
|
+
this.hoveredIoSection = null;
|
|
185
197
|
this.lastClickedToolCallId = undefined;
|
|
186
198
|
this.lastClickedPoint = undefined;
|
|
187
199
|
}
|
|
@@ -360,8 +372,33 @@ export class ConversationTimeline implements Component {
|
|
|
360
372
|
event.y >= span.clickStartLine &&
|
|
361
373
|
event.y < span.clickStartLine + (span.clickHeight ?? 1),
|
|
362
374
|
)?.toolCallId;
|
|
363
|
-
|
|
375
|
+
const fullSpan = this.snapshot.toolSpans.find((span) =>
|
|
376
|
+
event.y >= span.startLine && event.y < span.startLine + span.height,
|
|
377
|
+
);
|
|
378
|
+
let nextIoView: any;
|
|
379
|
+
let nextIoSection: "input" | "output" | null = null;
|
|
380
|
+
if (fullSpan) {
|
|
381
|
+
const component = this.toolComponents.get(fullSpan.toolCallId) as any;
|
|
382
|
+
const view = component?.resultRendererComponent;
|
|
383
|
+
const localRow = event.y - fullSpan.startLine;
|
|
384
|
+
if (view && typeof view.sectionAtLine === "function" && typeof view.showMoreLine === "function" && typeof view.setHoveredSection === "function") {
|
|
385
|
+
const section = view.sectionAtLine(localRow);
|
|
386
|
+
if (section && view.showMoreLine(section) === localRow) {
|
|
387
|
+
nextIoView = view;
|
|
388
|
+
nextIoSection = section;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const unchanged = hovered === this.hoveredToolCallId &&
|
|
393
|
+
nextIoView === this.hoveredIoView && nextIoSection === this.hoveredIoSection;
|
|
394
|
+
if (unchanged) return undefined;
|
|
395
|
+
if (this.hoveredIoView && this.hoveredIoView !== nextIoView && typeof this.hoveredIoView.setHoveredSection === "function") {
|
|
396
|
+
this.hoveredIoView.setHoveredSection(null);
|
|
397
|
+
}
|
|
364
398
|
this.hoveredToolCallId = hovered;
|
|
399
|
+
this.hoveredIoView = nextIoView;
|
|
400
|
+
this.hoveredIoSection = nextIoSection;
|
|
401
|
+
if (nextIoView) nextIoView.setHoveredSection(nextIoSection);
|
|
365
402
|
this.invalidate();
|
|
366
403
|
this.options.onChange?.();
|
|
367
404
|
return { handled: true, render: true };
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ExtensionAPI, ExtensionContext, AgentSession } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { copyToClipboard } from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import {
|
|
10
|
+
import { type Component, Input, Key, matchesKey, type TUI, type TuiMouseEvent, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
11
11
|
import type { AgentRecord } from "../types.js";
|
|
12
12
|
import { getLifetimeTotal, getSessionContextPercent } from "../usage.js";
|
|
13
13
|
import type { Theme } from "./agent-widget.js";
|
|
@@ -18,14 +18,14 @@ import { renderConversationRoleHeader } from "./conversation-role.js";
|
|
|
18
18
|
import { editLiveAssistantBlockInNvim, viewConversationBlockInNvim } from "./conversation-nvim.js";
|
|
19
19
|
import { findConversationMatches, highlightConversationLine, nearestConversationMatchAfter, nearestConversationMatchBefore, stripAnsi, type ConversationMatch } from "./conversation-search.js";
|
|
20
20
|
import { createViewerKeys, type ViewerKeybindings, type ViewerKeys } from "./viewer-keys.js";
|
|
21
|
-
import {
|
|
21
|
+
import { createViewerCcstyleResult } from "./ccstyle/tool-result.js";
|
|
22
22
|
|
|
23
23
|
/** Base lines consumed by the overlay: borders, header, separator and footer. */
|
|
24
24
|
const CHROME_LINES_BASE = 6;
|
|
25
25
|
const FULL_TOOL_PREVIEW_MAX_CHARS = 500_000;
|
|
26
26
|
|
|
27
27
|
class FullToolPreview implements Component {
|
|
28
|
-
private readonly
|
|
28
|
+
private readonly body: Component;
|
|
29
29
|
private scrollOffset = 0;
|
|
30
30
|
private pageSize = 1;
|
|
31
31
|
private totalLines = 1;
|
|
@@ -35,10 +35,10 @@ class FullToolPreview implements Component {
|
|
|
35
35
|
private readonly tui: TUI,
|
|
36
36
|
private readonly theme: Theme,
|
|
37
37
|
private readonly title: string,
|
|
38
|
-
|
|
38
|
+
body: Component,
|
|
39
39
|
private readonly done: (result: undefined) => void,
|
|
40
40
|
) {
|
|
41
|
-
this.
|
|
41
|
+
this.body = body;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
handleInput(data: string): void {
|
|
@@ -81,7 +81,7 @@ class FullToolPreview implements Component {
|
|
|
81
81
|
const bodyWidth = Math.max(1, inner - 1);
|
|
82
82
|
const terminalRows = Math.max(1, this.tui.terminal.rows);
|
|
83
83
|
const viewport = Math.max(1, Math.min(30, Math.floor(terminalRows * 0.8), terminalRows - 6));
|
|
84
|
-
const wrapped = this.
|
|
84
|
+
const wrapped = this.body.render(bodyWidth);
|
|
85
85
|
this.totalLines = wrapped.length;
|
|
86
86
|
this.pageSize = viewport;
|
|
87
87
|
this.scrollOffset = Math.min(this.scrollOffset, Math.max(0, this.totalLines - this.pageSize));
|
|
@@ -106,7 +106,7 @@ class FullToolPreview implements Component {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
invalidate(): void {
|
|
109
|
-
this.
|
|
109
|
+
this.body.invalidate();
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
private scrollTo(offset: number): void {
|
|
@@ -676,24 +676,25 @@ export class ConversationViewer implements Component {
|
|
|
676
676
|
const custom = this.operations?.ctx?.ui?.custom;
|
|
677
677
|
if (block?.kind !== "tool" || typeof custom !== "function") return;
|
|
678
678
|
|
|
679
|
-
const
|
|
680
|
-
const
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
new FullToolPreview(tui, overlayTheme as Theme, title,
|
|
679
|
+
const toolName = block.toolName || "tool";
|
|
680
|
+
const args = block.args ?? block.toolArguments;
|
|
681
|
+
const result = block.toolResult ?? block.result ?? { content: [] };
|
|
682
|
+
const title = `${toolName} · full preview`;
|
|
683
|
+
|
|
684
|
+
void custom.call(this.operations!.ctx.ui, (tui, overlayTheme, _keybindings, done) => {
|
|
685
|
+
const body = createViewerCcstyleResult(
|
|
686
|
+
toolName,
|
|
687
|
+
result,
|
|
688
|
+
{ expanded: true, isError: result.isError === true },
|
|
689
|
+
overlayTheme,
|
|
690
|
+
{
|
|
691
|
+
args,
|
|
692
|
+
maxChars: FULL_TOOL_PREVIEW_MAX_CHARS,
|
|
693
|
+
diffConfig: { expandedPreviewMaxLines: Number.MAX_SAFE_INTEGER },
|
|
694
|
+
},
|
|
695
|
+
);
|
|
696
|
+
return new FullToolPreview(tui, overlayTheme as Theme, title, body, done);
|
|
697
|
+
}, {
|
|
697
698
|
overlay: true,
|
|
698
699
|
overlayOptions: {
|
|
699
700
|
anchor: "center",
|
|
@@ -66,6 +66,25 @@ describe("viewer-local ccstyle diff renderer", () => {
|
|
|
66
66
|
expect(rendered).not.toContain("later");
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
it("matches the pi-cc Input/Output tree style and hover affordance", () => {
|
|
70
|
+
const component = createViewerCcstyleResult("read", {
|
|
71
|
+
content: [{ type: "text", text: "one\ntwo\nthree" }],
|
|
72
|
+
}, { expanded: true }, theme, {
|
|
73
|
+
args: { path: "file.txt", offset: 2 },
|
|
74
|
+
diffConfig: { expandedPreviewMaxLines: 2 },
|
|
75
|
+
}) as any;
|
|
76
|
+
|
|
77
|
+
const initial = component.render(100).join("\n");
|
|
78
|
+
expect(initial).toContain("<accent><bold>Input</bold></accent>");
|
|
79
|
+
expect(initial).toContain("<dim>path: </dim><muted>file.txt</muted>");
|
|
80
|
+
expect(initial).toContain("<accent><bold>Output</bold></accent>");
|
|
81
|
+
expect(initial).toContain("click to show more");
|
|
82
|
+
const outputHintLine = component.showMoreLine("output");
|
|
83
|
+
expect(outputHintLine).toEqual(expect.any(Number));
|
|
84
|
+
component.setHoveredSection("output");
|
|
85
|
+
expect(component.render(100).join("\n")).toContain("<text> click to show more</text>");
|
|
86
|
+
});
|
|
87
|
+
|
|
69
88
|
it("reads tool-hint hover state at render time", () => {
|
|
70
89
|
let hovered = false;
|
|
71
90
|
const component = createViewerCcstyleResult("read", {
|