@timurproko/a1 0.2.7-dev.1270 → 0.2.7-dev.1276

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 (154) hide show
  1. package/README.md +6 -0
  2. package/dist/app/session-shell/apps-dialog.d.ts +33 -0
  3. package/dist/app/session-shell/apps-dialog.js +62 -0
  4. package/dist/app/session-shell/dialog-coordinator.d.ts +12 -5
  5. package/dist/app/session-shell/dialog-coordinator.js +113 -15
  6. package/dist/app/session-shell/input-route.d.ts +23 -0
  7. package/dist/app/session-shell/input-route.js +59 -0
  8. package/dist/app/session-shell/session-selectors.d.ts +4 -4
  9. package/dist/app/session-shell/session-selectors.js +8 -3
  10. package/dist/app/session-shell/session-shell-root.js +6 -2
  11. package/dist/app/session-shell/session-shell.js +13 -5
  12. package/dist/app/session-shell/shell-host.d.ts +1 -0
  13. package/dist/app/session-shell/shell-host.js +22 -11
  14. package/dist/app/session-shell/viewport-pointer.js +5 -1
  15. package/dist/app/session-shell/workflow-output.js +12 -0
  16. package/dist/app/session-shell/workflow-router.d.ts +2 -2
  17. package/dist/composition/owned-ui.js +17 -13
  18. package/dist/composition/settings-route-host.js +1 -0
  19. package/dist/contracts/owned-ui/session-presenters.d.ts +1 -1
  20. package/dist/contracts/owned-ui/session-workflows.d.ts +3 -1
  21. package/dist/contracts/owned-ui/shell-components.d.ts +12 -0
  22. package/dist/contracts/presentation/component.d.ts +7 -0
  23. package/dist/contracts/presentation/index.d.ts +2 -0
  24. package/dist/contracts/presentation/index.js +4 -0
  25. package/dist/contracts/presentation/progress-palette.generated.d.ts +25 -1
  26. package/dist/contracts/presentation/progress-palette.generated.js +2 -135
  27. package/dist/contracts/presentation/terminal.d.ts +4 -0
  28. package/dist/contracts/presentation/theme.d.ts +2 -0
  29. package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
  30. package/dist/features/owned-ui/project-trust-dialog.js +5 -5
  31. package/dist/features/owned-ui/project-trust-prompt.js +2 -2
  32. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
  33. package/dist/features/owned-ui/reference-screen-app.js +4 -1
  34. package/dist/features/owned-ui/settings-app.js +6 -6
  35. package/dist/features/owned-ui/settings-model-dialog.js +3 -3
  36. package/dist/foundation/lifecycle/user-exports.d.ts +5 -0
  37. package/dist/foundation/lifecycle/user-exports.js +37 -0
  38. package/dist/foundation/startup/startup-descriptor.js +2 -2
  39. package/dist/integrations/pi/components/deferred-selector.d.ts +6 -0
  40. package/dist/integrations/pi/components/deferred-selector.js +40 -0
  41. package/dist/integrations/pi/components/login-dialog-loader.d.ts +3 -0
  42. package/dist/integrations/pi/components/login-dialog-loader.js +57 -0
  43. package/dist/integrations/pi/components/login-links.d.ts +6 -0
  44. package/dist/integrations/pi/components/login-links.js +31 -0
  45. package/dist/integrations/pi/components/login-text.d.ts +7 -0
  46. package/dist/integrations/pi/components/login-text.js +17 -0
  47. package/dist/integrations/pi/components/modal-frame.d.ts +5 -0
  48. package/dist/integrations/pi/components/modal-frame.js +30 -2
  49. package/dist/integrations/pi/components/models-dialog-loader.d.ts +11 -0
  50. package/dist/integrations/pi/components/models-dialog-loader.js +45 -0
  51. package/dist/integrations/pi/components/models-dialog.d.ts +2 -0
  52. package/dist/integrations/pi/components/models-dialog.js +13 -7
  53. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
  54. package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
  55. package/dist/integrations/pi/components/shell-editor-autocomplete.js +21 -15
  56. package/dist/integrations/pi/components/shell-footer-status.js +3 -2
  57. package/dist/integrations/pi/components/shell-presenters-info.js +1 -1
  58. package/dist/integrations/pi/components/shell-presenters-transcript.js +3 -1
  59. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +13 -9
  60. package/dist/integrations/pi/components/shell-selectors-dialogs.js +38 -43
  61. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +1 -1
  62. package/dist/integrations/pi/components/shell-session-info-reference.js +49 -46
  63. package/dist/integrations/pi/components/theme.d.ts +4 -0
  64. package/dist/integrations/pi/components/theme.js +9 -0
  65. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +8 -7
  66. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +13 -7
  67. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  68. package/dist/integrations/pi/components/upstream/components/auth-url.d.ts +16 -0
  69. package/dist/integrations/pi/components/upstream/components/auth-url.js +49 -0
  70. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  71. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  72. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  73. package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
  74. package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
  75. package/dist/integrations/pi/components/upstream/components/extension-selector.d.ts +1 -0
  76. package/dist/integrations/pi/components/upstream/components/extension-selector.js +30 -12
  77. package/dist/integrations/pi/components/upstream/components/login-dialog.d.ts +3 -0
  78. package/dist/integrations/pi/components/upstream/components/login-dialog.js +216 -0
  79. package/dist/integrations/pi/components/upstream/components/mermaid.d.ts +2 -1
  80. package/dist/integrations/pi/components/upstream/components/mermaid.js +26 -21
  81. package/dist/integrations/pi/components/upstream/components/oauth-selector.d.ts +19 -0
  82. package/dist/integrations/pi/components/upstream/components/oauth-selector.js +123 -0
  83. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  84. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  85. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  86. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  87. package/dist/integrations/pi/components/upstream/components/session-footer.js +17 -19
  88. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  89. package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
  90. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +6 -2
  91. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +27 -3
  92. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  93. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  94. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  95. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  96. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  97. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
  98. package/dist/integrations/pi/components/upstream/components/tool-execution.js +1 -1
  99. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  100. package/dist/integrations/pi/components/upstream/components/tree-selector.js +7 -6
  101. package/dist/integrations/pi/components/upstream/components/trust-selector.d.ts +2 -0
  102. package/dist/integrations/pi/components/upstream/components/trust-selector.js +30 -13
  103. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  104. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +1 -1
  105. package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -2
  106. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  107. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  108. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  109. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  110. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  111. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  112. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  113. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  114. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
  115. package/dist/integrations/pi/components/upstream/theme/theme.js +1 -1
  116. package/dist/integrations/pi/engine/adapter.d.ts +1 -1
  117. package/dist/integrations/pi/engine/adapter.js +3 -3
  118. package/dist/integrations/pi/engine/host.d.ts +1 -1
  119. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  120. package/dist/integrations/pi/engine/workflow-export.d.ts +4 -0
  121. package/dist/integrations/pi/engine/workflow-export.js +25 -0
  122. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -0
  123. package/dist/integrations/pi/engine/workflow-runner.js +6 -10
  124. package/dist/integrations/pi/engine/workflow-support.d.ts +2 -1
  125. package/dist/integrations/pi/engine/workflow-support.js +28 -25
  126. package/dist/integrations/pi/engine/workflows.d.ts +6 -1
  127. package/dist/integrations/pi/engine/workflows.js +1 -1
  128. package/dist/integrations/pi/session-presenters/components.d.ts +1 -1
  129. package/dist/integrations/pi/session-presenters/components.js +8 -8
  130. package/dist/integrations/pi/session-presenters/presentation.js +2 -1
  131. package/dist/integrations/pi/session-presenters/presenters.js +24 -12
  132. package/dist/integrations/pi/startup-public.js +289 -240
  133. package/dist/integrations/pi/startup-public.manifest.json +579 -363
  134. package/dist/integrations/pi/tui-runtime/adapter.js +3 -1
  135. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  136. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  137. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +5 -2
  138. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +12 -2
  139. package/dist/runtime-payload-inventory.json +8044 -6930
  140. package/dist/ui/components/dialog-panel.d.ts +4 -3
  141. package/dist/ui/components/dialog-panel.js +9 -13
  142. package/dist/ui/components/index.d.ts +1 -1
  143. package/dist/ui/components/index.js +1 -1
  144. package/dist/ui/components/list-block.d.ts +2 -2
  145. package/dist/ui/components/list-block.js +4 -3
  146. package/dist/ui/components/spans.d.ts +7 -0
  147. package/dist/ui/components/spans.js +63 -1
  148. package/dist/ui/settings/early-accent.d.ts +24 -0
  149. package/docs/architecture/prompt-suggestions.md +1 -1
  150. package/docs/architecture/ui-reference-provenance.md +2 -1
  151. package/docs/features/prompt-history.md +1 -1
  152. package/docs/features/session-exports.md +15 -0
  153. package/docs/local-worktree-cleanup.md +24 -0
  154. package/package.json +6 -6
