@timurproko/a1 0.2.5-dev.683 → 0.2.5-dev.704

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 (76) 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.js +15 -2
  4. package/dist/app/session-shell/session-shell.js +80 -23
  5. package/dist/app/session-shell/share-operation.d.ts +3 -0
  6. package/dist/app/session-shell/share-operation.js +11 -0
  7. package/dist/composition/owned-ui.js +2 -0
  8. package/dist/composition/settings-route-host.js +1 -1
  9. package/dist/contracts/presentation/index.d.ts +11 -1
  10. package/dist/contracts/presentation/index.js +6 -2
  11. package/dist/features/owned-ui/project-trust-dialog.js +7 -5
  12. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -2
  13. package/dist/features/owned-ui/reference-screen-app.js +10 -6
  14. package/dist/features/owned-ui/settings-app.d.ts +1 -1
  15. package/dist/features/owned-ui/settings-app.js +342 -66
  16. package/dist/integrations/pi/components/index.d.ts +3 -2
  17. package/dist/integrations/pi/components/index.js +2 -1
  18. package/dist/integrations/pi/components/modal-frame.d.ts +11 -1
  19. package/dist/integrations/pi/components/modal-frame.js +29 -1
  20. package/dist/integrations/pi/components/models-dialog.js +19 -19
  21. package/dist/integrations/pi/components/share-operation-dialog.d.ts +6 -0
  22. package/dist/integrations/pi/components/share-operation-dialog.js +40 -0
  23. package/dist/integrations/pi/components/shell-editor-autocomplete.js +55 -8
  24. package/dist/integrations/pi/components/shell-extension-ui.d.ts +3 -0
  25. package/dist/integrations/pi/components/shell-extension-ui.js +8 -4
  26. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +0 -4
  27. package/dist/integrations/pi/components/shell-selectors-dialogs.js +56 -34
  28. package/dist/integrations/pi/components/shell-session-info-reference.js +35 -2
  29. package/dist/integrations/pi/components/shell-shared-facade.d.ts +3 -1
  30. package/dist/integrations/pi/components/shell-shared-facade.js +13 -1
  31. package/dist/integrations/pi/components/skills-dialog.js +21 -8
  32. package/dist/integrations/pi/components/theme.d.ts +8 -1
  33. package/dist/integrations/pi/components/theme.js +28 -1
  34. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +10 -2
  35. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +20 -6
  36. package/dist/integrations/pi/components/upstream/components/extension-editor.js +2 -2
  37. package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -5
  38. package/dist/integrations/pi/components/upstream/components/extension-selector.js +2 -3
  39. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +4 -2
  40. package/dist/integrations/pi/components/upstream/components/owned-editor.js +48 -12
  41. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -2
  42. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +4 -15
  43. package/dist/integrations/pi/components/upstream/components/session-selector.d.ts +2 -0
  44. package/dist/integrations/pi/components/upstream/components/session-selector.js +137 -110
  45. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +9 -5
  46. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +65 -26
  47. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +31 -21
  48. package/dist/integrations/pi/components/upstream/components/tree-selector.js +255 -214
  49. package/dist/integrations/pi/components/upstream/components/trust-selector.js +13 -15
  50. package/dist/integrations/pi/engine/adapter.js +21 -2
  51. package/dist/integrations/pi/engine/workflow-runner.js +2 -1
  52. package/dist/native/darwin-arm64/manifest.json +1 -1
  53. package/dist/native/linux-x64/manifest.json +1 -1
  54. package/dist/native/win32-x64/manifest.json +2 -2
  55. package/dist/native/win32-x64/process-guardian.exe +0 -0
  56. package/dist/ui/apps/host.d.ts +4 -3
  57. package/dist/ui/apps/host.js +9 -4
  58. package/dist/ui/components/dialog-panel.d.ts +15 -0
  59. package/dist/ui/components/dialog-panel.js +55 -9
  60. package/dist/ui/components/index.d.ts +3 -3
  61. package/dist/ui/components/index.js +2 -2
  62. package/dist/ui/components/line-input.d.ts +2 -0
  63. package/dist/ui/components/line-input.js +2 -2
  64. package/dist/ui/components/list-view.d.ts +3 -3
  65. package/dist/ui/components/list-view.js +8 -9
  66. package/dist/ui/components/prompt-input.d.ts +2 -2
  67. package/dist/ui/components/prompt-input.js +5 -5
  68. package/dist/ui/components/shortcut-hints.d.ts +10 -3
  69. package/dist/ui/components/shortcut-hints.js +42 -6
  70. package/dist/ui/components/shortcuts.d.ts +1 -1
  71. package/dist/ui/components/shortcuts.js +4 -0
  72. package/dist/ui/components/theme.d.ts +1 -1
  73. package/dist/ui/components/value-menu.js +3 -1
  74. package/dist/ui/settings/manager.d.ts +1 -0
  75. package/dist/ui/settings/manager.js +9 -3
  76. package/package.json +1 -1
