pi-ui-extend 0.1.13 → 0.1.17
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 +7 -0
- package/dist/app/app.js +102 -17
- package/dist/app/commands/command-controller.js +2 -0
- package/dist/app/commands/command-host.d.ts +5 -0
- package/dist/app/commands/command-model-actions.d.ts +2 -0
- package/dist/app/commands/command-model-actions.js +40 -4
- package/dist/app/commands/command-navigation-actions.d.ts +9 -0
- package/dist/app/commands/command-navigation-actions.js +62 -0
- package/dist/app/commands/command-registry.d.ts +2 -0
- package/dist/app/commands/command-registry.js +16 -0
- package/dist/app/constants.d.ts +0 -1
- package/dist/app/constants.js +0 -1
- package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
- package/dist/app/extensions/extension-ui-controller.js +99 -61
- package/dist/app/icons.d.ts +1 -0
- package/dist/app/icons.js +2 -0
- package/dist/app/input/input-action-controller.d.ts +2 -0
- package/dist/app/input/input-action-controller.js +8 -1
- package/dist/app/logger.d.ts +25 -0
- package/dist/app/logger.js +90 -0
- package/dist/app/model/model-usage-status.js +30 -15
- package/dist/app/popup/menu-items-controller.d.ts +4 -0
- package/dist/app/popup/menu-items-controller.js +68 -6
- package/dist/app/popup/popup-action-controller.d.ts +2 -1
- package/dist/app/popup/popup-action-controller.js +7 -4
- package/dist/app/popup/popup-menu-controller.d.ts +36 -23
- package/dist/app/popup/popup-menu-controller.js +97 -326
- package/dist/app/rendering/conversation-entry-renderer.js +3 -3
- package/dist/app/rendering/conversation-viewport.d.ts +10 -2
- package/dist/app/rendering/conversation-viewport.js +157 -16
- package/dist/app/rendering/editor-panels.js +22 -9
- package/dist/app/rendering/popup-menu-renderer.d.ts +62 -0
- package/dist/app/rendering/popup-menu-renderer.js +405 -0
- package/dist/app/rendering/render-controller.js +30 -28
- package/dist/app/rendering/render-text.js +5 -2
- package/dist/app/rendering/status-line-renderer.d.ts +8 -1
- package/dist/app/rendering/status-line-renderer.js +217 -117
- package/dist/app/rendering/toast-controller.d.ts +12 -3
- package/dist/app/rendering/toast-controller.js +70 -12
- package/dist/app/runtime.d.ts +2 -1
- package/dist/app/runtime.js +20 -10
- package/dist/app/screen/mouse-controller.d.ts +2 -2
- package/dist/app/screen/mouse-controller.js +27 -48
- package/dist/app/screen/screen-styler.d.ts +1 -1
- package/dist/app/screen/screen-styler.js +9 -7
- package/dist/app/screen/scroll-controller.d.ts +12 -9
- package/dist/app/screen/scroll-controller.js +56 -45
- package/dist/app/screen/status-controller.js +2 -1
- package/dist/app/session/lazy-session-manager.d.ts +11 -0
- package/dist/app/session/lazy-session-manager.js +539 -0
- package/dist/app/session/pix-system-message.d.ts +16 -0
- package/dist/app/session/pix-system-message.js +64 -0
- package/dist/app/session/request-history.d.ts +4 -0
- package/dist/app/session/request-history.js +11 -0
- package/dist/app/session/session-event-controller.d.ts +11 -0
- package/dist/app/session/session-event-controller.js +58 -2
- package/dist/app/session/session-history.d.ts +18 -0
- package/dist/app/session/session-history.js +72 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +6 -2
- package/dist/app/session/session-lifecycle-controller.js +7 -2
- package/dist/app/session/session-search.js +10 -0
- package/dist/app/session/tabs-controller.d.ts +17 -5
- package/dist/app/session/tabs-controller.js +308 -29
- package/dist/app/todo/todo-model.d.ts +4 -2
- package/dist/app/todo/todo-model.js +23 -13
- package/dist/app/types.d.ts +17 -6
- package/dist/app/workspace/workspace-actions-controller.d.ts +2 -0
- package/dist/app/workspace/workspace-actions-controller.js +12 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +82 -25
- package/dist/default-pix-config.js +4 -0
- package/dist/fuzzy.d.ts +2 -0
- package/dist/fuzzy.js +27 -7
- package/dist/input-editor.d.ts +9 -0
- package/dist/input-editor.js +52 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +1 -0
- package/dist/schemas/pi-tools-suite-schema.js +1 -0
- package/dist/schemas/pix-schema.d.ts +3 -1
- package/dist/schemas/pix-schema.js +6 -4
- package/dist/terminal-width.d.ts +2 -0
- package/dist/terminal-width.js +64 -3
- package/dist/theme.js +6 -6
- package/dist/ui.d.ts +8 -0
- package/external/pi-tools-suite/README.md +3 -2
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +52 -8
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +3 -41
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +0 -2
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +11 -18
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +129 -61
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +82 -3
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +20 -7
- package/external/pi-tools-suite/src/antigravity-auth/types.ts +21 -0
- package/external/pi-tools-suite/src/config.ts +8 -0
- package/external/pi-tools-suite/src/dcp/index.ts +16 -1
- package/external/pi-tools-suite/src/dcp/state.ts +35 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +3 -0
- package/external/pi-tools-suite/src/todo/index.ts +123 -14
- package/external/pi-tools-suite/src/todo/state/persistence.ts +0 -1
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +26 -43
- package/external/pi-tools-suite/src/todo/todo.ts +12 -23
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +34 -16
- package/external/pi-tools-suite/src/todo/tool/types.ts +7 -28
- package/external/pi-tools-suite/src/todo/view/format.ts +2 -3
- package/external/pi-tools-suite/src/tool-descriptions.ts +6 -4
- package/external/pi-tools-suite/src/usage/index.ts +5 -2
- package/external/pi-tools-suite/src/usage/lib/google.ts +53 -40
- package/external/pi-tools-suite/src/usage/lib/types.ts +12 -2
- package/package.json +1 -1
- package/schemas/pi-tools-suite.json +4 -0
- package/schemas/pix.json +11 -2
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { fuzzySearch } from "../../fuzzy.js";
|
|
3
|
-
import { colorLine } from "../../theme.js";
|
|
4
3
|
import { PopupMenu } from "../../ui.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { RESUME_MENU_INITIAL_SESSION_ROWS, RESUME_MENU_LOAD_BATCH_ROWS, RESUME_MENU_LOAD_THRESHOLD_ROWS, RESUME_MENU_MAX_ROWS, SLASH_COMMAND_DESCRIPTION_COLUMN, SLASH_COMMAND_MENU_MAX_ROWS, THINKING_MENU_MAX_ROWS, } from "../constants.js";
|
|
8
|
-
import { APP_ICONS } from "../icons.js";
|
|
9
|
-
const POPUP_MENU_ESCAPE_BUTTON = "Esc";
|
|
4
|
+
import { sanitizeText } from "../rendering/render-text.js";
|
|
5
|
+
import { RESUME_MENU_INITIAL_SESSION_ROWS, RESUME_MENU_LOAD_BATCH_ROWS, RESUME_MENU_LOAD_THRESHOLD_ROWS, RESUME_MENU_MAX_ROWS, SLASH_COMMAND_MENU_MAX_ROWS, THINKING_MENU_MAX_ROWS, } from "../constants.js";
|
|
10
6
|
export class AppPopupMenuController {
|
|
11
7
|
host;
|
|
8
|
+
renderer;
|
|
12
9
|
menuController = {
|
|
13
10
|
show: (items, options) => this.showSdkMenu(items, options),
|
|
14
11
|
select: (title, options, menuOptions) => this.selectSdkMenu(title, options, menuOptions),
|
|
@@ -40,8 +37,9 @@ export class AppPopupMenuController {
|
|
|
40
37
|
resumeMenuAllSessionsLoaded = false;
|
|
41
38
|
activeUserMessageEntryId;
|
|
42
39
|
activeQueuedMessageEntryId;
|
|
43
|
-
constructor(host) {
|
|
40
|
+
constructor(host, renderer) {
|
|
44
41
|
this.host = host;
|
|
42
|
+
this.renderer = renderer;
|
|
45
43
|
}
|
|
46
44
|
get directMenu() {
|
|
47
45
|
return this.directPopupMenu;
|
|
@@ -121,7 +119,6 @@ export class AppPopupMenuController {
|
|
|
121
119
|
case "slash":
|
|
122
120
|
return this.slashCommandMenu;
|
|
123
121
|
}
|
|
124
|
-
throw new Error(`Unknown popup menu: ${active}`);
|
|
125
122
|
}
|
|
126
123
|
moveActivePopupMenuSelection(delta) {
|
|
127
124
|
const active = this.syncActivePopupMenu();
|
|
@@ -266,6 +263,29 @@ export class AppPopupMenuController {
|
|
|
266
263
|
this.slashCommandMenu.close();
|
|
267
264
|
this.dismissedSlashCommandMenuInput = undefined;
|
|
268
265
|
}
|
|
266
|
+
closeMenusForTabSwitch() {
|
|
267
|
+
if (this.directPopupMenu === "sdk-menu") {
|
|
268
|
+
this.closeSdkMenu(undefined, { render: false, restoreStatus: false });
|
|
269
|
+
}
|
|
270
|
+
this.directPopupMenu = undefined;
|
|
271
|
+
this.directPopupMenuQuery = "";
|
|
272
|
+
this.directPopupMenuPreserveStatus = false;
|
|
273
|
+
this.directPopupMenuPlacement = "default";
|
|
274
|
+
this.activeUserMessageEntryId = undefined;
|
|
275
|
+
this.activeQueuedMessageEntryId = undefined;
|
|
276
|
+
this.slashCommandMenu.close();
|
|
277
|
+
this.modelMenu.close();
|
|
278
|
+
this.thinkingMenu.close();
|
|
279
|
+
this.resumeMenu.close();
|
|
280
|
+
this.userMessageMenu.close();
|
|
281
|
+
this.userMessageJumpMenu.close();
|
|
282
|
+
this.queueMessageMenu.close();
|
|
283
|
+
this.sdkMenu.close();
|
|
284
|
+
const input = this.host.getInput();
|
|
285
|
+
this.dismissedSlashCommandMenuInput = input;
|
|
286
|
+
this.dismissedModelMenuInput = input;
|
|
287
|
+
this.dismissedThinkingMenuInput = input;
|
|
288
|
+
}
|
|
269
289
|
cancelActivePopupMenu() {
|
|
270
290
|
const active = this.syncActivePopupMenu();
|
|
271
291
|
if (this.directPopupMenu === "sdk-menu") {
|
|
@@ -414,60 +434,44 @@ export class AppPopupMenuController {
|
|
|
414
434
|
}
|
|
415
435
|
renderActivePopupMenu(width) {
|
|
416
436
|
if (this.syncQueueMessageMenu())
|
|
417
|
-
return this.renderQueueMessageMenu(width);
|
|
437
|
+
return this.renderer.renderQueueMessageMenu(width, this.queueMessageMenu);
|
|
418
438
|
// User-message actions are rendered inline inside the selected message block.
|
|
419
439
|
// They must never also appear as the global popup above the input editor.
|
|
420
440
|
if (this.syncUserMessageMenu())
|
|
421
441
|
return [];
|
|
422
442
|
if (this.syncUserMessageJumpMenu())
|
|
423
|
-
return this.renderUserMessageJumpMenu(width);
|
|
424
|
-
if (this.syncResumeMenu())
|
|
425
|
-
return this.renderResumeMenu(width
|
|
443
|
+
return this.renderer.renderUserMessageJumpMenu(width, this.userMessageJumpMenu, this.directPopupMenuQuery);
|
|
444
|
+
if (this.syncResumeMenu()) {
|
|
445
|
+
return this.renderer.renderResumeMenu(width, this.resumeMenu, {
|
|
446
|
+
directQuery: this.directPopupMenuQuery,
|
|
447
|
+
allSessionsLoaded: this.resumeMenuAllSessionsLoaded,
|
|
448
|
+
loadedSessionCount: this.resumeMenuLoadedSessionCount(),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
426
451
|
if (this.syncSdkMenu())
|
|
427
|
-
return this.renderSdkMenu(width);
|
|
452
|
+
return this.renderer.renderSdkMenu(width, this.sdkMenu, this.sdkMenuRequest, this.directPopupMenuQuery);
|
|
428
453
|
if (this.syncModelMenu())
|
|
429
|
-
return this.renderModelMenu(width);
|
|
454
|
+
return this.renderer.renderModelMenu(width, this.modelMenu);
|
|
430
455
|
if (this.syncThinkingMenu())
|
|
431
|
-
return this.renderThinkingMenu(width);
|
|
432
|
-
return this.renderSlashCommandMenu(width);
|
|
456
|
+
return this.renderer.renderThinkingMenu(width, this.thinkingMenu);
|
|
457
|
+
return this.syncSlashCommandMenu() ? this.renderer.renderSlashCommandMenu(width, this.slashCommandMenu) : [];
|
|
433
458
|
}
|
|
434
459
|
popupMenuWidth(columns) {
|
|
435
|
-
return columns;
|
|
460
|
+
return this.renderer.popupMenuWidth(columns);
|
|
436
461
|
}
|
|
437
462
|
popupMenuMargin(columns) {
|
|
438
|
-
return columns
|
|
463
|
+
return this.renderer.popupMenuMargin(columns);
|
|
439
464
|
}
|
|
440
465
|
effectivePopupMenuWidth(columns) {
|
|
441
|
-
|
|
442
|
-
return Math.min(this.popupMenuWidth(columns), Math.max(1, columns - sideMargin * 2));
|
|
466
|
+
return this.renderer.effectivePopupMenuWidth(columns);
|
|
443
467
|
}
|
|
444
468
|
styleOverlayLine(row, line, width) {
|
|
445
|
-
const colors = this.host.theme.colors;
|
|
446
|
-
const margin = this.popupMenuMargin(width);
|
|
447
|
-
const menuWidth = this.effectivePopupMenuWidth(width);
|
|
448
|
-
const rightMargin = Math.max(0, width - margin - menuWidth);
|
|
449
469
|
const activeMenuName = this.syncActivePopupMenu() ?? "slash";
|
|
450
470
|
const activeMenu = this.getActivePopupMenu(activeMenuName);
|
|
451
|
-
|
|
452
|
-
const foreground = this.popupLineForeground(line, selected);
|
|
453
|
-
const background = this.popupLineBackground(line, selected);
|
|
454
|
-
const plain = `${" ".repeat(margin)}${padOrTrimPlain(line.text, menuWidth)}${" ".repeat(rightMargin)}`;
|
|
455
|
-
if (this.host.screenStyler.selectionRangeForRow(row, width)) {
|
|
456
|
-
return this.host.screenStyler.styleLine(row, plain, width, { foreground, background });
|
|
457
|
-
}
|
|
458
|
-
return [
|
|
459
|
-
colorLine("", margin, { background: colors.background }),
|
|
460
|
-
line.segments && line.segments.length > 0
|
|
461
|
-
? this.host.screenStyler.styleLineSegments(row, line.text, menuWidth, { foreground, background, bold: selected }, line.segments)
|
|
462
|
-
: colorLine(line.text, menuWidth, { foreground, background, bold: selected }),
|
|
463
|
-
colorLine("", rightMargin, { background: colors.background }),
|
|
464
|
-
].join("");
|
|
471
|
+
return this.renderer.styleOverlayLine(row, line, width, activeMenu);
|
|
465
472
|
}
|
|
466
473
|
overlayPlainText(line, width) {
|
|
467
|
-
|
|
468
|
-
const menuWidth = this.effectivePopupMenuWidth(width);
|
|
469
|
-
const rightMargin = Math.max(0, width - margin - menuWidth);
|
|
470
|
-
return `${" ".repeat(margin)}${padOrTrimPlain(line.text, menuWidth)}${" ".repeat(rightMargin)}`;
|
|
474
|
+
return this.renderer.overlayPlainText(line, width);
|
|
471
475
|
}
|
|
472
476
|
isDynamicConversationBlock(entry) {
|
|
473
477
|
return entry.kind === "user" && this.directPopupMenu === "user-message" && this.activeUserMessageEntryId === entry.id;
|
|
@@ -478,46 +482,7 @@ export class AppPopupMenuController {
|
|
|
478
482
|
renderInlineUserMessageMenu(entry, options) {
|
|
479
483
|
if (!(this.directPopupMenu === "user-message" && this.activeUserMessageEntryId === entry.id && this.syncUserMessageMenu()))
|
|
480
484
|
return [];
|
|
481
|
-
|
|
482
|
-
headerLine.target = { kind: "popup-menu-close" };
|
|
483
|
-
headerLine.segments = [{
|
|
484
|
-
start: options.userContentLeft,
|
|
485
|
-
end: options.userContentLeft + options.userContentWidth,
|
|
486
|
-
foreground: this.host.theme.colors.accent,
|
|
487
|
-
background: this.host.theme.colors.popupHeaderBackground,
|
|
488
|
-
bold: true,
|
|
489
|
-
}];
|
|
490
|
-
const lines = [headerLine];
|
|
491
|
-
for (const item of this.userMessageMenu.visibleItems()) {
|
|
492
|
-
const label = item.label.padEnd(18, " ");
|
|
493
|
-
const description = item.description ?? "";
|
|
494
|
-
const marker = item.selected ? "›" : " ";
|
|
495
|
-
const rawText = `${marker} ${label}${description}`;
|
|
496
|
-
const text = ellipsizeDisplay(rawText, options.userContentWidth);
|
|
497
|
-
const line = options.userLine(text);
|
|
498
|
-
line.target = { kind: "popup-menu", index: item.index };
|
|
499
|
-
const contentStart = options.userContentLeft;
|
|
500
|
-
const labelStart = contentStart + 2;
|
|
501
|
-
const labelEnd = Math.min(contentStart + text.length, labelStart + item.label.length);
|
|
502
|
-
const descriptionStart = contentStart + 2 + label.length;
|
|
503
|
-
line.segments = [
|
|
504
|
-
...(item.selected ? [{ start: contentStart, end: contentStart + 1, foreground: this.host.theme.colors.accent, bold: true }] : []),
|
|
505
|
-
{
|
|
506
|
-
start: labelStart,
|
|
507
|
-
end: labelEnd,
|
|
508
|
-
foreground: this.userMessageActionForeground(item.selected, item.value),
|
|
509
|
-
bold: item.selected,
|
|
510
|
-
},
|
|
511
|
-
...(descriptionStart < contentStart + text.length
|
|
512
|
-
? [{ start: descriptionStart, end: contentStart + text.length, foreground: this.host.theme.colors.muted }]
|
|
513
|
-
: []),
|
|
514
|
-
];
|
|
515
|
-
lines.push(line);
|
|
516
|
-
}
|
|
517
|
-
return lines;
|
|
518
|
-
}
|
|
519
|
-
hasPopupActionItems(items) {
|
|
520
|
-
return items.length > 0;
|
|
485
|
+
return this.renderer.renderInlineUserMessageMenu(options, this.userMessageMenu);
|
|
521
486
|
}
|
|
522
487
|
withoutCloseMenuItems(items) {
|
|
523
488
|
return items.filter((item) => item.label.trim().toLowerCase() !== "cancel");
|
|
@@ -551,49 +516,6 @@ export class AppPopupMenuController {
|
|
|
551
516
|
resumeMenuLoadedSessionCount() {
|
|
552
517
|
return this.resumeMenu.items.filter((item) => item.value.kind === "session").length;
|
|
553
518
|
}
|
|
554
|
-
userMessageActionForeground(selected, value) {
|
|
555
|
-
if (selected)
|
|
556
|
-
return this.host.theme.colors.accent;
|
|
557
|
-
if (value === "undo")
|
|
558
|
-
return this.host.theme.colors.error;
|
|
559
|
-
return this.host.theme.colors.inputForeground;
|
|
560
|
-
}
|
|
561
|
-
selectableItemVariant(selected, value) {
|
|
562
|
-
if (selected)
|
|
563
|
-
return "accent";
|
|
564
|
-
return value.current ? "muted" : "normal";
|
|
565
|
-
}
|
|
566
|
-
queueMessageItemVariant(selected, value) {
|
|
567
|
-
if (selected)
|
|
568
|
-
return "accent";
|
|
569
|
-
return value === "cancel" ? "error" : "normal";
|
|
570
|
-
}
|
|
571
|
-
sdkItemVariant(selected, value) {
|
|
572
|
-
if (selected)
|
|
573
|
-
return "accent";
|
|
574
|
-
return value.variant ?? "normal";
|
|
575
|
-
}
|
|
576
|
-
resumeMenuItemSegments(value, label, description, text) {
|
|
577
|
-
if (value.kind !== "session")
|
|
578
|
-
return undefined;
|
|
579
|
-
const sessionLabel = value.session.name ?? value.session.firstMessage.slice(0, 50);
|
|
580
|
-
const sessionLabelStart = Math.max(0, label.length - sessionLabel.length);
|
|
581
|
-
const muted = this.host.theme.colors.popupMuted;
|
|
582
|
-
const segments = [];
|
|
583
|
-
if (sessionLabelStart > 0)
|
|
584
|
-
segments.push({ start: 0, end: sessionLabelStart, foreground: muted });
|
|
585
|
-
if (description.length > 0)
|
|
586
|
-
segments.push({ start: label.length, end: text.length, foreground: muted });
|
|
587
|
-
return segments.length > 0 ? segments : undefined;
|
|
588
|
-
}
|
|
589
|
-
popupMenuHeader(title, width) {
|
|
590
|
-
return {
|
|
591
|
-
text: formatPopupMenuHeader(title, width),
|
|
592
|
-
variant: "accent",
|
|
593
|
-
backgroundOverride: this.host.theme.colors.popupHeaderBackground,
|
|
594
|
-
target: { kind: "popup-menu-close" },
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
519
|
syncModelMenu() {
|
|
598
520
|
if (this.directPopupMenu === "model") {
|
|
599
521
|
this.closeMenusExcept("model");
|
|
@@ -744,202 +666,21 @@ export class AppPopupMenuController {
|
|
|
744
666
|
value: item,
|
|
745
667
|
label: item.label,
|
|
746
668
|
...(item.keywords === undefined ? {} : { keywords: item.keywords }),
|
|
747
|
-
})), query
|
|
669
|
+
})), query, {
|
|
670
|
+
...(request.options.minScorePerCharacter === undefined ? {} : { minScorePerCharacter: request.options.minScorePerCharacter }),
|
|
671
|
+
preferKeyboardLayoutMatches: request.options.preferKeyboardLayoutMatches ?? false,
|
|
672
|
+
}).map((match) => ({
|
|
673
|
+
...match.value,
|
|
674
|
+
labelHighlightRanges: match.matchedText === match.label ? match.matchedRanges : [],
|
|
675
|
+
}));
|
|
748
676
|
return this.withoutCloseMenuItems(items.map((item) => ({
|
|
749
677
|
value: item,
|
|
750
678
|
label: item.label,
|
|
679
|
+
...(item.labelHighlightRanges === undefined ? {} : { labelHighlightRanges: item.labelHighlightRanges }),
|
|
680
|
+
...(item.descriptionHighlightRanges === undefined ? {} : { descriptionHighlightRanges: item.descriptionHighlightRanges }),
|
|
751
681
|
...(item.description === undefined ? {} : { description: item.description }),
|
|
752
682
|
})));
|
|
753
683
|
}
|
|
754
|
-
renderSlashCommandMenu(_width) {
|
|
755
|
-
if (!this.syncSlashCommandMenu())
|
|
756
|
-
return [];
|
|
757
|
-
const lines = [this.popupMenuHeader("Commands", _width)];
|
|
758
|
-
const visibleItems = this.slashCommandMenu.visibleItems();
|
|
759
|
-
if (!this.hasPopupActionItems(this.slashCommandMenu.items)) {
|
|
760
|
-
lines.push({ text: " No matching slash commands", variant: "muted" });
|
|
761
|
-
}
|
|
762
|
-
for (const item of visibleItems) {
|
|
763
|
-
const command = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
|
|
764
|
-
const description = item.description ?? "";
|
|
765
|
-
lines.push({
|
|
766
|
-
text: `${command}${description}`,
|
|
767
|
-
variant: item.selected ? "accent" : "normal",
|
|
768
|
-
target: { kind: "popup-menu", index: item.index },
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
return lines;
|
|
772
|
-
}
|
|
773
|
-
renderModelMenu(_width) {
|
|
774
|
-
if (!this.syncModelMenu())
|
|
775
|
-
return [];
|
|
776
|
-
const lines = [this.popupMenuHeader("Select model", _width)];
|
|
777
|
-
const visibleItems = this.modelMenu.visibleItems();
|
|
778
|
-
if (!this.hasPopupActionItems(this.modelMenu.items)) {
|
|
779
|
-
lines.push({
|
|
780
|
-
text: this.host.session ? " No matching favorite models" : " Model menu unavailable",
|
|
781
|
-
variant: "muted",
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
for (const item of visibleItems) {
|
|
785
|
-
const model = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
|
|
786
|
-
const description = item.description ?? "";
|
|
787
|
-
lines.push({
|
|
788
|
-
text: `${model}${description}`,
|
|
789
|
-
variant: this.selectableItemVariant(item.selected, item.value),
|
|
790
|
-
target: { kind: "popup-menu", index: item.index },
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
return lines;
|
|
794
|
-
}
|
|
795
|
-
renderThinkingMenu(_width) {
|
|
796
|
-
if (!this.syncThinkingMenu())
|
|
797
|
-
return [];
|
|
798
|
-
const lines = [this.popupMenuHeader("Thinking level", _width)];
|
|
799
|
-
const visibleItems = this.thinkingMenu.visibleItems();
|
|
800
|
-
if (!this.hasPopupActionItems(this.thinkingMenu.items)) {
|
|
801
|
-
lines.push({ text: " No matching thinking levels", variant: "muted" });
|
|
802
|
-
}
|
|
803
|
-
for (const item of visibleItems) {
|
|
804
|
-
const level = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
|
|
805
|
-
const description = item.description ?? "";
|
|
806
|
-
lines.push({
|
|
807
|
-
text: `${level}${description}`,
|
|
808
|
-
variant: this.selectableItemVariant(item.selected, item.value),
|
|
809
|
-
target: { kind: "popup-menu", index: item.index },
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
return lines;
|
|
813
|
-
}
|
|
814
|
-
renderResumeMenu(_width) {
|
|
815
|
-
if (!this.syncResumeMenu())
|
|
816
|
-
return [];
|
|
817
|
-
const title = this.host.resumeLoading ? `Resume session ${APP_ICONS.timerSand}` : "Resume session";
|
|
818
|
-
const lines = [this.popupMenuHeader(title, _width)];
|
|
819
|
-
const visibleItems = this.resumeMenu.visibleItems();
|
|
820
|
-
if (!this.host.resumeLoading && !this.hasPopupActionItems(this.resumeMenu.items)) {
|
|
821
|
-
lines.push({
|
|
822
|
-
text: this.host.resumeSessionCount === 0 ? " No sessions found" : " No matching sessions",
|
|
823
|
-
variant: "muted",
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
for (const item of visibleItems) {
|
|
827
|
-
const label = item.label;
|
|
828
|
-
const description = item.description ?? "";
|
|
829
|
-
const text = `${label} ${description}`;
|
|
830
|
-
const segments = this.resumeMenuItemSegments(item.value, label, description, text);
|
|
831
|
-
lines.push({
|
|
832
|
-
text,
|
|
833
|
-
variant: item.selected ? "accent" : "normal",
|
|
834
|
-
...(segments ? { segments } : {}),
|
|
835
|
-
target: { kind: "popup-menu", index: item.index },
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
if (!this.resumeMenuAllSessionsLoaded && this.resumeMenuLoadedSessionCount() > 0) {
|
|
839
|
-
lines.push({ text: ` Loaded ${this.resumeMenuLoadedSessionCount()} sessions · scroll for more`, variant: "muted" });
|
|
840
|
-
}
|
|
841
|
-
if (this.directPopupMenuQuery) {
|
|
842
|
-
lines.push({ text: ` Search: ${this.directPopupMenuQuery}`, variant: "muted" });
|
|
843
|
-
}
|
|
844
|
-
return lines;
|
|
845
|
-
}
|
|
846
|
-
renderUserMessageJumpMenu(width) {
|
|
847
|
-
if (!this.syncUserMessageJumpMenu())
|
|
848
|
-
return [];
|
|
849
|
-
const lines = [this.popupMenuHeader("Jump to user message", width)];
|
|
850
|
-
if (!this.hasPopupActionItems(this.userMessageJumpMenu.items)) {
|
|
851
|
-
lines.push({
|
|
852
|
-
text: this.host.entries.some((entry) => entry.kind === "user") ? " No matching user messages" : " No user messages yet",
|
|
853
|
-
variant: "muted",
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
const labelWidth = Math.max(1, width);
|
|
857
|
-
for (const item of this.userMessageJumpMenu.visibleItems()) {
|
|
858
|
-
const label = ellipsizeDisplay(item.label, labelWidth);
|
|
859
|
-
lines.push({
|
|
860
|
-
text: label,
|
|
861
|
-
variant: item.selected ? "accent" : "normal",
|
|
862
|
-
target: { kind: "popup-menu", index: item.index },
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
if (this.directPopupMenuQuery) {
|
|
866
|
-
lines.push({ text: ` Search: ${this.directPopupMenuQuery}`, variant: "muted" });
|
|
867
|
-
}
|
|
868
|
-
return lines;
|
|
869
|
-
}
|
|
870
|
-
renderQueueMessageMenu(_width) {
|
|
871
|
-
if (!this.syncQueueMessageMenu())
|
|
872
|
-
return [];
|
|
873
|
-
const lines = [this.popupMenuHeader("Queued message", _width)];
|
|
874
|
-
for (const item of this.queueMessageMenu.visibleItems()) {
|
|
875
|
-
const label = item.label.padEnd(18, " ");
|
|
876
|
-
const description = item.description ?? "";
|
|
877
|
-
lines.push({
|
|
878
|
-
text: `${label}${description}`,
|
|
879
|
-
variant: this.queueMessageItemVariant(item.selected, item.value),
|
|
880
|
-
target: { kind: "popup-menu", index: item.index },
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
return lines;
|
|
884
|
-
}
|
|
885
|
-
renderSdkMenu(_width) {
|
|
886
|
-
if (!this.syncSdkMenu())
|
|
887
|
-
return [];
|
|
888
|
-
const request = this.sdkMenuRequest;
|
|
889
|
-
const lines = [this.popupMenuHeader(request?.options.title ?? "Menu", _width)];
|
|
890
|
-
if (!this.hasPopupActionItems(this.sdkMenu.items)) {
|
|
891
|
-
lines.push({ text: ` ${request?.options.emptyText ?? "No matching items"}`, variant: "muted" });
|
|
892
|
-
}
|
|
893
|
-
for (const item of this.sdkMenu.visibleItems()) {
|
|
894
|
-
const label = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
|
|
895
|
-
const description = item.description ?? "";
|
|
896
|
-
lines.push({
|
|
897
|
-
text: `${label}${description}`,
|
|
898
|
-
variant: this.sdkItemVariant(item.selected, item.value),
|
|
899
|
-
target: { kind: "popup-menu", index: item.index },
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
if (request?.options.searchable !== false && this.directPopupMenuQuery) {
|
|
903
|
-
lines.push({ text: ` ${request?.options.placeholder ?? "Search"}: ${this.directPopupMenuQuery}`, variant: "muted" });
|
|
904
|
-
}
|
|
905
|
-
return lines;
|
|
906
|
-
}
|
|
907
|
-
popupLineForeground(line, selected) {
|
|
908
|
-
const colors = this.host.theme.colors;
|
|
909
|
-
if (selected)
|
|
910
|
-
return colors.popupSelectedForeground;
|
|
911
|
-
if (line.colorOverride)
|
|
912
|
-
return line.colorOverride;
|
|
913
|
-
switch (line.variant) {
|
|
914
|
-
case "accent":
|
|
915
|
-
return colors.accent;
|
|
916
|
-
case "muted":
|
|
917
|
-
return colors.popupMuted;
|
|
918
|
-
case "error":
|
|
919
|
-
return colors.error;
|
|
920
|
-
case "normal":
|
|
921
|
-
case undefined:
|
|
922
|
-
return colors.popupForeground;
|
|
923
|
-
}
|
|
924
|
-
return colors.popupForeground;
|
|
925
|
-
}
|
|
926
|
-
popupLineBackground(line, selected) {
|
|
927
|
-
const colors = this.host.theme.colors;
|
|
928
|
-
if (selected)
|
|
929
|
-
return colors.popupSelectedBackground;
|
|
930
|
-
return line.backgroundOverride ?? colors.popupBackground;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
export function formatPopupMenuHeader(title, width) {
|
|
934
|
-
const safeWidth = Math.max(1, width);
|
|
935
|
-
const sanitizedTitle = sanitizeText(title).replace(/\s+/g, " ").trim() || "Menu";
|
|
936
|
-
const buttonWidth = stringDisplayWidth(POPUP_MENU_ESCAPE_BUTTON);
|
|
937
|
-
if (safeWidth <= buttonWidth + 1)
|
|
938
|
-
return padOrTrimPlain(POPUP_MENU_ESCAPE_BUTTON, safeWidth);
|
|
939
|
-
const titleWidth = safeWidth - buttonWidth - 1;
|
|
940
|
-
const titleText = ellipsizeDisplay(sanitizedTitle, titleWidth);
|
|
941
|
-
const gapWidth = Math.max(1, safeWidth - stringDisplayWidth(titleText) - buttonWidth);
|
|
942
|
-
return `${titleText}${" ".repeat(gapWidth)}${POPUP_MENU_ESCAPE_BUTTON}`;
|
|
943
684
|
}
|
|
944
685
|
function canonicalSessionPath(sessionPath) {
|
|
945
686
|
return sessionPath ? resolve(sessionPath) : undefined;
|
|
@@ -1000,7 +741,8 @@ function formatSessionMenuDateTime(dateTime) {
|
|
|
1000
741
|
time: dateTime.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit", hourCycle: "h23" }),
|
|
1001
742
|
};
|
|
1002
743
|
}
|
|
1003
|
-
function formatSessionInfoMenuItem(
|
|
744
|
+
function formatSessionInfoMenuItem(source) {
|
|
745
|
+
const { session, labelPrefix } = source;
|
|
1004
746
|
const { date, time } = formatSessionMenuDateTime(session.modified);
|
|
1005
747
|
const messages = `${session.messageCount} msg${session.messageCount !== 1 ? "s" : ""}`;
|
|
1006
748
|
const label = session.name ?? session.firstMessage.slice(0, 50);
|
|
@@ -1008,6 +750,7 @@ function formatSessionInfoMenuItem(session, labelPrefix = "") {
|
|
|
1008
750
|
value: session,
|
|
1009
751
|
label: `${labelPrefix}${label}`,
|
|
1010
752
|
description: `${date} ${time} · ${messages} · ${session.id.slice(0, 8)}`,
|
|
753
|
+
...(source.labelHighlightRanges === undefined ? {} : { labelHighlightRanges: source.labelHighlightRanges }),
|
|
1011
754
|
};
|
|
1012
755
|
}
|
|
1013
756
|
function buildSessionInfoMenuSource(sessions, currentSessionFile, query) {
|
|
@@ -1028,7 +771,11 @@ function buildSessionInfoMenuSource(sessions, currentSessionFile, query) {
|
|
|
1028
771
|
session.id,
|
|
1029
772
|
],
|
|
1030
773
|
}));
|
|
1031
|
-
return fuzzySearch(items, query).map((match) => ({
|
|
774
|
+
return fuzzySearch(items, query).map((match) => ({
|
|
775
|
+
session: match.value,
|
|
776
|
+
labelPrefix: "",
|
|
777
|
+
labelHighlightRanges: match.matchedText === match.label ? match.matchedRanges : [],
|
|
778
|
+
}));
|
|
1032
779
|
}
|
|
1033
780
|
export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, query) {
|
|
1034
781
|
const source = buildSessionInfoMenuSource(sessions, currentSessionFile, query);
|
|
@@ -1042,7 +789,7 @@ export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, q
|
|
|
1042
789
|
const cached = cachedItems.get(effectiveLimit);
|
|
1043
790
|
if (cached)
|
|
1044
791
|
return cached;
|
|
1045
|
-
const result = source.slice(0, effectiveLimit).map((item) => formatSessionInfoMenuItem(item
|
|
792
|
+
const result = source.slice(0, effectiveLimit).map((item) => formatSessionInfoMenuItem(item));
|
|
1046
793
|
cachedItems.set(effectiveLimit, result);
|
|
1047
794
|
return result;
|
|
1048
795
|
},
|
|
@@ -1051,20 +798,44 @@ export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, q
|
|
|
1051
798
|
export function formatSessionInfoMenuItems(sessions, currentSessionFile, query, options = {}) {
|
|
1052
799
|
return createSessionInfoMenuItemsLoader(sessions, currentSessionFile, query).items(options.limit);
|
|
1053
800
|
}
|
|
1054
|
-
export function buildUserMessageJumpItems(entries
|
|
1055
|
-
const userEntries = entries.
|
|
1056
|
-
|
|
801
|
+
export function buildUserMessageJumpItems(entries) {
|
|
802
|
+
const userEntries = entries.flatMap((entry) => {
|
|
803
|
+
if ("kind" in entry) {
|
|
804
|
+
return entry.kind === "user"
|
|
805
|
+
? [{ text: entry.text, entryId: entry.id, ...(entry.sessionEntryId === undefined ? {} : { sessionEntryId: entry.sessionEntryId }) }]
|
|
806
|
+
: [];
|
|
807
|
+
}
|
|
808
|
+
return [entry];
|
|
809
|
+
});
|
|
810
|
+
return userEntries.map((entry, index) => {
|
|
1057
811
|
const preview = sanitizeText(entry.text).replace(/\s+/g, " ").trim();
|
|
1058
812
|
const label = `${index + 1}. ${preview || "(empty message)"}`;
|
|
1059
813
|
return {
|
|
1060
|
-
value: { entryId: entry.
|
|
814
|
+
value: { ...(entry.entryId === undefined ? {} : { entryId: entry.entryId }), ...(entry.sessionEntryId === undefined ? {} : { sessionEntryId: entry.sessionEntryId }) },
|
|
1061
815
|
label,
|
|
1062
|
-
|
|
816
|
+
...(entry.entryId ? {} : { description: "load older history and jump" }),
|
|
817
|
+
aliases: [entry.sessionEntryId ?? "", entry.entryId ?? ""],
|
|
1063
818
|
keywords: [entry.text],
|
|
1064
819
|
};
|
|
1065
820
|
});
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
821
|
+
}
|
|
822
|
+
export function filterUserMessageJumpItems(items, query) {
|
|
823
|
+
const searchableItems = items.map((item) => ({
|
|
824
|
+
value: item,
|
|
825
|
+
label: item.label,
|
|
826
|
+
...(item.aliases === undefined ? {} : { aliases: item.aliases }),
|
|
827
|
+
...(item.keywords === undefined ? {} : { keywords: item.keywords }),
|
|
1069
828
|
}));
|
|
829
|
+
return fuzzySearch(searchableItems, query).map((match) => ({
|
|
830
|
+
...match.value,
|
|
831
|
+
labelHighlightRanges: labelHighlightRangesFromMatch(match.matchedText, match.matchedRanges, match.label),
|
|
832
|
+
}));
|
|
833
|
+
}
|
|
834
|
+
function labelHighlightRangesFromMatch(matchedText, matchedRanges, label) {
|
|
835
|
+
if (matchedText === label)
|
|
836
|
+
return matchedRanges;
|
|
837
|
+
const offset = label.toLocaleLowerCase().indexOf(matchedText.toLocaleLowerCase());
|
|
838
|
+
if (offset < 0)
|
|
839
|
+
return [];
|
|
840
|
+
return matchedRanges.map((range) => ({ start: offset + range.start, end: offset + range.end }));
|
|
1070
841
|
}
|
|
@@ -16,10 +16,10 @@ export function renderConversationEntry(entry, width, options) {
|
|
|
16
16
|
...(entryId === undefined ? {} : { target: { kind: "user-message", id: entryId } }),
|
|
17
17
|
});
|
|
18
18
|
const queuedLine = (text, entryId, segments) => ({
|
|
19
|
-
text
|
|
19
|
+
text,
|
|
20
20
|
variant: "muted",
|
|
21
21
|
backgroundOverride: options.colors.userMessageBackground,
|
|
22
|
-
...(segments && segments.length > 0 ? { segments
|
|
22
|
+
...(segments && segments.length > 0 ? { segments } : {}),
|
|
23
23
|
target: { kind: "queue-message", id: entryId },
|
|
24
24
|
});
|
|
25
25
|
const userMessageLines = (userEntry) => {
|
|
@@ -33,7 +33,7 @@ export function renderConversationEntry(entry, width, options) {
|
|
|
33
33
|
};
|
|
34
34
|
const queuedMessageLines = (queuedEntry) => {
|
|
35
35
|
const icon = queuedEntry.queueSource === "deferred" ? APP_ICONS.pause : APP_ICONS.timerSand;
|
|
36
|
-
const contentLines = wrapText(`${icon} ${queuedEntry.text}`,
|
|
36
|
+
const contentLines = wrapText(`${icon} ${queuedEntry.text}`, width);
|
|
37
37
|
return contentLines.map((text, index) => queuedLine(text, queuedEntry.id, index === 0 ? [{ start: 0, end: icon.length, foreground: options.colors.info }] : undefined));
|
|
38
38
|
};
|
|
39
39
|
switch (entry.kind) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type
|
|
2
|
+
import { type PixConfig } from "../../config.js";
|
|
3
3
|
import type { Theme } from "../../theme.js";
|
|
4
4
|
import { type InlineUserMessageMenuContext } from "./conversation-entry-renderer.js";
|
|
5
5
|
import type { ConversationBlockCache, Entry, RenderedLine, SubmittedUserMessage } from "../types.js";
|
|
@@ -35,19 +35,27 @@ export declare class ConversationViewport {
|
|
|
35
35
|
deleteEntry(entryId: string): void;
|
|
36
36
|
lineCount(width: number): number;
|
|
37
37
|
slice(width: number, start: number, count: number): RenderedLine[];
|
|
38
|
+
private sliceMeasured;
|
|
38
39
|
entries(): Entry[];
|
|
39
40
|
blockForEntry(entry: Entry, width: number): ConversationBlockCache;
|
|
40
41
|
entryBlockPositions(width: number): ConversationEntryBlockPosition[];
|
|
42
|
+
measuredLineCountForEntries(width: number, entryIds: readonly string[]): number;
|
|
41
43
|
private queuedEntries;
|
|
42
44
|
private layoutForWidth;
|
|
43
45
|
private buildLayout;
|
|
46
|
+
private previousMeasuredLineCount;
|
|
44
47
|
private layoutStructureChanged;
|
|
45
48
|
private refreshDirtyLayoutEntries;
|
|
46
49
|
private blockCacheForWidth;
|
|
47
50
|
private refreshDynamicLayoutEntries;
|
|
51
|
+
private ensureEntryMeasured;
|
|
48
52
|
private refreshLayoutEntry;
|
|
49
|
-
private
|
|
53
|
+
private measuredLineCountForEntry;
|
|
54
|
+
private estimatedLineCountForEntry;
|
|
55
|
+
private lineCountWithGap;
|
|
56
|
+
private estimatedBlockLineCountForEntry;
|
|
50
57
|
private nextVisibleEntry;
|
|
58
|
+
private nextEstimatedVisibleEntry;
|
|
51
59
|
private gapAfterEntry;
|
|
52
60
|
private isSuperCompactGaplessEntry;
|
|
53
61
|
private entryIndexForOffset;
|