@@ -19,6 +19,17 @@ function renderOwnedAutocompleteSelection(text) {
19
19
  const description = row === null ? "" : theme.fg("muted", row[2]);
20
20
  return renderPiListSelection(arrow + label + description);
21
21
  }
22
+ /** Models and thinking share the active declarations, independent of input presentation. */
23
+ function bindModelActions(editor, options) {
24
+ if (options.onModelSelect !== undefined)
25
+ editor.onAction("app.model.select", options.onModelSelect);
26
+ if (options.onModelCycle !== undefined) {
27
+ editor.onAction("app.model.cycleForward", () => options.onModelCycle?.("forward"));
28
+ editor.onAction("app.model.cycleBackward", () => options.onModelCycle?.("backward"));
29
+ }
30
+ if (options.onThinkingCycle !== undefined)
31
+ editor.onAction("app.thinking.cycle", options.onThinkingCycle);
32
+ }
22
33
  const PINNED_THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level", argumentHint: "<level>" };
23
34
  const OWNED_THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level" };
24
35
  const OWNED_LOGIN_SLASH_COMMAND = { name: "login", description: "Configure provider authentication" };
@@ -52,8 +63,10 @@ export const OWNED_BUILTIN_SLASH_COMMANDS = PINNED_PI_BUILTIN_SLASH_COMMANDS.fla
52
63
  if (command.name === "model") {
53
64
  return [{ name: "models", description: "Switch models and manage scoped model cycling" }, OWNED_THINKING_SLASH_COMMAND];
54
65
  }
55
- if (command.name === "thinking" || command.name === "scoped-models")
66
+ if (command.name === "thinking" || command.name === "scoped-models" || command.name === "settings" || command.name === "changelog" || command.name === "hotkeys")
56
67
  return [];
68
+ if (command.name === "export")
69
+ return [{ ...command, description: "Export session to user storage (--jsonl or an explicit path)" }];
57
70
  if (command.name === "login")
58
71
  return [OWNED_LOGIN_SLASH_COMMAND];
59
72
  return [command];
@@ -179,14 +192,7 @@ export function createPiShellEditor(options) {
179
192
  }
180
193
  if (options.onExtensionShortcut !== undefined)
181
194
  editor.onExtensionShortcut = options.onExtensionShortcut;
182
- if (options.onModelSelect !== undefined)
183
- editor.onAction("app.model.select", options.onModelSelect);
184
- if (options.onModelCycle !== undefined) {
185
- editor.onAction("app.model.cycleForward", () => options.onModelCycle?.("forward"));
186
- editor.onAction("app.model.cycleBackward", () => options.onModelCycle?.("backward"));
187
- }
188
- if (options.onThinkingCycle !== undefined)
189
- editor.onAction("app.thinking.cycle", options.onThinkingCycle);
195
+ bindModelActions(editor, options);
190
196
  if (options.onThinkingToggle !== undefined)
191
197
  editor.onAction("app.thinking.toggle", options.onThinkingToggle);
192
198
  if (options.onToolsExpand !== undefined)
@@ -230,6 +236,7 @@ export function createPiShellEditor(options) {
230
236
  },
231
237
  activateKeybindings: () => host.ensureActive(),
232
238
  keybindingConfig: () => keybindings.getEffectiveConfig(),
239
+ matchesAction: (data, action) => keybindings.matches(data, action),
233
240
  reloadKeybindings: () => host.reload(),
234
241
  matchesTerminalKey: (data, key) => matchesKey(data, key),
