pi-ui-extend 0.1.9 → 0.1.11
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 +23 -2
- package/dist/app/app.d.ts +4 -0
- package/dist/app/app.js +74 -7
- package/dist/app/cli/install.d.ts +2 -0
- package/dist/app/cli/install.js +16 -1
- package/dist/app/commands/command-controller.js +4 -0
- package/dist/app/commands/command-host.d.ts +4 -0
- package/dist/app/commands/command-model-actions.d.ts +5 -0
- package/dist/app/commands/command-model-actions.js +104 -0
- package/dist/app/commands/command-navigation-actions.d.ts +6 -1
- package/dist/app/commands/command-navigation-actions.js +37 -14
- package/dist/app/commands/command-registry.d.ts +4 -0
- package/dist/app/commands/command-registry.js +32 -0
- package/dist/app/commands/command-session-actions.d.ts +1 -0
- package/dist/app/commands/command-session-actions.js +15 -5
- package/dist/app/commands/shell-controller.d.ts +1 -0
- package/dist/app/commands/shell-controller.js +1 -1
- package/dist/app/constants.d.ts +1 -1
- package/dist/app/constants.js +1 -1
- package/dist/app/icons.js +1 -1
- package/dist/app/input/autocomplete-controller.d.ts +52 -0
- package/dist/app/input/autocomplete-controller.js +352 -0
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +21 -0
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +2 -0
- package/dist/app/input/input-paste-handler.d.ts +1 -0
- package/dist/app/input/input-paste-handler.js +22 -18
- package/dist/app/input/voice-controller.d.ts +2 -0
- package/dist/app/input/voice-controller.js +27 -15
- package/dist/app/model/model-usage-status.d.ts +9 -0
- package/dist/app/model/model-usage-status.js +124 -34
- package/dist/app/popup/popup-action-controller.js +1 -1
- package/dist/app/process.d.ts +17 -0
- package/dist/app/process.js +68 -0
- package/dist/app/rendering/conversation-entry-renderer.js +17 -6
- package/dist/app/rendering/conversation-tool-renderer.js +3 -2
- package/dist/app/rendering/editor-layout-renderer.d.ts +1 -0
- package/dist/app/rendering/editor-layout-renderer.js +11 -1
- package/dist/app/rendering/message-content.js +65 -7
- package/dist/app/rendering/render-controller.js +6 -1
- package/dist/app/rendering/render-text.d.ts +3 -0
- package/dist/app/rendering/render-text.js +51 -3
- package/dist/app/rendering/status-line-renderer.d.ts +5 -1
- package/dist/app/rendering/status-line-renderer.js +69 -25
- package/dist/app/rendering/tool-block-renderer.js +13 -31
- package/dist/app/runtime.d.ts +6 -1
- package/dist/app/runtime.js +35 -2
- package/dist/app/screen/clipboard.d.ts +2 -2
- package/dist/app/screen/clipboard.js +13 -18
- package/dist/app/screen/mouse-controller.d.ts +5 -2
- package/dist/app/screen/mouse-controller.js +16 -1
- package/dist/app/screen/screen-styler.d.ts +4 -1
- package/dist/app/screen/screen-styler.js +3 -2
- package/dist/app/screen/status-controller.d.ts +3 -0
- package/dist/app/screen/status-controller.js +23 -8
- package/dist/app/session/queued-message-controller.d.ts +7 -1
- package/dist/app/session/queued-message-controller.js +32 -21
- package/dist/app/session/resume-session-loader.d.ts +15 -0
- package/dist/app/session/resume-session-loader.js +204 -0
- package/dist/app/session/session-event-controller.d.ts +5 -1
- package/dist/app/session/session-event-controller.js +72 -5
- package/dist/app/session/session-history.js +4 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +5 -0
- package/dist/app/session/session-lifecycle-controller.js +9 -1
- package/dist/app/session/tabs-controller.d.ts +10 -1
- package/dist/app/session/tabs-controller.js +101 -5
- package/dist/app/terminal/nerd-font-controller.js +16 -17
- package/dist/app/terminal/terminal-controller.d.ts +1 -0
- package/dist/app/terminal/terminal-controller.js +1 -0
- package/dist/app/types.d.ts +14 -0
- package/dist/app/workspace/workspace-actions-controller.d.ts +1 -1
- package/dist/app/workspace/workspace-actions-controller.js +3 -3
- package/dist/app/workspace/workspace-undo.d.ts +1 -1
- package/dist/app/workspace/workspace-undo.js +22 -20
- package/dist/config.d.ts +27 -0
- package/dist/config.js +174 -1
- package/dist/default-pix-config.js +38 -353
- package/dist/input-editor.d.ts +7 -1
- package/dist/input-editor.js +47 -6
- package/dist/markdown-format.d.ts +1 -0
- package/dist/markdown-format.js +26 -1
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +1 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +45 -195
- package/external/pi-tools-suite/src/lib/lsp.ts +2 -1
- package/external/pi-tools-suite/src/lsp/_shared/output.ts +8 -7
- package/external/pi-tools-suite/src/lsp/manager.ts +4 -4
- package/external/pi-tools-suite/src/repo-discovery/index.ts +49 -2
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +9 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { expandTabs, padOrTrimDisplay, sliceByDisplayWidth, stringDisplayWidth, wrapDisplayLine } from "../../terminal-width.js";
|
|
3
3
|
import { APP_ICONS } from "../icons.js";
|
|
4
|
+
const LSP_DIAGNOSTIC_ICON = "\u{f0026}";
|
|
4
5
|
export function sanitizeText(text) {
|
|
5
|
-
return expandTabs(text.replace(
|
|
6
|
+
return expandTabs(text.replace(/⚠️?|\u{f0026}/gu, APP_ICONS.alert).replace(/\x1b/g, "␛").replace(/\r/g, ""));
|
|
7
|
+
}
|
|
8
|
+
export function alertIconPrefixLength(text) {
|
|
9
|
+
if (text.startsWith(APP_ICONS.alert))
|
|
10
|
+
return APP_ICONS.alert.length;
|
|
11
|
+
if (text.startsWith(LSP_DIAGNOSTIC_ICON))
|
|
12
|
+
return LSP_DIAGNOSTIC_ICON.length;
|
|
13
|
+
return /^⚠️?/u.exec(text)?.[0].length;
|
|
6
14
|
}
|
|
7
15
|
export function normalizePastedTextForDuplicateKey(text) {
|
|
8
16
|
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -17,12 +25,49 @@ const LSP_DIAGNOSTIC_MUTATION_TOOLS = new Set(["apply_patch", "ast_apply"]);
|
|
|
17
25
|
export function hasToolLspDiagnosticsAfterMutation(entry) {
|
|
18
26
|
return LSP_DIAGNOSTIC_MUTATION_TOOLS.has(entry.toolName.toLowerCase()) && hasLspDiagnosticsAfterMutation(entry.output);
|
|
19
27
|
}
|
|
28
|
+
export function lspDiagnosticSeverityForLine(line) {
|
|
29
|
+
const counts = lspDiagnosticCounts(line);
|
|
30
|
+
const countSeverity = lspDiagnosticCountSeverity(counts);
|
|
31
|
+
if (countSeverity)
|
|
32
|
+
return countSeverity;
|
|
33
|
+
if (counts.length > 0)
|
|
34
|
+
return undefined;
|
|
35
|
+
const severityMatch = /(?:^|[^\p{L}\p{N}_])(?:diagnosticseverity\.)?(errors?|warnings?|warn|hints?)(?=$|[^\p{L}\p{N}_])/iu.exec(line);
|
|
36
|
+
const severity = severityMatch?.[1]?.toLowerCase();
|
|
37
|
+
if (!severity)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (severity.startsWith("error"))
|
|
40
|
+
return "error";
|
|
41
|
+
if (severity.startsWith("warn"))
|
|
42
|
+
return "warning";
|
|
43
|
+
return "hint";
|
|
44
|
+
}
|
|
45
|
+
function lspDiagnosticCounts(line) {
|
|
46
|
+
return [...line.matchAll(/\b(\d+)\s+(errors?|warnings?|hints?)\b/giu)];
|
|
47
|
+
}
|
|
48
|
+
function lspDiagnosticCountSeverity(counts) {
|
|
49
|
+
for (const severity of ["error", "warning", "hint"]) {
|
|
50
|
+
if (counts.some((match) => Number(match[1]) > 0 && match[2]?.toLowerCase().startsWith(severity)))
|
|
51
|
+
return severity;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
export function toolLspDiagnosticsAfterMutationSeverity(entry) {
|
|
56
|
+
if (!hasToolLspDiagnosticsAfterMutation(entry))
|
|
57
|
+
return undefined;
|
|
58
|
+
if (/\blsp\s+errors?\s+after\s+mutation\b/i.test(entry.output))
|
|
59
|
+
return "error";
|
|
60
|
+
const diagnosticLines = entry.output.split("\n").map((line) => line.trim());
|
|
61
|
+
if (diagnosticLines.some((line) => lspDiagnosticSeverityForLine(line) === "error"))
|
|
62
|
+
return "error";
|
|
63
|
+
return "warning";
|
|
64
|
+
}
|
|
20
65
|
export function toolStatusIcon(entry) {
|
|
21
66
|
if (entry.status === "running")
|
|
22
67
|
return APP_ICONS.timerSand;
|
|
23
68
|
if (entry.isError)
|
|
24
69
|
return APP_ICONS.closeCircle;
|
|
25
|
-
if (
|
|
70
|
+
if (toolLspDiagnosticsAfterMutationSeverity(entry))
|
|
26
71
|
return APP_ICONS.alert;
|
|
27
72
|
return APP_ICONS.checkCircle;
|
|
28
73
|
}
|
|
@@ -31,7 +76,10 @@ export function toolStatusIconColor(entry, colors) {
|
|
|
31
76
|
return colors.muted;
|
|
32
77
|
if (entry.isError)
|
|
33
78
|
return colors.error;
|
|
34
|
-
|
|
79
|
+
const lspSeverity = toolLspDiagnosticsAfterMutationSeverity(entry);
|
|
80
|
+
if (lspSeverity === "error")
|
|
81
|
+
return colors.error;
|
|
82
|
+
if (lspSeverity === "warning")
|
|
35
83
|
return colors.warning;
|
|
36
84
|
return colors.success;
|
|
37
85
|
}
|
|
@@ -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, 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, 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,7 @@ export type StatusLineRendererHost = {
|
|
|
26
26
|
terminalBellSoundStatusWidgetEnabled(): boolean;
|
|
27
27
|
voiceStatusWidgetText(): string;
|
|
28
28
|
voiceStatusWidgetActive(): boolean;
|
|
29
|
+
queueableInputActive?(): boolean;
|
|
29
30
|
userMessageJumpMenuActive?(): boolean;
|
|
30
31
|
allThinkingExpandedActive?(): boolean;
|
|
31
32
|
superCompactToolsActive?(): boolean;
|
|
@@ -43,6 +44,7 @@ export declare class StatusLineRenderer {
|
|
|
43
44
|
voiceMicTarget(layout: StatusLineLayout, row: number): StatusVoiceMicTarget | undefined;
|
|
44
45
|
voiceLanguageTarget(layout: StatusLineLayout, row: number): StatusVoiceLanguageTarget | undefined;
|
|
45
46
|
userJumpTarget(layout: StatusLineLayout, row: number): StatusUserJumpTarget | undefined;
|
|
47
|
+
draftQueueTarget(layout: StatusLineLayout, row: number): StatusDraftQueueTarget | undefined;
|
|
46
48
|
thinkingExpandTarget(layout: StatusLineLayout, row: number): StatusThinkingExpandTarget | undefined;
|
|
47
49
|
compactToolsTarget(layout: StatusLineLayout, row: number): StatusCompactToolsTarget | undefined;
|
|
48
50
|
terminalBellSoundTarget(layout: StatusLineLayout, row: number): StatusTerminalBellSoundTarget | undefined;
|
|
@@ -50,6 +52,8 @@ export declare class StatusLineRenderer {
|
|
|
50
52
|
private segments;
|
|
51
53
|
private pushPromptEnhancerWidgetSegment;
|
|
52
54
|
private pushUserJumpWidgetSegment;
|
|
55
|
+
private pushDraftQueueWidgetSegment;
|
|
56
|
+
private draftQueueWidgetText;
|
|
53
57
|
private pushThinkingExpandWidgetSegment;
|
|
54
58
|
private pushCompactToolsWidgetSegment;
|
|
55
59
|
private pushTerminalBellSoundWidgetSegment;
|
|
@@ -13,13 +13,17 @@ export class StatusLineRenderer {
|
|
|
13
13
|
const contentWidth = Math.max(1, width);
|
|
14
14
|
const left = 0;
|
|
15
15
|
const statusDot = APP_ICONS.record;
|
|
16
|
+
const draftQueueButton = this.draftQueueWidgetText();
|
|
16
17
|
const userJumpButton = APP_ICONS.user;
|
|
17
18
|
const thinkingExpandButton = APP_ICONS.thinkingExpanded;
|
|
18
19
|
const compactToolsButton = APP_ICONS.compactTools;
|
|
19
20
|
const terminalBellSoundWidgetText = this.host.terminalBellSoundStatusWidgetText();
|
|
20
21
|
const promptEnhancerWidgetText = this.host.promptEnhancerStatusWidgetText();
|
|
21
22
|
const voiceWidgetText = this.host.voiceStatusWidgetText();
|
|
22
|
-
const
|
|
23
|
+
const rightWidgetParts = draftQueueButton.length > 0
|
|
24
|
+
? [draftQueueButton, promptEnhancerWidgetText, userJumpButton, terminalBellSoundWidgetText, thinkingExpandButton, compactToolsButton, voiceWidgetText]
|
|
25
|
+
: [userJumpButton, terminalBellSoundWidgetText, thinkingExpandButton, compactToolsButton, promptEnhancerWidgetText, voiceWidgetText];
|
|
26
|
+
const rightWidgetText = rightWidgetParts.filter((text) => text.length > 0).join(" ");
|
|
23
27
|
const rightWidgetWidth = stringDisplayWidth(rightWidgetText);
|
|
24
28
|
const leftWidth = rightWidgetWidth > 0 && contentWidth > rightWidgetWidth + 1 ? contentWidth - rightWidgetWidth - 1 : contentWidth;
|
|
25
29
|
const baseStatus = this.host.currentStatus();
|
|
@@ -34,37 +38,60 @@ export class StatusLineRenderer {
|
|
|
34
38
|
const innerText = leftWidth < contentWidth ? `${leftText} ${rightWidgetText}` : padOrTrimPlain(leftText, contentWidth);
|
|
35
39
|
const text = padOrTrimPlain(innerText, width);
|
|
36
40
|
let nextWidgetStartColumn = left + leftWidth + 2;
|
|
37
|
-
|
|
38
|
-
? this.widgetLayout(nextWidgetStartColumn,
|
|
41
|
+
let draftQueueWidget = leftWidth < contentWidth && draftQueueButton.length > 0
|
|
42
|
+
? this.widgetLayout(nextWidgetStartColumn, draftQueueButton)
|
|
39
43
|
: undefined;
|
|
40
|
-
if (
|
|
41
|
-
nextWidgetStartColumn =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
44
|
+
if (draftQueueWidget)
|
|
45
|
+
nextWidgetStartColumn = draftQueueWidget.endColumn + 1;
|
|
46
|
+
let promptEnhancerWidget;
|
|
47
|
+
let userJumpWidget;
|
|
48
|
+
let terminalBellSoundWidget;
|
|
49
|
+
let thinkingExpandWidget;
|
|
50
|
+
let compactToolsWidget;
|
|
51
|
+
const appendPromptEnhancerWidget = () => {
|
|
52
|
+
promptEnhancerWidget = leftWidth < contentWidth && promptEnhancerWidgetText.length > 0
|
|
53
|
+
? this.widgetLayout(nextWidgetStartColumn, promptEnhancerWidgetText)
|
|
54
|
+
: undefined;
|
|
55
|
+
if (promptEnhancerWidget)
|
|
56
|
+
nextWidgetStartColumn = promptEnhancerWidget.endColumn + 1;
|
|
57
|
+
};
|
|
58
|
+
const appendCoreStatusWidgets = () => {
|
|
59
|
+
userJumpWidget = leftWidth < contentWidth
|
|
60
|
+
? this.widgetLayout(nextWidgetStartColumn, userJumpButton)
|
|
61
|
+
: undefined;
|
|
62
|
+
if (userJumpWidget)
|
|
63
|
+
nextWidgetStartColumn = userJumpWidget.endColumn + 1;
|
|
64
|
+
terminalBellSoundWidget = leftWidth < contentWidth && terminalBellSoundWidgetText.length > 0
|
|
65
|
+
? this.widgetLayout(nextWidgetStartColumn, terminalBellSoundWidgetText)
|
|
66
|
+
: undefined;
|
|
67
|
+
if (terminalBellSoundWidget)
|
|
68
|
+
nextWidgetStartColumn = terminalBellSoundWidget.endColumn + 1;
|
|
69
|
+
thinkingExpandWidget = leftWidth < contentWidth
|
|
70
|
+
? this.widgetLayout(nextWidgetStartColumn, thinkingExpandButton)
|
|
71
|
+
: undefined;
|
|
72
|
+
if (thinkingExpandWidget)
|
|
73
|
+
nextWidgetStartColumn = thinkingExpandWidget.endColumn + 1;
|
|
74
|
+
compactToolsWidget = leftWidth < contentWidth
|
|
75
|
+
? this.widgetLayout(nextWidgetStartColumn, compactToolsButton)
|
|
76
|
+
: undefined;
|
|
77
|
+
if (compactToolsWidget)
|
|
78
|
+
nextWidgetStartColumn = compactToolsWidget.endColumn + 1;
|
|
79
|
+
};
|
|
80
|
+
if (draftQueueWidget) {
|
|
81
|
+
appendPromptEnhancerWidget();
|
|
82
|
+
appendCoreStatusWidgets();
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
appendCoreStatusWidgets();
|
|
86
|
+
appendPromptEnhancerWidget();
|
|
87
|
+
}
|
|
62
88
|
const voiceWidget = leftWidth < contentWidth && voiceWidgetText.length > 0 ? this.voiceWidgetLayout(nextWidgetStartColumn, voiceWidgetText) : undefined;
|
|
63
89
|
return {
|
|
64
90
|
details,
|
|
65
91
|
text,
|
|
66
92
|
sessionLabel,
|
|
67
93
|
workspaceLabel,
|
|
94
|
+
...(draftQueueWidget ? { draftQueueWidget } : {}),
|
|
68
95
|
...(userJumpWidget ? { userJumpWidget } : {}),
|
|
69
96
|
...(thinkingExpandWidget ? { thinkingExpandWidget } : {}),
|
|
70
97
|
...(compactToolsWidget ? { compactToolsWidget } : {}),
|
|
@@ -157,6 +184,12 @@ export class StatusLineRenderer {
|
|
|
157
184
|
return undefined;
|
|
158
185
|
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn };
|
|
159
186
|
}
|
|
187
|
+
draftQueueTarget(layout, row) {
|
|
188
|
+
const widget = layout.draftQueueWidget;
|
|
189
|
+
if (!widget)
|
|
190
|
+
return undefined;
|
|
191
|
+
return { row, startColumn: widget.startColumn, endColumn: widget.endColumn };
|
|
192
|
+
}
|
|
160
193
|
thinkingExpandTarget(layout, row) {
|
|
161
194
|
const widget = layout.thinkingExpandWidget;
|
|
162
195
|
if (!widget)
|
|
@@ -195,6 +228,7 @@ export class StatusLineRenderer {
|
|
|
195
228
|
end: statusDotStart + APP_ICONS.record.length,
|
|
196
229
|
foreground: this.statusDotColor(),
|
|
197
230
|
}] : [];
|
|
231
|
+
this.pushDraftQueueWidgetSegment(segments, statusText);
|
|
198
232
|
this.pushUserJumpWidgetSegment(segments, statusText);
|
|
199
233
|
this.pushThinkingExpandWidgetSegment(segments, statusText);
|
|
200
234
|
this.pushCompactToolsWidgetSegment(segments, statusText);
|
|
@@ -248,6 +282,16 @@ export class StatusLineRenderer {
|
|
|
248
282
|
: this.host.theme.colors.muted;
|
|
249
283
|
this.pushSegment(segments, start, buttonText.length, foreground);
|
|
250
284
|
}
|
|
285
|
+
pushDraftQueueWidgetSegment(segments, statusText) {
|
|
286
|
+
const buttonText = this.draftQueueWidgetText();
|
|
287
|
+
const start = statusText.indexOf(buttonText);
|
|
288
|
+
if (start < 0 || buttonText.length <= 0)
|
|
289
|
+
return;
|
|
290
|
+
this.pushSegment(segments, start, buttonText.length, this.host.theme.colors.info);
|
|
291
|
+
}
|
|
292
|
+
draftQueueWidgetText() {
|
|
293
|
+
return this.host.queueableInputActive?.() ? APP_ICONS.timerSand : "";
|
|
294
|
+
}
|
|
251
295
|
pushThinkingExpandWidgetSegment(segments, statusText) {
|
|
252
296
|
const buttonText = APP_ICONS.thinkingExpanded;
|
|
253
297
|
const start = statusText.indexOf(buttonText);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { resolveColor } from "../../config.js";
|
|
2
2
|
import { expandTabs, sliceByDisplayWidth, stringDisplayWidth, wrapDisplayLineByWords } from "../../terminal-width.js";
|
|
3
|
-
import { hasToolLspDiagnosticsAfterMutation, sanitizeText, toolStatusIcon, toolStatusIconColor, wrapLine } from "./render-text.js";
|
|
3
|
+
import { alertIconPrefixLength, hasToolLspDiagnosticsAfterMutation, lspDiagnosticSeverityForLine, sanitizeText, toolStatusIcon, toolStatusIconColor, wrapLine } from "./render-text.js";
|
|
4
4
|
const TRUNCATED_PREVIEW_MARKER = "… ";
|
|
5
5
|
export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
6
6
|
if (rule.hidden)
|
|
7
7
|
return [];
|
|
8
8
|
const hasLspDiagnostics = hasToolLspDiagnosticsAfterMutation(entry);
|
|
9
|
-
const expanded = entry.expanded
|
|
9
|
+
const expanded = entry.expanded;
|
|
10
10
|
const stateIcon = toolStatusIcon(entry);
|
|
11
11
|
const toolColor = resolveColor(rule.color, colors);
|
|
12
12
|
const toolOutputColor = colors.statusForeground;
|
|
@@ -31,7 +31,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
31
31
|
headerLines.push(...renderToolBodyLines(entry.expandedText, width, target, toolOutputColor, entry.bodyStyle, colors, entry.syntaxHighlight, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi));
|
|
32
32
|
return headerLines;
|
|
33
33
|
}
|
|
34
|
-
if (rule.compactHidden || rule.defaultExpanded === true)
|
|
34
|
+
if (rule.compactHidden || (rule.defaultExpanded === true && !options.superCompact))
|
|
35
35
|
return headerLines;
|
|
36
36
|
const body = entry.collapsedBody.trimEnd();
|
|
37
37
|
if (!body || rule.previewLines === 0)
|
|
@@ -111,7 +111,10 @@ function renderToolBodyLines(text, width, target, color, style, colors, syntaxHi
|
|
|
111
111
|
segment.bold = diffStyle.bold;
|
|
112
112
|
line.segments = [segment];
|
|
113
113
|
}
|
|
114
|
-
else if (lspDiagnosticStyle) {
|
|
114
|
+
else if (lspDiagnosticStyle?.kind === "alert" && wrapIndex === 0) {
|
|
115
|
+
line.segments = [{ start: 2, end: 2 + lspDiagnosticStyle.length, foreground: colors.warning, bold: true }];
|
|
116
|
+
}
|
|
117
|
+
else if (lspDiagnosticStyle?.kind === "severity") {
|
|
115
118
|
line.segments = [{ start: 2, end: line.text.length, foreground: lspDiagnosticStyle.foreground }];
|
|
116
119
|
}
|
|
117
120
|
else if (bodyLineStyle && line.text.length > 2) {
|
|
@@ -348,37 +351,16 @@ function bodyLineStyleForLine(styles, lineIndex, colors) {
|
|
|
348
351
|
return resolvedForeground ? { ...segment, foreground: resolvedForeground } : segment;
|
|
349
352
|
}
|
|
350
353
|
function lspDiagnosticLineStyle(line, colors) {
|
|
354
|
+
const alertLength = alertIconPrefixLength(line);
|
|
355
|
+
if (alertLength != null)
|
|
356
|
+
return { kind: "alert", length: alertLength };
|
|
351
357
|
const severity = lspDiagnosticSeverityForLine(line);
|
|
352
358
|
if (severity === "error")
|
|
353
|
-
return { foreground: colors.error };
|
|
359
|
+
return { kind: "severity", foreground: colors.error };
|
|
354
360
|
if (severity === "warning")
|
|
355
|
-
return { foreground: colors.warning };
|
|
361
|
+
return { kind: "severity", foreground: colors.warning };
|
|
356
362
|
if (severity === "hint")
|
|
357
|
-
return { foreground: colors.muted };
|
|
358
|
-
return undefined;
|
|
359
|
-
}
|
|
360
|
-
function lspDiagnosticSeverityForLine(line) {
|
|
361
|
-
const countSeverity = lspDiagnosticCountSeverity(line);
|
|
362
|
-
if (countSeverity)
|
|
363
|
-
return countSeverity;
|
|
364
|
-
const severityMatch = /(?:^|[^\p{L}\p{N}_])(?:diagnosticseverity\.)?(errors?|warnings?|warn|hints?)(?=$|[^\p{L}\p{N}_])/iu.exec(line);
|
|
365
|
-
const severity = severityMatch?.[1]?.toLowerCase();
|
|
366
|
-
if (!severity)
|
|
367
|
-
return undefined;
|
|
368
|
-
if (severity.startsWith("error"))
|
|
369
|
-
return "error";
|
|
370
|
-
if (severity.startsWith("warn"))
|
|
371
|
-
return "warning";
|
|
372
|
-
return "hint";
|
|
373
|
-
}
|
|
374
|
-
function lspDiagnosticCountSeverity(line) {
|
|
375
|
-
const counts = [...line.matchAll(/\b(\d+)\s+(errors?|warnings?|hints?)\b/giu)];
|
|
376
|
-
if (counts.length === 0)
|
|
377
|
-
return undefined;
|
|
378
|
-
for (const severity of ["error", "warning", "hint"]) {
|
|
379
|
-
if (counts.some((match) => Number(match[1]) > 0 && match[2]?.toLowerCase().startsWith(severity)))
|
|
380
|
-
return severity;
|
|
381
|
-
}
|
|
363
|
+
return { kind: "severity", foreground: colors.muted };
|
|
382
364
|
return undefined;
|
|
383
365
|
}
|
|
384
366
|
function syntaxHighlightForLine(highlights, lineIndex) {
|
package/dist/app/runtime.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type EventBus, type AgentSessionRuntime, type LoadExtensionsResult } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { SessionManager, type EventBus, type AgentSessionRuntime, type LoadExtensionsResult, type SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type PixConfig } from "../config.js";
|
|
2
3
|
import type { AppOptions } from "./types.js";
|
|
3
4
|
export type PiToolsSuiteInstallAction = "installed" | "already-installed" | "existing-kept" | "missing-source";
|
|
4
5
|
export type PiToolsSuiteInstallResult = {
|
|
@@ -36,4 +37,8 @@ export declare function prioritizeBundledQuestionExtension(base: LoadExtensionsR
|
|
|
36
37
|
export type CreatePixRuntimeOptions = {
|
|
37
38
|
eventBus?: EventBus;
|
|
38
39
|
};
|
|
40
|
+
type RuntimeSessionManagerModelState = Pick<SessionManager, "getEntries" | "getBranch">;
|
|
41
|
+
export declare function resolvePixRuntimeModelRef(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string | undefined;
|
|
42
|
+
export declare function resolveSessionModelRefFromTail(entries: readonly SessionEntry[]): string | undefined;
|
|
39
43
|
export declare function createPixRuntime(options: AppOptions, runtimeOptions?: CreatePixRuntimeOptions): Promise<AgentSessionRuntime>;
|
|
44
|
+
export {};
|
package/dist/app/runtime.js
CHANGED
|
@@ -4,8 +4,9 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { loadPixConfig, resolveDefaultModelRef } from "../config.js";
|
|
7
8
|
import { PI_FAVORITE_MODEL_REFS } from "./constants.js";
|
|
8
|
-
import { parseModelRef, parseScopedModelRef } from "./model/model-ref.js";
|
|
9
|
+
import { isThinkingLevel, parseModelRef, parseScopedModelRef } from "./model/model-ref.js";
|
|
9
10
|
const BUNDLED_QUESTION_EXTENSION_NAME = "question";
|
|
10
11
|
const PI_TOOLS_SUITE_EXTENSION_NAME = "pi-tools-suite";
|
|
11
12
|
const BUNDLED_EXTENSIONS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../..", "extensions");
|
|
@@ -131,10 +132,42 @@ function isBundledQuestionConflict(error, bundledExtensionPaths) {
|
|
|
131
132
|
}
|
|
132
133
|
return false;
|
|
133
134
|
}
|
|
135
|
+
export function resolvePixRuntimeModelRef(options, sessionManager, config = loadPixConfig()) {
|
|
136
|
+
if (options.modelRef)
|
|
137
|
+
return options.modelRef;
|
|
138
|
+
const existingEntryCount = sessionManager.getEntries().length;
|
|
139
|
+
if (existingEntryCount > 0)
|
|
140
|
+
return resolveSessionModelRefFromTail(sessionManager.getBranch());
|
|
141
|
+
return resolveDefaultModelRef(config);
|
|
142
|
+
}
|
|
143
|
+
export function resolveSessionModelRefFromTail(entries) {
|
|
144
|
+
let modelRef;
|
|
145
|
+
let thinkingLevel;
|
|
146
|
+
for (let index = entries.length - 1; index >= 0 && (modelRef === undefined || thinkingLevel === undefined); index--) {
|
|
147
|
+
const entry = entries[index];
|
|
148
|
+
if (!entry)
|
|
149
|
+
continue;
|
|
150
|
+
if (thinkingLevel === undefined && entry.type === "thinking_level_change" && isThinkingLevel(entry.thinkingLevel)) {
|
|
151
|
+
thinkingLevel = entry.thinkingLevel;
|
|
152
|
+
}
|
|
153
|
+
if (modelRef !== undefined)
|
|
154
|
+
continue;
|
|
155
|
+
if (entry.type === "model_change") {
|
|
156
|
+
modelRef = `${entry.provider}/${entry.modelId}`;
|
|
157
|
+
}
|
|
158
|
+
else if (entry.type === "message" && entry.message.role === "assistant") {
|
|
159
|
+
modelRef = `${entry.message.provider}/${entry.message.model}`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!modelRef)
|
|
163
|
+
return undefined;
|
|
164
|
+
return thinkingLevel ? `${modelRef}:${thinkingLevel}` : modelRef;
|
|
165
|
+
}
|
|
134
166
|
export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
135
|
-
const parsedModel = options.modelRef ? parseModelRef(options.modelRef) : undefined;
|
|
136
167
|
const agentDir = getAgentDir();
|
|
137
168
|
const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
169
|
+
const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager);
|
|
170
|
+
const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
|
|
138
171
|
await ensureBundledSkillsInstalled();
|
|
139
172
|
await ensurePiToolsSuiteExtensionInstalled({ agentDir });
|
|
140
173
|
const bundledExtensionPaths = getBundledExtensionPaths();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function copyTextToClipboard(text: string): void
|
|
2
|
-
export declare function clipboardSupportAvailable(env?: NodeJS.ProcessEnv): boolean
|
|
1
|
+
export declare function copyTextToClipboard(text: string): Promise<void>;
|
|
2
|
+
export declare function clipboardSupportAvailable(env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
3
3
|
export declare function clipboardInstallHint(): string;
|
|
4
4
|
export declare function osc52ClipboardSequence(text: string, env?: NodeJS.ProcessEnv): string;
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { commandExists, runProcess } from "../process.js";
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
|
-
export function copyTextToClipboard(text) {
|
|
4
|
+
export async function copyTextToClipboard(text) {
|
|
5
5
|
const commands = clipboardCommands();
|
|
6
6
|
for (const [command, args] of commands) {
|
|
7
|
-
const result =
|
|
7
|
+
const result = await runProcess(command, args, { input: text, maxBufferBytes: 1024 });
|
|
8
8
|
if (!result.error && result.status === 0)
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
if (copyWithNativeClipboard(text))
|
|
11
|
+
if (await copyWithNativeClipboard(text))
|
|
12
12
|
return;
|
|
13
13
|
if (copyWithOsc52(text))
|
|
14
14
|
return;
|
|
15
15
|
throw new Error(`No clipboard command found. ${clipboardInstallHint()}`);
|
|
16
16
|
}
|
|
17
|
-
export function clipboardSupportAvailable(env = process.env) {
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
export async function clipboardSupportAvailable(env = process.env) {
|
|
18
|
+
for (const [command] of clipboardCommands()) {
|
|
19
|
+
if (await commandExists(command, env))
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
20
22
|
return resolveNativeClipboardEntrypoint() !== undefined;
|
|
21
23
|
}
|
|
22
24
|
export function clipboardInstallHint() {
|
|
@@ -44,7 +46,7 @@ function clipboardCommands() {
|
|
|
44
46
|
];
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
|
-
function copyWithNativeClipboard(text) {
|
|
49
|
+
async function copyWithNativeClipboard(text) {
|
|
48
50
|
const entrypoint = resolveNativeClipboardEntrypoint();
|
|
49
51
|
if (!entrypoint)
|
|
50
52
|
return false;
|
|
@@ -55,10 +57,10 @@ function copyWithNativeClipboard(text) {
|
|
|
55
57
|
const clipboard = require(${JSON.stringify(entrypoint)});
|
|
56
58
|
await clipboard.setText(readFileSync(0, "utf8"));
|
|
57
59
|
`;
|
|
58
|
-
const result =
|
|
60
|
+
const result = await runProcess(process.execPath, ["--input-type=module", "-e", script], {
|
|
59
61
|
input: text,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
timeoutMs: 3_000,
|
|
63
|
+
maxBufferBytes: 1024,
|
|
62
64
|
});
|
|
63
65
|
return !result.error && result.status === 0;
|
|
64
66
|
}
|
|
@@ -84,10 +86,3 @@ function resolveNativeClipboardEntrypoint() {
|
|
|
84
86
|
return undefined;
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
|
-
function commandExists(command, env) {
|
|
88
|
-
const names = process.platform === "win32" ? [command, command.replace(/\.exe$/iu, ".cmd"), command.replace(/\.exe$/iu, ".bat")] : [command];
|
|
89
|
-
return names.some((name) => spawnSync(process.platform === "win32" ? "where" : "sh", process.platform === "win32" ? [name] : ["-lc", `command -v ${shellQuote(name)}`], { env, stdio: "ignore" }).status === 0);
|
|
90
|
-
}
|
|
91
|
-
function shellQuote(value) {
|
|
92
|
-
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
93
|
-
}
|
|
@@ -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, 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, 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,10 +56,11 @@ export type AppMouseControllerHost = {
|
|
|
56
56
|
}): void;
|
|
57
57
|
dismissToast(toastId: number): void;
|
|
58
58
|
refreshModelUsageStatus(): void | Promise<void>;
|
|
59
|
+
queueInputFromStatus?(): void | Promise<void>;
|
|
59
60
|
toggleAllThinkingExpanded?(): void;
|
|
60
61
|
toggleSuperCompactTools?(): void;
|
|
61
62
|
toggleTerminalBellSound?(): void;
|
|
62
|
-
copyTextToClipboard?(text: string): void
|
|
63
|
+
copyTextToClipboard?(text: string): void | Promise<void>;
|
|
63
64
|
handleExtensionInputMouse(event: MouseEvent & {
|
|
64
65
|
localRow: number;
|
|
65
66
|
localColumn: number;
|
|
@@ -100,6 +101,7 @@ export declare class AppMouseController {
|
|
|
100
101
|
statusContextTarget: StatusContextTarget | undefined;
|
|
101
102
|
statusModelUsageTarget: StatusModelUsageTarget | undefined;
|
|
102
103
|
statusUserJumpTarget: StatusUserJumpTarget | undefined;
|
|
104
|
+
statusDraftQueueTarget: StatusDraftQueueTarget | undefined;
|
|
103
105
|
statusThinkingExpandTarget: StatusThinkingExpandTarget | undefined;
|
|
104
106
|
statusCompactToolsTarget: StatusCompactToolsTarget | undefined;
|
|
105
107
|
statusTerminalBellSoundTarget: StatusTerminalBellSoundTarget | undefined;
|
|
@@ -153,6 +155,7 @@ export declare class AppMouseController {
|
|
|
153
155
|
private handleStatusContextClick;
|
|
154
156
|
private handleStatusModelUsageClick;
|
|
155
157
|
private handleStatusUserJumpClick;
|
|
158
|
+
private handleStatusDraftQueueClick;
|
|
156
159
|
private handleStatusThinkingExpandClick;
|
|
157
160
|
private handleStatusCompactToolsClick;
|
|
158
161
|
private handleStatusTerminalBellSoundClick;
|
|
@@ -24,6 +24,7 @@ export class AppMouseController {
|
|
|
24
24
|
statusContextTarget;
|
|
25
25
|
statusModelUsageTarget;
|
|
26
26
|
statusUserJumpTarget;
|
|
27
|
+
statusDraftQueueTarget;
|
|
27
28
|
statusThinkingExpandTarget;
|
|
28
29
|
statusCompactToolsTarget;
|
|
29
30
|
statusTerminalBellSoundTarget;
|
|
@@ -75,6 +76,8 @@ export class AppMouseController {
|
|
|
75
76
|
return;
|
|
76
77
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusModelUsageClick(event)))
|
|
77
78
|
return;
|
|
79
|
+
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusDraftQueueClick(event)))
|
|
80
|
+
return;
|
|
78
81
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusUserJumpClick(event)))
|
|
79
82
|
return;
|
|
80
83
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusThinkingExpandClick(event)))
|
|
@@ -286,6 +289,7 @@ export class AppMouseController {
|
|
|
286
289
|
this.statusThinkingTarget,
|
|
287
290
|
this.statusContextTarget,
|
|
288
291
|
this.statusModelUsageTarget,
|
|
292
|
+
this.statusDraftQueueTarget,
|
|
289
293
|
this.statusUserJumpTarget,
|
|
290
294
|
this.statusThinkingExpandTarget,
|
|
291
295
|
this.statusCompactToolsTarget,
|
|
@@ -509,6 +513,15 @@ export class AppMouseController {
|
|
|
509
513
|
this.host.render();
|
|
510
514
|
return true;
|
|
511
515
|
}
|
|
516
|
+
handleStatusDraftQueueClick(event) {
|
|
517
|
+
const target = this.statusDraftQueueTarget;
|
|
518
|
+
if (!target)
|
|
519
|
+
return false;
|
|
520
|
+
if (event.y !== target.row || event.x < target.startColumn || event.x >= target.endColumn)
|
|
521
|
+
return false;
|
|
522
|
+
void this.host.queueInputFromStatus?.();
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
512
525
|
handleStatusThinkingExpandClick(event) {
|
|
513
526
|
const target = this.statusThinkingExpandTarget;
|
|
514
527
|
if (!target)
|
|
@@ -769,7 +782,9 @@ export class AppMouseController {
|
|
|
769
782
|
return this.getSelectedScreenText(selection.anchor, selection.current);
|
|
770
783
|
}
|
|
771
784
|
copyTextToClipboard(text) {
|
|
772
|
-
(this.host.copyTextToClipboard ?? copyTextToClipboard)(text)
|
|
785
|
+
void Promise.resolve((this.host.copyTextToClipboard ?? copyTextToClipboard)(text)).catch((error) => {
|
|
786
|
+
this.host.showToast(error instanceof Error ? error.message : String(error), "error");
|
|
787
|
+
});
|
|
773
788
|
}
|
|
774
789
|
getSelectedScreenText(anchor, current) {
|
|
775
790
|
const range = orderedSelection(anchor, current);
|
|
@@ -24,7 +24,10 @@ export declare class ScreenStyler {
|
|
|
24
24
|
styleInputLine(row: number, text: string, tagSpans: readonly {
|
|
25
25
|
start: number;
|
|
26
26
|
end: number;
|
|
27
|
-
}[] | undefined,
|
|
27
|
+
}[] | undefined, suggestionSpans: readonly {
|
|
28
|
+
start: number;
|
|
29
|
+
end: number;
|
|
30
|
+
}[] | undefined, width: number, tagColor: string, suggestionColor: string, frameColor?: string): string;
|
|
28
31
|
private styleAnsiLine;
|
|
29
32
|
selectionRangeForRow(row: number, width: number): {
|
|
30
33
|
startIndex: number;
|
|
@@ -72,14 +72,14 @@ export class ScreenStyler {
|
|
|
72
72
|
colorize(after, options),
|
|
73
73
|
].join("");
|
|
74
74
|
}
|
|
75
|
-
styleInputLine(row, text, tagSpans, width, tagColor, frameColor) {
|
|
75
|
+
styleInputLine(row, text, tagSpans, suggestionSpans, width, tagColor, suggestionColor, frameColor) {
|
|
76
76
|
const colors = this.host.theme.colors;
|
|
77
77
|
const baseOptions = { foreground: colors.inputForeground };
|
|
78
78
|
if (this.selectionRangeForRow(row, width))
|
|
79
79
|
return this.styleLine(row, text, width, baseOptions);
|
|
80
80
|
const plain = padOrTrimPlain(text, width);
|
|
81
81
|
const frameSpans = inputFrameSpans(plain, width, frameColor);
|
|
82
|
-
if ((!tagSpans || tagSpans.length === 0) && frameSpans.length === 0) {
|
|
82
|
+
if ((!tagSpans || tagSpans.length === 0) && (!suggestionSpans || suggestionSpans.length === 0) && frameSpans.length === 0) {
|
|
83
83
|
return hasAnsi(plain) ? this.styleAnsiLine(plain, baseOptions) : colorize(plain, baseOptions);
|
|
84
84
|
}
|
|
85
85
|
const chunks = [];
|
|
@@ -88,6 +88,7 @@ export class ScreenStyler {
|
|
|
88
88
|
const spans = [
|
|
89
89
|
...frameSpans,
|
|
90
90
|
...(tagSpans ?? []).map((span) => ({ ...span, foreground: tagColor, bold: true })),
|
|
91
|
+
...(suggestionSpans ?? []).map((span) => ({ ...span, foreground: suggestionColor })),
|
|
91
92
|
].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
92
93
|
for (const span of spans) {
|
|
93
94
|
const start = Math.max(offset, Math.min(endOffset, span.start));
|
|
@@ -7,12 +7,14 @@ export type AppStatusControllerHost = {
|
|
|
7
7
|
readonly theme: Theme;
|
|
8
8
|
readonly blinkController: AppBlinkController;
|
|
9
9
|
runtimeSession(): AgentSession | undefined;
|
|
10
|
+
render(): void;
|
|
10
11
|
};
|
|
11
12
|
export declare class AppStatusController {
|
|
12
13
|
private readonly host;
|
|
13
14
|
private status;
|
|
14
15
|
private statusFollowsSession;
|
|
15
16
|
private gitBranchCache;
|
|
17
|
+
private gitBranchLookupInFlight;
|
|
16
18
|
sessionActivity: SessionActivity;
|
|
17
19
|
get statusDotBright(): boolean;
|
|
18
20
|
constructor(host: AppStatusControllerHost);
|
|
@@ -31,5 +33,6 @@ export declare class AppStatusController {
|
|
|
31
33
|
roundedContextUsagePercent(session: AgentSession): number | undefined;
|
|
32
34
|
contextUsagePercentColor(percent: number): string;
|
|
33
35
|
private currentGitBranchName;
|
|
36
|
+
private refreshGitBranchName;
|
|
34
37
|
private startStatusBlink;
|
|
35
38
|
}
|