pi-ui-extend 0.1.32 → 0.1.33
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 +1 -1
- package/dist/app/app.d.ts +2 -0
- package/dist/app/app.js +28 -0
- package/dist/app/commands/command-session-actions.js +29 -1
- package/dist/app/constants.d.ts +1 -1
- package/dist/app/constants.js +2 -2
- package/dist/app/icons.d.ts +4 -9
- package/dist/app/icons.js +11 -34
- package/dist/app/rendering/conversation-entry-renderer.d.ts +1 -0
- package/dist/app/rendering/conversation-tool-renderer.d.ts +1 -0
- package/dist/app/rendering/conversation-tool-renderer.js +12 -18
- package/dist/app/rendering/conversation-viewport.d.ts +4 -0
- package/dist/app/rendering/conversation-viewport.js +144 -13
- package/dist/app/rendering/dcp-stats.js +42 -16
- package/dist/app/rendering/render-controller.js +4 -0
- package/dist/app/rendering/status-line-renderer.d.ts +7 -1
- package/dist/app/rendering/status-line-renderer.js +21 -0
- package/dist/app/rendering/tab-line-renderer.js +2 -2
- package/dist/app/rendering/tool-block-renderer.d.ts +1 -0
- package/dist/app/rendering/tool-block-renderer.js +37 -11
- package/dist/app/runtime.js +1 -1
- package/dist/app/screen/mouse-controller.d.ts +5 -1
- package/dist/app/screen/mouse-controller.js +16 -0
- package/dist/app/screen/scroll-controller.d.ts +20 -0
- package/dist/app/screen/scroll-controller.js +127 -10
- package/dist/app/session/lazy-session-manager.js +35 -5
- package/dist/app/session/pix-system-message.d.ts +1 -0
- package/dist/app/session/pix-system-message.js +14 -3
- package/dist/app/session/queued-message-controller.d.ts +11 -4
- package/dist/app/session/queued-message-controller.js +74 -59
- package/dist/app/session/queued-message-entries.d.ts +2 -1
- package/dist/app/session/queued-message-entries.js +12 -1
- package/dist/app/session/session-event-controller.d.ts +42 -1
- package/dist/app/session/session-event-controller.js +473 -29
- package/dist/app/session/session-history.js +23 -4
- package/dist/app/session/tabs-controller.d.ts +11 -1
- package/dist/app/session/tabs-controller.js +102 -21
- package/dist/app/types.d.ts +14 -1
- package/dist/bundled-extensions/question/contract.d.ts +25 -0
- package/dist/bundled-extensions/question/contract.js +94 -0
- package/dist/bundled-extensions/question/index.d.ts +7 -0
- package/dist/bundled-extensions/question/index.js +28 -0
- package/dist/bundled-extensions/question/render.d.ts +4 -0
- package/dist/bundled-extensions/question/render.js +27 -0
- package/dist/bundled-extensions/question/result.d.ts +6 -0
- package/dist/bundled-extensions/question/result.js +84 -0
- package/dist/bundled-extensions/question/tool-description.d.ts +7 -0
- package/dist/bundled-extensions/question/tool-description.js +11 -0
- package/dist/bundled-extensions/question/tui.d.ts +2 -0
- package/dist/bundled-extensions/question/tui.js +577 -0
- package/dist/bundled-extensions/question/types.d.ts +103 -0
- package/dist/bundled-extensions/question/types.js +1 -0
- package/dist/bundled-extensions/session-title/config.d.ts +17 -0
- package/dist/bundled-extensions/session-title/config.js +150 -0
- package/dist/bundled-extensions/session-title/index.d.ts +5 -0
- package/dist/bundled-extensions/session-title/index.js +384 -0
- package/dist/bundled-extensions/session-title/title-generation.d.ts +26 -0
- package/dist/bundled-extensions/session-title/title-generation.js +141 -0
- package/dist/bundled-extensions/terminal-bell/index.d.ts +14 -0
- package/dist/bundled-extensions/terminal-bell/index.js +491 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -1
- package/dist/default-pix-config.js +2 -1
- package/dist/icon-theme.d.ts +7 -0
- package/dist/icon-theme.js +36 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +4 -0
- package/dist/schemas/pi-tools-suite-schema.js +5 -0
- package/dist/schemas/pix-schema.d.ts +1 -0
- package/dist/schemas/pix-schema.js +1 -0
- package/external/pi-tools-suite/README.md +7 -7
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +16 -16
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +18 -4
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +4 -0
- package/external/pi-tools-suite/src/async-subagents/tools/result.ts +14 -26
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +0 -1
- package/external/pi-tools-suite/src/dcp/config.ts +14 -14
- package/external/pi-tools-suite/src/dcp/index.ts +31 -43
- package/external/pi-tools-suite/src/dcp/state-persistence.ts +151 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +25 -18
- package/external/pi-tools-suite/src/tool-descriptions.ts +6 -7
- package/package.json +3 -2
- package/schemas/pi-tools-suite.json +14 -0
- package/schemas/pix.json +7 -0
- package/extensions/question/contract.ts +0 -100
- package/extensions/question/index.ts +0 -34
- package/extensions/question/render.ts +0 -28
- package/extensions/question/result.ts +0 -86
- package/extensions/question/tool-description.ts +0 -11
- package/extensions/question/tui.ts +0 -629
- package/extensions/question/types.ts +0 -123
- package/extensions/session-title/config.ts +0 -164
- package/extensions/session-title/index.ts +0 -502
- package/extensions/terminal-bell/index.ts +0 -345
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import { normalizeToolName, parseArgsText } from "../../tool-renderers/utils.js";
|
|
2
4
|
const NUDGE_TYPES = ["turn", "iteration", "context-soft", "context-strong"];
|
|
3
5
|
export function formatDcpStatsToast(session) {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
+
const latestState = resolveLatestDcpState(session);
|
|
7
|
+
const stats = collectDcpSessionStats(session, latestState);
|
|
8
|
+
const nudgeStats = collectDcpNudgeStats(session, latestState?.data);
|
|
6
9
|
const activeBlocks = stats.activeBlocks ?? 0;
|
|
7
10
|
const totalBlocks = stats.totalBlocks ?? stats.activeBlocks ?? 0;
|
|
8
11
|
const totalNudgeEvents = nudgeStats.emitted + nudgeStats.upgraded;
|
|
@@ -12,7 +15,8 @@ export function formatDcpStatsToast(session) {
|
|
|
12
15
|
` Tokens saved (estimated): ${fmt(stats.tokensSaved)}`,
|
|
13
16
|
` Total pruning operations: ${fmt(stats.totalPruneCount)}`,
|
|
14
17
|
` Compression blocks active: ${activeBlocks} / ${totalBlocks} total`,
|
|
15
|
-
|
|
18
|
+
` Manual mode: ${stats.manualMode === true ? "on" : stats.manualMode === false ? "off" : "unknown"}`,
|
|
19
|
+
` State source: ${formatStateSource(stats.stateSource)}`,
|
|
16
20
|
"",
|
|
17
21
|
"Nudge telemetry:",
|
|
18
22
|
` Sent: ${fmt(nudgeStats.emitted)} emitted, ${fmt(nudgeStats.upgraded)} upgraded`,
|
|
@@ -28,7 +32,7 @@ export function formatDcpStatsToast(session) {
|
|
|
28
32
|
];
|
|
29
33
|
return lines.join("\n");
|
|
30
34
|
}
|
|
31
|
-
function collectDcpSessionStats(session) {
|
|
35
|
+
function collectDcpSessionStats(session, latestState) {
|
|
32
36
|
const usage = session.getContextUsage();
|
|
33
37
|
const stats = {
|
|
34
38
|
runs: 0,
|
|
@@ -37,14 +41,14 @@ function collectDcpSessionStats(session) {
|
|
|
37
41
|
items: 0,
|
|
38
42
|
summaryTokens: 0,
|
|
39
43
|
prunedTools: 0,
|
|
44
|
+
stateSource: latestState?.source ?? "tool-results",
|
|
40
45
|
...(usage?.tokens != null ? { contextTokens: usage.tokens } : {}),
|
|
41
46
|
...(usage?.contextWindow != null ? { contextWindow: usage.contextWindow } : {}),
|
|
42
47
|
...(usage?.percent != null ? { contextPercent: usage.percent } : {}),
|
|
43
48
|
};
|
|
44
49
|
const branch = session.sessionManager.getBranch();
|
|
45
|
-
const latestState = latestCustomEntryData(branch, "dcp-state");
|
|
46
50
|
if (latestState)
|
|
47
|
-
applyDcpStateStats(stats, latestState);
|
|
51
|
+
applyDcpStateStats(stats, latestState.data);
|
|
48
52
|
for (const entry of branch) {
|
|
49
53
|
if (entry.type !== "message")
|
|
50
54
|
continue;
|
|
@@ -94,8 +98,10 @@ function applyDcpStateStats(stats, data) {
|
|
|
94
98
|
}
|
|
95
99
|
if (Array.isArray(data.prunedToolIds))
|
|
96
100
|
stats.prunedTools = data.prunedToolIds.length;
|
|
101
|
+
if (typeof data.manualMode === "boolean")
|
|
102
|
+
stats.manualMode = data.manualMode;
|
|
97
103
|
}
|
|
98
|
-
function collectDcpNudgeStats(session) {
|
|
104
|
+
function collectDcpNudgeStats(session, latestState) {
|
|
99
105
|
const stats = {
|
|
100
106
|
emitted: 0,
|
|
101
107
|
upgraded: 0,
|
|
@@ -105,7 +111,6 @@ function collectDcpNudgeStats(session) {
|
|
|
105
111
|
activeByType: { "turn": 0, "iteration": 0, "context-soft": 0, "context-strong": 0 },
|
|
106
112
|
};
|
|
107
113
|
const branch = session.sessionManager.getBranch();
|
|
108
|
-
const latestState = latestCustomEntryData(branch, "dcp-state");
|
|
109
114
|
if (latestState)
|
|
110
115
|
applyActiveAnchorStats(stats, latestState);
|
|
111
116
|
for (const entry of branch) {
|
|
@@ -156,19 +161,40 @@ function applyActiveAnchorStats(stats, data) {
|
|
|
156
161
|
};
|
|
157
162
|
}
|
|
158
163
|
}
|
|
164
|
+
function resolveLatestDcpState(session) {
|
|
165
|
+
const sidecar = loadSidecarDcpState(session);
|
|
166
|
+
if (sidecar)
|
|
167
|
+
return { data: sidecar, source: "sidecar" };
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
function loadSidecarDcpState(session) {
|
|
171
|
+
const sessionManager = session.sessionManager;
|
|
172
|
+
const sessionDir = sessionManager.getSessionDir?.();
|
|
173
|
+
const sessionId = sessionManager.getSessionId?.();
|
|
174
|
+
if (!sessionDir || !sessionId)
|
|
175
|
+
return undefined;
|
|
176
|
+
try {
|
|
177
|
+
const statePath = join(sessionDir, "dcp-state", safeSessionFileName(sessionId));
|
|
178
|
+
const parsed = JSON.parse(readFileSync(statePath, "utf8"));
|
|
179
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function safeSessionFileName(sessionId) {
|
|
186
|
+
return `${sessionId.replace(/[^a-zA-Z0-9._-]/g, "_")}.json`;
|
|
187
|
+
}
|
|
188
|
+
function formatStateSource(source) {
|
|
189
|
+
if (source === "sidecar")
|
|
190
|
+
return "dcp-state sidecar";
|
|
191
|
+
return "compress tool results";
|
|
192
|
+
}
|
|
159
193
|
function customEntryData(entry, customType) {
|
|
160
194
|
if (!isRecord(entry) || entry.type !== "custom" || entry.customType !== customType)
|
|
161
195
|
return undefined;
|
|
162
196
|
return isRecord(entry.data) ? entry.data : undefined;
|
|
163
197
|
}
|
|
164
|
-
function latestCustomEntryData(entries, customType) {
|
|
165
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
166
|
-
const data = customEntryData(entries[i], customType);
|
|
167
|
-
if (data)
|
|
168
|
-
return data;
|
|
169
|
-
}
|
|
170
|
-
return undefined;
|
|
171
|
-
}
|
|
172
198
|
function parseToolResultText(content) {
|
|
173
199
|
const parsed = parseArgsText(textContent(content));
|
|
174
200
|
return isRecord(parsed) ? parsed : undefined;
|
|
@@ -67,6 +67,8 @@ export class AppRenderController {
|
|
|
67
67
|
this.deps.mouseController.statusUserJumpTarget = undefined;
|
|
68
68
|
this.deps.mouseController.statusThinkingExpandTarget = undefined;
|
|
69
69
|
this.deps.mouseController.statusCompactToolsTarget = undefined;
|
|
70
|
+
this.deps.mouseController.statusQuickScrollUpTarget = undefined;
|
|
71
|
+
this.deps.mouseController.statusQuickScrollDownTarget = undefined;
|
|
70
72
|
this.deps.mouseController.statusTerminalBellSoundTarget = undefined;
|
|
71
73
|
this.deps.mouseController.statusSessionTarget = undefined;
|
|
72
74
|
this.deps.mouseController.statusPromptEnhancerTarget = undefined;
|
|
@@ -289,6 +291,8 @@ export class AppRenderController {
|
|
|
289
291
|
this.deps.mouseController.statusUserJumpTarget = this.deps.statusLineRenderer.userJumpTarget?.(widgetLayout, widgetRow);
|
|
290
292
|
this.deps.mouseController.statusThinkingExpandTarget = this.deps.statusLineRenderer.thinkingExpandTarget?.(widgetLayout, widgetRow);
|
|
291
293
|
this.deps.mouseController.statusCompactToolsTarget = this.deps.statusLineRenderer.compactToolsTarget?.(widgetLayout, widgetRow);
|
|
294
|
+
this.deps.mouseController.statusQuickScrollUpTarget = this.deps.statusLineRenderer.quickScrollUpTarget?.(widgetLayout, widgetRow);
|
|
295
|
+
this.deps.mouseController.statusQuickScrollDownTarget = this.deps.statusLineRenderer.quickScrollDownTarget?.(widgetLayout, widgetRow);
|
|
292
296
|
this.deps.mouseController.statusTerminalBellSoundTarget = this.deps.statusLineRenderer.terminalBellSoundTarget?.(widgetLayout, widgetRow);
|
|
293
297
|
}
|
|
294
298
|
this.deps.mouseController.statusSessionTarget = this.deps.statusLineRenderer.sessionTarget(statusLayout.text, statusRow, statusLayout.sessionLabel, statusLayout.workspaceLabel);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { Theme } from "../../theme.js";
|
|
3
|
-
import type { SessionActivity, StatusCompactToolsTarget, StatusContextTarget, StatusDraftQueueTarget, StatusLineLayout, StatusModelTarget, StatusModelUsageTarget, StatusPromptEnhancerTarget, StatusSessionTarget, StatusTerminalBellSoundTarget, StatusThinkingExpandTarget, StatusThinkingTarget, StatusUserJumpTarget, StatusVoiceLanguageTarget, StatusVoiceMicTarget } from "../types.js";
|
|
3
|
+
import type { SessionActivity, StatusCompactToolsTarget, StatusContextTarget, StatusDraftQueueTarget, StatusLineLayout, StatusModelTarget, StatusModelUsageTarget, StatusPromptEnhancerTarget, StatusQuickScrollTarget, StatusSessionTarget, StatusTerminalBellSoundTarget, StatusThinkingExpandTarget, StatusThinkingTarget, StatusUserJumpTarget, StatusVoiceLanguageTarget, StatusVoiceMicTarget } from "../types.js";
|
|
4
4
|
import type { ScreenStyler } from "../screen/screen-styler.js";
|
|
5
5
|
import { type ModelColorsConfig } from "../../config.js";
|
|
6
6
|
export type StatusLineRendererHost = {
|
|
@@ -26,6 +26,10 @@ export type StatusLineRendererHost = {
|
|
|
26
26
|
terminalBellSoundStatusWidgetEnabled(): boolean;
|
|
27
27
|
voiceStatusWidgetText(): string;
|
|
28
28
|
voiceStatusWidgetActive(): boolean;
|
|
29
|
+
conversationQuickScrollDirections?(): {
|
|
30
|
+
up: boolean;
|
|
31
|
+
down: boolean;
|
|
32
|
+
};
|
|
29
33
|
queueableInputActive?(): boolean;
|
|
30
34
|
userMessageJumpMenuActive?(): boolean;
|
|
31
35
|
allThinkingExpandedActive?(): boolean;
|
|
@@ -50,6 +54,8 @@ export declare class StatusLineRenderer {
|
|
|
50
54
|
draftQueueTarget(layout: StatusLineLayout, row: number): StatusDraftQueueTarget | undefined;
|
|
51
55
|
thinkingExpandTarget(layout: StatusLineLayout, row: number): StatusThinkingExpandTarget | undefined;
|
|
52
56
|
compactToolsTarget(layout: StatusLineLayout, row: number): StatusCompactToolsTarget | undefined;
|
|
57
|
+
quickScrollUpTarget(layout: StatusLineLayout, row: number): StatusQuickScrollTarget | undefined;
|
|
58
|
+
quickScrollDownTarget(layout: StatusLineLayout, row: number): StatusQuickScrollTarget | undefined;
|
|
53
59
|
terminalBellSoundTarget(layout: StatusLineLayout, row: number): StatusTerminalBellSoundTarget | undefined;
|
|
54
60
|
sessionTarget(statusText: string, row: number, label: string, workspaceLabel: string): StatusSessionTarget | undefined;
|
|
55
61
|
private segments;
|
|
@@ -58,6 +58,7 @@ export class StatusLineRenderer {
|
|
|
58
58
|
widgets.push({ text, assign });
|
|
59
59
|
hasParts = true;
|
|
60
60
|
};
|
|
61
|
+
const quickScrollDirections = this.host.conversationQuickScrollDirections?.() ?? { up: false, down: false };
|
|
61
62
|
const draftQueueButton = this.draftQueueWidgetText();
|
|
62
63
|
appendWidget(draftQueueButton ? this.iconButtonText(draftQueueButton) : "", (column, text) => {
|
|
63
64
|
layout.draftQueueWidget = this.widgetLayout(column, text);
|
|
@@ -79,6 +80,12 @@ export class StatusLineRenderer {
|
|
|
79
80
|
appendWidget(this.iconButtonText(APP_ICONS.compactTools), (column, text) => {
|
|
80
81
|
layout.compactToolsWidget = this.widgetLayout(column, text);
|
|
81
82
|
});
|
|
83
|
+
appendWidget(quickScrollDirections.up ? this.iconButtonText(APP_ICONS.up) : "", (column, text) => {
|
|
84
|
+
layout.quickScrollUpWidget = this.widgetLayout(column, text);
|
|
85
|
+
});
|
|
86
|
+
appendWidget(quickScrollDirections.down ? this.iconButtonText(APP_ICONS.down) : "", (column, text) => {
|
|
87
|
+
layout.quickScrollDownWidget = this.widgetLayout(column, text);
|
|
88
|
+
});
|
|
82
89
|
const voiceWidgetText = this.host.voiceStatusWidgetText();
|
|
83
90
|
appendWidget(this.voiceBorderWidgetText(voiceWidgetText), (column, text) => {
|
|
84
91
|
layout.voiceWidget = this.voiceWidgetLayout(column, voiceWidgetText, text);
|
|
@@ -137,6 +144,8 @@ export class StatusLineRenderer {
|
|
|
137
144
|
: this.host.promptEnhancerStatusWidgetEnabled()
|
|
138
145
|
? colors.info
|
|
139
146
|
: colors.muted);
|
|
147
|
+
pushWidgetSegment(layout.quickScrollUpWidget, colors.info);
|
|
148
|
+
pushWidgetSegment(layout.quickScrollDownWidget, colors.info);
|
|
140
149
|
pushWidgetSegment(layout.userJumpWidget, this.host.userMessageJumpMenuActive?.() ? colors.info : colors.muted);
|
|
141
150
|
pushWidgetSegment(layout.terminalBellSoundWidget, this.host.terminalBellSoundStatusWidgetEnabled() ? colors.info : colors.muted);
|
|
142
151
|
pushWidgetSegment(layout.thinkingExpandWidget, this.host.allThinkingExpandedActive?.() ? colors.info : colors.muted);
|
|
@@ -252,6 +261,18 @@ export class StatusLineRenderer {
|
|
|
252
261
|
return undefined;
|
|
253
262
|
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn };
|
|
254
263
|
}
|
|
264
|
+
quickScrollUpTarget(layout, row) {
|
|
265
|
+
const widget = layout.quickScrollUpWidget;
|
|
266
|
+
if (!widget)
|
|
267
|
+
return undefined;
|
|
268
|
+
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn, direction: "up" };
|
|
269
|
+
}
|
|
270
|
+
quickScrollDownTarget(layout, row) {
|
|
271
|
+
const widget = layout.quickScrollDownWidget;
|
|
272
|
+
if (!widget)
|
|
273
|
+
return undefined;
|
|
274
|
+
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn, direction: "down" };
|
|
275
|
+
}
|
|
255
276
|
terminalBellSoundTarget(layout, row) {
|
|
256
277
|
const widget = layout.terminalBellSoundWidget;
|
|
257
278
|
if (!widget)
|
|
@@ -197,9 +197,9 @@ export class TabLineRenderer {
|
|
|
197
197
|
return { foreground: this.host.theme.colors.error, bold: true };
|
|
198
198
|
}
|
|
199
199
|
if (tab.activity === "running" || tab.activity === "thinking") {
|
|
200
|
-
return { foreground: this.host.theme.colors.
|
|
200
|
+
return { foreground: this.host.theme.colors.warning, bold: true };
|
|
201
201
|
}
|
|
202
|
-
return { foreground: this.host.theme.colors.
|
|
202
|
+
return { foreground: this.host.theme.colors.success };
|
|
203
203
|
}
|
|
204
204
|
statusIndicatorIcon(tab) {
|
|
205
205
|
if (tab.status !== "active" && tab.attention === "terminal-bell" && tab.attentionVisible !== false) {
|
|
@@ -26,5 +26,6 @@ export type ToolBlockRenderOptions = {
|
|
|
26
26
|
backgroundOverride?: string;
|
|
27
27
|
skipHeaderBackground?: boolean;
|
|
28
28
|
showGutter?: boolean;
|
|
29
|
+
headerColorOverride?: string;
|
|
29
30
|
};
|
|
30
31
|
export declare function renderToolBlock(entry: ToolBlockEntry, rule: ResolvedToolRule, width: number, colors: Theme["colors"], options?: ToolBlockRenderOptions): RenderedLine[];
|
|
@@ -18,7 +18,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
18
18
|
const hasLspDiagnostics = hasToolLspDiagnosticsAfterMutation(entry);
|
|
19
19
|
const expanded = entry.expanded;
|
|
20
20
|
const stateIcon = toolStatusIcon(entry);
|
|
21
|
-
const toolColor = resolveColor(rule.color, colors);
|
|
21
|
+
const toolColor = options.headerColorOverride ?? resolveColor(rule.color, colors);
|
|
22
22
|
const toolOutputColor = colors.statusForeground;
|
|
23
23
|
const headerLabel = (entry.headerLabel ?? entry.toolName).toLowerCase();
|
|
24
24
|
const bg = options.backgroundOverride;
|
|
@@ -87,18 +87,42 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
87
87
|
return headerLines;
|
|
88
88
|
}
|
|
89
89
|
function renderCollapsedPreviewLines(entry, body, rule, width, target, color, colors, hasLspDiagnostics, showGutter) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const preview = previewBodyText(body, rule.direction, rule.previewLines);
|
|
91
|
+
const allPreviewLines = renderToolBodyLines(preview.text, width, target, color, entry.bodyStyle, colors, undefined, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi, showGutter, { rawLineOffset: preview.rawLineOffset, bodyEndsAfterText: !preview.omittedAfter });
|
|
92
|
+
const overflow = preview.omittedBefore || preview.omittedAfter || allPreviewLines.length > rule.previewLines;
|
|
93
|
+
if (!overflow)
|
|
94
|
+
return allPreviewLines;
|
|
95
|
+
const previewLines = rule.direction === "tail" ? allPreviewLines.slice(-rule.previewLines) : allPreviewLines.slice(0, rule.previewLines);
|
|
94
96
|
return markTruncatedPreviewLine(previewLines, rule.direction, colors.statusDotBase);
|
|
95
97
|
}
|
|
96
98
|
function collapsedInlinePreview(text, rule, preserveAnsi = false) {
|
|
97
|
-
const
|
|
99
|
+
const preview = previewBodyText(text, rule.direction, rule.previewLines);
|
|
100
|
+
const rawLines = sanitizeToolBodyText(preview.text, preserveAnsi).split("\n").map((line) => stripAnsi(line).trim()).filter(Boolean);
|
|
98
101
|
if (rawLines.length === 0)
|
|
99
102
|
return { text: "", overflow: false };
|
|
100
103
|
const selectedLines = rule.direction === "tail" ? rawLines.slice(-rule.previewLines) : rawLines.slice(0, rule.previewLines);
|
|
101
|
-
return { text: selectedLines.join(" "), overflow: rawLines.length > rule.previewLines };
|
|
104
|
+
return { text: selectedLines.join(" "), overflow: preview.omittedBefore || preview.omittedAfter || rawLines.length > rule.previewLines };
|
|
105
|
+
}
|
|
106
|
+
function previewBodyText(text, direction, previewLines) {
|
|
107
|
+
const rawLines = text.split("\n");
|
|
108
|
+
const previewRawLines = Math.max(1, previewLines + 1);
|
|
109
|
+
if (rawLines.length <= previewRawLines)
|
|
110
|
+
return { text, rawLineOffset: 0, omittedBefore: false, omittedAfter: false };
|
|
111
|
+
if (direction === "tail") {
|
|
112
|
+
const start = Math.max(0, rawLines.length - previewRawLines);
|
|
113
|
+
return {
|
|
114
|
+
text: rawLines.slice(start).join("\n"),
|
|
115
|
+
rawLineOffset: start,
|
|
116
|
+
omittedBefore: start > 0,
|
|
117
|
+
omittedAfter: false,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
text: rawLines.slice(0, previewRawLines).join("\n"),
|
|
122
|
+
rawLineOffset: 0,
|
|
123
|
+
omittedBefore: false,
|
|
124
|
+
omittedAfter: rawLines.length > previewRawLines,
|
|
125
|
+
};
|
|
102
126
|
}
|
|
103
127
|
function markTruncatedPreviewLine(lines, direction, markerColor) {
|
|
104
128
|
if (lines.length === 0)
|
|
@@ -122,7 +146,7 @@ function markTruncatedPreviewLine(lines, direction, markerColor) {
|
|
|
122
146
|
};
|
|
123
147
|
});
|
|
124
148
|
}
|
|
125
|
-
function renderToolBodyLines(text, width, target, color, style, colors, syntaxHighlight, bodyWrap = "char", hasLspDiagnostics = false, bodyLineStyles, preserveAnsi = false, showGutter = false) {
|
|
149
|
+
function renderToolBodyLines(text, width, target, color, style, colors, syntaxHighlight, bodyWrap = "char", hasLspDiagnostics = false, bodyLineStyles, preserveAnsi = false, showGutter = false, options = {}) {
|
|
126
150
|
const displayPrefix = showGutter ? toolBodyGutterPrefix() : TOOL_BODY_PREFIX;
|
|
127
151
|
const prefixWidth = stringDisplayWidth(displayPrefix);
|
|
128
152
|
const bodyWidth = Math.max(1, width - prefixWidth);
|
|
@@ -131,13 +155,15 @@ function renderToolBodyLines(text, width, target, color, style, colors, syntaxHi
|
|
|
131
155
|
const gutterSegment = showGutter
|
|
132
156
|
? { start: 0, end: 1, foreground: colors.statusDotBase }
|
|
133
157
|
: undefined;
|
|
158
|
+
const rawLineOffset = options.rawLineOffset ?? 0;
|
|
134
159
|
for (const [rawLineIndex, rawLine] of sanitizeToolBodyText(text, preserveAnsi).split("\n").entries()) {
|
|
160
|
+
const absoluteRawLineIndex = rawLineOffset + rawLineIndex;
|
|
135
161
|
const ansiLine = preserveAnsi ? ansiStyledLine(rawLine) : undefined;
|
|
136
162
|
const displayLine = ansiLine?.text ?? rawLine;
|
|
137
163
|
const diffStyle = style === "diff" ? diffLineStyle(displayLine, colors) : undefined;
|
|
138
164
|
const lspDiagnosticStyle = hasLspDiagnostics ? lspDiagnosticLineStyle(displayLine, colors) : undefined;
|
|
139
|
-
const bodyLineStyle = bodyLineStyleForLine(bodyLineStyles,
|
|
140
|
-
const lineSyntaxHighlight = syntaxHighlightForLine(syntaxHighlight,
|
|
165
|
+
const bodyLineStyle = bodyLineStyleForLine(bodyLineStyles, absoluteRawLineIndex, colors);
|
|
166
|
+
const lineSyntaxHighlight = syntaxHighlightForLine(syntaxHighlight, absoluteRawLineIndex);
|
|
141
167
|
const wrappedLines = ansiLine && !diffStyle && !lspDiagnosticStyle && !bodyLineStyle && !lineSyntaxHighlight
|
|
142
168
|
? wrapAnsiStyledDisplayLine(ansiLine, bodyWidth)
|
|
143
169
|
: wrapBodyLine(displayLine, bodyWidth).map((wrapped) => ({ text: wrapped, segments: [] }));
|
|
@@ -195,7 +221,7 @@ function renderToolBodyLines(text, width, target, color, style, colors, syntaxHi
|
|
|
195
221
|
lines.push(line);
|
|
196
222
|
}
|
|
197
223
|
}
|
|
198
|
-
if (showGutter && lines.length > 0) {
|
|
224
|
+
if (showGutter && lines.length > 0 && options.bodyEndsAfterText !== false) {
|
|
199
225
|
const lastLine = lines.at(-1);
|
|
200
226
|
if (!lastLine)
|
|
201
227
|
return lines;
|
package/dist/app/runtime.js
CHANGED
|
@@ -10,7 +10,7 @@ import { isThinkingLevel, parseModelRef, parseScopedModelRef } from "./model/mod
|
|
|
10
10
|
import { openLazySessionManager } from "./session/lazy-session-manager.js";
|
|
11
11
|
const BUNDLED_QUESTION_EXTENSION_NAME = "question";
|
|
12
12
|
const PI_TOOLS_SUITE_EXTENSION_NAME = "pi-tools-suite";
|
|
13
|
-
const BUNDLED_EXTENSIONS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "
|
|
13
|
+
const BUNDLED_EXTENSIONS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "bundled-extensions");
|
|
14
14
|
const BUNDLED_QUESTION_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, BUNDLED_QUESTION_EXTENSION_NAME);
|
|
15
15
|
const BUNDLED_SESSION_TITLE_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, "session-title");
|
|
16
16
|
const BUNDLED_TERMINAL_BELL_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, "terminal-bell");
|
|
@@ -6,7 +6,7 @@ import type { ToastEntry, ToastVariant } from "../../ui.js";
|
|
|
6
6
|
import type { AppPopupActionController } from "../popup/popup-action-controller.js";
|
|
7
7
|
import type { AppPopupMenuController } from "../popup/popup-menu-controller.js";
|
|
8
8
|
import type { AppScrollController } from "./scroll-controller.js";
|
|
9
|
-
import type { Entry, ImageClickTarget, MouseEvent, MouseSelection, StatusContextTarget, StatusCompactToolsTarget, StatusDraftQueueTarget, StatusModelTarget, StatusModelUsageTarget, StatusPromptEnhancerTarget, StatusSessionTarget, StatusTerminalBellSoundTarget, TabLineMouseTarget, StatusThinkingExpandTarget, StatusThinkingTarget, StatusUserJumpTarget, StatusVoiceLanguageTarget, StatusVoiceMicTarget } from "../types.js";
|
|
9
|
+
import type { Entry, ImageClickTarget, MouseEvent, MouseSelection, StatusContextTarget, StatusCompactToolsTarget, StatusDraftQueueTarget, StatusModelTarget, StatusModelUsageTarget, StatusPromptEnhancerTarget, StatusQuickScrollTarget, StatusSessionTarget, StatusTerminalBellSoundTarget, TabLineMouseTarget, StatusThinkingExpandTarget, StatusThinkingTarget, StatusUserJumpTarget, StatusVoiceLanguageTarget, StatusVoiceMicTarget } from "../types.js";
|
|
10
10
|
import { type RenderedLink } from "./file-links.js";
|
|
11
11
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
type ClickFlash = {
|
|
@@ -56,6 +56,7 @@ export type AppMouseControllerHost = {
|
|
|
56
56
|
refreshModelUsageStatus(): void | Promise<void>;
|
|
57
57
|
refreshUserMessageJumpMenuItems?(): Promise<void>;
|
|
58
58
|
queueInputFromStatus?(): void | Promise<void>;
|
|
59
|
+
scrollConversationQuick(direction: "up" | "down"): void | Promise<void>;
|
|
59
60
|
toggleAllThinkingExpanded?(): void;
|
|
60
61
|
toggleSuperCompactTools?(): void;
|
|
61
62
|
toggleTerminalBellSound?(): void;
|
|
@@ -103,6 +104,8 @@ export declare class AppMouseController {
|
|
|
103
104
|
statusDraftQueueTarget: StatusDraftQueueTarget | undefined;
|
|
104
105
|
statusThinkingExpandTarget: StatusThinkingExpandTarget | undefined;
|
|
105
106
|
statusCompactToolsTarget: StatusCompactToolsTarget | undefined;
|
|
107
|
+
statusQuickScrollUpTarget: StatusQuickScrollTarget | undefined;
|
|
108
|
+
statusQuickScrollDownTarget: StatusQuickScrollTarget | undefined;
|
|
106
109
|
statusTerminalBellSoundTarget: StatusTerminalBellSoundTarget | undefined;
|
|
107
110
|
statusSessionTarget: StatusSessionTarget | undefined;
|
|
108
111
|
statusPromptEnhancerTarget: StatusPromptEnhancerTarget | undefined;
|
|
@@ -165,6 +168,7 @@ export declare class AppMouseController {
|
|
|
165
168
|
private handleStatusPromptEnhancerClick;
|
|
166
169
|
private handleStatusVoiceMicClick;
|
|
167
170
|
private handleStatusVoiceLanguageClick;
|
|
171
|
+
private handleStatusQuickScrollClick;
|
|
168
172
|
private handleInputClick;
|
|
169
173
|
private handleExtensionInputClick;
|
|
170
174
|
private openUserMessageMenu;
|
|
@@ -28,6 +28,8 @@ export class AppMouseController {
|
|
|
28
28
|
statusDraftQueueTarget;
|
|
29
29
|
statusThinkingExpandTarget;
|
|
30
30
|
statusCompactToolsTarget;
|
|
31
|
+
statusQuickScrollUpTarget;
|
|
32
|
+
statusQuickScrollDownTarget;
|
|
31
33
|
statusTerminalBellSoundTarget;
|
|
32
34
|
statusSessionTarget;
|
|
33
35
|
statusPromptEnhancerTarget;
|
|
@@ -78,6 +80,8 @@ export class AppMouseController {
|
|
|
78
80
|
return;
|
|
79
81
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusModelUsageClick(event)))
|
|
80
82
|
return;
|
|
83
|
+
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusQuickScrollClick(event)))
|
|
84
|
+
return;
|
|
81
85
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusSessionClick(event)))
|
|
82
86
|
return;
|
|
83
87
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleExtensionInputClick(event)))
|
|
@@ -316,6 +320,8 @@ export class AppMouseController {
|
|
|
316
320
|
this.statusUserJumpTarget,
|
|
317
321
|
this.statusThinkingExpandTarget,
|
|
318
322
|
this.statusCompactToolsTarget,
|
|
323
|
+
this.statusQuickScrollUpTarget,
|
|
324
|
+
this.statusQuickScrollDownTarget,
|
|
319
325
|
this.statusTerminalBellSoundTarget,
|
|
320
326
|
this.statusSessionTarget,
|
|
321
327
|
this.statusPromptEnhancerTarget,
|
|
@@ -600,6 +606,16 @@ export class AppMouseController {
|
|
|
600
606
|
this.host.toggleVoiceLanguage();
|
|
601
607
|
return true;
|
|
602
608
|
}
|
|
609
|
+
handleStatusQuickScrollClick(event) {
|
|
610
|
+
const target = [this.statusQuickScrollUpTarget, this.statusQuickScrollDownTarget].find((candidate) => !!candidate
|
|
611
|
+
&& event.y === candidate.row
|
|
612
|
+
&& event.x >= candidate.startColumn
|
|
613
|
+
&& event.x < candidate.endColumn);
|
|
614
|
+
if (!target)
|
|
615
|
+
return false;
|
|
616
|
+
this.host.scrollConversationQuick(target.direction);
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
603
619
|
handleInputClick(event) {
|
|
604
620
|
const columns = this.host.terminalColumns();
|
|
605
621
|
const terminalRows = this.host.terminalRows();
|
|
@@ -12,6 +12,10 @@ export type ConversationTextScrollTarget = {
|
|
|
12
12
|
entryId?: string;
|
|
13
13
|
needles: readonly string[];
|
|
14
14
|
};
|
|
15
|
+
export type AppScrollState = {
|
|
16
|
+
scrollFromBottom: number;
|
|
17
|
+
detachedScrollStart?: number;
|
|
18
|
+
};
|
|
15
19
|
export type AppScrollControllerHost = {
|
|
16
20
|
conversationViewport(): ConversationViewport;
|
|
17
21
|
editorLayoutRenderer(): EditorLayoutRenderer;
|
|
@@ -24,6 +28,11 @@ export type AppScrollControllerHost = {
|
|
|
24
28
|
render?: boolean;
|
|
25
29
|
onPrependedEntries?: (entries: readonly Entry[]) => void;
|
|
26
30
|
}): Promise<boolean>;
|
|
31
|
+
hasNewerSessionHistory?(): boolean;
|
|
32
|
+
isLoadingNewerSessionHistory?(): boolean;
|
|
33
|
+
loadNewerSessionHistory?(options?: {
|
|
34
|
+
render?: boolean;
|
|
35
|
+
}): Promise<boolean>;
|
|
27
36
|
render(): void;
|
|
28
37
|
};
|
|
29
38
|
export declare class AppScrollController {
|
|
@@ -34,6 +43,15 @@ export declare class AppScrollController {
|
|
|
34
43
|
constructor(host: AppScrollControllerHost);
|
|
35
44
|
reset(): void;
|
|
36
45
|
scrollToBottom(): boolean;
|
|
46
|
+
scrollToTop(): boolean;
|
|
47
|
+
scrollToAbsoluteTop(): Promise<boolean>;
|
|
48
|
+
scrollToAbsoluteBottom(): Promise<boolean>;
|
|
49
|
+
quickScrollDirections(columns: number, bodyHeight: number): {
|
|
50
|
+
up: boolean;
|
|
51
|
+
down: boolean;
|
|
52
|
+
};
|
|
53
|
+
captureState(): AppScrollState;
|
|
54
|
+
restoreState(state: AppScrollState): void;
|
|
37
55
|
conversationView(columns: number, bodyHeight: number): {
|
|
38
56
|
lines: RenderedLine[];
|
|
39
57
|
metrics: AppScrollMetrics;
|
|
@@ -45,7 +63,9 @@ export declare class AppScrollController {
|
|
|
45
63
|
render?: boolean;
|
|
46
64
|
}): boolean;
|
|
47
65
|
private shouldLoadOlderHistory;
|
|
66
|
+
private shouldLoadNewerHistory;
|
|
48
67
|
private loadOlderHistoryAnchored;
|
|
68
|
+
private loadNewerHistoryAnchored;
|
|
49
69
|
adjustForHistoryWindowPrune(edge: "top" | "bottom", lineCount: number): void;
|
|
50
70
|
scrollToConversationEntry(entryId: string): boolean;
|
|
51
71
|
scrollToConversationText(target: ConversationTextScrollTarget): boolean;
|