@@ -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();
@@ -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";
@@ -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();
@@ -657,7 +661,10 @@ 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
669
  dismissNotice() {
663
670
  if (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 }) };
@@ -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(),
@@ -280,7 +285,7 @@ export class OwnedUiSessionShell {
280
285
  }),
281
286
  ...(terminal === undefined ? {} : { terminal: terminal }),
282
287
  hardwareCursor: this.backend.view().terminal.hardwareCursor,
283
- wheelScrollLines: initialPiSettings.fullscreenWheelScrollLines,
288
+ ...(this.#customViewport ? {} : { wheelScrollLines: initialPiSettings.fullscreenWheelScrollLines }),
284
289
  };
285
290
  runtime = new PiTuiRuntimeAdapter(runtimeOptions);
286
291
  this.runtime = runtime;
@@ -358,8 +363,15 @@ export class OwnedUiSessionShell {
358
363
  ? this.runtime.addPreInputListener(data => {
359
364
  if (inputPresentation?.coordination === false)
360
365
  this.#streamPresentation.noteImmediatePresentation();
361
- // Compatibility: route boundary keys before Pi.
366
+ // Compatibility: route modal paging and boundary keys before Pi's fullscreen handlers.
362
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
+ }
363
375
  if (!this.runtime.hasOverlay() && (this.root.editor.matchesTerminalKey(data, "home")
364
376
  || this.root.editor.matchesTerminalKey(data, "end")
365
377
  || !isDefault && (this.root.editor.matchesTerminalKey(data, "ctrl+home")
@@ -458,13 +470,14 @@ export class OwnedUiSessionShell {
458
470
  if (typeof value !== "boolean")
459
471
  throw new TypeError("Fullscreen copy-on-select setting is invalid");
460
472
  this.root.setFullscreenCopyOnSelect(value);
461
- } } } : {}),
462
- fullscreenWheelScrollLines: { apply: value => {
463
- if (value !== "auto" && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > 100)) {
464
- throw new TypeError("Fullscreen wheel-scroll distance is invalid");
465
- }
466
- this.runtime.setWheelScrollLines(value);
467
- } },
473
+ } } } : {
474
+ fullscreenWheelScrollLines: { apply: (value) => {
475
+ if (value !== "auto" && (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > 100)) {
476
+ throw new TypeError("Fullscreen wheel-scroll distance is invalid");
477
+ }
478
+ this.runtime.setWheelScrollLines(value);
479
+ } },
480
+ }),
468
481
  });
469
482
  if (this.#customViewport && promptHistory !== undefined) {
470
483
  this.#promptHistoryImageSidecar = promptHistory.imageSidecar;
@@ -1065,12 +1078,14 @@ export class OwnedUiSessionShell {
1065
1078
  },
1066
1079
  onCancel: close,
1067
1080
  onSelect: entryId => {
1068
- close();
1069
1081
  if (entryId === context.currentLeafId) {
1082
+ close();
1070
1083
  this.root.appendWorkflowStatus("Already at this point");
1071
1084
  this.runtime.requestRender();
1072
1085
  return;
1073
1086
  }
1087
+ if (context.skipSummaryPrompt)
1088
+ close();
1074
1089
  void this.#completeTreeSelection(entryId, context.skipSummaryPrompt);
1075
1090
  },
1076
1091
  });
