pi-ui-extend 0.1.50 → 0.1.51
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/app/icons.d.ts +1 -0
- package/dist/app/icons.js +2 -0
- package/dist/app/input/autocomplete-controller.js +1 -1
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +7 -24
- package/dist/app/input/input-paste-handler.d.ts +1 -0
- package/dist/app/input/input-paste-handler.js +13 -16
- package/dist/app/rendering/conversation-tool-renderer.js +1 -0
- package/dist/app/rendering/popup-menu-renderer.js +2 -2
- package/dist/app/rendering/render-controller.js +10 -16
- package/dist/app/rendering/status-line-renderer.d.ts +0 -7
- package/dist/app/rendering/status-line-renderer.js +7 -79
- package/dist/app/rendering/tab-line-renderer.js +1 -1
- package/dist/app/rendering/toast-renderer.js +40 -15
- package/dist/app/rendering/tool-block-renderer.d.ts +2 -1
- package/dist/app/rendering/tool-block-renderer.js +43 -3
- package/dist/app/screen/mouse-controller.d.ts +1 -0
- package/dist/app/screen/mouse-controller.js +30 -76
- package/dist/app/session/lazy-session-manager.js +0 -7
- package/dist/app/session/queued-message-controller.d.ts +0 -1
- package/dist/app/session/queued-message-controller.js +5 -13
- package/dist/app/session/queued-message-entries.d.ts +1 -0
- package/dist/app/session/queued-message-entries.js +8 -0
- package/dist/app/session/tabs-controller.d.ts +2 -1
- package/dist/app/session/tabs-controller.js +67 -24
- package/dist/app/types.d.ts +1 -0
- package/dist/bundled-extensions/session-title/title-generation.js +1 -1
- package/dist/tool-renderers/compress.js +28 -7
- package/dist/tool-renderers/patch-normalize.js +4 -4
- package/dist/tool-renderers/types.d.ts +10 -0
- package/external/pi-tools-suite/package.json +3 -3
- package/external/pi-tools-suite/src/async-subagents/core/cleanup.ts +2 -2
- package/external/pi-tools-suite/src/async-subagents/core/paths.ts +12 -0
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +3 -4
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/sessions.ts +2 -2
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +3 -3
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/lib.ts +1 -1
- package/external/pi-tools-suite/src/coding-discipline/index.ts +1 -1
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +1 -1
- package/package.json +4 -4
package/dist/app/icons.d.ts
CHANGED
package/dist/app/icons.js
CHANGED
|
@@ -23,6 +23,7 @@ const NERD_FONT_ICONS = {
|
|
|
23
23
|
deleted: "\u{f0159}",
|
|
24
24
|
deferred: "\u{f0377}",
|
|
25
25
|
info: "\u{f02fc}",
|
|
26
|
+
fork: "└",
|
|
26
27
|
lightbulb: "\u{f0335}",
|
|
27
28
|
microphone: "\u{f036c}",
|
|
28
29
|
plus: "\u{f0415}",
|
|
@@ -54,6 +55,7 @@ const FALLBACK_ICONS = {
|
|
|
54
55
|
deleted: "×",
|
|
55
56
|
deferred: "↷",
|
|
56
57
|
info: "i",
|
|
58
|
+
fork: "└",
|
|
57
59
|
lightbulb: "💡",
|
|
58
60
|
microphone: "m",
|
|
59
61
|
plus: "+",
|
|
@@ -51,6 +51,7 @@ export declare class AppInputController {
|
|
|
51
51
|
private consumeEscapeKeySequence;
|
|
52
52
|
private consumeClipboardImagePasteSequence;
|
|
53
53
|
private consumeShiftEnterSequence;
|
|
54
|
+
private consumeModifiedKeySequence;
|
|
54
55
|
private consumeTerminalInterruptSequence;
|
|
55
56
|
private handleArrowUp;
|
|
56
57
|
private handleArrowDown;
|
|
@@ -341,42 +341,25 @@ export class AppInputController {
|
|
|
341
341
|
return "consumed";
|
|
342
342
|
}
|
|
343
343
|
consumeEscapeKeySequence() {
|
|
344
|
-
|
|
345
|
-
if (result.kind === "pending")
|
|
346
|
-
return "pending";
|
|
347
|
-
if (result.kind === "none")
|
|
348
|
-
return "none";
|
|
349
|
-
if (!terminalKeyIsEscape(result.key))
|
|
350
|
-
return "none";
|
|
351
|
-
this.inputBuffer = this.inputBuffer.slice(result.key.length);
|
|
352
|
-
if (!terminalKeyShouldIgnore(result.key))
|
|
353
|
-
void this.host.handleEscape();
|
|
354
|
-
return "consumed";
|
|
344
|
+
return this.consumeModifiedKeySequence(terminalKeyIsEscape, () => { void this.host.handleEscape(); });
|
|
355
345
|
}
|
|
356
346
|
consumeClipboardImagePasteSequence() {
|
|
357
|
-
|
|
358
|
-
if (result.kind === "pending")
|
|
359
|
-
return "pending";
|
|
360
|
-
if (result.kind === "none")
|
|
361
|
-
return "none";
|
|
362
|
-
if (!terminalKeyIsClipboardImagePaste(result.key))
|
|
363
|
-
return "none";
|
|
364
|
-
this.inputBuffer = this.inputBuffer.slice(result.key.length);
|
|
365
|
-
if (!terminalKeyShouldIgnore(result.key))
|
|
366
|
-
void this.pasteHandler.handleClipboardImagePaste();
|
|
367
|
-
return "consumed";
|
|
347
|
+
return this.consumeModifiedKeySequence(terminalKeyIsClipboardImagePaste, () => { void this.pasteHandler.handleClipboardImagePaste(); });
|
|
368
348
|
}
|
|
369
349
|
consumeShiftEnterSequence() {
|
|
350
|
+
return this.consumeModifiedKeySequence(terminalKeyIsShiftEnter, () => this.insertInputNewline());
|
|
351
|
+
}
|
|
352
|
+
consumeModifiedKeySequence(matches, handle) {
|
|
370
353
|
const result = parseTerminalModifiedKeySequence(this.inputBuffer);
|
|
371
354
|
if (result.kind === "pending")
|
|
372
355
|
return "pending";
|
|
373
356
|
if (result.kind === "none")
|
|
374
357
|
return "none";
|
|
375
|
-
if (!
|
|
358
|
+
if (!matches(result.key))
|
|
376
359
|
return "none";
|
|
377
360
|
this.inputBuffer = this.inputBuffer.slice(result.key.length);
|
|
378
361
|
if (!terminalKeyShouldIgnore(result.key))
|
|
379
|
-
|
|
362
|
+
handle();
|
|
380
363
|
return "consumed";
|
|
381
364
|
}
|
|
382
365
|
consumeTerminalInterruptSequence() {
|
|
@@ -14,15 +14,8 @@ export class InputPasteHandler {
|
|
|
14
14
|
this.host = host;
|
|
15
15
|
}
|
|
16
16
|
handlePlainData(data) {
|
|
17
|
-
|
|
18
|
-
if (plainFilePath) {
|
|
19
|
-
if (isImagePath(plainFilePath) && Date.now() < this.suppressImagePathPasteUntil) {
|
|
20
|
-
this.host.render();
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
void this.handleFilePaste(plainFilePath);
|
|
17
|
+
if (!this.host.inputEditor.isInBracketedPaste && this.handlePastedFilePath(data))
|
|
24
18
|
return true;
|
|
25
|
-
}
|
|
26
19
|
if (!this.host.inputEditor.isInBracketedPaste && this.isPlainMultilinePasteChunk(data)) {
|
|
27
20
|
this.schedulePastedText(data);
|
|
28
21
|
return true;
|
|
@@ -96,17 +89,21 @@ export class InputPasteHandler {
|
|
|
96
89
|
void this.handleClipboardImagePaste();
|
|
97
90
|
return;
|
|
98
91
|
}
|
|
99
|
-
|
|
100
|
-
if (filePath) {
|
|
101
|
-
if (isImagePath(filePath) && Date.now() < this.suppressImagePathPasteUntil) {
|
|
102
|
-
this.host.render();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
void this.handleFilePaste(filePath);
|
|
92
|
+
if (this.handlePastedFilePath(text))
|
|
106
93
|
return;
|
|
107
|
-
}
|
|
108
94
|
this.schedulePastedText(text);
|
|
109
95
|
}
|
|
96
|
+
handlePastedFilePath(text) {
|
|
97
|
+
const filePath = this.plainPasteFilePath(text);
|
|
98
|
+
if (!filePath)
|
|
99
|
+
return false;
|
|
100
|
+
if (isImagePath(filePath) && Date.now() < this.suppressImagePathPasteUntil) {
|
|
101
|
+
this.host.render();
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
void this.handleFilePaste(filePath);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
110
107
|
schedulePastedText(text) {
|
|
111
108
|
const timer = setTimeout(() => {
|
|
112
109
|
if (this.isDuplicatePaste("text", text)) {
|
|
@@ -31,6 +31,7 @@ export function renderConversationToolEntry(entry, width, options) {
|
|
|
31
31
|
id: entry.id,
|
|
32
32
|
toolName,
|
|
33
33
|
headerArgs: display.headerArgs,
|
|
34
|
+
headerArgSegments: display.headerArgSegments,
|
|
34
35
|
bodyLineStyles: display.bodyLineStyles,
|
|
35
36
|
bodyStyle: display.bodyStyle,
|
|
36
37
|
preserveAnsi: display.preserveAnsi,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { colorLine } from "../../theme.js";
|
|
2
2
|
import { stringDisplayWidth } from "../../terminal-width.js";
|
|
3
3
|
import { resolveColor, resolveModelColor } from "../../config.js";
|
|
4
|
-
import { SLASH_COMMAND_DESCRIPTION_COLUMN, } from "../constants.js";
|
|
4
|
+
import { SLASH_COMMAND_DESCRIPTION_COLUMN, THINKING_LEVELS, } from "../constants.js";
|
|
5
5
|
import { APP_ICONS } from "../icons.js";
|
|
6
6
|
import { ellipsizeDisplay, padOrTrimPlain, sanitizeText } from "./render-text.js";
|
|
7
7
|
import { modelProviderThemeColor, thinkingLevelThemeColor } from "./status-line-renderer.js";
|
|
@@ -287,7 +287,7 @@ export class PopupMenuRenderer {
|
|
|
287
287
|
}
|
|
288
288
|
availableThinkingLevels() {
|
|
289
289
|
const levels = this.host.session?.getAvailableThinkingLevels();
|
|
290
|
-
return Array.isArray(levels) && levels.length > 0 ? levels.map(String) : [
|
|
290
|
+
return Array.isArray(levels) && levels.length > 0 ? levels.map(String) : [...THINKING_LEVELS];
|
|
291
291
|
}
|
|
292
292
|
queueMessageItemVariant(value) {
|
|
293
293
|
return value === "cancel" ? "error" : "normal";
|
|
@@ -263,27 +263,21 @@ export class AppRenderController {
|
|
|
263
263
|
return `\x1b[${flash.y};${flash.startColumn}H\x1b[7m${text}${ANSI_RESET}`;
|
|
264
264
|
}
|
|
265
265
|
updateStatusMouseState(statusLayout, statusRow) {
|
|
266
|
-
const widgetLayout = statusLayout;
|
|
267
|
-
const widgetRow = statusRow;
|
|
268
266
|
this.deps.mouseController.statusModelTarget = this.deps.statusLineRenderer.modelTarget(statusLayout.text, statusRow);
|
|
269
267
|
this.deps.mouseController.statusThinkingTarget = this.deps.statusLineRenderer.thinkingTarget(statusLayout.text, statusRow);
|
|
270
268
|
this.deps.mouseController.statusContextTarget = this.deps.statusLineRenderer.contextTarget(statusLayout.text, statusRow, statusLayout);
|
|
271
269
|
this.deps.mouseController.statusModelUsageTarget = this.deps.statusLineRenderer.modelUsageTarget(statusLayout.text, statusRow, statusLayout);
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this.deps.mouseController.statusTerminalBellSoundTarget = this.deps.statusLineRenderer.terminalBellSoundTarget?.(widgetLayout, widgetRow);
|
|
280
|
-
}
|
|
270
|
+
this.deps.mouseController.statusDraftQueueTarget = this.deps.statusLineRenderer.draftQueueTarget?.(statusLayout, statusRow);
|
|
271
|
+
this.deps.mouseController.statusUserJumpTarget = this.deps.statusLineRenderer.userJumpTarget?.(statusLayout, statusRow);
|
|
272
|
+
this.deps.mouseController.statusThinkingExpandTarget = this.deps.statusLineRenderer.thinkingExpandTarget?.(statusLayout, statusRow);
|
|
273
|
+
this.deps.mouseController.statusCompactToolsTarget = this.deps.statusLineRenderer.compactToolsTarget?.(statusLayout, statusRow);
|
|
274
|
+
this.deps.mouseController.statusQuickScrollUpTarget = this.deps.statusLineRenderer.quickScrollUpTarget?.(statusLayout, statusRow);
|
|
275
|
+
this.deps.mouseController.statusQuickScrollDownTarget = this.deps.statusLineRenderer.quickScrollDownTarget?.(statusLayout, statusRow);
|
|
276
|
+
this.deps.mouseController.statusTerminalBellSoundTarget = this.deps.statusLineRenderer.terminalBellSoundTarget?.(statusLayout, statusRow);
|
|
281
277
|
this.deps.mouseController.statusSessionTarget = this.deps.statusLineRenderer.sessionTarget(statusLayout.text, statusRow, statusLayout.sessionLabel, statusLayout.workspaceLabel);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
this.deps.mouseController.statusVoiceLanguageTarget = this.deps.statusLineRenderer.voiceLanguageTarget(widgetLayout, widgetRow);
|
|
286
|
-
}
|
|
278
|
+
this.deps.mouseController.statusPromptEnhancerTarget = this.deps.statusLineRenderer.promptEnhancerTarget(statusLayout, statusRow);
|
|
279
|
+
this.deps.mouseController.statusVoiceMicTarget = this.deps.statusLineRenderer.voiceMicTarget(statusLayout, statusRow);
|
|
280
|
+
this.deps.mouseController.statusVoiceLanguageTarget = this.deps.statusLineRenderer.voiceLanguageTarget(statusLayout, statusRow);
|
|
287
281
|
this.deps.mouseController.renderedRowTexts.set(statusRow, statusLayout.text);
|
|
288
282
|
}
|
|
289
283
|
renderVoiceProgressOverlay(message, width, rows) {
|
|
@@ -61,14 +61,7 @@ export declare class StatusLineRenderer {
|
|
|
61
61
|
private segments;
|
|
62
62
|
private fitWorkspaceLabel;
|
|
63
63
|
private pushStatusWidgetSegments;
|
|
64
|
-
private pushPromptEnhancerWidgetSegment;
|
|
65
|
-
private pushUserJumpWidgetSegment;
|
|
66
|
-
private pushDraftQueueWidgetSegment;
|
|
67
64
|
private draftQueueWidgetText;
|
|
68
|
-
private pushThinkingExpandWidgetSegment;
|
|
69
|
-
private pushCompactToolsWidgetSegment;
|
|
70
|
-
private pushTerminalBellSoundWidgetSegment;
|
|
71
|
-
private pushVoiceWidgetSegment;
|
|
72
65
|
private pushWorkspaceSegments;
|
|
73
66
|
private pushModelUsageSegments;
|
|
74
67
|
private pushResetDurationSegments;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { compactProgressBarSegments, formatCompactProgressBar } from "../../context-progress-bar.js";
|
|
2
2
|
import { ellipsizeDisplay, padOrTrimPlain } from "./render-text.js";
|
|
3
3
|
import { displayIndexForColumn, stringDisplayWidth } from "../../terminal-width.js";
|
|
4
|
+
import { THINKING_LEVELS } from "../constants.js";
|
|
4
5
|
import { APP_ICONS } from "../icons.js";
|
|
5
6
|
import { resolveColor, resolveModelColor } from "../../config.js";
|
|
6
7
|
const MODEL_USAGE_PROGRESS_BAR_WIDTH = stringDisplayWidth(formatCompactProgressBar(100));
|
|
7
8
|
const STATUS_WIDGET_GAP = " ";
|
|
9
|
+
const DEFAULT_THINKING_LEVEL_LABELS = THINKING_LEVELS;
|
|
8
10
|
export class StatusLineRenderer {
|
|
9
11
|
host;
|
|
10
12
|
constructor(host) {
|
|
@@ -370,81 +372,9 @@ export class StatusLineRenderer {
|
|
|
370
372
|
}
|
|
371
373
|
}
|
|
372
374
|
}
|
|
373
|
-
pushPromptEnhancerWidgetSegment(segments, statusText) {
|
|
374
|
-
const widgetText = this.host.promptEnhancerStatusWidgetText();
|
|
375
|
-
const start = statusText.lastIndexOf(widgetText);
|
|
376
|
-
if (start < 0 || widgetText.length <= 0)
|
|
377
|
-
return;
|
|
378
|
-
const foreground = this.host.promptEnhancerStatusWidgetActive()
|
|
379
|
-
? this.host.theme.colors.warning
|
|
380
|
-
: this.host.promptEnhancerStatusWidgetEnabled()
|
|
381
|
-
? this.host.theme.colors.info
|
|
382
|
-
: this.host.theme.colors.muted;
|
|
383
|
-
this.pushSegment(segments, start, widgetText.length, foreground);
|
|
384
|
-
}
|
|
385
|
-
pushUserJumpWidgetSegment(segments, statusText) {
|
|
386
|
-
const buttonText = APP_ICONS.user;
|
|
387
|
-
const start = statusText.indexOf(buttonText);
|
|
388
|
-
if (start < 0)
|
|
389
|
-
return;
|
|
390
|
-
const foreground = this.host.userMessageJumpMenuActive?.()
|
|
391
|
-
? this.host.theme.colors.info
|
|
392
|
-
: this.host.theme.colors.muted;
|
|
393
|
-
this.pushSegment(segments, start, buttonText.length, foreground);
|
|
394
|
-
}
|
|
395
|
-
pushDraftQueueWidgetSegment(segments, statusText) {
|
|
396
|
-
const buttonText = this.draftQueueWidgetText();
|
|
397
|
-
const start = statusText.indexOf(buttonText);
|
|
398
|
-
if (start < 0 || buttonText.length <= 0)
|
|
399
|
-
return;
|
|
400
|
-
this.pushSegment(segments, start, buttonText.length, this.host.theme.colors.info);
|
|
401
|
-
}
|
|
402
375
|
draftQueueWidgetText() {
|
|
403
376
|
return this.host.queueableInputActive?.() ? APP_ICONS.timerSand : "";
|
|
404
377
|
}
|
|
405
|
-
pushThinkingExpandWidgetSegment(segments, statusText, layout) {
|
|
406
|
-
if (!layout.thinkingExpandWidget)
|
|
407
|
-
return;
|
|
408
|
-
const buttonText = APP_ICONS.thinkingExpanded;
|
|
409
|
-
const buttonMarker = ` ${buttonText} `;
|
|
410
|
-
const markerStart = statusText.indexOf(buttonMarker);
|
|
411
|
-
const start = markerStart >= 0 ? markerStart + 1 : -1;
|
|
412
|
-
if (start < 0)
|
|
413
|
-
return;
|
|
414
|
-
this.pushSegment(segments, start, buttonText.length, this.host.allThinkingExpandedActive?.() ? this.host.theme.colors.info : this.host.theme.colors.muted);
|
|
415
|
-
}
|
|
416
|
-
pushCompactToolsWidgetSegment(segments, statusText) {
|
|
417
|
-
const buttonText = APP_ICONS.compactTools;
|
|
418
|
-
const start = statusText.indexOf(buttonText);
|
|
419
|
-
if (start < 0)
|
|
420
|
-
return;
|
|
421
|
-
this.pushSegment(segments, start, buttonText.length, this.host.superCompactToolsActive?.() ? this.host.theme.colors.info : this.host.theme.colors.muted);
|
|
422
|
-
}
|
|
423
|
-
pushTerminalBellSoundWidgetSegment(segments, statusText) {
|
|
424
|
-
const widgetText = this.host.terminalBellSoundStatusWidgetText();
|
|
425
|
-
const start = statusText.lastIndexOf(widgetText);
|
|
426
|
-
if (start < 0 || widgetText.length <= 0)
|
|
427
|
-
return;
|
|
428
|
-
this.pushSegment(segments, start, widgetText.length, this.host.terminalBellSoundStatusWidgetEnabled() ? this.host.theme.colors.info : this.host.theme.colors.muted);
|
|
429
|
-
}
|
|
430
|
-
pushVoiceWidgetSegment(segments, statusText) {
|
|
431
|
-
const widgetText = this.host.voiceStatusWidgetText();
|
|
432
|
-
if (widgetText.length <= 0)
|
|
433
|
-
return;
|
|
434
|
-
const start = statusText.lastIndexOf(widgetText);
|
|
435
|
-
const micStart = statusText.lastIndexOf(APP_ICONS.microphone);
|
|
436
|
-
if (start < 0 && micStart < 0)
|
|
437
|
-
return;
|
|
438
|
-
if (this.host.voiceStatusWidgetActive()) {
|
|
439
|
-
const separatorIndex = widgetText.indexOf(" ");
|
|
440
|
-
const micLength = separatorIndex >= 0 ? separatorIndex : widgetText.length;
|
|
441
|
-
this.pushSegment(segments, micStart >= 0 ? micStart : start, micLength, this.host.theme.colors.error);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
const separatorIndex = widgetText.indexOf(" ");
|
|
445
|
-
const micLength = separatorIndex >= 0 ? separatorIndex : widgetText.length;
|
|
446
|
-
this.pushSegment(segments, micStart >= 0 ? micStart : start, micLength, this.host.theme.colors.muted);
|
|
447
|
-
}
|
|
448
378
|
pushWorkspaceSegments(segments, statusText, workspaceLabel) {
|
|
449
379
|
const start = statusText.lastIndexOf(workspaceLabel);
|
|
450
380
|
if (start < 0 || workspaceLabel.length <= 0)
|
|
@@ -566,7 +496,7 @@ export class StatusLineRenderer {
|
|
|
566
496
|
}
|
|
567
497
|
availableThinkingLevels() {
|
|
568
498
|
const levels = this.host.session?.getAvailableThinkingLevels();
|
|
569
|
-
return Array.isArray(levels) && levels.length > 0 ? levels.map(String) : [
|
|
499
|
+
return Array.isArray(levels) && levels.length > 0 ? levels.map(String) : [...DEFAULT_THINKING_LEVEL_LABELS];
|
|
570
500
|
}
|
|
571
501
|
contextBarLabel(status, width, workspaceLabel) {
|
|
572
502
|
const session = this.host.session;
|
|
@@ -651,14 +581,13 @@ function overlayText(text, startColumn, overlay) {
|
|
|
651
581
|
return `${padded.slice(0, startIndex)}${overlay}${padded.slice(endIndex)}`;
|
|
652
582
|
}
|
|
653
583
|
export function thinkingLevelThemeColor(label, colors, availableLevels) {
|
|
654
|
-
const levels = availableLevels && availableLevels.length > 0 ? availableLevels.map(String) :
|
|
584
|
+
const levels = availableLevels && availableLevels.length > 0 ? availableLevels.map(String) : DEFAULT_THINKING_LEVEL_LABELS;
|
|
655
585
|
const rank = levels.indexOf(label);
|
|
656
586
|
if (rank >= 0)
|
|
657
587
|
return thinkingRankThemeColor(label, rank, levels.length, colors);
|
|
658
|
-
const
|
|
659
|
-
const fallbackRank = fallbackLevels.indexOf(label);
|
|
588
|
+
const fallbackRank = DEFAULT_THINKING_LEVEL_LABELS.indexOf(label);
|
|
660
589
|
return fallbackRank >= 0
|
|
661
|
-
? thinkingRankThemeColor(label, fallbackRank,
|
|
590
|
+
? thinkingRankThemeColor(label, fallbackRank, DEFAULT_THINKING_LEVEL_LABELS.length, colors)
|
|
662
591
|
: colors.info;
|
|
663
592
|
}
|
|
664
593
|
function thinkingRankThemeColor(label, rank, count, colors) {
|
|
@@ -671,8 +600,7 @@ function thinkingRankThemeColor(label, rank, count, colors) {
|
|
|
671
600
|
colors.thinkingXHigh,
|
|
672
601
|
];
|
|
673
602
|
const palette = count > baseColors.length ? [colors.statusForeground, ...baseColors] : baseColors;
|
|
674
|
-
const
|
|
675
|
-
const fallbackRank = fallbackLevels.indexOf(label);
|
|
603
|
+
const fallbackRank = DEFAULT_THINKING_LEVEL_LABELS.indexOf(label);
|
|
676
604
|
const colorIndex = count <= baseColors.length && fallbackRank >= 0 ? fallbackRank : rank;
|
|
677
605
|
return palette[Math.max(0, Math.min(palette.length - 1, colorIndex))] ?? colors.info;
|
|
678
606
|
}
|
|
@@ -157,7 +157,7 @@ export class TabLineRenderer {
|
|
|
157
157
|
return this.buttonLayoutFromText(`${prefix}${ellipsizeDisplay(title, titleWidth)}${suffix}`, 0, statusText.length);
|
|
158
158
|
}
|
|
159
159
|
displayTitle(tab) {
|
|
160
|
-
return tab.title;
|
|
160
|
+
return tab.isFork ? `${APP_ICONS.fork}${tab.title}` : tab.title;
|
|
161
161
|
}
|
|
162
162
|
buttonLayoutFromText(text, statusStart, statusLength) {
|
|
163
163
|
const closeStart = Math.max(0, text.lastIndexOf(APP_ICONS.close));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { stringDisplayWidth, wrapDisplayLine } from "../../terminal-width.js";
|
|
1
|
+
import { ANSI_RESET, ansiStylePrefix } from "../../theme.js";
|
|
2
|
+
import { expandTabs, stringDisplayWidth, wrapDisplayLine } from "../../terminal-width.js";
|
|
3
3
|
import { APP_ICONS } from "../icons.js";
|
|
4
|
-
import { padOrTrimPlain
|
|
4
|
+
import { padOrTrimPlain } from "./render-text.js";
|
|
5
5
|
export function renderToastOverlays(states, width, maxRows, theme) {
|
|
6
6
|
if (maxRows <= 0)
|
|
7
7
|
return [];
|
|
@@ -22,11 +22,12 @@ export function renderToastOverlays(states, width, maxRows, theme) {
|
|
|
22
22
|
const toastWidth = Math.min(Math.max(12, contentWidth + 2), Math.max(1, width - 4));
|
|
23
23
|
const leftWidth = Math.max(0, width - toastWidth - 2);
|
|
24
24
|
const column = leftWidth + 1;
|
|
25
|
+
const style = toastStyle(state, theme);
|
|
25
26
|
for (const line of visibleLines) {
|
|
26
27
|
const message = ` ${padOrTrimPlain(line, Math.max(0, toastWidth - 2))} `;
|
|
27
|
-
const text = padOrTrimPlain(
|
|
28
|
-
const output =
|
|
29
|
-
...
|
|
28
|
+
const text = padOrTrimPlain(` ${padOrTrimPlain(stripToastAnsi(line), Math.max(0, toastWidth - 2))} `, toastWidth);
|
|
29
|
+
const output = colorToastLine(message, toastWidth, {
|
|
30
|
+
...style,
|
|
30
31
|
bold: true,
|
|
31
32
|
});
|
|
32
33
|
overlays.push({
|
|
@@ -47,7 +48,7 @@ function toastMessageLines(message, icon, maxWidth) {
|
|
|
47
48
|
const safeMaxWidth = Math.max(1, maxWidth);
|
|
48
49
|
const safeContinuationPrefix = stringDisplayWidth(continuationPrefix) < safeMaxWidth ? continuationPrefix : "";
|
|
49
50
|
const lines = [];
|
|
50
|
-
const logicalLines =
|
|
51
|
+
const logicalLines = sanitizeToastText(message).split("\n");
|
|
51
52
|
for (const [index, logicalLine] of logicalLines.entries()) {
|
|
52
53
|
const prefix = index === 0 ? firstPrefix : continuationPrefix;
|
|
53
54
|
const prefixWidth = stringDisplayWidth(prefix);
|
|
@@ -74,21 +75,21 @@ function renderDialogToastOverlay(state, width, maxRows, theme, rowOffset) {
|
|
|
74
75
|
const visibleBodyLines = bodyLines.slice(0, bodyRows);
|
|
75
76
|
const includeBottom = maxRows > 1;
|
|
76
77
|
const dialogRows = [
|
|
77
|
-
dialogTopLine(closeLabel, dialogWidth),
|
|
78
|
-
...visibleBodyLines.map((line) => `│ ${padOrTrimPlain(line, bodyWidth)} │`),
|
|
79
|
-
...(includeBottom ? [`╰${"─".repeat(Math.max(0, dialogWidth - 2))}╯`] : []),
|
|
78
|
+
toastRow(dialogTopLine(closeLabel, dialogWidth)),
|
|
79
|
+
...visibleBodyLines.map((line) => toastRow(`│ ${padOrTrimPlain(line, bodyWidth)} │`, `│ ${padOrTrimPlain(stripToastAnsi(line), bodyWidth)} │`)),
|
|
80
|
+
...(includeBottom ? [toastRow(`╰${"─".repeat(Math.max(0, dialogWidth - 2))}╯`)] : []),
|
|
80
81
|
].slice(0, maxRows);
|
|
81
82
|
const leftWidth = Math.max(0, width - dialogWidth - 2);
|
|
82
83
|
const column = leftWidth + 1;
|
|
83
|
-
const style =
|
|
84
|
+
const style = toastStyle(state, theme);
|
|
84
85
|
const closeStartColumn = column + 1 + dialogTopCloseOffset(closeLabel, dialogWidth);
|
|
85
86
|
const closeEndColumn = closeStartColumn + stringDisplayWidth(closeLabel);
|
|
86
|
-
return dialogRows.map((
|
|
87
|
+
return dialogRows.map((row, index) => ({
|
|
87
88
|
id: state.id,
|
|
88
89
|
row: rowOffset + index + 1,
|
|
89
90
|
column,
|
|
90
|
-
text,
|
|
91
|
-
output:
|
|
91
|
+
text: row.text,
|
|
92
|
+
output: colorToastLine(row.output, dialogWidth, { ...style, bold: true }),
|
|
92
93
|
target: index === 0
|
|
93
94
|
? { kind: "toast", id: state.id, action: "close", startColumn: closeStartColumn, endColumn: closeEndColumn }
|
|
94
95
|
: { kind: "toast", id: state.id, action: "body", startColumn: column, endColumn: column + dialogWidth },
|
|
@@ -96,9 +97,29 @@ function renderDialogToastOverlay(state, width, maxRows, theme, rowOffset) {
|
|
|
96
97
|
}
|
|
97
98
|
function dialogMessageLines(message, maxWidth) {
|
|
98
99
|
const safeMaxWidth = Math.max(1, maxWidth);
|
|
99
|
-
const lines =
|
|
100
|
+
const lines = sanitizeToastText(message).split("\n").flatMap((line) => wrapDisplayLine(line, safeMaxWidth));
|
|
100
101
|
return lines.length > 0 ? lines : [""];
|
|
101
102
|
}
|
|
103
|
+
function toastRow(output, text = output) {
|
|
104
|
+
return { output, text };
|
|
105
|
+
}
|
|
106
|
+
function sanitizeToastText(text) {
|
|
107
|
+
return expandTabs(text.replace(/⚠️?|\u{f0026}/gu, APP_ICONS.alert).replace(/\r/g, ""))
|
|
108
|
+
.replace(/\x1b(?!\[[\d;:]*m)/gu, "␛");
|
|
109
|
+
}
|
|
110
|
+
function stripToastAnsi(text) {
|
|
111
|
+
return text.replace(/\x1b\[[\d;:]*m/gu, "");
|
|
112
|
+
}
|
|
113
|
+
function hasToastAnsiColor(text) {
|
|
114
|
+
return /\x1b\[[\d;:]*m/u.test(text);
|
|
115
|
+
}
|
|
116
|
+
function colorToastLine(text, width, options) {
|
|
117
|
+
const line = padOrTrimPlain(text, width);
|
|
118
|
+
const prefix = ansiStylePrefix(options);
|
|
119
|
+
if (!prefix)
|
|
120
|
+
return line;
|
|
121
|
+
return `${prefix}${line.replaceAll(ANSI_RESET, `${ANSI_RESET}${prefix}`)}${ANSI_RESET}`;
|
|
122
|
+
}
|
|
102
123
|
function dialogTopLine(closeLabel, width) {
|
|
103
124
|
const innerWidth = Math.max(0, width - 2);
|
|
104
125
|
const closeOffset = dialogTopCloseOffset(closeLabel, width);
|
|
@@ -135,3 +156,7 @@ function toastKindStyle(kind, theme) {
|
|
|
135
156
|
return { foreground: theme.colors.background, background: theme.colors.info };
|
|
136
157
|
}
|
|
137
158
|
}
|
|
159
|
+
function toastStyle(state, theme) {
|
|
160
|
+
const style = toastKindStyle(state.kind, theme);
|
|
161
|
+
return hasToastAnsiColor(state.message) ? { ...style, background: "#000000" } : style;
|
|
162
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { type ResolvedToolRule } from "../../config.js";
|
|
2
2
|
import type { ToolBodySyntaxHighlights } from "../../syntax-highlight.js";
|
|
3
3
|
import type { Theme } from "../../theme.js";
|
|
4
|
-
import type { RenderedLine } from "../types.js";
|
|
4
|
+
import type { RenderedLine, StyledSegment } from "../types.js";
|
|
5
5
|
import type { ToolBodyLineStyle } from "../../tool-renderers/types.js";
|
|
6
6
|
export type ToolBlockEntry = {
|
|
7
7
|
id: string;
|
|
8
8
|
toolName: string;
|
|
9
9
|
headerLabel?: string | undefined;
|
|
10
10
|
headerArgs?: string | undefined;
|
|
11
|
+
headerArgSegments?: readonly StyledSegment[] | undefined;
|
|
11
12
|
bodyLineStyles?: readonly ToolBodyLineStyle[] | undefined;
|
|
12
13
|
bodyStyle?: "diff" | undefined;
|
|
13
14
|
preserveAnsi?: boolean | undefined;
|
|
@@ -28,9 +28,16 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
28
28
|
const headerArgs = formatToolHeaderArgs(entry.headerArgs);
|
|
29
29
|
const headerArgsWidth = width - stringDisplayWidth(headerPrefix) - 1;
|
|
30
30
|
const clippedHeaderArgs = headerArgsWidth > 0 ? sliceByDisplayWidth(headerArgs, headerArgsWidth) : "";
|
|
31
|
+
const header = clippedHeaderArgs ? `${headerPrefix} ${clippedHeaderArgs}` : headerPrefix;
|
|
32
|
+
const headerArgsStart = headerPrefix.length + 1;
|
|
33
|
+
const clippedHeaderArgSegments = clippedHeaderArgs
|
|
34
|
+
? clipAndShiftSegments(entry.headerArgSegments, headerArgsStart, clippedHeaderArgs.length)
|
|
35
|
+
: [];
|
|
36
|
+
const headerArgBaseSegments = clippedHeaderArgs
|
|
37
|
+
? uncoveredSegments(headerArgsStart, header.length, toolOutputColor, clippedHeaderArgSegments)
|
|
38
|
+
: [];
|
|
31
39
|
const target = { kind: "tool", id: entry.id };
|
|
32
40
|
const showGutter = options.showGutter ?? true;
|
|
33
|
-
const header = clippedHeaderArgs ? `${headerPrefix} ${clippedHeaderArgs}` : headerPrefix;
|
|
34
41
|
const headerLine = {
|
|
35
42
|
text: header,
|
|
36
43
|
target,
|
|
@@ -39,7 +46,8 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
39
46
|
segments: [
|
|
40
47
|
{ start: 0, end: stateIcon.length, foreground: toolStatusIconColor(entry, colors), bold: true },
|
|
41
48
|
{ start: stateIcon.length, end: headerPrefix.length, bold: true },
|
|
42
|
-
...
|
|
49
|
+
...headerArgBaseSegments,
|
|
50
|
+
...clippedHeaderArgSegments,
|
|
43
51
|
],
|
|
44
52
|
};
|
|
45
53
|
const headerLines = [headerLine];
|
|
@@ -85,6 +93,35 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
85
93
|
];
|
|
86
94
|
return headerLines;
|
|
87
95
|
}
|
|
96
|
+
function clipAndShiftSegments(segments, offset, length) {
|
|
97
|
+
if (!segments || length <= 0)
|
|
98
|
+
return [];
|
|
99
|
+
return segments.flatMap((segment) => {
|
|
100
|
+
const start = Math.max(0, segment.start);
|
|
101
|
+
const end = Math.min(length, segment.end);
|
|
102
|
+
if (end <= start)
|
|
103
|
+
return [];
|
|
104
|
+
return [{ ...segment, start: offset + start, end: offset + end }];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function uncoveredSegments(start, end, foreground, coveredSegments) {
|
|
108
|
+
if (end <= start)
|
|
109
|
+
return [];
|
|
110
|
+
const result = [];
|
|
111
|
+
let offset = start;
|
|
112
|
+
for (const segment of [...coveredSegments].sort((a, b) => a.start - b.start || a.end - b.end)) {
|
|
113
|
+
const segmentStart = Math.max(start, Math.min(end, segment.start));
|
|
114
|
+
const segmentEnd = Math.max(segmentStart, Math.min(end, segment.end));
|
|
115
|
+
if (segmentEnd <= offset)
|
|
116
|
+
continue;
|
|
117
|
+
if (segmentStart > offset)
|
|
118
|
+
result.push({ start: offset, end: segmentStart, foreground });
|
|
119
|
+
offset = segmentEnd;
|
|
120
|
+
}
|
|
121
|
+
if (offset < end)
|
|
122
|
+
result.push({ start: offset, end, foreground });
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
88
125
|
function renderCollapsedPreviewLines(entry, body, rule, width, target, color, colors, hasLspDiagnostics, showGutter) {
|
|
89
126
|
const preview = previewBodyText(body, rule.direction, rule.previewLines);
|
|
90
127
|
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 });
|
|
@@ -467,7 +504,10 @@ function syntaxHighlightForLine(highlights, lineIndex) {
|
|
|
467
504
|
return list.find((highlight) => lineIndex >= highlight.startLine && (highlight.endLine == null || lineIndex < highlight.endLine));
|
|
468
505
|
}
|
|
469
506
|
function diffLineStyle(line, colors) {
|
|
470
|
-
|
|
507
|
+
// Diff markers are only markers at column 0. Context lines in unified/apply_patch
|
|
508
|
+
// output start with a leading space, so trimming would incorrectly color
|
|
509
|
+
// markdown bullets like ` - item` as deletions and ` + item` as additions.
|
|
510
|
+
const content = line;
|
|
471
511
|
if (/^(?:diff --git|index |\*\*\* (?:Begin|End) Patch)/.test(content))
|
|
472
512
|
return { foreground: colors.muted, bold: true };
|
|
473
513
|
if (/^(?:---|\+\+\+|\*\*\* (?:Update|Add|Delete) File:)/.test(content))
|
|
@@ -169,6 +169,7 @@ export declare class AppMouseController {
|
|
|
169
169
|
private handleStatusVoiceMicClick;
|
|
170
170
|
private handleStatusVoiceLanguageClick;
|
|
171
171
|
private handleStatusQuickScrollClick;
|
|
172
|
+
private statusTargetContains;
|
|
172
173
|
private handleInputClick;
|
|
173
174
|
private handleExtensionInputClick;
|
|
174
175
|
private openUserMessageMenu;
|