235
242
  handleInput: data => {
@@ -348,12 +355,11 @@ function autocompleteCommandSetter(options, editor, state, enabled) {
348
355
  state.skills = collapsed.skills;
349
356
  const additions = new Map(collapsed.commands.map(command => [command.name, command]));
350
357
  const builtInNames = new Set(builtInCatalog.map(command => command.name));
351
- const builtIns = builtInCatalog.flatMap(command => [
352
- autocompleteCommand(command, additions.get(command.name)),
353
- // Rationale: the collapsed browser is a primary bare-A1 command, not a resource after quit.
354
- ...(command.name === "settings" && collapsed.collapsedCommand !== undefined
355
- ? [autocompleteCommand(collapsed.collapsedCommand)] : []),
356
- ]);
358
+ // Rationale: Skills is a primary command, independent of retired application commands.
359
+ const builtIns = [
360
+ ...(collapsed.collapsedCommand === undefined ? [] : [autocompleteCommand(collapsed.collapsedCommand)]),
361
+ ...builtInCatalog.map(command => autocompleteCommand(command, additions.get(command.name))),
362
+ ];
357
363
  const resources = collapsed.commands
358
364
  .filter(command => command.name !== collapsed.collapsedCommand?.name && command.source !== "builtin" && !builtInNames.has(command.name))
359
365
  .map(command => autocompleteCommand(command));
@@ -301,8 +301,9 @@ function expandedHeaderText(bindings) {
301
301
  rawKeyHint(process.platform === "win32" ? "" : "ctrl+z", "to suspend"),
302
302
  rawKeyHint("ctrl+k", "to delete to end"),
303
303
  rawKeyHint(keys?.getKeys("app.thinking.cycle").join("/") ?? "shift+tab", "to cycle thinking level"),
304
- rawKeyHint("ctrl+p/shift+ctrl+p", "to cycle models"),
305
- // Invariant: only bare A1 supplies live bindings, and its unbound fallback is the unified `/models` command.
304
+ rawKeyHint(keys === undefined ? "ctrl+p/shift+ctrl+p" : [...keys.getKeys("app.model.cycleForward"), ...keys.getKeys("app.model.cycleBackward")].join("/") || "unbound", "to cycle models"),
305
+ ...(keys === undefined ? [] : [rawKeyHint(keys.getKeys("owned.apps.open").join("/") || "unbound", "to open Launcher")]),
306
+ // Invariant: only bare A1 supplies live bindings; unbound selection remains reachable through `/models`.
306
307
  rawKeyHint(keys === undefined ? "ctrl+l" : keys.getKeys("app.model.select").join("/") || "/models", "to select model"),
307
308
  rawKeyHint("ctrl+o", "to expand tools"),
308
309
  rawKeyHint("ctrl+t", "to expand thinking"),
@@ -180,7 +180,7 @@ export function hotkeysMarkdown(bindings, getShortcuts, profile) {
180
180
  ? action === "app.model.select" ? "Unbound (`/models`)" : "Unbound"
181
181
  : `\`${label}\``;
182
182
  }).join(" / ")} | ${description} |`;
183
- let markdown = ["**Navigation**", "| Key | Action |", "|-----|--------|", row(["tui.editor.cursorUp", "tui.editor.cursorDown", "tui.editor.cursorLeft", "tui.editor.cursorRight"], "Move cursor / browse history"), row(["tui.editor.cursorWordLeft", "tui.editor.cursorWordRight"], "Move by word"), row(["tui.editor.cursorLineStart"], profile === "a1" ? "Start of prompt line" : "Start of line"), row(["tui.editor.cursorLineEnd"], profile === "a1" ? "End of prompt line" : "End of line"), ...(profile === "a1" ? ["| `Ctrl+Home` | Start of content |", "| `Ctrl+End` | End of content / follow output |"] : []), row(["tui.editor.jumpForward"], "Jump forward to character"), row(["tui.editor.jumpBackward"], "Jump backward to character"), row(["tui.editor.pageUp", "tui.editor.pageDown"], "Scroll by page"), "", "**Editing**", "| Key | Action |", "|-----|--------|", row(["tui.input.submit"], "Send message"), row(["tui.input.newLine"], `New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""}`), row(["tui.editor.deleteWordBackward"], "Delete word backwards"), row(["tui.editor.deleteWordForward"], "Delete word forwards"), row(["tui.editor.deleteToLineStart"], "Delete to start of line"), row(["tui.editor.deleteToLineEnd"], "Delete to end of line"), row(["tui.editor.yank"], "Paste the most-recently-deleted text"), row(["tui.editor.yankPop"], "Cycle through the deleted text after pasting"), row(["tui.editor.undo"], "Undo"), "", "**Other**", "| Key | Action |", "|-----|--------|", row(["tui.input.tab"], "Path completion / accept autocomplete"), row(["app.interrupt"], "Cancel autocomplete / abort streaming"), row(["app.clear"], "Clear editor (first) / exit (second)"), row(["app.exit"], "Exit (when editor is empty)"), row(["app.suspend"], "Suspend to background"), row(["app.thinking.cycle"], "Cycle thinking level"), row(["app.model.cycleForward", "app.model.cycleBackward"], "Cycle models"), row(["app.model.select"], profile === "a1" ? "Open the Models dialog" : "Open model selector"), row(["app.tools.expand"], "Toggle tool output expansion"), row(["app.thinking.toggle"], "Toggle thinking block visibility"), row(["app.editor.external"], "Edit message in external editor"), row(["app.message.copy"], "Copy selection or last assistant message"), row(["app.message.followUp"], "Queue follow-up message"), row(["app.message.dequeue"], "Restore queued messages"), row(["app.clipboard.pasteImage"], profile === "a1" ? "Paste image or text from clipboard" : "Paste files on macOS, images, or text from clipboard"), "| `/` | Slash commands |", "| `!` | Run bash command |", "| `!!` | Run bash command (excluded from context) |", ...(profile === "a1" ? ["", "**Models dialog**", "| Key | Action |", "|-----|--------|", "| `Space` | Toggle the selected model in the cycling scope |", "| `Tab` | Switch the all/scoped filter |", row(["app.models.save"], "Save the scope to settings"), row(["app.models.enableAll"], "Scope every listed model"), row(["app.models.clearAll"], "Clear the listed models from the scope"), row(["app.models.toggleProvider"], "Toggle the selected model's provider"), row(["app.models.reorderUp", "app.models.reorderDown"], "Reorder the cycling scope")] : [])].join("\n");
183
+ let markdown = ["**Navigation**", "| Key | Action |", "|-----|--------|", row(["tui.editor.cursorUp", "tui.editor.cursorDown", "tui.editor.cursorLeft", "tui.editor.cursorRight"], "Move cursor / browse history"), row(["tui.editor.cursorWordLeft", "tui.editor.cursorWordRight"], "Move by word"), row(["tui.editor.cursorLineStart"], profile === "a1" ? "Start of prompt line" : "Start of line"), row(["tui.editor.cursorLineEnd"], profile === "a1" ? "End of prompt line" : "End of line"), ...(profile === "a1" ? ["| `Ctrl+Home` | Start of content |", "| `Ctrl+End` | End of content / follow output |"] : []), row(["tui.editor.jumpForward"], "Jump forward to character"), row(["tui.editor.jumpBackward"], "Jump backward to character"), row(["tui.editor.pageUp", "tui.editor.pageDown"], "Scroll by page"), "", "**Editing**", "| Key | Action |", "|-----|--------|", row(["tui.input.submit"], "Send message"), row(["tui.input.newLine"], `New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""}`), row(["tui.editor.deleteWordBackward"], "Delete word backwards"), row(["tui.editor.deleteWordForward"], "Delete word forwards"), row(["tui.editor.deleteToLineStart"], "Delete to start of line"), row(["tui.editor.deleteToLineEnd"], "Delete to end of line"), row(["tui.editor.yank"], "Paste the most-recently-deleted text"), row(["tui.editor.yankPop"], "Cycle through the deleted text after pasting"), row(["tui.editor.undo"], "Undo"), "", "**Other**", "| Key | Action |", "|-----|--------|", row(["tui.input.tab"], "Path completion / accept autocomplete"), row(["app.interrupt"], "Cancel autocomplete / abort streaming"), row(["app.clear"], "Clear editor (first) / exit (second)"), row(["app.exit"], "Exit (when editor is empty)"), row(["app.suspend"], "Suspend to background"), row(["app.thinking.cycle"], "Cycle thinking level"), row(["app.model.cycleForward", "app.model.cycleBackward"], "Cycle models"), row(["app.model.select"], profile === "a1" ? "Open the in-place Models selector (`/models`)" : "Open model selector"), ...(profile === "a1" ? [row(["owned.apps.open"], "Open Launcher (Agent, Settings, Changelog, Hotkeys)")] : []), row(["app.tools.expand"], "Toggle tool output expansion"), row(["app.thinking.toggle"], "Toggle thinking block visibility"), row(["app.editor.external"], "Edit message in external editor"), row(["app.message.copy"], "Copy selection or last assistant message"), row(["app.message.followUp"], "Queue follow-up message"), row(["app.message.dequeue"], "Restore queued messages"), row(["app.clipboard.pasteImage"], profile === "a1" ? "Paste image or text from clipboard" : "Paste files on macOS, images, or text from clipboard"), "| `/` | Slash commands |", "| `!` | Run bash command |", "| `!!` | Run bash command (excluded from context) |", ...(profile === "a1" ? ["", "Legacy Ctrl+M that is indistinguishable from Enter keeps Enter behavior; use `/models` to select a model.", "", "**Models dialog**", "| Key | Action |", "|-----|--------|", "| `Space` | Toggle the selected model in the cycling scope |", "| `Tab` | Switch the all/scoped filter |", row(["app.models.save"], "Save the scope to settings"), row(["app.models.enableAll"], "Scope every listed model"), row(["app.models.clearAll"], "Clear the listed models from the scope"), row(["app.models.toggleProvider"], "Toggle the selected model's provider"), row(["app.models.reorderUp", "app.models.reorderDown"], "Reorder the cycling scope")] : [])].join("\n");
184
184
  const shortcuts = getShortcuts(bindings ?? keys.getEffectiveConfig());
185
185
  if (shortcuts.length > 0) {
186
186
  markdown += "\n\n**Extensions**\n| Key | Action |\n|-----|--------|\n";
@@ -206,7 +206,9 @@ function transcriptComponent(block, cwd, expanded, extensions, submittedPrompt,
206
206
  summary: block.text,
207
207
  tokensBefore: numericPayload(block, "tokensBefore"),
208
208
  timestamp: numericPayload(block, "timestamp") || 0,
209
- }, getPiMarkdownTheme());
209
+ }, getPiMarkdownTheme(), outputPad, [
210
+ createMermaidMarkdownTransformer({ getMode: () => mermaidRenderingMode, theme: piTheme() }),
211
+ ], block.id);
210
212
  component.setExpanded(expanded);
211
213
  return component;
212
214
  }
@@ -1,10 +1,11 @@
1
1
  import { ModelSelectorComponent, type SessionInfo, type SettingsCallbacks, type SettingsConfig } from "../startup-public.js";
2
2
  import type { OwnedUiDialog } from "../../../contracts/owned-ui/index.js";
3
- import { type ModelsDialogCallbacks, type ModelsDialogConfig } from "./models-dialog.js";
4
- import { type TrustDecision, type TrustOption, type TrustUpdate } from "./upstream/components/trust-selector.js";
3
+ import type { ModelsDialogCallbacks, ModelsDialogConfig } from "./models-dialog.js";
4
+ import type { TrustDecision, TrustOption, TrustUpdate } from "./upstream/components/trust-selector.js";
5
5
  import { type PiShellComponentPort, type PiShellEditorOptions, type PiShellSelectorOption, type PiShellSelectorOptions } from "./shell-shared-facade.js";
6
- export { createPiShellSkillsSelector, type PiShellSkillsSelectorOptions } from "./skills-dialog.js";
7
- export declare function createPiShellSelector(options: PiShellSelectorOptions): PiShellComponentPort;
6
+ export { createPiShellSkillsSelector } from "./deferred-selector.js";
7
+ export type { PiShellSkillsSelectorOptions } from "./skills-dialog.js";
8
+ export declare function createPiShellSelector(options: PiShellSelectorOptions, standard?: boolean): PiShellComponentPort;
8
9
  export interface PiShellSettingsSelectorOptions {
9
10
  readonly config: SettingsConfig;
10
11
  readonly onChange: (callback: keyof SettingsCallbacks, value: unknown) => void;
@@ -13,6 +14,7 @@ export interface PiShellSettingsSelectorOptions {
13
14
  export declare function createPiShellSettingsSelector(options: PiShellSettingsSelectorOptions): PiShellComponentPort;
14
15
  type PiModelSelectorArguments = ConstructorParameters<typeof ModelSelectorComponent>;
15
16
  export interface PiShellModelSelectorOptions {
17
+ readonly standardStyle?: boolean;
16
18
  readonly currentModel: unknown;
17
19
  readonly modelRuntime: unknown;
18
20
  readonly scopedModels: readonly unknown[];
@@ -50,6 +52,7 @@ export declare function createPiShellScopedModelsSelector(options: PiShellScoped
50
52
  export interface PiShellModelsDialogOptions extends ModelsDialogConfig, ModelsDialogCallbacks {
51
53
  }
52
54
  export interface PiShellModelsDialogPort extends PiShellComponentPort {
55
+ readonly ready?: Promise<void>;
53
56
  updateModels(models: readonly PiShellScopedModelDescriptor[]): void;
54
57
  setRefreshStatus(message: string, kind: "muted" | "success" | "warning"): void;
55
58
  readonly dirty: () => boolean;
@@ -57,6 +60,7 @@ export interface PiShellModelsDialogPort extends PiShellComponentPort {
57
60
  /** The bare-A1 unified Models dialog behind the owned component boundary; the pinned selectors above stay for `a1 pi`. */
58
61
  export declare function createPiShellModelsDialog(options: PiShellModelsDialogOptions): PiShellModelsDialogPort;
59
62
  export declare function createPiShellTrustSelector(options: {
63
+ readonly standardStyle?: boolean;
60
64
  readonly cwd: string;
61
65
  readonly savedDecision: TrustDecision | null;
62
66
  readonly projectTrusted: boolean;
@@ -90,7 +94,7 @@ export declare function createPiShellTreeSelector(options: {
90
94
  readonly initialSelectedId?: string;
91
95
  readonly initialFilterMode?: "default" | "no-tools" | "user-only" | "labeled-only" | "all";
92
96
  }): Promise<PiShellComponentPort>;
93
- export declare function createPiShellUserMessageSelector(messages: readonly PiShellSelectorOption[], onSelect: (id: string) => void, onCancel: () => void, initialSelectedId?: string): PiShellComponentPort;
97
+ export declare function createPiShellUserMessageSelector(messages: readonly PiShellSelectorOption[], onSelect: (id: string) => void, onCancel: () => void, initialSelectedId?: string, standard?: boolean): PiShellComponentPort;
94
98
  export interface PiShellLoginDialogPort extends PiShellComponentPort {
95
99
  showAuth(url: string, instructions?: string): void;
96
100
  showDeviceCode(info: {
@@ -107,7 +111,7 @@ export interface PiShellLoginDialogPort extends PiShellComponentPort {
107
111
  showWaiting(message: string): void;
108
112
  showProgress(message: string): void;
109
113
  }
110
- export declare function createPiShellLoginDialog(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">, providerId: string, onComplete: (success: boolean, message?: string) => void, providerName?: string, title?: string): PiShellLoginDialogPort;
114
+ export declare function createPiShellLoginDialog(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">, providerId: string, onComplete: (success: boolean, message?: string) => void, providerName?: string, title?: string, standard?: boolean): PiShellLoginDialogPort;
111
115
  export declare function createPiShellArmin(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellComponentPort;
112
116
  export declare function createPiShellEarendilAnnouncement(): PiShellComponentPort;
113
117
  export declare function createPiShellReloadBox(): PiShellComponentPort;
@@ -119,11 +123,11 @@ export interface PiShellAuthProviderOption extends PiShellSelectorOption {
119
123
  readonly source?: string;
120
124
  };
121
125
  }
122
- export declare function createPiShellAuthProviderSelector(mode: "login" | "logout", providers: readonly PiShellAuthProviderOption[], onSelect: (id: string) => void, onCancel: () => void, initialSearchInput?: string): PiShellComponentPort;
123
- export declare function createPiShellExtensionSelector(title: string, options: readonly string[], onSelect: (value: string) => void, onCancel: () => void): PiShellComponentPort;
126
+ export declare function createPiShellAuthProviderSelector(mode: "login" | "logout", providers: readonly PiShellAuthProviderOption[], onSelect: (id: string) => void, onCancel: () => void, initialSearchInput?: string, standardStyle?: boolean): PiShellComponentPort;
127
+ export declare function createPiShellExtensionSelector(title: string, options: readonly string[], onSelect: (value: string) => void, onCancel: () => void, standardSelection?: boolean): PiShellComponentPort;
124
128
  export declare function createPiShellThemeSelector(currentTheme: string, onSelect: (theme: string) => void, onCancel: () => void, onPreview: (theme: string) => void): PiShellComponentPort;
125
129
  export declare function createPiShellShowImagesSelector(currentValue: boolean, onSelect: (show: boolean) => void, onCancel: () => void): PiShellComponentPort;
126
130
  export declare function createPiShellDialog(dialog: OwnedUiDialog, handlers?: {
127
131
  readonly onSelect?: (id: string) => void;
128
132
  readonly onCancel?: () => void;
129
- }): PiShellComponentPort;
133
+ }, standard?: boolean): PiShellComponentPort;
@@ -3,13 +3,14 @@ import { EarendilAnnouncementComponent } from "./upstream/components/earendil-an
3
3
  import { ExtensionSelectorComponent } from "./upstream/components/extension-selector.js";
4
4
  import { Box, Container, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
5
5
  import { ScopedModelsSelectorComponent } from "./upstream/components/scoped-models-selector.js";
6
- import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader, adoptPiModalSearchInput } from "./modal-frame.js";
7
- import { ModelsDialogComponent } from "./models-dialog.js";
8
- import { TrustSelectorComponent } from "./upstream/components/trust-selector.js";
9
- import { canonicalizePiSettingsHint, DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, getPiSelectListTheme, paintPiBorder, piTheme, renderPiModalShortcutHints, } from "./theme.js";
6
+ import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader, adoptPiModalSearchInput, renderPiModalSelectedPrimary, standardInternalChoicePort } from "./modal-frame.js";
7
+ import { deferredLoginDialog } from "./login-dialog-loader.js";
8
+ import { deferredSelector } from "./deferred-selector.js";
9
+ import { createDeferredModelsDialog } from "./models-dialog-loader.js";
10
+ import { canonicalizePiSettingsHint, DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, getPiSelectListTheme, paintPiBorder, piTheme, renderPiModalShortcutHints, internalDialogTitle, } from "./theme.js";
10
11
  import { componentFromPort, componentPort, createTuiFacade, ensureTheme, isRecord, } from "./shell-shared-facade.js";
11
- export { createPiShellSkillsSelector } from "./skills-dialog.js";
12
- export function createPiShellSelector(options) {
12
+ export { createPiShellSkillsSelector } from "./deferred-selector.js";
13
+ export function createPiShellSelector(options, standard = false) {
13
14
  ensureTheme();
14
15
  const items = options.options.map(toSelectItem);
15
16
  const list = new SelectList(items, options.maxVisible ?? Math.min(PINNED_PI_LAYOUT.selectorMaxVisible, Math.max(1, items.length)), getPiSelectListTheme());
@@ -17,11 +18,11 @@ export function createPiShellSelector(options) {
17
18
  list.onSelect = item => options.onSelect?.(item.value);
18
19
  if (options.onCancel !== undefined)
19
20
  list.onCancel = options.onCancel;
20
- if (!options.title && options.onCancel === undefined)
21
+ if (!options.title && options.onCancel === undefined && !standard)
21
22
  return componentPort(list);
22
23
  const container = new Container();
23
24
  if (options.title)
24
- container.addChild(new Text(piTheme().fg("accent", piTheme().bold(options.title)), PINNED_PI_LAYOUT.contentPaddingX, 0));
25
+ container.addChild(new Text(piTheme().fg("accent", piTheme().bold(standard ? internalDialogTitle(options.title) : options.title)), PINNED_PI_LAYOUT.contentPaddingX, 0));
25
26
  container.addChild(list);
26
27
  if (options.onCancel !== undefined) {
27
28
  container.addChild(new Spacer(1));
@@ -31,7 +32,8 @@ export function createPiShellSelector(options) {
31
32
  DIALOG_CLOSE_SHORTCUT_HINT,
32
33
  ]), 0, 0));
33
34
  }
34
- return componentPort(container, data => list.handleInput(data));
35
+ const port = componentPort(container, data => list.handleInput(data));
36
+ return standard ? { ...port, render: width => port.render(width).map(row => renderPiModalSelectedPrimary(` ${row}`, width + 1, options.options.map(option => option.label)).slice(1)) } : port;
35
37
  }
36
38
  export function createPiShellSettingsSelector(options) {
37
39
  ensureTheme();
@@ -98,7 +100,8 @@ export function createPiShellModelSelector(options) {
98
100
  const selector = new ModelSelectorComponent(createTuiFacade(options.runtime), options.currentModel, options.modelRuntime, options.scopedModels, options.onSelect, options.onCancel, options.initialSearchInput, options.onSelectAsDefault, options.defaultModel);
99
101
  adoptPiModalSearchInput(selector, selector.getSearchInput());
100
102
  adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
101
- return componentPort(selector);
103
+ const port = componentPort(selector);
104
+ return options.standardStyle ? standardInternalChoicePort(port, true) : port;
102
105
  }
103
106
  function toPiScopedModel(model) {
104
107
  const candidate = { provider: model.provider, id: model.id, name: model.name };
@@ -133,17 +136,12 @@ export function createPiShellScopedModelsSelector(options) {
133
136
  /** The bare-A1 unified Models dialog behind the owned component boundary; the pinned selectors above stay for `a1 pi`. */
134
137
  export function createPiShellModelsDialog(options) {
135
138
  ensureTheme();
136
- const dialog = new ModelsDialogComponent(options, options);
137
- return {
138
- ...componentPort(dialog),
139
- updateModels: models => dialog.updateModels(models),
140
- setRefreshStatus: (message, kind) => dialog.setRefreshStatus(message, kind),
141
- dirty: () => dialog.dirty,
142
- };
139
+ return createDeferredModelsDialog(options);
143
140
  }
144
141
  export function createPiShellTrustSelector(options) {
145
142
  ensureTheme();
146
- return componentPort(new TrustSelectorComponent(options));
143
+ return deferredSelector(() => import("./upstream/components/trust-selector.js")
144
+ .then(({ TrustSelectorComponent }) => componentPort(new TrustSelectorComponent(options))), options.onCancel);
147
145
  }
148
146
  export async function createPiShellSessionSelector(options) {
149
147
  ensureTheme();
@@ -159,21 +157,26 @@ export async function createPiShellTreeSelector(options) {
159
157
  selector.onCopy = options.onCopy;
160
158
  return componentPort(selector);
161
159
  }
162
- export function createPiShellUserMessageSelector(messages, onSelect, onCancel, initialSelectedId) {
160
+ export function createPiShellUserMessageSelector(messages, onSelect, onCancel, initialSelectedId, standard = false) {
163
161
  ensureTheme();
164
162
  const selector = new UserMessageSelectorComponent(messages.map(message => ({ id: message.id, text: message.label })), onSelect, onCancel, initialSelectedId);
165
163
  const list = selector.getMessageList();
164
+ if (standard)
165
+ selector.children[1] = new Text(piTheme().fg("accent", piTheme().bold("Fork from Message")), 1, 0);
166
166
  addPiModalSelectFooter(selector);
167
167
  adoptPiModalFrame(selector, { topIndex: 4, bottomIndex: selector.children.length - 1 });
168
- return componentPort(selector, data => list.handleInput(data));
168
+ const port = componentPort(selector, data => list.handleInput(data));
169
+ return standard ? standardInternalChoicePort(port) : port;
169
170
  }
170
- export function createPiShellLoginDialog(runtime, providerId, onComplete, providerName, title) {
171
+ export function createPiShellLoginDialog(runtime, providerId, onComplete, providerName, title, standard = false) {
171
172
  ensureTheme();
173
+ if (standard)
174
+ return deferredLoginDialog(runtime, providerId, onComplete, providerName, title);
172
175
  const dialog = new LoginDialogComponent(createTuiFacade(runtime), providerId, onComplete, providerName, title);
173
176
  const preInsetTitle = dialog.children[1];
174
177
  const content = dialog.children[2];
175
178
  dialog.children.splice(dialog.children.length - 1, 0, new Spacer(1), new Text(renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT]), 0, 0));
176
- const removeDependencyHint = () => { content.children.pop(); };
179
+ const stripHint = (result) => { content.children.pop(); return result; };
177
180
  const header = adoptPiModalHeader(dialog, 0, 1);
178
181
  adoptPiModalFrame(dialog, {
179
182
  topIndex: 0,
@@ -185,23 +188,12 @@ export function createPiShellLoginDialog(runtime, providerId, onComplete, provid
185
188
  ...componentPort(dialog),
186
189
  showAuth: (url, instructions) => dialog.showAuth(url, instructions),
187
190
  showDeviceCode: info => dialog.showDeviceCode(info),
188
- showManualInput(message) {
189
- const result = dialog.showManualInput(message);
190
- removeDependencyHint();
191
- return result;
192
- },
193
- showPrompt(message, placeholder) {
194
- const result = dialog.showPrompt(message, placeholder);
195
- removeDependencyHint();
196
- return result;
197
- },
191
+ showManualInput: message => stripHint(dialog.showManualInput(message)),
192
+ showPrompt: (message, placeholder) => stripHint(dialog.showPrompt(message, placeholder)),
198
193
  showDetails: lines => dialog.showDetails([...lines]),
199
- // Rationale: The persistent canonical footer replaces the dependency's optional close hint.
194
+ // Rationale: one footer replaces optional hints.
200
195
  showInfo: (message, links = []) => dialog.showInfo(message, [...links], false),
201
- showWaiting(message) {
202
- dialog.showWaiting(message);
203
- removeDependencyHint();
204
- },
196
+ showWaiting: message => stripHint(dialog.showWaiting(message)),
205
197
  showProgress: message => dialog.showProgress(message),
206
198
  };
207
199
  }
@@ -224,8 +216,11 @@ export function createPiShellReloadBox() {
224
216
  adoptPiModalFrame(container, { topIndex: 0, bottomIndex: container.children.length - 1 });
225
217
  return componentPort(container);
226
218
  }
227
- export function createPiShellAuthProviderSelector(mode, providers, onSelect, onCancel, initialSearchInput) {
219
+ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onCancel, initialSearchInput, standardStyle = false) {
228
220
  ensureTheme();
221
+ if (standardStyle)
222
+ return deferredSelector(() => import("./upstream/components/oauth-selector.js")
223
+ .then(({ AuthProviderSelector }) => componentPort(new AuthProviderSelector(mode, providers, onSelect, onCancel, initialSearchInput))), onCancel);
229
224
  const selector = new OAuthSelectorComponent(mode, providers.map(provider => ({
230
225
  id: provider.providerId,
231
226
  name: provider.label,
@@ -236,7 +231,7 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
236
231
  if (selected)
237
232
  onSelect(selected.id);
238
233
  }, onCancel, initialSearchInput);
239
- // Provenance: OAuth selector slot is top rule, spacer, title, spacer, then its search Input.
234
+ // Provenance: public search slot 4.
240
235
  adoptPiModalSearchInput(selector, selector.children[4]);
241
236
  addPiModalSelectFooter(selector);
242
237
  const preInsetContent = [selector.children[2], selector.children[6]];
@@ -249,9 +244,9 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
249
244
  });
250
245
  return componentPort(selector);
251
246
  }
252
- export function createPiShellExtensionSelector(title, options, onSelect, onCancel) {
247
+ export function createPiShellExtensionSelector(title, options, onSelect, onCancel, standardSelection = false) {
253
248
  ensureTheme();
254
- return componentPort(new ExtensionSelectorComponent(title, [...options], onSelect, onCancel));
249
+ return componentPort(new ExtensionSelectorComponent(standardSelection ? internalDialogTitle(title) : title, [...options], onSelect, onCancel, { standardSelection }));
255
250
  }
256
251
  export function createPiShellThemeSelector(currentTheme, onSelect, onCancel, onPreview) {
257
252
  ensureTheme();
@@ -269,7 +264,7 @@ export function createPiShellShowImagesSelector(currentValue, onSelect, onCancel
269
264
  adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
270
265
  return componentPort(selector, data => list.handleInput(data));
271
266
  }
272
- export function createPiShellDialog(dialog, handlers = {}) {
267
+ export function createPiShellDialog(dialog, handlers = {}, standard = false) {
273
268
  ensureTheme();
274
269
  const selector = createPiShellSelector({
275
270
  title: dialog.title,
@@ -277,7 +272,7 @@ export function createPiShellDialog(dialog, handlers = {}) {
277
272
  maxVisible: 8,
278
273
  ...(handlers.onSelect === undefined ? {} : { onSelect: handlers.onSelect }),
279
274
  ...(handlers.onCancel === undefined ? {} : { onCancel: handlers.onCancel }),
280
- });
275
+ }, standard);
281
276
  const box = new Box(1, 1);
282
277
  box.addChild(componentFromPort(selector));
283
278
  return componentPort(box, data => selector.handleInput?.(data));
@@ -6,6 +6,6 @@ interface PiShellSessionInfoReferenceDocument {
6
6
  readonly rows: readonly string[];
7
7
  }[];
8
8
  }
9
- /** Renders the pinned report values while leaving group names semantic for the owned screen. */
9
+ /** Renders complete report data in semantic rows sharing a left-aligned value column. */
10
10
  export declare function renderPiShellSessionInfoReferenceDocument(presentation: PiShellSessionInfoPresentation, width: number): PiShellSessionInfoReferenceDocument;
11
11
  export {};
@@ -14,7 +14,7 @@ function splitSessionFile(sessionFile, firstWidth, continuationWidth) {
14
14
  rowWidth = continuationWidth;
15
15
  continue;
16
16
  }
17
- // Compatibility: a one-column frame cannot fit a wide grapheme; preserve it for outer truncation.
17
+ // Compatibility: preserve a wide grapheme even when the outer frame cannot fit it.
18
18
  if (!row)
19
19
  row = sliceByColumn(remaining, 0, rowWidth);
20
20
  rows.push(row);
@@ -23,72 +23,75 @@ function splitSessionFile(sessionFile, firstWidth, continuationWidth) {
23
23
  }
24
24
  return rows.length > 0 ? rows : [""];
25
25
  }
26
- /** Keeps the file value on its labelled row before using full continuation rows. */
27
- function sessionFileRows(sessionFile, width) {
28
- // Compatibility: match Text's reduced-padding rule so these explicit breaks survive its renderer.
29
- const paddingX = Math.min(1, Math.max(0, Math.floor((width - 1) / 2)));
30
- const contentWidth = Math.max(1, width - paddingX * 2);
31
- const label = piTheme().fg("dim", "File:");
32
- const firstValueWidth = Math.max(0, contentWidth - visibleWidth("File: "));
33
- const [first = "", ...continuation] = splitSessionFile(sessionFile, firstValueWidth, contentWidth);
34
- return firstValueWidth > 0
35
- ? [`${label} ${first}`, ...continuation]
36
- : [label, ...continuation];
37
- }
38
- /** Renders the pinned report values while leaving group names semantic for the owned screen. */
26
+ /** Renders complete report data in semantic rows sharing a left-aligned value column. */
39
27
  export function renderPiShellSessionInfoReferenceDocument(presentation, width) {
40
28
  ensureTheme();
41
29
  const { stats, sessionName, cacheWaste, usageBreakdown, cacheWarming } = presentation;
42
- const render = (rows) => new Text(rows.join("\n"), 1, 0).render(width);
43
- const preamble = [
44
- ...(sessionName ? [`${piTheme().fg("dim", "Name:")} ${sessionName}`] : []),
45
- ...sessionFileRows(stats.sessionFile ?? "In-memory", width),
46
- `${piTheme().fg("dim", "ID:")} ${stats.sessionId}`,
30
+ const identity = [
31
+ ...(sessionName ? [{ label: "Name:", value: sessionName }] : []),
32
+ { label: "File:", value: stats.sessionFile ?? "In-memory" },
33
+ { label: "ID:", value: stats.sessionId },
47
34
  ];
48
35
  const messages = [
49
- `${piTheme().fg("dim", "Total:")} ${stats.totalMessages}`,
50
- `${piTheme().fg("dim", "User:")} ${stats.userMessages}`,
51
- `${piTheme().fg("dim", "Assistant:")} ${stats.assistantMessages}`,
52
- `${piTheme().fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results`,
36
+ { label: "Total:", value: String(stats.totalMessages) },
37
+ { label: "User:", value: String(stats.userMessages) },
38
+ { label: "Assistant:", value: String(stats.assistantMessages) },
39
+ { label: "Tools:", value: `${stats.toolCalls} calls, ${stats.toolResults} results` },
53
40
  ];
54
41
  const { input, cacheRead, cacheWrite } = stats.tokens;
55
42
  const promptTokens = input + cacheRead + cacheWrite;
56
- const tokens = [`${piTheme().fg("dim", "Input:")} ${promptTokens.toLocaleString()}`];
43
+ const tokens = [{ label: "Input:", value: promptTokens.toLocaleString() }];
57
44
  if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
58
- tokens.push(` ${piTheme().fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${piTheme().fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`)}`);
45
+ tokens.push({ label: "Cached:", indent: 2, value: `${cacheRead.toLocaleString()} ${piTheme().fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`)}` });
59
46
  const written = cacheWrite > 0 ? ` ${piTheme().fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
60
- tokens.push(` ${piTheme().fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}`);
47
+ tokens.push({ label: "Uncached:", indent: 2, value: `${(input + cacheWrite).toLocaleString()}${written}` });
61
48
  }
62
- tokens.push(`${piTheme().fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}`);
63
- tokens.push(`${piTheme().fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}`);
49
+ tokens.push({ label: "Output:", value: stats.tokens.output.toLocaleString() });
50
+ tokens.push({ label: "Total:", value: stats.tokens.total.toLocaleString() });
64
51
  const warmingStatus = cacheWarming.status;
65
52
  const warming = [
66
- `${piTheme().fg("dim", "Mode:")} ${cacheWarming.mode}`,
67
- `${piTheme().fg("dim", "Status:")} ${warmingStatus ? formatCacheWarmingStatus(warmingStatus) : "Inactive (cache warming unavailable)"}`,
53
+ { label: "Mode:", value: cacheWarming.mode },
54
+ { label: "Status:", value: warmingStatus ? formatCacheWarmingStatus(warmingStatus) : "Inactive (cache warming unavailable)" },
68
55
  ];
69
- const warmingDecision = warmingStatus?.decision;
70
- if (warmingDecision?.economicsAvailable) {
71
- warming.push(`${piTheme().fg("dim", "Cache miss penalty:")} $${warmingDecision.missCost.toFixed(3)}`);
72
- warming.push(`${piTheme().fg("dim", "Refresh cost:")} $${warmingDecision.warmCost.toFixed(3)}`);
56
+ const decision = warmingStatus?.decision;
57
+ if (decision?.economicsAvailable) {
58
+ warming.push({ label: "Cache miss penalty:", value: `$${decision.missCost.toFixed(3)}` });
59
+ warming.push({ label: "Refresh cost:", value: `$${decision.warmCost.toFixed(3)}` });
73
60
  }
74
- const sections = [
75
- { title: "Messages", rows: render(messages) },
76
- { title: "Tokens", rows: render(tokens) },
77
- { title: "Cache Warming", rows: render(warming) },
78
- ];
61
+ const groups = [{ title: "Messages", rows: messages }, { title: "Tokens", rows: tokens }, { title: "Cache Warming", rows: warming }];
79
62
  if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
80
- const cost = [`${piTheme().fg("dim", "Total:")} $${stats.cost.toFixed(3)}`];
63
+ const cost = [{ label: "Total:", value: `$${stats.cost.toFixed(3)}` }];
81
64
  if (usageBreakdown.length > 1)
82
65
  for (const entry of usageBreakdown) {
83
- cost.push(` ${piTheme().fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${piTheme().fg("dim", `(${formatSessionTokens(entry.tokens)} tokens)`)}`);
66
+ cost.push({ label: `${entry.key}:`, indent: 2, value: `$${entry.cost.toFixed(3)} ${piTheme().fg("dim", `(${formatSessionTokens(entry.tokens)} tokens)`)}` });
84
67
  }
85
68
  if (cacheWaste.missedTokens > 0) {
86
69
  const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`}`;
87
- cost.push(cacheWaste.missedCost >= 0.0001
88
- ? `${piTheme().fg("dim", "Cache Re-billed:")} $${cacheWaste.missedCost.toFixed(3)} ${piTheme().fg("dim", `(${detail})`)}`
89
- : `${piTheme().fg("dim", "Cache Re-billed:")} ${detail}`);
70
+ cost.push({ label: "Cache Re-billed:", value: cacheWaste.missedCost >= 0.0001
71
+ ? `$${cacheWaste.missedCost.toFixed(3)} ${piTheme().fg("dim", `(${detail})`)}` : detail });
72
+ }
73
+ groups.push({ title: "Cost", rows: cost });
74
+ }
75
+ // Compatibility: match Text's reduced-padding rule so explicit path breaks survive rendering.
76
+ const padding = Math.min(1, Math.max(0, Math.floor((width - 1) / 2)));
77
+ const contentWidth = Math.max(1, width - padding * 2);
78
+ const allRows = [...identity, ...groups.flatMap(group => group.rows)];
79
+ const column = Math.min(Math.max(...allRows.map(row => (row.indent ?? 0) + visibleWidth(row.label))) + 1, contentWidth - 1);
80
+ const prefix = (row) => `${" ".repeat(row.indent ?? 0)}${piTheme().fg("dim", row.label)}${" ".repeat(Math.max(1, column - (row.indent ?? 0) - visibleWidth(row.label)))}`;
81
+ const render = (rows) => new Text(rows.map(row => `${prefix(row)}${row.value}`).join("\n"), 1, 0).render(width);
82
+ const preamble = [];
83
+ for (const row of identity) {
84
+ if (row.label !== "File:") {
85
+ preamble.push(`${prefix(row)}${row.value}`);
86
+ continue;
90
87
  }
91
- sections.push({ title: "Cost", rows: render(cost) });
88
+ const label = prefix(row);
89
+ const firstWidth = Math.max(0, contentWidth - visibleWidth(label));
90
+ const [first = "", ...continuation] = splitSessionFile(row.value, firstWidth, contentWidth);
91
+ preamble.push(firstWidth > 0 ? `${label}${first}` : piTheme().fg("dim", row.label), ...continuation);
92
92
  }
93
- return { preamble: render(preamble), sections };
93
+ return {
94
+ preamble: new Text(preamble.join("\n"), 1, 0).render(width),
95
+ sections: groups.map(group => ({ title: group.title, rows: render(group.rows) })),
96
+ };
94
97
  }
@@ -2,6 +2,8 @@ import { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel, type SemanticShort
2
2
  import { type MarkdownTheme, type SelectListTheme } from "@earendil-works/pi-tui";
3
3
  export { PINNED_PI_LAYOUT, applyPiTheme, applyPiThemeInstance, currentPiThemeName, ensurePiTheme, getAvailablePiThemes, loadPiTheme, piCanvasBackgroundAnsi, piTheme, setPiAccentColor, setPiPackageBorderProjectionEnabled, } from "./upstream/theme/theme.js";
4
4
  type Paint = (text: string) => string;
5
+ /** The owned floating-panel background in the live terminal color capability. */
6
+ export declare function paintPiFloatingDialog(text: string): string;
5
7
  /** The live Pi theme in neutral roles; `panel` and `disabled` default to plain and dim. */
6
8
  export declare function piUiTheme(options?: {
7
9
  readonly panel?: Paint;
@@ -18,6 +20,8 @@ export declare function getPiMarkdownTheme(): MarkdownTheme;
18
20
  export declare function getPiHotkeysMarkdownTheme(): MarkdownTheme;
19
21
  /** Select-list roles resolved through A1's active theme rather than Pi's package-global base theme. */
20
22
  export declare function getPiSelectListTheme(): SelectListTheme;
23
+ /** Display casing for internal headings, preserving provider spelling and acronyms. */
24
+ export declare function internalDialogTitle(title: string): string;
21
25
  /** Paint one selected bare-A1 list item with the standard blue selection surface. */
22
26
  export declare function renderPiListSelection(content: string): string;
23
27
  /** One standard bare-A1 modal row, clipped before its item-bounded selection is painted. */
@@ -1,8 +1,13 @@
1
1
  import { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel, renderSemanticShortcutHints, } from "../../../contracts/presentation/index.js";
2
+ import { renderFloatingDialogSurface } from "../../../ui/components/spans.js";
2
3
  import { stripTerminalSequences, truncateToWidth } from "@earendil-works/pi-tui";
3
4
  import { getMarkdownTheme } from "../startup-public.js";
4
5
  import { onPiThemeChange, piTheme, renderPiAccentPreview } from "./upstream/theme/theme.js";
5
6
  export { PINNED_PI_LAYOUT, applyPiTheme, applyPiThemeInstance, currentPiThemeName, ensurePiTheme, getAvailablePiThemes, loadPiTheme, piCanvasBackgroundAnsi, piTheme, setPiAccentColor, setPiPackageBorderProjectionEnabled, } from "./upstream/theme/theme.js";
7
+ /** The owned floating-panel background in the live terminal color capability. */
8
+ export function paintPiFloatingDialog(text) {
9
+ return renderFloatingDialogSurface(text, piTheme().getColorMode());
10
+ }
6
11
  /** The live Pi theme in neutral roles; `panel` and `disabled` default to plain and dim. */
7
12
  export function piUiTheme(options = {}) {
8
13
  return {
@@ -48,6 +53,10 @@ export function getPiSelectListTheme() {
48
53
  noMatch: text => piTheme().fg("muted", text),
49
54
  };
50
55
  }
56
+ /** Display casing for internal headings, preserving provider spelling and acronyms. */
57
+ export function internalDialogTitle(title) {
58
+ return title.replace(/:\s*$/u, "").replace(/\b[a-z][a-z]*/gu, (word, offset) => offset > 0 && /^(a|an|and|for|in|of|on|or|the|to|with)$/u.test(word) ? word : word[0].toUpperCase() + word.slice(1));
59
+ }
51
60
  /** Paint one selected bare-A1 list item with the standard blue selection surface. */
52
61
  export function renderPiListSelection(content) {
53
62
  return piTheme().bg("selectedBg", content);
@@ -1,16 +1,17 @@
1
1
  /**
2
- * Provenance: @earendil-works/pi-coding-agent 1.5.0 (MIT), commit f9a26365568127461c47a07a37dcb42c140a3ac5,
2
+ * Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
3
3
  * packages/coding-agent/src/core/keybindings.ts.
4
4
  * Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
5
- * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
6
- * selection, Tab tree-filter cycling, tree-local navigation aliases, cross-platform Alt+Up
7
- * queued-message restoration, and an implicit Ctrl+C selection-cancel alias omitted from shortcut
8
- * labels.
9
- * Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut,
5
+ * resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, Ctrl+M model cycling
6
+ * with ordinary Enter preserved, Ctrl+P Launcher, command-only model selection, Tab tree-filter
7
+ * cycling, tree-local navigation aliases, cross-platform Alt+Up queued-message restoration, and an
8
+ * implicit Ctrl+C selection-cancel alias omitted from shortcut labels.
9
+ * Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut, owned-apps-shortcuts,
10
10
  * owned-input-keybinding-aliases, owned-session-tree-dialog, owned-dialog-ctrl-c-cancel.
11
11
  */
12
12
  import { type Keybinding, type KeybindingDefinitions, type KeybindingsConfig, type KeyId, KeybindingsManager as TuiKeybindingsManager } from "@earendil-works/pi-tui";
13
13
  export interface AppKeybindings {
14
+ "owned.apps.open": true;
14
15
  "app.interrupt": true;
15
16
  "app.clear": true;
16
17
  "app.exit": true;
@@ -153,7 +154,7 @@ export declare const KEYBINDINGS: {
153
154
  readonly description: "Restore queued messages";
154
155
  };
155
156
  readonly "app.clipboard.pasteImage": {
156
- readonly defaultKeys: "ctrl+v" | "alt+v";
157
+ readonly defaultKeys: "ctrl+v" | ("ctrl+v" | "alt+v")[];
157
158
  readonly description: "Paste files on macOS, images, or text from clipboard";
158
159
  };
159
160
  readonly "app.session.new": {