@timurproko/a1 0.2.4 → 0.2.5-dev.698

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.
Files changed (116) hide show
  1. package/dist/app/session-shell/prompt-suggestion-controller.d.ts +2 -0
  2. package/dist/app/session-shell/prompt-suggestion-controller.js +10 -0
  3. package/dist/app/session-shell/session-shell-root.d.ts +1 -0
  4. package/dist/app/session-shell/session-shell-root.js +19 -6
  5. package/dist/app/session-shell/session-shell.js +77 -19
  6. package/dist/app/session-shell/share-operation.d.ts +3 -0
  7. package/dist/app/session-shell/share-operation.js +11 -0
  8. package/dist/composition/owned-ui.js +1 -0
  9. package/dist/composition/session-info-reference.d.ts +4 -0
  10. package/dist/composition/session-info-reference.js +20 -0
  11. package/dist/composition/settings-route-host.d.ts +2 -5
  12. package/dist/composition/settings-route-host.js +8 -12
  13. package/dist/contracts/presentation/index.d.ts +5 -0
  14. package/dist/contracts/presentation/index.js +2 -0
  15. package/dist/features/owned-ui/project-trust-dialog.js +7 -5
  16. package/dist/features/owned-ui/reference-screen-app.d.ts +4 -2
  17. package/dist/features/owned-ui/reference-screen-app.js +17 -7
  18. package/dist/features/owned-ui/settings-app.js +10 -5
  19. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  20. package/dist/foundation/release/progress-palette.generated.js +2 -2
  21. package/dist/foundation/startup/startup-descriptor.js +2 -2
  22. package/dist/integrations/pi/components/index.d.ts +3 -2
  23. package/dist/integrations/pi/components/index.js +2 -1
  24. package/dist/integrations/pi/components/modal-frame.d.ts +11 -1
  25. package/dist/integrations/pi/components/modal-frame.js +29 -1
  26. package/dist/integrations/pi/components/models-dialog.js +17 -17
  27. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
  28. package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
  29. package/dist/integrations/pi/components/share-operation-dialog.d.ts +6 -0
  30. package/dist/integrations/pi/components/share-operation-dialog.js +40 -0
  31. package/dist/integrations/pi/components/shell-editor-autocomplete.js +15 -6
  32. package/dist/integrations/pi/components/shell-extension-ui.d.ts +3 -0
  33. package/dist/integrations/pi/components/shell-extension-ui.js +8 -4
  34. package/dist/integrations/pi/components/shell-presenters-info.d.ts +2 -0
  35. package/dist/integrations/pi/components/shell-presenters-info.js +1 -1
  36. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +0 -4
  37. package/dist/integrations/pi/components/shell-selectors-dialogs.js +56 -34
  38. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +10 -0
  39. package/dist/integrations/pi/components/shell-session-info-reference.js +94 -0
  40. package/dist/integrations/pi/components/shell-shared-facade.d.ts +3 -1
  41. package/dist/integrations/pi/components/shell-shared-facade.js +13 -1
  42. package/dist/integrations/pi/components/skills-dialog.js +19 -6
  43. package/dist/integrations/pi/components/theme.d.ts +8 -1
  44. package/dist/integrations/pi/components/theme.js +28 -1
  45. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +15 -7
  46. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +21 -7
  47. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  48. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  49. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  50. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  51. package/dist/integrations/pi/components/upstream/components/extension-editor.js +3 -3
  52. package/dist/integrations/pi/components/upstream/components/extension-input.js +5 -6
  53. package/dist/integrations/pi/components/upstream/components/extension-selector.js +3 -4
  54. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  55. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  56. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  57. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  58. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +4 -3
  59. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +4 -15
  60. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  61. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  62. package/dist/integrations/pi/components/upstream/components/session-selector.d.ts +2 -0
  63. package/dist/integrations/pi/components/upstream/components/session-selector.js +137 -110
  64. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
  65. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
  66. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  67. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  68. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  69. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  70. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +9 -6
  71. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +64 -27
  72. package/dist/integrations/pi/components/upstream/components/tool-execution.js +1 -1
  73. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +30 -21
  74. package/dist/integrations/pi/components/upstream/components/tree-selector.js +246 -211
  75. package/dist/integrations/pi/components/upstream/components/trust-selector.js +14 -16
  76. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  77. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +1 -1
  78. package/dist/integrations/pi/components/upstream/history/editor-core.js +1 -1
  79. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  80. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  81. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  82. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  83. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  84. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  85. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  86. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  87. package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +1 -1
  88. package/dist/integrations/pi/components/upstream/theme/system-theme.js +1 -1
  89. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  90. package/dist/integrations/pi/components/upstream/theme/theme.js +1 -1
  91. package/dist/integrations/pi/engine/adapter.js +21 -2
  92. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  93. package/dist/integrations/pi/engine/workflow-runner.js +2 -1
  94. package/dist/integrations/pi/startup-public.js +186 -81
  95. package/dist/integrations/pi/startup-public.manifest.json +107 -95
  96. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  97. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  98. package/dist/native/darwin-arm64/manifest.json +1 -1
  99. package/dist/native/linux-x64/manifest.json +1 -1
  100. package/dist/native/win32-x64/manifest.json +2 -2
  101. package/dist/native/win32-x64/process-guardian.exe +0 -0
  102. package/dist/runtime-payload-inventory.json +2717 -2630
  103. package/dist/ui/apps/host.d.ts +4 -3
  104. package/dist/ui/apps/host.js +9 -4
  105. package/dist/ui/components/dialog-panel.js +6 -7
  106. package/dist/ui/components/index.d.ts +1 -1
  107. package/dist/ui/components/index.js +1 -1
  108. package/dist/ui/components/list-view.d.ts +3 -3
  109. package/dist/ui/components/list-view.js +8 -9
  110. package/dist/ui/components/shortcut-hints.d.ts +2 -0
  111. package/dist/ui/components/shortcut-hints.js +17 -1
  112. package/dist/ui/components/theme.d.ts +1 -1
  113. package/docs/assets/readme/separator-dark.svg +2 -4
  114. package/docs/assets/readme/separator.svg +2 -4
  115. package/docs/releases/0.2.4.md +1 -5
  116. package/package.json +3 -3