@@ -1206,6 +1221,19 @@ export class OwnedUiSessionShell {
1206
1221
  async runWorkflow(request) {
1207
1222
  if (request.command === "quit")
1208
1223
  return this.shutdown();
1224
+ if (this.#customViewport && request.command === "name" && request.argument.trim().length === 0) {
1225
+ void this.#extensionBridge.input("Session Name", "Enter name").then(value => {
1226
+ if (this.#disposed || value === undefined || value.trim().length === 0)
1227
+ return;
1228
+ return this.runWorkflow({ ...request, argument: value });
1229
+ }).catch(error => {
1230
+ if (this.#disposed)
1231
+ return;
1232
+ this.root.appendWorkflowResult({ command: "name", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
1233
+ this.runtime.requestRender();
1234
+ });
1235
+ return { outcome: "completed", diagnostic: null };
1236
+ }
1209
1237
  const copyGeneration = request.command === "copy" ? this.backend.sessionBindingGeneration : undefined;
1210
1238
  if (request.command === "login" && request.selection !== undefined) {
1211
1239
  const setup = this.backend.pinnedAmbientAuthentication(request.selection);
@@ -1281,13 +1309,15 @@ export class OwnedUiSessionShell {
1281
1309
  }
1282
1310
  this.root.resetExtensionUi();
1283
1311
  }
1284
- const shareSurface = request.command === "share"
1285
- ? createPiShellOperationLoader({
1312
+ const shareDialog = request.command === "share"
1313
+ ? await import("./share-operation.js")
1314
+ : undefined;
1315
+ const shareSurface = shareDialog === undefined ? undefined
1316
+ : (this.#customViewport ? shareDialog.createPiShellShareOperationDialog : shareDialog.createPiShellOperationLoader)({
1286
1317
  getColumns: () => this.runtime.viewport().columns,
1287
1318
  getRows: () => this.runtime.viewport().rows,
1288
1319
  requestRender: () => this.runtime.requestRender(),
1289
- }, "Creating gist...")
1290
- : undefined;
1320
+ }, "Creating gist…");
1291
1321
  const operationSurface = shareSurface ?? (request.command === "reload" ? createPiShellReloadBox() : undefined);
1292
1322
  const now = this.#reloadPresentation?.now ?? Date.now;
1293
1323
  const shownAt = now();
@@ -1343,7 +1373,17 @@ export class OwnedUiSessionShell {
1343
1373
  this.root.editor.reloadKeybindings();
1344
1374
  this.#installAutocompleteCommands();
1345
1375
  }
1346
- this.root.appendWorkflowResult(result);
1376
+ if (request.command === "tree" && result.outcome === "completed") {
1377
+ if (result.detail && !this.root.editor.getText().trim())
1378
+ this.root.editor.setText(result.detail);
1379
+ this.root.resumeViewportFollowing();
1380
+ }
1381
+ if (shareDialog !== undefined && this.#customViewport && result.outcome === "completed" && result.detail !== undefined) {
1382
+ this.root.appendWorkflowStatus(shareDialog.renderPiShellShareResult(result.message, result.detail));
1383
+ }
1384
+ else {
1385
+ this.root.appendWorkflowResult(result);
1386
+ }
1347
1387
  this.runtime.requestRender();
1348
1388
  return workflowAdapterResult(result);
1349
1389
  }
@@ -1782,6 +1822,19 @@ export class OwnedUiSessionShell {
1782
1822
  const removeInterruptWatch = this.runtime.addInputListener(data => {
1783
1823
  if (!data.includes(INTERRUPT))
1784
1824
  return undefined;
1825
+ // Protocol: route hosts decide whether one interrupt closes their app. Raw-input forwarding
1826
+ // is required because Pi handles Ctrl+C before the fullscreen overlay receives normal input.
1827
+ const consumedBySurface = surface.handleInput(INTERRUPT);
1828
+ if (surface.isClosed()) {
1829
+ armedAt = 0;
1830
+ closeSurface();
1831
+ return { consume: true };
1832
+ }
1833
+ if (consumedBySurface) {
1834
+ armedAt = 0;
1835
+ this.runtime.requestRender();
1836
+ return { consume: true };
1837
+ }
1785
1838
  const now = Date.now();
1786
1839
  if (armedAt !== 0 && now - armedAt <= INTERRUPT_CHORD_MS) {
1787
1840
  armedAt = 0;
@@ -1970,7 +2023,7 @@ export class OwnedUiSessionShell {
1970
2023
  }
1971
2024
  return this.#submitSkillPrompt(skillPrompt(skill.name, separator < 0 ? "" : trimmed.slice(separator + 1)), text);
1972
2025
  }
1973
- // Rationale: the engine expands "/skill:<name> args" itself; history keeps the form the user typed so recall restores the invocation.
2026
+ // Rationale: engine expands skill arguments; history retains typed input for recall.
1974
2027
  #submitSkillPrompt(prompt, typed) {
1975
2028
  this.#rememberInput(typed, "slash");
1976
2029
  this.root.resumeViewportFollowing();
@@ -1981,29 +2034,33 @@ export class OwnedUiSessionShell {
1981
2034
  text: prompt,
1982
2035
  });
1983
2036
  }
2037
+ #treeSummary() {
2038
+ return new Promise(resolve => {
2039
+ this.root.setInputSurface(createPiShellExtensionSelector("Summarize Branch?", ["No summary", "Summarize", "Summarize with custom prompt"], resolve, () => resolve(undefined)), true, "opaque");
2040
+ this.runtime.requestRender();
2041
+ });
2042
+ }
1984
2043
  async #completeTreeSelection(entryId, skipSummaryPrompt) {
1985
2044
  let summarize = false;
1986
2045
  let customInstructions;
1987
2046
  if (!skipSummaryPrompt) {
1988
2047
  while (true) {
1989
- const choice = await this.#extensionBridge.context.select("Summarize branch?", [
1990
- "No summary",
1991
- "Summarize",
1992
- "Summarize with custom prompt",
1993
- ]);
2048
+ const choice = await this.#treeSummary();
1994
2049
  if (choice === undefined) {
1995
2050
  await this.showTreeSelector(entryId);
1996
2051
  return;
1997
2052
  }
1998
2053
  summarize = choice !== "No summary";
1999
2054
  if (choice === "Summarize with custom prompt") {
2000
- customInstructions = await this.#extensionBridge.context.editor("Custom summarization instructions", "");
2055
+ customInstructions = await this.#extensionBridge.input("Custom Summarization Instructions", "", { retainSurfaceOnSettle: true });
2001
2056
  if (customInstructions === undefined)
2002
2057
  continue;
2003
2058
  }
2004
2059
  break;
2005
2060
  }
2006
2061
  }
