pi-ui-extend 0.1.13 → 0.1.15
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 +5 -0
- package/dist/app/app.js +82 -12
- package/dist/app/commands/command-controller.js +1 -0
- package/dist/app/commands/command-host.d.ts +3 -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.js +3 -0
- package/dist/app/commands/command-registry.d.ts +1 -0
- package/dist/app/commands/command-registry.js +8 -0
- package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
- package/dist/app/extensions/extension-ui-controller.js +99 -61
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +8 -2
- 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 +2 -0
- package/dist/app/popup/menu-items-controller.js +45 -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 +68 -322
- 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 +4 -2
- package/dist/app/rendering/popup-menu-renderer.d.ts +50 -0
- package/dist/app/rendering/popup-menu-renderer.js +307 -0
- package/dist/app/rendering/render-controller.js +5 -13
- package/dist/app/rendering/status-line-renderer.d.ts +1 -1
- package/dist/app/rendering/status-line-renderer.js +27 -24
- package/dist/app/rendering/toast-controller.d.ts +11 -3
- package/dist/app/rendering/toast-controller.js +53 -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 +11 -9
- package/dist/app/screen/scroll-controller.js +50 -45
- 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/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/tabs-controller.d.ts +13 -1
- package/dist/app/session/tabs-controller.js +248 -27
- package/dist/app/todo/todo-model.d.ts +3 -1
- package/dist/app/todo/todo-model.js +14 -2
- package/dist/app/types.d.ts +5 -2
- 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 +5 -1
- package/dist/config.js +73 -25
- package/dist/default-pix-config.js +2 -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 +2 -1
- package/dist/schemas/pix-schema.js +5 -4
- package/dist/terminal-width.d.ts +2 -0
- package/dist/terminal-width.js +64 -3
- package/external/pi-tools-suite/README.md +1 -0
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +12 -3
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +2 -4
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +2 -2
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +8 -2
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +102 -50
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +81 -2
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +29 -8
- 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 +181 -11
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +23 -10
- package/external/pi-tools-suite/src/todo/todo.ts +10 -5
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +33 -6
- package/external/pi-tools-suite/src/todo/tool/types.ts +9 -1
- package/external/pi-tools-suite/src/todo/view/format.ts +2 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +2 -1
- package/external/pi-tools-suite/src/usage/index.ts +5 -2
- package/external/pi-tools-suite/src/usage/lib/google.ts +6 -13
- package/package.json +1 -1
- package/schemas/pi-tools-suite.json +4 -0
- package/schemas/pix.json +6 -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");
|
|
@@ -751,195 +673,6 @@ export class AppPopupMenuController {
|
|
|
751
673
|
...(item.description === undefined ? {} : { description: item.description }),
|
|
752
674
|
})));
|
|
753
675
|
}
|
|
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
676
|
}
|
|
944
677
|
function canonicalSessionPath(sessionPath) {
|
|
945
678
|
return sessionPath ? resolve(sessionPath) : undefined;
|
|
@@ -1051,20 +784,33 @@ export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, q
|
|
|
1051
784
|
export function formatSessionInfoMenuItems(sessions, currentSessionFile, query, options = {}) {
|
|
1052
785
|
return createSessionInfoMenuItemsLoader(sessions, currentSessionFile, query).items(options.limit);
|
|
1053
786
|
}
|
|
1054
|
-
export function buildUserMessageJumpItems(entries
|
|
1055
|
-
const userEntries = entries.
|
|
1056
|
-
|
|
787
|
+
export function buildUserMessageJumpItems(entries) {
|
|
788
|
+
const userEntries = entries.flatMap((entry) => {
|
|
789
|
+
if ("kind" in entry) {
|
|
790
|
+
return entry.kind === "user"
|
|
791
|
+
? [{ text: entry.text, entryId: entry.id, ...(entry.sessionEntryId === undefined ? {} : { sessionEntryId: entry.sessionEntryId }) }]
|
|
792
|
+
: [];
|
|
793
|
+
}
|
|
794
|
+
return [entry];
|
|
795
|
+
});
|
|
796
|
+
return userEntries.map((entry, index) => {
|
|
1057
797
|
const preview = sanitizeText(entry.text).replace(/\s+/g, " ").trim();
|
|
1058
798
|
const label = `${index + 1}. ${preview || "(empty message)"}`;
|
|
1059
799
|
return {
|
|
1060
|
-
value: { entryId: entry.
|
|
800
|
+
value: { ...(entry.entryId === undefined ? {} : { entryId: entry.entryId }), ...(entry.sessionEntryId === undefined ? {} : { sessionEntryId: entry.sessionEntryId }) },
|
|
1061
801
|
label,
|
|
1062
|
-
|
|
802
|
+
...(entry.entryId ? {} : { description: "load older history and jump" }),
|
|
803
|
+
aliases: [entry.sessionEntryId ?? "", entry.entryId ?? ""],
|
|
1063
804
|
keywords: [entry.text],
|
|
1064
805
|
};
|
|
1065
806
|
});
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
807
|
+
}
|
|
808
|
+
export function filterUserMessageJumpItems(items, query) {
|
|
809
|
+
const searchableItems = items.map((item) => ({
|
|
810
|
+
value: item,
|
|
811
|
+
label: item.label,
|
|
812
|
+
...(item.aliases === undefined ? {} : { aliases: item.aliases }),
|
|
813
|
+
...(item.keywords === undefined ? {} : { keywords: item.keywords }),
|
|
1069
814
|
}));
|
|
815
|
+
return fuzzySearch(searchableItems, query).map((match) => match.value);
|
|
1070
816
|
}
|
|
@@ -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;
|