@@ -31,6 +31,8 @@ export declare class ContextualPromptSuggestionController {
31
31
  invalidate(): void;
32
32
  /** Retires only a generating request; a prepared or available suggestion survives the draft edit. */
33
33
  abortPending(): void;
34
+ /** Reasserts retained ghost text after an editor mutation makes the prompt eligible again. */
35
+ restoreAvailable(): void;
34
36
  dispose(): void;
35
37
  }
36
38
  export declare function samePromptSuggestionIdentity(left: OwnedUiPromptSuggestionIdentity, right: OwnedUiPromptSuggestionIdentity): boolean;
@@ -100,6 +100,16 @@ export class ContextualPromptSuggestionController {
100
100
  this.#finish(request, "cancelled");
101
101
  request.abort.abort();
102
102
  }
103
+ /** Reasserts retained ghost text after an editor mutation makes the prompt eligible again. */
104
+ restoreAvailable() {
105
+ if (this.#disposed || !this.#enabled || this.#state.status !== "available")
106
+ return;
107
+ const reason = this.options.surface.presentationBlockReason?.(this.#state.identity)
108
+ ?? (this.options.surface.canPresent(this.#state.identity) ? null : "presentation-unavailable");
109
+ if (reason !== null || !this.options.surface.present(this.#state.text))
110
+ return;
111
+ this.options.surface.requestRender();
112
+ }
103
113
  dispose() {
104
114
  this.#disposed = true;
105
115
  this.invalidate();
@@ -257,6 +257,7 @@ export declare class OwnedUiSessionShellRoot implements PiTuiComponentPort {
257
257
  setViewportOverlaySurfaces(surfaces: readonly PiTuiPointerSurface[] | null): void;
258
258
  viewportInputGeometryReady(width: number, height: number): boolean;
259
259
  handleViewportPreInput(data: string, allowWheel?: boolean, now?: number, editorActive?: boolean): SessionViewportInputResult;
260
+ dismissNotice(): void;
260
261
  layoutRoot(): PiTuiLayoutNode;
261
262
  transcriptComponent(id: string): PiShellTranscriptComponentPort | undefined;
262
263
  exitTranscript(width: number): string;
@@ -9,7 +9,7 @@ import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES } from
9
9
  import { createPiExtensionUiBridge } from "../../integrations/pi/components/shell-extension-ui.js";
10
10
  import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
11
11
  import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
12
- import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
12
+ import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
13
13
  import { createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellHotkeys, createPiShellSessionInfo, renderPiShellCommandMessage, renderPiShellPackageUpdateNotice, renderPiShellReleaseUpdateBanner, renderPiShellReleaseUpdateNotice, renderPiShellStatusText, } from "../../integrations/pi/components/shell-presenters-info.js";
14
14
  import { createPiShellTranscriptComponent, isPiPromptStyleCompaction, paintPiSubmittedPromptTimestamp, renderPiShellStartupDiagnostic, renderPiShellTranscriptBlock, } from "../../integrations/pi/components/shell-presenters-transcript.js";
15
15
  import { onPiThemeChange, PINNED_PI_LAYOUT, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
@@ -286,7 +286,7 @@ export class OwnedUiSessionShellRoot {
286
286
  reconcilePromptImageLimitNotice(text = this.editor.getText()) {
287
287
  if (this.#dockNotice?.errorCode !== "image-count" || !this.#promptChips.imageLimitState(text).corrected)
288
288
  return false;
289
- this.#dismissDockNotice();
289
+ this.dismissNotice();
290
290
  return true;
291
291
  }
292
292
  hasPendingPastes(text) { return this.#promptChips.hasPending(text); }
@@ -333,6 +333,10 @@ export class OwnedUiSessionShellRoot {
333
333
  }
334
334
  const promptAccepted = this.#view.lifecycle !== "busy" && view.lifecycle === "busy";
335
335
  this.#view = view;
336
+ // Invariant: the idle new-session confirmation yields to the first truthful live status;
337
+ // ordinary dock notices deliberately remain visible below Working during a run.
338
+ if (promptAccepted && this.#dockNotice?.kind === "new-session")
339
+ this.#dockNotice = undefined;
336
340
  if (promptAccepted && this.#imageSubmissionsSending > 0) {
337
341
  this.#imageSubmissionsSending = 0;
338
342
  this.#syncWorkingOverride();
@@ -375,7 +379,7 @@ export class OwnedUiSessionShellRoot {
375
379
  // Invariant: the notice answers the reader's last command, so only their next prompt or
376
380
  // shell command retires it; assistant, tool, and compaction blocks streaming in keep it.
377
381
  if (block.kind === "user" || block.kind === "bash")
378
- this.#dismissDockNotice();
382
+ this.dismissNotice();
379
383
  const created = createPiShellTranscriptComponent(block, this.#cwd, this.#extensionRenderers, this.#submittedPromptComposer, this.#outputPad, !this.#thinkingVisible, this.#mermaidRenderingMode, this.#showImages, this.#imageWidthCells, this.#imageAssets, { ...this.#componentRuntime, changed: () => {
380
384
  if (this.#transcript.get(block.id) !== created)
381
385
  return;
@@ -657,9 +661,12 @@ export class OwnedUiSessionShellRoot {
657
661
  // Compatibility: Pi pads its status text by one cell regardless of the output pad setting.
658
662
  return ["", ...renderPiShellStatusText(notice.message, width, PINNED_PI_LAYOUT.outputPad)];
659
663
  }
660
- return renderPiShellCommandMessage({ kind: notice.kind, message: notice.message }, width, this.#outputPad);
664
+ return renderPiShellCommandMessage({
665
+ kind: notice.kind === "new-session" ? "accent" : notice.kind,
666
+ message: notice.message,
667
+ }, width, this.#outputPad);
661
668
  }
662
- #dismissDockNotice() {
669
+ dismissNotice() {
663
670
  if (this.#dockNotice === undefined)
664
671
  return;
665
672
  this.#dockNotice = undefined;
@@ -960,6 +967,12 @@ export class OwnedUiSessionShellRoot {
960
967
  }
961
968
  if (result.outcome === "completed" && (result.command === "quit" || result.command === "compact"))
962
969
  return;
970
+ if (result.outcome === "completed" && result.command === "new" && this.#customViewport) {
971
+ // Rationale: bare A1 keeps the replacement session visually empty while preserving Pi's accent notice shape.
972
+ this.#dockNotice = { kind: "new-session", message: result.message };
973
+ this.#invalidateChrome();
974
+ return;
975
+ }
963
976
  if (result.outcome === "completed" && (result.command === "new" || result.command === "name" || result.command === "debug")) {
964
977
  this.#lastWorkflowStatusId = undefined;
965
978
  const presentation = { kind: result.command, message: result.message, ...(result.detail === undefined ? {} : { detail: result.detail }) };
@@ -1247,7 +1260,7 @@ export class OwnedUiSessionShellRoot {
1247
1260
  }
1248
1261
  #appendAnchoredWorkflowComponent(render, dispose) {
1249
1262
  // Invariant: transcript-bound workflow output supersedes a pending acknowledgement.
1250
- this.#dismissDockNotice();
1263
+ this.dismissNotice();
1251
1264
  this.#workflowTranscriptSequence += 1;
1252
1265
  const id = `workflow-status-${this.#workflowTranscriptSequence}`;
1253
1266
  const component = {
@@ -14,7 +14,7 @@ import { createPiExtensionUiBridge } from "../../integrations/pi/components/shel
14
14
  import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
15
15
  import { SKILLS_COMMAND_NAME, findSkillByArgument, rewriteSkillsTunnelSubmission, skillPrompt, skillsFromCommands, } from "../../integrations/pi/components/skills-command.js";
16
16
  import { createPiQueuedInputStatus, createPiShellFooter, createPiShellHeader, createPiShellLoadedResources, createPiShellStatus, } from "../../integrations/pi/components/shell-footer-status.js";
17
- import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellSkillsSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
17
+ import { createPiShellArmin, createPiShellAuthProviderSelector, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellExtensionSelector, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellSkillsSelector, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, } from "../../integrations/pi/components/shell-selectors-dialogs.js";
18
18
  import { createPiShellChangelog, createPiShellHotkeys, createPiShellSessionInfo, renderPiShellPackageUpdateNotice, renderPiShellStatusText, } from "../../integrations/pi/components/shell-presenters-info.js";
19
19
  import { createPiShellTranscriptComponent, renderPiShellStartupDiagnostic, renderPiShellTranscriptBlock, } from "../../integrations/pi/components/shell-presenters-transcript.js";
20
20
  import { onPiThemeChange, piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
@@ -188,6 +188,11 @@ export class OwnedUiSessionShell {
188
188
  onEditorChange: text => {
189
189
  this.#editorRevision++;
190
190
  promptSuggestionController?.abortPending();
191
+ if (text.length === 0) {
192
+ // Concurrency: the editor synchronizes deletion-owned autocomplete after onChange.
193
+ // Reassert retained ghost text in the microtask before Pi emits its scheduled frame.
194
+ queueMicrotask(() => promptSuggestionController?.restoreAvailable());
195
+ }
191
196
  this.root.reconcilePromptImageLimitNotice(text);
192
197
  },
193
198
  onPromptSuggestionAccepted: () => promptSuggestionController?.accept(),
@@ -358,12 +363,20 @@ export class OwnedUiSessionShell {
358
363
  ? this.runtime.addPreInputListener(data => {
359
364
  if (inputPresentation?.coordination === false)
360
365
  this.#streamPresentation.noteImmediatePresentation();
361
- // Compatibility: Pi's fullscreen renderer also intercepts plain Home/End.
362
- // Deliver them to the focused owned input before that outer scroll handler;
363
- // overlays retain Pi's normal dispatch, and comparison profiles never enter here.
366
+ // Compatibility: route modal paging and boundary keys before Pi's fullscreen handlers.
367
+ const isDefault = this.root.usesDefaultInputSurface();
368
+ if (!this.runtime.hasOverlay() && !isDefault
369
+ && (this.root.editor.matchesTerminalKey(data, "pageUp")
370
+ || this.root.editor.matchesTerminalKey(data, "pageDown"))) {
371
+ this.root.handleInput(data);
372
+ this.runtime.requestRender();
373
+ return { consume: true };
374
+ }
364
375
  if (!this.runtime.hasOverlay() && (this.root.editor.matchesTerminalKey(data, "home")
365
- || this.root.editor.matchesTerminalKey(data, "end"))) {
366
- if (this.root.usesDefaultInputSurface())
376
+ || this.root.editor.matchesTerminalKey(data, "end")
377
+ || !isDefault && (this.root.editor.matchesTerminalKey(data, "ctrl+home")
378
+ || this.root.editor.matchesTerminalKey(data, "ctrl+end")))) {
379
+ if (isDefault)
367
380
  this.root.handleViewportPreInput(data, true);
368
381
  this.root.handleInput(data);
369
382
  this.runtime.requestRender();
@@ -1064,12 +1077,14 @@ export class OwnedUiSessionShell {
1064
1077
  },
1065
1078
  onCancel: close,
1066
1079
  onSelect: entryId => {
1067
- close();
1068
1080
  if (entryId === context.currentLeafId) {
1081
+ close();
1069
1082
  this.root.appendWorkflowStatus("Already at this point");
1070
1083
  this.runtime.requestRender();
1071
1084
  return;
1072
1085
  }
1086
+ if (context.skipSummaryPrompt)
1087
+ close();
1073
1088
  void this.#completeTreeSelection(entryId, context.skipSummaryPrompt);
1074
1089
  },
1075
1090
  });
@@ -1205,6 +1220,19 @@ export class OwnedUiSessionShell {
1205
1220
  async runWorkflow(request) {
1206
1221
  if (request.command === "quit")
1207
1222
  return this.shutdown();
1223
+ if (this.#customViewport && request.command === "name" && request.argument.trim().length === 0) {
1224
+ void this.#extensionBridge.input("Session Name", "Enter name").then(value => {
1225
+ if (this.#disposed || value === undefined || value.trim().length === 0)
1226
+ return;
1227
+ return this.runWorkflow({ ...request, argument: value });
1228
+ }).catch(error => {
1229
+ if (this.#disposed)
1230
+ return;
1231
+ this.root.appendWorkflowResult({ command: "name", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
1232
+ this.runtime.requestRender();
1233
+ });
1234
+ return { outcome: "completed", diagnostic: null };
1235
+ }
1208
1236
  const copyGeneration = request.command === "copy" ? this.backend.sessionBindingGeneration : undefined;
1209
1237
  if (request.command === "login" && request.selection !== undefined) {
1210
1238
  const setup = this.backend.pinnedAmbientAuthentication(request.selection);
@@ -1280,13 +1308,15 @@ export class OwnedUiSessionShell {
1280
1308
  }
1281
1309
  this.root.resetExtensionUi();
1282
1310
  }
1283
- const shareSurface = request.command === "share"
1284
- ? createPiShellOperationLoader({
1311
+ const shareDialog = request.command === "share"
1312
+ ? await import("./share-operation.js")
1313
+ : undefined;
1314
+ const shareSurface = shareDialog === undefined ? undefined
1315
+ : (this.#customViewport ? shareDialog.createPiShellShareOperationDialog : shareDialog.createPiShellOperationLoader)({
1285
1316
  getColumns: () => this.runtime.viewport().columns,
1286
1317
  getRows: () => this.runtime.viewport().rows,
1287
1318
  requestRender: () => this.runtime.requestRender(),
1288
- }, "Creating gist...")
1289
- : undefined;
1319
+ }, "Creating gist…");
1290
1320
  const operationSurface = shareSurface ?? (request.command === "reload" ? createPiShellReloadBox() : undefined);
1291
1321
  const now = this.#reloadPresentation?.now ?? Date.now;
1292
1322
  const shownAt = now();
@@ -1342,7 +1372,17 @@ export class OwnedUiSessionShell {
1342
1372
  this.root.editor.reloadKeybindings();
1343
1373
  this.#installAutocompleteCommands();
1344
1374
  }
1345
- this.root.appendWorkflowResult(result);
1375
+ if (request.command === "tree" && result.outcome === "completed") {
1376
+ if (result.detail && !this.root.editor.getText().trim())
1377
+ this.root.editor.setText(result.detail);
1378
+ this.root.resumeViewportFollowing();
1379
+ }
1380
+ if (shareDialog !== undefined && this.#customViewport && result.outcome === "completed" && result.detail !== undefined) {
1381
+ this.root.appendWorkflowStatus(shareDialog.renderPiShellShareResult(result.message, result.detail));
1382
+ }
1383
+ else {
1384
+ this.root.appendWorkflowResult(result);
1385
+ }
1346
1386
  this.runtime.requestRender();
1347
1387
  return workflowAdapterResult(result);
1348
1388
  }
@@ -1768,6 +1808,7 @@ export class OwnedUiSessionShell {
1768
1808
  return { outcome: "failed", diagnostic: `route is unavailable: ${route}` };
1769
1809
  if (!this.runtime.active)
1770
1810
  return { outcome: "failed", diagnostic: "runtime is not active" };
1811
+ this.root.dismissNotice();
1771
1812
  this.#dialogHandle?.hide();
1772
1813
  this.#dialogSource = "route";
1773
1814
  // Protocol: any-event reporting: hover and drag are what the screen is driven by, and
@@ -1780,6 +1821,19 @@ export class OwnedUiSessionShell {
1780
1821
  const removeInterruptWatch = this.runtime.addInputListener(data => {
1781
1822
  if (!data.includes(INTERRUPT))
1782
1823
  return undefined;
1824
+ // Protocol: route hosts decide whether one interrupt closes their app. Raw-input forwarding
1825
+ // is required because Pi handles Ctrl+C before the fullscreen overlay receives normal input.
1826
+ const consumedBySurface = surface.handleInput(INTERRUPT);
1827
+ if (surface.isClosed()) {
1828
+ armedAt = 0;
1829
+ closeSurface();
1830
+ return { consume: true };
1831
+ }
1832
+ if (consumedBySurface) {
1833
+ armedAt = 0;
1834
+ this.runtime.requestRender();
1835
+ return { consume: true };
1836
+ }
1783
1837
  const now = Date.now();
1784
1838
  if (armedAt !== 0 && now - armedAt <= INTERRUPT_CHORD_MS) {
1785
1839
  armedAt = 0;
@@ -1968,7 +2022,7 @@ export class OwnedUiSessionShell {
1968
2022
  }
1969
2023
  return this.#submitSkillPrompt(skillPrompt(skill.name, separator < 0 ? "" : trimmed.slice(separator + 1)), text);
1970
2024
  }
1971
- // Rationale: the engine expands "/skill:<name> args" itself; history keeps the form the user typed so recall restores the invocation.
2025
+ // Rationale: engine expands skill arguments; history retains typed input for recall.
1972
2026
  #submitSkillPrompt(prompt, typed) {
1973
2027
  this.#rememberInput(typed, "slash");
1974
2028
  this.root.resumeViewportFollowing();
@@ -1979,29 +2033,33 @@ export class OwnedUiSessionShell {
1979
2033
  text: prompt,
1980
2034
  });
1981
2035
  }
2036
+ #treeSummary() {
2037
+ return new Promise(resolve => {
2038
+ this.root.setInputSurface(createPiShellExtensionSelector("Summarize Branch?", ["No summary", "Summarize", "Summarize with custom prompt"], resolve, () => resolve(undefined)), true, "opaque");
2039
+ this.runtime.requestRender();
2040
+ });
2041
+ }
1982
2042
  async #completeTreeSelection(entryId, skipSummaryPrompt) {
1983
2043
  let summarize = false;
1984
2044
  let customInstructions;
1985
2045
  if (!skipSummaryPrompt) {
1986
2046
  while (true) {
1987
- const choice = await this.#extensionBridge.context.select("Summarize branch?", [
1988
- "No summary",
1989
- "Summarize",
1990
- "Summarize with custom prompt",
1991
- ]);
2047
+ const choice = await this.#treeSummary();
1992
2048
  if (choice === undefined) {
1993
2049
  await this.showTreeSelector(entryId);
1994
2050
  return;
1995
2051
  }
1996
2052
  summarize = choice !== "No summary";
1997
2053
  if (choice === "Summarize with custom prompt") {
1998
- customInstructions = await this.#extensionBridge.context.editor("Custom summarization instructions", "");
2054
+ customInstructions = await this.#extensionBridge.input("Custom Summarization Instructions", "", { retainSurfaceOnSettle: true });
1999
2055
  if (customInstructions === undefined)
2000
2056
  continue;
2001
2057
  }
2002
2058
  break;
2003
2059
  }
2004
2060
  }
2061
+ this.root.setInputSurface(null);
2062
+ this.runtime.requestRender();
2005
2063
  const result = await this.runWorkflow({
2006
2064
  command: "tree",
2007
2065
  argument: "",
@@ -0,0 +1,3 @@
1
+ import { createPiShellOperationLoader, createPiShellShareOperationDialog } from "../../integrations/pi/components/index.js";
2
+ export { createPiShellOperationLoader, createPiShellShareOperationDialog };
3
+ export declare function renderPiShellShareResult(message: string, gistUrl: string): string;
@@ -0,0 +1,11 @@
1
+ import { createPiShellOperationLoader, createPiShellShareOperationDialog, piShellHyperlink, piTheme, } from "../../integrations/pi/components/index.js";
2
+ import { nativeHyperlinkStyle } from "../../ui/components/index.js";
3
+ export { createPiShellOperationLoader, createPiShellShareOperationDialog };
4
+ export function renderPiShellShareResult(message, gistUrl) {
5
+ const theme = piTheme();
6
+ const match = /^Share URL: (\S+)$/u.exec(message);
7
+ const linked = match === null
8
+ ? theme.fg("dim", message)
9
+ : `${theme.fg("dim", "Share URL: ")}${piShellHyperlink(match[1])}`;
10
+ return nativeHyperlinkStyle(`${linked}\n${theme.fg("dim", "Gist: ")}${piShellHyperlink(gistUrl)}`, text => theme.fg("mdLink", text));
11
+ }
@@ -82,6 +82,7 @@ export async function composeOwnedUi(options = {}) {
82
82
  const { renderPiShellHotkeySections } = await import("../integrations/pi/components/shell-hotkey-sections.js");
83
83
  return { sections: width => renderPiShellHotkeySections(presentation, width) };
84
84
  },
85
+ session: () => import("./session-info-reference.js").then(m => m.loadSessionInfoReference(adapter)),
85
86
  };
86
87
  const routeHost = settings === null || !ownedSurfaces ? null : createOwnedRouteHost(settings, references);
87
88
  const viewportSettings = settings === null || !ownedSurfaces ? null : {
@@ -0,0 +1,4 @@
1
+ import type { ReferenceDocumentProvider } from "../features/owned-ui/reference-screen-app.js";
2
+ import type { PiEngineAdapter } from "../integrations/pi/engine/adapter.js";
3
+ /** Captures one current session snapshot and adapts it to the neutral reference document. */
4
+ export declare function loadSessionInfoReference(adapter: PiEngineAdapter): Promise<ReferenceDocumentProvider>;
@@ -0,0 +1,20 @@
1
+ import { renderPiShellSessionInfoReferenceDocument } from "../integrations/pi/components/shell-session-info-reference.js";
2
+ /** Captures one current session snapshot and adapts it to the neutral reference document. */
3
+ export async function loadSessionInfoReference(adapter) {
4
+ const result = await adapter.executeWorkflow({ command: "session", argument: "" });
5
+ if (result.outcome !== "completed")
6
+ throw new Error(result.message);
7
+ if (result.presentation?.kind !== "session-info")
8
+ throw new Error("Session workflow returned no structured information");
9
+ const presentation = result.presentation;
10
+ let cached = null;
11
+ const document = (width) => {
12
+ if (cached?.width !== width)
13
+ cached = { width, document: renderPiShellSessionInfoReferenceDocument(presentation, width) };
14
+ return cached.document;
15
+ };
16
+ return {
17
+ preamble: width => document(width).preamble,
18
+ sections: width => document(width).sections,
19
+ };
20
+ }
@@ -7,10 +7,7 @@ export type OwnedReferenceRows = NonNullable<ReferenceDocumentProvider["rows"]>;
7
7
  export interface OwnedReferenceProviders {
8
8
  changelog(input?: UiRouteInput): Promise<ReferenceDocumentProvider>;
9
9
  hotkeys(): Promise<ReferenceDocumentProvider>;
10
+ session(): Promise<ReferenceDocumentProvider>;
10
11
  }
11
- /**
12
- * Declares the A1-owned settings route, and the changelog and hotkeys reference
13
- * routes when their documents are supplied, without evaluating their presentation
14
- * graphs during startup. Opening a route retains input while the optional module loads.
15
- */
12
+ /** Declares settings and lazy document routes. */
16
13
  export declare function createOwnedRouteHost(settings: OwnedSettingsManager, references?: OwnedReferenceProviders): UiRouteHost;
@@ -1,14 +1,10 @@
1
- import { CHANGELOG_APP_ID, CHANGELOG_ROUTE, CHANGELOG_TITLE, HOTKEYS_APP_ID, HOTKEYS_ROUTE, HOTKEYS_TITLE, } from "../features/owned-ui/reference-routes.js";
1
+ import { CHANGELOG_ROUTE, CHANGELOG_TITLE, HOTKEYS_ROUTE, HOTKEYS_TITLE, } from "../features/owned-ui/reference-routes.js";
2
2
  import { SETTINGS_APP_ID, SETTINGS_ROUTE } from "../features/owned-ui/settings-route.js";
3
3
  import { piTheme } from "../integrations/pi/components/upstream/theme/theme.js";
4
4
  import { faint } from "../ui/components/text.js";
5
- /**
6
- * Declares the A1-owned settings route, and the changelog and hotkeys reference
7
- * routes when their documents are supplied, without evaluating their presentation
8
- * graphs during startup. Opening a route retains input while the optional module loads.
9
- */
5
+ /** Declares settings and lazy document routes. */
10
6
  export function createOwnedRouteHost(settings, references) {
11
- const routes = new Set([SETTINGS_ROUTE, ...(references === undefined ? [] : [CHANGELOG_ROUTE, HOTKEYS_ROUTE])]);
7
+ const routes = new Set([SETTINGS_ROUTE, ...(references === undefined ? [] : [CHANGELOG_ROUTE, HOTKEYS_ROUTE, "session"])]);
12
8
  return {
13
9
  claims: route => routes.has(route),
14
10
  open: (route, input) => {
@@ -18,10 +14,10 @@ export function createOwnedRouteHost(settings, references) {
18
14
  return deferredSurface(SETTINGS_APP_ID, "settings", () => loadSettingsSurface(settings));
19
15
  }
20
16
  const changelog = route === CHANGELOG_ROUTE;
21
- const id = changelog ? CHANGELOG_APP_ID : HOTKEYS_APP_ID;
22
- const title = changelog ? CHANGELOG_TITLE : HOTKEYS_TITLE;
23
- const document = changelog ? references.changelog(input) : references.hotkeys();
24
- return deferredSurface(id, title, () => loadReferenceSurface(settings, id, route, title, document));
17
+ const session = route === "session";
18
+ const title = session ? "Session Info" : changelog ? CHANGELOG_TITLE : HOTKEYS_TITLE;
19
+ const document = session ? references.session() : changelog ? references.changelog(input) : references.hotkeys();
20
+ return deferredSurface(route, title, () => loadReferenceSurface(settings, route, route, title, document));
25
21
  },
26
22
  };
27
23
  }
@@ -135,7 +131,7 @@ function pinnedTheme() {
135
131
  bold: (text) => piTheme().bold(text),
136
132
  plain: (text) => text,
137
133
  disabled: (text) => faint(piTheme().fg("dim", text)),
138
- highlight: (text) => `\u001b[48;2;82;82;82m\u001b[97m${text}\u001b[39m\u001b[49m`,
134
+ highlight: (text) => piTheme().bg("selectedBg", text),
139
135
  panel: (text) => `\u001b[48;2;55;55;55m${text}\u001b[49m`,
140
136
  };
141
137
  }
@@ -4,6 +4,11 @@ export interface SemanticShortcutHint {
4
4
  readonly action: string;
5
5
  readonly actionFirst?: boolean;
6
6
  }
7
+ /** Canonical dismissal guidance for every A1-rendered dialog surface. */
8
+ export declare const DIALOG_CLOSE_SHORTCUT_HINT: Readonly<{
9
+ key: string;
10
+ action: string;
11
+ }>;
7
12
  export interface ShortcutHintRoles {
8
13
  key(label: string): string;
9
14
  action(name: string): string;
@@ -1,3 +1,5 @@
1
+ /** Canonical dismissal guidance for every A1-rendered dialog surface. */
2
+ export const DIALOG_CLOSE_SHORTCUT_HINT = Object.freeze({ key: "esc", action: "close" });
1
3
  const SHORTCUT_KEY_NAMES = Object.freeze({
2
4
  alt: "Alt", backspace: "Backspace", cmd: "Cmd", ctrl: "Ctrl", delete: "Delete", down: "Down", end: "End",
3
5
  enter: "Enter", esc: "Esc", escape: "Escape", home: "Home", insert: "Insert", left: "Left", meta: "Meta",
@@ -1,7 +1,8 @@
1
- const ACCENT = "\u001b[38;2;138;190;183m";
2
- const BORDER = "\u001b[38;2;95;135;255m";
3
- const MUTED = "\u001b[38;2;128;128;128m";
4
- const DIM = "\u001b[38;2;102;102;102m";
1
+ import { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel } from "../../contracts/presentation/index.js";
2
+ const ACCENT = "\u001b[38;2;167;152;215m";
3
+ const BORDER = "\u001b[38;2;97;133;204m";
4
+ const MUTED = "\u001b[38;2;157;165;169m";
5
+ const DIM = "\u001b[38;2;126;136;142m";
5
6
  const RESET_FG = "\u001b[39m";
6
7
  const BOLD = "\u001b[1m";
7
8
  const RESET_BOLD = "\u001b[22m";
@@ -13,7 +14,8 @@ export function renderProjectTrustDialog(cwd, options, selected, terminalWidth,
13
14
  const title = ` ${BOLD}${ACCENT}Trust project folder?${RESET_FG}${RESET_BOLD}`;
14
15
  const path = ` ${MUTED}${sanitize(cwd)}${RESET_FG}`;
15
16
  const choices = options.map((label, index) => choice(label, selected === index));
16
- const hint = ` ${DIM}↑/↓${MUTED} to navigate ${DIM}Enter${MUTED} to select ${DIM}Esc${MUTED} to exit${RESET_FG}`;
17
+ const closeKey = displayShortcutKeyLabel(DIALOG_CLOSE_SHORTCUT_HINT.key);
18
+ const hint = ` ${DIM}↑↓${MUTED} navigate ${DIM}Enter${MUTED} select ${DIM}${closeKey}${MUTED} ${DIALOG_CLOSE_SHORTCUT_HINT.action}${RESET_FG}`;
17
19
  const explanation = wrap(EXPLANATION, Math.max(1, width - 2)).map(line => ` ${line}`);
18
20
  const preferred = [rule, title, path, "", ...explanation, "", ...choices, "", hint, rule];
19
21
  if (preferred.length <= terminalRows)
@@ -11,6 +11,8 @@ export interface ReferenceDocumentSection {
11
11
  export interface ReferenceDocumentProvider {
12
12
  /** Flat rows for this width, or null while the document is not available yet. */
13
13
  readonly rows?: (width: number) => readonly string[] | null;
14
+ /** Rows between the screen title and structured groups, absent for group-only documents. */
15
+ readonly preamble?: (width: number) => readonly string[] | null;
14
16
  /** Structured groups opt into shared Settings-style headers and pinning. */
15
17
  readonly sections?: (width: number) => readonly ReferenceDocumentSection[] | null;
16
18
  /** Settles once the selected content provider answers; absent when available at once. */
@@ -27,8 +29,8 @@ export interface ReferenceScreenOptions {
27
29
  * Presents one read-only document full screen between two accent rules: the bold
28
30
  * title leads the scrolled rows, the shared scrollbar rail runs beside them, and
29
31
  * the hint line closes the frame. Rows come from a provider per content width and
30
- * are cached until the width changes; the screen closes on Escape and leaves the
31
- * interrupt chord to its host.
32
+ * are cached until the width changes; Escape closes locally, while an opted-in
33
+ * host closes on Ctrl+C before dispatching it into the screen.
32
34
  */
33
35
  export declare class ReferenceScreenApp implements UiApp {
34
36
  #private;
@@ -1,4 +1,5 @@
1
- import { GLOBAL_SCOPE, PLAIN_THEME, RAIL_COLUMNS, ShortcutRegistry, ScrollbarRails, assertNoShortcutConflicts, displayWidth, layoutList, padToWidth, renderGroupHeader, renderShortcutHints, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, scrollbarWheelRows, truncateToWidth, withScrollbarRail, } from "../../ui/components/index.js";
1
+ import { DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
2
+ import { GLOBAL_SCOPE, PLAIN_THEME, RAIL_COLUMNS, ShortcutRegistry, ScrollbarRails, assertNoShortcutConflicts, displayWidth, layoutList, padToWidth, renderGroupHeader, renderShortcutHintsWithClose, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, scrollbarWheelRows, truncateToWidth, withScrollbarRail, } from "../../ui/components/index.js";
2
3
  const SCOPE = "reference-screen";
3
4
  /** Rows above the document: the top rule. */
4
5
  const HEADER_ROWS = 1;
@@ -9,14 +10,17 @@ const SCROLL_LINGER_MS = 900;
9
10
  const SCROLL_LINGER_REPAINT_MS = 925;
10
11
  const LOADING_NOTICE = "Loading…";
11
12
  export const REFERENCE_SCREEN_SHORTCUTS = new ShortcutRegistry();
12
- // Compatibility: Ctrl+C remains the A1 interrupt chord here rather than a close key.
13
- REFERENCE_SCREEN_SHORTCUTS.declare({ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen", hint: { keys: "esc", does: "close" } }, "close");
14
13
  REFERENCE_SCREEN_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Scroll up", section: "Navigate", hint: { keys: "↑↓", does: "scroll" } }, "up");
15
14
  REFERENCE_SCREEN_SHORTCUTS.declare({ key: "down", scope: SCOPE, description: "Scroll down", section: "Navigate", hint: { keys: "↑↓", does: "scroll" } }, "down");
16
15
  REFERENCE_SCREEN_SHORTCUTS.declare({ key: "pageUp", scope: SCOPE, description: "Up a page", section: "Navigate" }, "page-up");
17
16
  REFERENCE_SCREEN_SHORTCUTS.declare({ key: "pageDown", scope: SCOPE, description: "Down a page", section: "Navigate" }, "page-down");
18
17
  REFERENCE_SCREEN_SHORTCUTS.declare({ key: "home", scope: SCOPE, description: "First row", section: "Navigate" }, "first");
19
18
  REFERENCE_SCREEN_SHORTCUTS.declare({ key: "end", scope: SCOPE, description: "Last row", section: "Navigate" }, "last");
19
+ // Compatibility: Ctrl+C remains the A1 interrupt chord here rather than a visible close key.
20
+ REFERENCE_SCREEN_SHORTCUTS.declare({
21
+ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen",
22
+ hint: { keys: DIALOG_CLOSE_SHORTCUT_HINT.key, does: DIALOG_CLOSE_SHORTCUT_HINT.action },
23
+ }, "close");
20
24
  assertNoShortcutConflicts(REFERENCE_SCREEN_SHORTCUTS.assemble());
21
25
  const KEYS = {
22
26
  "\u001b[A": "up",
@@ -36,8 +40,8 @@ const KEYS = {
36
40
  * Presents one read-only document full screen between two accent rules: the bold
37
41
  * title leads the scrolled rows, the shared scrollbar rail runs beside them, and
38
42
  * the hint line closes the frame. Rows come from a provider per content width and
39
- * are cached until the width changes; the screen closes on Escape and leaves the
40
- * interrupt chord to its host.
43
+ * are cached until the width changes; Escape closes locally, while an opted-in
44
+ * host closes on Ctrl+C before dispatching it into the screen.
41
45
  */
42
46
  export class ReferenceScreenApp {
43
47
  id;
@@ -135,7 +139,7 @@ export class ReferenceScreenApp {
135
139
  const withRail = withScrollbarRail(body.slice(0, bodyHeight), geometry, contentWidth, theme, { presentation });
136
140
  const hint = this.#interruptArmed
137
141
  ? theme.fg("dim", " press ctrl+c again to exit a1")
138
- : renderShortcutHints(REFERENCE_SCREEN_SHORTCUTS.hintEntries(SCOPE), theme, 1);
142
+ : renderShortcutHintsWithClose(REFERENCE_SCREEN_SHORTCUTS.hintEntries(SCOPE), theme, rect.width, 1);
139
143
  // Compatibility: the v2 reference screen frames its document between two border-coloured rules.
140
144
  const rule = theme.fg("border", "─".repeat(rect.width));
141
145
  // Invariant: dialog chrome shares the title's one-cell inset; document content keeps its own layout.
@@ -222,10 +226,16 @@ export class ReferenceScreenApp {
222
226
  if (cached !== null && cached.width === width)
223
227
  return this.#withTitle(cached.content, title);
224
228
  const sections = this.#document.sections?.(width);
225
- if (sections === null)
229
+ const preamble = sections === undefined ? undefined : this.#document.preamble?.(width);
230
+ if (sections === null || preamble === null)
226
231
  return { kind: "flat", rows: [title, "", LOADING_NOTICE] };
227
232
  if (sections !== undefined) {
228
233
  const rows = [];
234
+ if (preamble !== undefined && preamble.length > 0) {
235
+ for (const row of preamble)
236
+ rows.push({ kind: "note", group: "", text: row });
237
+ rows.push({ kind: "spacer" });
238
+ }
229
239
  sections.forEach((section, sectionIndex) => {
230
240
  if (sectionIndex > 0)
231
241
  rows.push({ kind: "spacer" });
@@ -1,4 +1,5 @@
1
- import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, dialogValueColumn, numericValues, RAIL_COLUMNS, renderDialogPanel, renderEmptyState, renderGroupHeader, renderInputRow, renderListRow, renderNote, renderShortcutHints, dialogRowAt, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, isThumbRow, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
1
+ import { DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
2
+ import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, dialogValueColumn, numericValues, RAIL_COLUMNS, renderDialogPanel, renderEmptyState, renderGroupHeader, renderInputRow, renderListRow, renderNote, renderShortcutHintsWithClose, dialogRowAt, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, isThumbRow, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
2
3
  import { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
3
4
  export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
4
5
  const SCOPE = SETTINGS_APP_ID;
@@ -35,13 +36,16 @@ SETTINGS_SHORTCUTS.declare({ key: "enter", scope: SCOPE, description: "Change va
35
36
  SETTINGS_SHORTCUTS.declare({ key: "space", scope: SCOPE, description: "Change value", section: "Change", hint: { keys: "Enter/Space", does: "to change" } }, "activate");
36
37
  SETTINGS_SHORTCUTS.declare({ key: "left", scope: SCOPE, description: "Previous value", section: "Change", hint: { keys: "←→", does: "to adjust" } }, "previous-value");
37
38
  SETTINGS_SHORTCUTS.declare({ key: "right", scope: SCOPE, description: "Next value", section: "Change", hint: { keys: "←→", does: "to adjust" } }, "next-value");
38
- SETTINGS_SHORTCUTS.declare({ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen", hint: { keys: "Esc", does: "to cancel" } }, "close");
39
39
  SETTINGS_SHORTCUTS.declare({ key: "enter", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts", hint: { keys: "Enter/Space", does: "to change" } }, "part-change");
40
40
  SETTINGS_SHORTCUTS.declare({ key: "space", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts", hint: { keys: "Enter/Space", does: "to change" } }, "part-change");
41
41
  SETTINGS_SHORTCUTS.declare({ key: "left", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts" }, "part-change");
42
42
  SETTINGS_SHORTCUTS.declare({ key: "right", scope: DIALOG_SCOPE, description: "Change this part", section: "Parts" }, "part-change");
43
43
  SETTINGS_SHORTCUTS.declare({ key: "up", scope: DIALOG_SCOPE, description: "Previous part", section: "Parts" }, "part-previous");
44
44
  SETTINGS_SHORTCUTS.declare({ key: "down", scope: DIALOG_SCOPE, description: "Next part", section: "Parts" }, "part-next");
45
+ SETTINGS_SHORTCUTS.declare({
46
+ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen",
47
+ hint: { keys: DIALOG_CLOSE_SHORTCUT_HINT.key, does: DIALOG_CLOSE_SHORTCUT_HINT.action },
48
+ }, "close");
45
49
  assertNoShortcutConflicts(SETTINGS_SHORTCUTS.assemble());
46
50
  const KEYS = {
47
51
  "\u001b[A": "up",
@@ -451,8 +455,9 @@ export class SettingsApp {
451
455
  return;
452
456
  }
453
457
  const current = shown === null ? 0 : Math.max(0, entry.choices.indexOf(shown));
454
- // Compatibility: pinned SelectList opens on the value currently in effect.
455
- this.#menu = { entry, current, anchorKey: `${entry.backend}:${entry.id}`, choices: entry.choices, index: current };
458
+ // Invariant: the opening press targeted the setting value, not an option. Keep the
459
+ // effective value as the keyboard origin without painting it as pointer-picked.
460
+ this.#menu = { entry, current, anchorKey: `${entry.backend}:${entry.id}`, choices: entry.choices, index: -1 };
456
461
  }
457
462
  // Rationale: a structured setting opens as its own flag list rather than a value menu.
458
463
  #openStructured(entry) {
@@ -797,7 +802,7 @@ export class SettingsApp {
797
802
  ? notice
798
803
  : this.#interruptArmed ? "press ctrl+c again to exit a1" : null;
799
804
  const status = plainStatus === null
800
- ? truncateToWidth(renderShortcutHints(SETTINGS_SHORTCUTS.hintEntries(SCOPE), theme, width > 0 ? 1 : 0), width)
805
+ ? renderShortcutHintsWithClose(SETTINGS_SHORTCUTS.hintEntries(SCOPE), theme, width, width > 0 ? 1 : 0)
801
806
  : truncateToWidth(`${width > 0 ? " " : ""}${theme.fg("dim", plainStatus)}`, width);
802
807
  const input = this.#filter;
803
808
  if (input === null)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generated by scripts/pi/sync-progress-palette.ts from @earendil-works/pi-coding-agent 1.0.2
3
- * (MIT), commit cd32f7725fdbddbaecdff5b1e68491563394e0ca. Do not edit directly.
2
+ * Generated by scripts/pi/sync-progress-palette.ts from @earendil-works/pi-coding-agent 1.0.4
3
+ * (MIT), commit 7c10bd4337495ee613f2224843ecdf349b80d1df. Do not edit directly.
4
4
  */
5
5
  export declare const SHARED_PROGRESS_ACCENT_ANSI = "\u001B[38;2;167;152;215m";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generated by scripts/pi/sync-progress-palette.ts from @earendil-works/pi-coding-agent 1.0.2
3
- * (MIT), commit cd32f7725fdbddbaecdff5b1e68491563394e0ca. Do not edit directly.
2
+ * Generated by scripts/pi/sync-progress-palette.ts from @earendil-works/pi-coding-agent 1.0.4
3
+ * (MIT), commit 7c10bd4337495ee613f2224843ecdf349b80d1df. Do not edit directly.
4
4
  */
5
5
  export const SHARED_PROGRESS_ACCENT_ANSI = "\u001b[38;2;167;152;215m";