2062
+ this.root.setInputSurface(null);
2063
+ this.runtime.requestRender();
2007
2064
  const result = await this.runWorkflow({
2008
2065
  command: "tree",
2009
2066
  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
+ }
@@ -44,6 +44,8 @@ export async function composeOwnedUi(options = {}) {
44
44
  configDir: resolveProductPaths().configDir,
45
45
  profileId: options.profileId,
46
46
  agentProvider: () => adapter.settingsPort(),
47
+ hiddenAgentSettingIds: ["fullscreenWheelScrollLines"],
48
+ agentSettingLabelOverrides: { fullscreenCopyOnSelect: "Copy on select" },
47
49
  });
48
50
  let releaseNotes = null;
49
51
  let releaseNotesFailure;
@@ -131,7 +131,7 @@ function pinnedTheme() {
131
131
  bold: (text) => piTheme().bold(text),
132
132
  plain: (text) => text,
133
133
  disabled: (text) => faint(piTheme().fg("dim", text)),
134
- highlight: (text) => `\u001b[48;2;82;82;82m\u001b[97m${text}\u001b[39m\u001b[49m`,
134
+ highlight: (text) => piTheme().bg("selectedBg", text),
135
135
  panel: (text) => `\u001b[48;2;55;55;55m${text}\u001b[49m`,
136
136
  };
137
137
  }
@@ -4,6 +4,16 @@ 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
+ }>;
12
+ /** Canonical return guidance for a nested step within a dialog surface. */
13
+ export declare const DIALOG_BACK_SHORTCUT_HINT: Readonly<{
14
+ key: string;
15
+ action: string;
16
+ }>;
7
17
  export interface ShortcutHintRoles {
8
18
  key(label: string): string;
9
19
  action(name: string): string;
@@ -11,7 +21,7 @@ export interface ShortcutHintRoles {
11
21
  /** Applies the common display casing to each named key and letter in a shortcut label. */
12
22
  export declare function displayShortcutKeyLabel(label: string): string;
13
23
  /** Renders the canonical semantic shortcut row independently of either presentation framework. */
14
- export declare function renderSemanticShortcutHints(entries: readonly SemanticShortcutHint[], roles: ShortcutHintRoles, indent?: number): string;
24
+ export declare function renderSemanticShortcutHints(entries: readonly SemanticShortcutHint[], roles: ShortcutHintRoles, indent?: number, separator?: string): string;
15
25
  export interface PresentationComponentPort {
16
26
  render(width: number): readonly string[];
17
27
  invalidate(): void;
@@ -1,3 +1,7 @@
1
+ /** Canonical dismissal guidance for every A1-rendered dialog surface. */
2
+ export const DIALOG_CLOSE_SHORTCUT_HINT = Object.freeze({ key: "esc", action: "close" });
3
+ /** Canonical return guidance for a nested step within a dialog surface. */
4
+ export const DIALOG_BACK_SHORTCUT_HINT = Object.freeze({ key: "esc", action: "back" });
1
5
  const SHORTCUT_KEY_NAMES = Object.freeze({
2
6
  alt: "Alt", backspace: "Backspace", cmd: "Cmd", ctrl: "Ctrl", delete: "Delete", down: "Down", end: "End",
3
7
  enter: "Enter", esc: "Esc", escape: "Escape", home: "Home", insert: "Insert", left: "Left", meta: "Meta",
@@ -16,7 +20,7 @@ export function displayShortcutKeyLabel(label) {
16
20
  }).join("+")).join("/");
17
21
  }
18
22
  /** Renders the canonical semantic shortcut row independently of either presentation framework. */
19
- export function renderSemanticShortcutHints(entries, roles, indent = 0) {
23
+ export function renderSemanticShortcutHints(entries, roles, indent = 0, separator = " ") {
20
24
  const rendered = entries.flatMap(entry => {
21
25
  if (entry.key === "" || (entry.key !== undefined && entry.key.trim().length === 0))
22
26
  return [];
@@ -26,7 +30,7 @@ export function renderSemanticShortcutHints(entries, roles, indent = 0) {
26
30
  const action = roles.action(entry.action);
27
31
  return [entry.actionFirst ? `${action} ${key}` : `${key} ${action}`];
28
32
  });
29
- return `${" ".repeat(Math.max(0, indent))}${rendered.join(" ")}`;
33
+ return `${" ".repeat(Math.max(0, indent))}${rendered.join(separator)}`;
30
34
  }
31
35
  export function assertPresentationComponent(component) {
32
36
  requiredFunctions(component, ["render", "invalidate"], "presentation component");
@@ -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)
@@ -29,8 +29,8 @@ export interface ReferenceScreenOptions {
29
29
  * Presents one read-only document full screen between two accent rules: the bold
30
30
  * title leads the scrolled rows, the shared scrollbar rail runs beside them, and
31
31
  * the hint line closes the frame. Rows come from a provider per content width and
32
- * are cached until the width changes; the screen closes on Escape and leaves the
33
- * 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.
34
34
  */
35
35
  export declare class ReferenceScreenApp implements UiApp {
36
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.
@@ -2,7 +2,7 @@ import type { AppHostServices, UiApp } from "../../ui/apps/index.js";
2
2
  import { ShortcutRegistry, type PaneInputResult, type PaneMouseEvent, type PaneRect } from "../../ui/components/index.js";
3
3
  import type { OwnedSettingsManager } from "../../ui/settings/index.js";
4
4
  export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
5
- type Action = "move-up" | "move-down" | "block-up" | "block-down" | "first" | "last" | "page-up" | "page-down" | "previous-value" | "next-value" | "activate" | "open-filter" | "close" | "part-previous" | "part-next" | "part-change";
5
+ type Action = "move-up" | "move-down" | "block-up" | "block-down" | "first" | "last" | "page-up" | "page-down" | "previous-value" | "next-value" | "activate" | "open-filter" | "close" | "undo" | "part-previous" | "part-next" | "part-change";
6
6
  export declare const SETTINGS_SHORTCUTS: ShortcutRegistry<Action>;
7
7
  /**
8
8
  * The settings screen. Rows, sticky headers, scrolling, and the keymap come from