@timurproko/a1 0.2.7-dev.1252 → 0.2.7-dev.1255

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 (253) hide show
  1. package/bin/ui.js +3 -3
  2. package/dist/app/session-shell/clipboard-diagnostics.d.ts +2 -2
  3. package/dist/app/session-shell/clipboard-image.d.ts +0 -1
  4. package/dist/app/session-shell/clipboard-image.js +1 -1
  5. package/dist/app/session-shell/dialog-coordinator.d.ts +6 -5
  6. package/dist/app/session-shell/dialog-coordinator.js +3 -4
  7. package/dist/app/session-shell/dock-chrome.d.ts +11 -6
  8. package/dist/app/session-shell/dock-chrome.js +5 -8
  9. package/dist/app/session-shell/editor-hyperlink-budget.d.ts +0 -2
  10. package/dist/app/session-shell/editor-hyperlink-budget.js +1 -1
  11. package/dist/app/session-shell/exit-notice.d.ts +2 -2
  12. package/dist/app/session-shell/exit-notice.js +1 -1
  13. package/dist/app/session-shell/extension-surface.d.ts +8 -7
  14. package/dist/app/session-shell/helper-pool.d.ts +3 -4
  15. package/dist/app/session-shell/helper-pool.js +1 -1
  16. package/dist/app/session-shell/image-preparation-client.d.ts +4 -3
  17. package/dist/app/session-shell/image-preparation.d.ts +0 -2
  18. package/dist/app/session-shell/image-preparation.js +2 -2
  19. package/dist/app/session-shell/image-source.d.ts +2 -3
  20. package/dist/app/session-shell/image-source.js +2 -2
  21. package/dist/app/session-shell/input-surface-switch.d.ts +9 -7
  22. package/dist/app/session-shell/input-surface-switch.js +1 -2
  23. package/dist/app/session-shell/login-interaction.d.ts +5 -4
  24. package/dist/app/session-shell/login-interaction.js +2 -3
  25. package/dist/app/session-shell/paste-executor.d.ts +4 -8
  26. package/dist/app/session-shell/paste-executor.js +2 -2
  27. package/dist/app/session-shell/paste-protocol.d.ts +2 -2
  28. package/dist/app/session-shell/path-chip-presentation.d.ts +0 -5
  29. package/dist/app/session-shell/path-chip-presentation.js +3 -3
  30. package/dist/app/session-shell/prompt-chips.d.ts +8 -7
  31. package/dist/app/session-shell/prompt-history-controller.d.ts +3 -3
  32. package/dist/app/session-shell/prompt-submission.d.ts +5 -2
  33. package/dist/app/session-shell/prompt-submission.js +4 -5
  34. package/dist/app/session-shell/quit-outro-effects.d.ts +3 -3
  35. package/dist/app/session-shell/quit-outro-effects.js +1 -4
  36. package/dist/app/session-shell/quit-outro.d.ts +9 -12
  37. package/dist/app/session-shell/quit-outro.js +11 -11
  38. package/dist/app/session-shell/response-copy-protocol.d.ts +3 -2
  39. package/dist/app/session-shell/response-copy-transport.d.ts +2 -14
  40. package/dist/app/session-shell/response-copy-transport.js +1 -1
  41. package/dist/app/session-shell/session-lifecycle.d.ts +2 -1
  42. package/dist/app/session-shell/session-shell-root.d.ts +74 -89
  43. package/dist/app/session-shell/session-shell-root.js +81 -113
  44. package/dist/app/session-shell/session-shell.d.ts +13 -16
  45. package/dist/app/session-shell/session-shell.js +144 -268
  46. package/dist/app/session-shell/session-viewport-controller.d.ts +7 -6
  47. package/dist/app/session-shell/session-viewport-controller.js +2 -2
  48. package/dist/app/session-shell/settings-bindings.d.ts +24 -2
  49. package/dist/app/session-shell/settings-bindings.js +24 -0
  50. package/dist/app/session-shell/share-operation.d.ts +3 -3
  51. package/dist/app/session-shell/share-operation.js +4 -6
  52. package/dist/app/session-shell/shell-clipboard.d.ts +59 -0
  53. package/dist/app/session-shell/shell-clipboard.js +119 -0
  54. package/dist/app/session-shell/shell-editor-factory.d.ts +8 -4
  55. package/dist/app/session-shell/shell-editor-factory.js +13 -15
  56. package/dist/app/session-shell/shell-host.d.ts +16 -17
  57. package/dist/app/session-shell/shell-host.js +18 -19
  58. package/dist/app/session-shell/shell-layout.d.ts +2 -2
  59. package/dist/app/session-shell/shell-prompt-suggestions.d.ts +26 -0
  60. package/dist/app/session-shell/shell-prompt-suggestions.js +48 -0
  61. package/dist/app/session-shell/suggestion-surface.d.ts +1 -6
  62. package/dist/app/session-shell/suggestion-surface.js +1 -1
  63. package/dist/app/session-shell/system-clipboard.d.ts +2 -14
  64. package/dist/app/session-shell/system-clipboard.js +2 -7
  65. package/dist/app/session-shell/transcript-document.d.ts +11 -8
  66. package/dist/app/session-shell/transcript-document.js +13 -14
  67. package/dist/app/session-shell/viewport-layout.d.ts +2 -2
  68. package/dist/app/session-shell/workflow-output.d.ts +5 -3
  69. package/dist/app/session-shell/workflow-output.js +13 -15
  70. package/dist/cli/package-messages.d.ts +0 -1
  71. package/dist/cli/package-messages.js +1 -1
  72. package/dist/cli/session-context.d.ts +1 -1
  73. package/dist/cli/uninstall.d.ts +3 -2
  74. package/dist/composition/owned-settings.d.ts +2 -1
  75. package/dist/composition/owned-settings.js +0 -3
  76. package/dist/composition/owned-ui.d.ts +1 -1
  77. package/dist/composition/owned-ui.js +27 -27
  78. package/dist/composition/reference-selection-options.d.ts +1 -1
  79. package/dist/composition/reference-selection-options.js +1 -1
  80. package/dist/composition/session-runtime-registry.d.ts +2 -1
  81. package/dist/composition/settings-route-host.js +3 -10
  82. package/dist/composition/settings-search-input.d.ts +2 -1
  83. package/dist/composition/startup-route.d.ts +2 -2
  84. package/dist/composition/startup-route.js +2 -2
  85. package/dist/contracts/owned-ui/extension-ui.d.ts +2 -1
  86. package/dist/contracts/owned-ui/index.d.ts +1 -0
  87. package/dist/contracts/owned-ui/session-presenters.d.ts +3 -0
  88. package/dist/contracts/owned-ui/shell-components.d.ts +543 -0
  89. package/dist/contracts/owned-ui/shell-components.js +1 -0
  90. package/dist/contracts/presentation/component.d.ts +17 -0
  91. package/dist/contracts/presentation/component.js +8 -0
  92. package/dist/contracts/presentation/index.d.ts +6 -15
  93. package/dist/contracts/presentation/index.js +2 -0
  94. package/dist/contracts/presentation/terminal.d.ts +290 -0
  95. package/dist/contracts/presentation/terminal.js +1 -0
  96. package/dist/contracts/presentation/theme.d.ts +46 -0
  97. package/dist/contracts/presentation/theme.js +1 -0
  98. package/dist/features/launch/development-launch.js +1 -1
  99. package/dist/features/launch/runtime-selection.d.ts +2 -2
  100. package/dist/features/launch/runtime-selection.js +2 -2
  101. package/dist/features/owned-ui/project-trust-prompt.d.ts +2 -2
  102. package/dist/features/owned-ui/project-trust-prompt.js +1 -1
  103. package/dist/features/owned-ui/release-note-state.d.ts +2 -1
  104. package/dist/features/owned-ui/settings-app.js +38 -219
  105. package/dist/features/owned-ui/settings-edits.d.ts +31 -0
  106. package/dist/features/owned-ui/settings-edits.js +139 -0
  107. package/dist/features/owned-ui/settings-scroll-rail.d.ts +29 -0
  108. package/dist/features/owned-ui/settings-scroll-rail.js +79 -0
  109. package/dist/features/owned-ui/settings-search-input.d.ts +2 -1
  110. package/dist/features/prompt-history/service.d.ts +3 -2
  111. package/dist/foundation/lifecycle/claim-gate.d.ts +2 -1
  112. package/dist/foundation/release/automatic-update.d.ts +5 -14
  113. package/dist/foundation/release/automatic-update.js +14 -14
  114. package/dist/foundation/release/bootstrap.js +4 -4
  115. package/dist/foundation/release/dependency-certification.d.ts +0 -1
  116. package/dist/foundation/release/dependency-certification.js +1 -1
  117. package/dist/foundation/release/early-accent.d.ts +2 -2
  118. package/dist/foundation/release/early-accent.js +1 -1
  119. package/dist/foundation/release/index.d.ts +2 -2
  120. package/dist/foundation/release/index.js +1 -1
  121. package/dist/foundation/release/latest-release.d.ts +3 -18
  122. package/dist/foundation/release/latest-release.js +10 -10
  123. package/dist/foundation/release/ordinary-launch-selection.d.ts +1 -1
  124. package/dist/foundation/release/ordinary-launch-selection.js +1 -1
  125. package/dist/foundation/release/release-gc.d.ts +3 -3
  126. package/dist/foundation/release/release-gc.js +4 -4
  127. package/dist/foundation/release/{cohort-state.d.ts → release-state.d.ts} +23 -23
  128. package/dist/foundation/release/{cohort-state.js → release-state.js} +8 -8
  129. package/dist/foundation/release/update-activation.js +2 -2
  130. package/dist/foundation/release/update-execution-lease.d.ts +2 -3
  131. package/dist/foundation/release/update-execution-lease.js +1 -1
  132. package/dist/foundation/release/update-launch.d.ts +2 -2
  133. package/dist/foundation/release/update.d.ts +0 -2
  134. package/dist/foundation/release/update.js +14 -14
  135. package/dist/foundation/startup/startup-budget.d.ts +5 -5
  136. package/dist/foundation/startup/startup-budget.js +2 -2
  137. package/dist/integrations/pi/components/editor-interaction.d.ts +4 -12
  138. package/dist/integrations/pi/components/index.d.ts +15 -9
  139. package/dist/integrations/pi/components/index.js +9 -5
  140. package/dist/integrations/pi/components/lazy-selectors.d.ts +2 -10
  141. package/dist/integrations/pi/components/lazy-selectors.js +1 -1
  142. package/dist/integrations/pi/components/modal-frame.d.ts +5 -22
  143. package/dist/integrations/pi/components/modal-frame.js +3 -23
  144. package/dist/integrations/pi/components/owned-editor-ux.d.ts +4 -3
  145. package/dist/integrations/pi/components/prompt-input-port.d.ts +2 -19
  146. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -1
  147. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +3 -2
  148. package/dist/integrations/pi/components/shell-footer-status.d.ts +2 -2
  149. package/dist/integrations/pi/components/shell-footer-status.js +3 -2
  150. package/dist/integrations/pi/components/shell-presenters-info.d.ts +6 -5
  151. package/dist/integrations/pi/components/shell-presenters-info.js +2 -1
  152. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +2 -1
  153. package/dist/integrations/pi/components/shell-shared-facade.d.ts +33 -227
  154. package/dist/integrations/pi/components/shell-shared-facade.js +8 -1
  155. package/dist/integrations/pi/components/skills-command.d.ts +1 -9
  156. package/dist/integrations/pi/components/skills-command.js +5 -5
  157. package/dist/integrations/pi/components/theme.d.ts +9 -2
  158. package/dist/integrations/pi/components/theme.js +17 -2
  159. package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +2 -1
  160. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  161. package/dist/integrations/pi/engine/adapter.d.ts +3 -5
  162. package/dist/integrations/pi/engine/adapter.js +3 -12
  163. package/dist/integrations/pi/engine/changelog.d.ts +2 -2
  164. package/dist/integrations/pi/engine/changelog.js +3 -3
  165. package/dist/integrations/pi/engine/compaction-progress.d.ts +2 -3
  166. package/dist/integrations/pi/engine/compaction-progress.js +1 -1
  167. package/dist/integrations/pi/engine/conformance.d.ts +2 -1
  168. package/dist/integrations/pi/engine/default-model-setting.d.ts +0 -2
  169. package/dist/integrations/pi/engine/default-model-setting.js +1 -2
  170. package/dist/integrations/pi/engine/event-delivery.d.ts +2 -1
  171. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -1
  172. package/dist/integrations/pi/engine/host.js +2 -2
  173. package/dist/integrations/pi/engine/message-values.d.ts +0 -1
  174. package/dist/integrations/pi/engine/message-values.js +1 -1
  175. package/dist/integrations/pi/engine/pending-delivery.d.ts +0 -4
  176. package/dist/integrations/pi/engine/pending-delivery.js +3 -3
  177. package/dist/integrations/pi/engine/ports.d.ts +7 -1
  178. package/dist/integrations/pi/engine/ports.js +4 -0
  179. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +2 -1
  180. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +3 -2
  181. package/dist/integrations/pi/engine/provider-authentication.d.ts +1 -1
  182. package/dist/integrations/pi/engine/public-main-entry.d.ts +1 -5
  183. package/dist/integrations/pi/engine/public-main-entry.js +1 -1
  184. package/dist/integrations/pi/engine/repository-pr-rest.d.ts +1 -9
  185. package/dist/integrations/pi/engine/repository-pr-rest.js +2 -2
  186. package/dist/integrations/pi/engine/repository-pr.d.ts +1 -3
  187. package/dist/integrations/pi/engine/repository-pr.js +1 -1
  188. package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
  189. package/dist/integrations/pi/engine/session-integration.d.ts +1 -1
  190. package/dist/integrations/pi/engine/session-runtime.d.ts +4 -3
  191. package/dist/integrations/pi/engine/settings-bridge.d.ts +0 -2
  192. package/dist/integrations/pi/engine/settings-bridge.js +1 -1
  193. package/dist/integrations/pi/engine/settings-port.d.ts +3 -2
  194. package/dist/integrations/pi/engine/transcript-projection.d.ts +2 -1
  195. package/dist/integrations/pi/engine/workflow-contexts.d.ts +1 -1
  196. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -1
  197. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -6
  198. package/dist/integrations/pi/engine/workflow-support.js +4 -18
  199. package/dist/integrations/pi/engine/workflows.d.ts +5 -5
  200. package/dist/integrations/pi/session-presenters/components.d.ts +3 -0
  201. package/dist/integrations/pi/session-presenters/components.js +75 -0
  202. package/dist/integrations/pi/session-presenters/index.d.ts +1 -0
  203. package/dist/integrations/pi/session-presenters/index.js +1 -0
  204. package/dist/integrations/pi/session-presenters/presentation.d.ts +3 -0
  205. package/dist/integrations/pi/session-presenters/presentation.js +10 -0
  206. package/dist/integrations/pi/session-presenters/presenters.d.ts +2 -1
  207. package/dist/integrations/pi/session-presenters/presenters.js +3 -1
  208. package/dist/integrations/pi/tui-runtime/adapter.js +21 -30
  209. package/dist/integrations/pi/tui-runtime/contracts.d.ts +22 -166
  210. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +5 -40
  211. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -0
  212. package/dist/integrations/pi/tui-runtime/index.js +1 -0
  213. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +4 -6
  214. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -8
  215. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +3 -9
  216. package/dist/integrations/pi/tui-runtime/runtime-factory.d.ts +3 -0
  217. package/dist/integrations/pi/tui-runtime/runtime-factory.js +11 -0
  218. package/dist/ui/components/index.d.ts +2 -2
  219. package/dist/ui/components/index.js +1 -1
  220. package/dist/ui/components/selection-copy.d.ts +0 -1
  221. package/dist/ui/components/selection-copy.js +0 -12
  222. package/dist/ui/components/theme.d.ts +4 -19
  223. package/dist/ui/components/theme.js +6 -0
  224. package/dist/ui/settings/declarations.d.ts +2 -3
  225. package/dist/ui/settings/declarations.js +2 -3
  226. package/dist/ui/settings/early-accent.js +1 -1
  227. package/dist/ui/settings/index.d.ts +3 -5
  228. package/dist/ui/settings/index.js +2 -3
  229. package/dist/ui/settings/manager.d.ts +0 -4
  230. package/dist/ui/settings/manager.js +6 -62
  231. package/dist/ui/settings/resolution.d.ts +6 -18
  232. package/dist/ui/settings/resolution.js +4 -103
  233. package/dist/ui/settings/sections.d.ts +0 -4
  234. package/dist/ui/settings/sections.js +1 -1
  235. package/docs/architecture/boundaries.md +2 -2
  236. package/docs/architecture/internal-naming.md +2 -0
  237. package/docs/architecture/project-structure.md +4 -2
  238. package/docs/validation.md +1 -1
  239. package/package.json +3 -4
  240. package/dist/integrations/pi/components/components.d.ts +0 -4
  241. package/dist/integrations/pi/components/components.js +0 -48
  242. package/dist/integrations/pi/components/conformance.d.ts +0 -18
  243. package/dist/integrations/pi/components/conformance.js +0 -70
  244. package/dist/integrations/pi/components/shell-components.d.ts +0 -7
  245. package/dist/integrations/pi/components/shell-components.js +0 -7
  246. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +0 -33
  247. package/dist/integrations/pi/components/upstream/theme/theme-controller.js +0 -114
  248. package/dist/ui/settings/migrations.d.ts +0 -19
  249. package/dist/ui/settings/migrations.js +0 -225
  250. package/dist/ui/settings/progress-palette.generated.d.ts +0 -139
  251. package/dist/ui/settings/progress-palette.generated.js +0 -139
  252. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.d.ts +0 -0
  253. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.js +0 -0
@@ -1,12 +1,12 @@
1
- import type { PiTuiPointerSurface } from "../../integrations/pi/tui-runtime/contracts.js";
1
+ import type { OwnedUiShellEditorPort } from "../../contracts/owned-ui/index.js";
2
+ import type { TerminalPointerSurface } from "../../contracts/presentation/index.js";
2
3
  import type { OwnedUiViewportSettings } from "../../contracts/owned-ui/index.js";
3
4
  import { type TranscriptViewportFrame, type TranscriptViewportFrameInput } from "../../ui/components/transcript-viewport.js";
4
5
  import type { SelectionCopySnapshot } from "../../ui/components/selection-copy.js";
5
- import type { PiShellEditorPort } from "../../integrations/pi/components/shell-shared-facade.js";
6
6
  import type { PasteEvent } from "./paste-protocol.js";
7
- export interface SessionViewportControllerOptions {
7
+ interface SessionViewportControllerOptions {
8
8
  readonly enabled: boolean;
9
- readonly editor: PiShellEditorPort;
9
+ readonly editor: OwnedUiShellEditorPort;
10
10
  readonly requestRender: (force?: boolean) => void;
11
11
  readonly requestHyperlinkCleanup?: (rows?: readonly number[]) => void;
12
12
  /** Whether mutable editor URL chips need targeted link-cell cleanup on deletion. */
@@ -61,8 +61,8 @@ export declare class SessionViewportController {
61
61
  tailControlHovered(target: TailControlRegion | undefined): boolean;
62
62
  /** Replacement input changed before its new bounds have been painted. */
63
63
  invalidateInputSurface(): void;
64
- setInputSurfaceFrame(surface: PiTuiPointerSurface | undefined): void;
65
- setOverlaySurfaces(surfaces: readonly PiTuiPointerSurface[] | null): void;
64
+ setInputSurfaceFrame(surface: TerminalPointerSurface | undefined): void;
65
+ setOverlaySurfaces(surfaces: readonly TerminalPointerSurface[] | null): void;
66
66
  get inputGeometryReady(): boolean;
67
67
  compose(input: Omit<TranscriptViewportFrameInput, "pointerPosition">): TranscriptViewportFrame;
68
68
  composeDockOnly(dockRows: readonly string[], width: number, height: number, copyableDockRows?: TranscriptViewportFrameInput["copyableDockRows"]): TranscriptViewportFrame | null;
@@ -74,3 +74,4 @@ export declare class SessionViewportController {
74
74
  clearPointerState(): void;
75
75
  handlePreInput(data: string, allowWheel?: boolean, now?: number, editorActive?: boolean): SessionViewportInputResult;
76
76
  }
77
+ export {};
@@ -1,4 +1,4 @@
1
- import { samePointerSurfaces } from "../../integrations/pi/tui-runtime/overlay-geometry.js";
1
+ import { samePointerSurfaces } from "../../contracts/presentation/index.js";
2
2
  import { TranscriptViewport } from "../../ui/components/transcript-viewport.js";
3
3
  import { readVisibleHyperlinks } from "../../ui/components/visible-hyperlinks.js";
4
4
  import { routeMouseInput } from "../../ui/components/mouse.js";
@@ -657,6 +657,6 @@ export class SessionViewportController {
657
657
  const SELECTION_AUTO_SCROLL_INTERVAL_MS = 30;
658
658
  const EDITOR_LINK_DELETION_INPUTS = new Set(["\b", "\u007f", "\u001b[3~"]);
659
659
  // Platform: Windows Terminal may consume Shift+Arrow for native selection. Ctrl+Arrow matches
660
- // solo A1's Windows binding while retaining Shift+Arrow whenever the terminal delivers it.
660
+ // A1's Windows binding while retaining Shift+Arrow whenever the terminal delivers it.
661
661
  const PROMPT_UP_INPUTS = new Set(["\u001b[1;2A", "\u001b[1;5A"]);
662
662
  const PROMPT_DOWN_INPUTS = new Set(["\u001b[1;2B", "\u001b[1;5B"]);
@@ -1,8 +1,8 @@
1
- import type { OwnedUiSessionSettingsPort, OwnedUiSessionSettingsSnapshot } from "../../contracts/owned-ui/index.js";
1
+ import type { OwnedUiSessionSettingsPort, OwnedUiSessionSettingsSnapshot, OwnedUiViewportSettingsPort } from "../../contracts/owned-ui/index.js";
2
2
  import type { OwnedUiPresenterTerminal } from "./shell-host.js";
3
3
  import type { OwnedUiSessionShellRoot } from "./session-shell-root.js";
4
4
  /** What the session's live setting effects reach: terminal modes, root presentation, and the presenter's progress flag. */
5
- export interface SessionSettingsTargets {
5
+ interface SessionSettingsTargets {
6
6
  readonly terminal: Pick<OwnedUiPresenterTerminal, "setHardwareCursor" | "setClearOnShrink" | "setWheelScrollLines">;
7
7
  readonly root: Pick<OwnedUiSessionShellRoot, "setEditorPaddingX" | "setAutocompleteMaxVisible" | "setOutputPad" | "setHideThinkingBlock" | "setMermaidRenderingMode" | "setImagePresentation" | "setFullscreenCopyOnSelect">;
8
8
  setTerminalProgress(enabled: boolean): void;
@@ -15,3 +15,25 @@ export interface SessionSettingsTargets {
15
15
  export declare function bindSessionSettings(settings: Pick<OwnedUiSessionSettingsPort, "bindOwner" | "productMode">, snapshot: OwnedUiSessionSettingsSnapshot, targets: SessionSettingsTargets, options: {
16
16
  readonly customViewport: boolean;
17
17
  }): () => void;
18
+ /** The shell's own live setting sources besides the session settings; absent sources bind nothing. */
19
+ export interface ShellSettingSources {
20
+ readonly customViewport: boolean;
21
+ readonly promptImages: {
22
+ onChange(listener: () => void): () => void;
23
+ } | null;
24
+ readonly skills: {
25
+ onChange(listener: () => void): () => void;
26
+ } | null;
27
+ readonly viewport: OwnedUiViewportSettingsPort | undefined;
28
+ readonly historyScope: ((listener: (scope: "unique" | "shared") => void) => () => void) | undefined;
29
+ }
30
+ /** What those sources change: the prompt-image notice, the command menu, the viewport, and the transcript binding. */
31
+ export interface ShellSettingEffects {
32
+ readonly root: Pick<OwnedUiSessionShellRoot, "setViewportConfig">;
33
+ promptImageLimitChanged(): void;
34
+ skillsChanged(): void;
35
+ historyScopeChanged(scope: "unique" | "shared"): void;
36
+ }
37
+ /** Seeds the viewport and subscribes every shell setting source; the returned release undoes all of them. */
38
+ export declare function bindShellSettings(sources: ShellSettingSources, effects: ShellSettingEffects): () => void;
39
+ export {};
@@ -89,3 +89,27 @@ export function bindSessionSettings(settings, snapshot, targets, options) {
89
89
  }
90
90
  return release;
91
91
  }
92
+ /** Seeds the viewport and subscribes every shell setting source; the returned release undoes all of them. */
93
+ export function bindShellSettings(sources, effects) {
94
+ const releases = [];
95
+ const release = () => { for (const unbind of releases.splice(0).reverse())
96
+ unbind(); };
97
+ try {
98
+ if (sources.promptImages !== null)
99
+ releases.push(sources.promptImages.onChange(() => effects.promptImageLimitChanged()));
100
+ // Rationale: the same listener refreshes the menu for the A1 presentation choice and for the engine's
101
+ // skill-command registration, both of which the owned settings manager reports through one change.
102
+ if (sources.skills !== null)
103
+ releases.push(sources.skills.onChange(() => effects.skillsChanged()));
104
+ effects.root.setViewportConfig(sources.viewport?.snapshot() ?? { scrollbarAppearance: "auto", scrollbarStyle: "thin", scrollbarSpeed: "normal" });
105
+ if (sources.customViewport && sources.viewport !== undefined)
106
+ releases.push(sources.viewport.onChange(settings => effects.root.setViewportConfig(settings)));
107
+ if (sources.customViewport && sources.historyScope !== undefined)
108
+ releases.push(sources.historyScope(scope => effects.historyScopeChanged(scope)));
109
+ }
110
+ catch (error) {
111
+ release();
112
+ throw error;
113
+ }
114
+ return release;
115
+ }
@@ -1,3 +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;
1
+ import type { UiTheme } from "../../contracts/presentation/index.js";
2
+ /** The completed share result in bare A1: dim labels with the share and gist URLs made clickable. */
3
+ export declare function renderShellShareResult(message: string, gistUrl: string, theme: UiTheme, hyperlink: (url: string) => string): string;
@@ -1,11 +1,9 @@
1
- import { createPiShellOperationLoader, createPiShellShareOperationDialog, piShellHyperlink, piTheme, } from "../../integrations/pi/components/index.js";
2
1
  import { nativeHyperlinkStyle } from "../../ui/components/index.js";
3
- export { createPiShellOperationLoader, createPiShellShareOperationDialog };
4
- export function renderPiShellShareResult(message, gistUrl) {
5
- const theme = piTheme();
2
+ /** The completed share result in bare A1: dim labels with the share and gist URLs made clickable. */
3
+ export function renderShellShareResult(message, gistUrl, theme, hyperlink) {
6
4
  const match = /^Share URL: (\S+)$/u.exec(message);
7
5
  const linked = match === null
8
6
  ? 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));
7
+ : `${theme.fg("dim", "Share URL: ")}${hyperlink(match[1])}`;
8
+ return nativeHyperlinkStyle(`${linked}\n${theme.fg("dim", "Gist: ")}${hyperlink(gistUrl)}`, text => theme.fg("mdLink", text));
11
9
  }
@@ -0,0 +1,59 @@
1
+ import { type OwnedUiShellClipboardContent } from "../../contracts/owned-ui/index.js";
2
+ import type { OwnedUiClipboardServices } from "./clipboard-services.js";
3
+ import type { PastePreparationClientOptions } from "./paste-preparation-client.js";
4
+ import type { PasteSource } from "./paste-protocol.js";
5
+ import { ResponseCopyCoordinator } from "./response-copy-coordinator.js";
6
+ import type { SelectionCopySnapshot } from "../../ui/components/index.js";
7
+ import { type CopyResult, type ResponseCopyEvent } from "./response-copy-protocol.js";
8
+ import { type ResponseCopyExecutor } from "./response-copy-transport.js";
9
+ /** A platform clipboard seam; production reads and writes through A1's system clipboard adapter. */
10
+ export interface OwnedUiClipboardPort {
11
+ readText(signal?: AbortSignal): Promise<string | null>;
12
+ readImage?(signal?: AbortSignal): Promise<{
13
+ readonly data: string;
14
+ readonly mimeType: string;
15
+ } | null>;
16
+ writeText?(text: string, signal?: AbortSignal): Promise<void>;
17
+ }
18
+ /** What one shell's clipboard needs: the borrowed process-wide helpers, its terminal, and optional test seams. */
19
+ export interface ShellClipboardOptions {
20
+ /** Bare A1 owns response copy; comparison profiles keep the terminal-control write. */
21
+ readonly ownedCopy: boolean;
22
+ readonly shared?: OwnedUiClipboardServices;
23
+ readonly clipboard?: OwnedUiClipboardPort;
24
+ readonly responseCopy?: {
25
+ readonly execute?: ResponseCopyExecutor;
26
+ readonly onEvent?: (event: ResponseCopyEvent) => void;
27
+ };
28
+ readonly pastePreparation?: Omit<PastePreparationClientOptions, "onEvent" | "spareIdleMs">;
29
+ /** True when a test terminal stands in for the process terminal. */
30
+ readonly substituteTerminal: boolean;
31
+ readonly disposed: () => boolean;
32
+ /** Whether the shell's terminal is running and attached. */
33
+ readonly terminalAttached: () => boolean;
34
+ readonly writeControl: (control: string) => void;
35
+ }
36
+ /**
37
+ * One shell's clipboard: response copy, the comparison write path, clipboard reads, and paste capture. It
38
+ * borrows the process-wide helper pools from composition and never disposes them.
39
+ */
40
+ export declare class ShellClipboard {
41
+ #private;
42
+ /** Null in comparison profiles, which keep the pinned terminal-control write. */
43
+ readonly responseCopy: ResponseCopyCoordinator | null;
44
+ constructor(options: ShellClipboardOptions);
45
+ /** Paste-preparation options carrying the shared paste pool, or undefined when neither is supplied. */
46
+ pastePreparation(): Omit<PastePreparationClientOptions, "onEvent" | "spareIdleMs"> | undefined;
47
+ /** Copies text: through owned response copy when present, otherwise the comparison terminal-control write. */
48
+ copyText(text: string): Promise<CopyResult> | undefined;
49
+ /** Copies a selection after any pending comparison write has settled. */
50
+ copySelection(snapshot: SelectionCopySnapshot): Promise<CopyResult> | undefined;
51
+ /** Reads the clipboard once any write this shell started has settled. */
52
+ readContent(signal?: AbortSignal): Promise<OwnedUiShellClipboardContent | null>;
53
+ capturePaste(): PasteSource;
54
+ /** The extension clipboard writer: true once delivered, false when submitted without confirmation. */
55
+ writeForExtension(text: string): Promise<boolean>;
56
+ warm(): void;
57
+ reset(): void;
58
+ dispose(): void;
59
+ }
@@ -0,0 +1,119 @@
1
+ import { ImageAttachmentError } from "../../contracts/owned-ui/index.js";
2
+ import { runImageWorker } from "./image-preparation-client.js";
3
+ import { ResponseCopyCoordinator } from "./response-copy-coordinator.js";
4
+ import { MAX_COPY_CONTROL_BYTES } from "./response-copy-protocol.js";
5
+ import { createResponseCopyExecutor, responseCopyDestination } from "./response-copy-transport.js";
6
+ import { writeSystemClipboardText } from "./system-clipboard.js";
7
+ /**
8
+ * One shell's clipboard: response copy, the comparison write path, clipboard reads, and paste capture. It
9
+ * borrows the process-wide helper pools from composition and never disposes them.
10
+ */
11
+ export class ShellClipboard {
12
+ /** Null in comparison profiles, which keep the pinned terminal-control write. */
13
+ responseCopy;
14
+ #options;
15
+ #executor;
16
+ #pendingWrite = Promise.resolve();
17
+ constructor(options) {
18
+ this.#options = options;
19
+ const { clipboard, shared, responseCopy } = options;
20
+ this.#executor = !options.ownedCopy || responseCopy?.execute !== undefined ? undefined : createResponseCopyExecutor({
21
+ ...(shared === undefined ? {} : { pool: shared.copy }),
22
+ ...(!options.substituteTerminal && clipboard === undefined ? {} : { destination: "terminal" }),
23
+ ...(clipboard?.writeText === undefined ? {} : { writeText: (text, signal) => clipboard.writeText(text, signal) }),
24
+ // Invariant: the owned host is the route contract. Pseudoterminal children must not infer
25
+ // clipboard reachability from Node's view of stdout, and inactive shells are fenced here.
26
+ terminal: { submit: async (control, signal) => {
27
+ if (signal.aborted || options.disposed() || !options.terminalAttached())
28
+ throw new Error("Copy canceled");
29
+ // Performance: never grow the real terminal's pending buffer with another clipboard payload.
30
+ if (!options.substituteTerminal && process.stdout.writableLength + control.length > MAX_COPY_CONTROL_BYTES) {
31
+ throw new Error("Clipboard terminal is busy");
32
+ }
33
+ options.writeControl(control);
34
+ } },
35
+ });
36
+ this.responseCopy = !options.ownedCopy ? null : new ResponseCopyCoordinator({
37
+ execute: responseCopy?.execute ?? this.#executor,
38
+ ...(responseCopy?.onEvent === undefined ? {} : { onEvent: responseCopy.onEvent }),
39
+ });
40
+ }
41
+ /** Paste-preparation options carrying the shared paste pool, or undefined when neither is supplied. */
42
+ pastePreparation() {
43
+ const { pastePreparation, shared } = this.#options;
44
+ if (pastePreparation === undefined && shared === undefined)
45
+ return undefined;
46
+ return { ...pastePreparation, ...(shared === undefined ? {} : { pool: shared.paste }) };
47
+ }
48
+ /** Copies text: through owned response copy when present, otherwise the comparison terminal-control write. */
49
+ copyText(text) {
50
+ if (this.responseCopy !== null)
51
+ return this.responseCopy.submitText(text);
52
+ const { clipboard } = this.#options;
53
+ const write = () => {
54
+ if (this.#options.disposed())
55
+ return Promise.resolve();
56
+ this.#options.writeControl(`\u001b]52;c;${Buffer.from(text, "utf8").toString("base64")}\u0007`);
57
+ return clipboard === undefined ? writeSystemClipboardText(text) : clipboard.writeText?.(text) ?? Promise.resolve();
58
+ };
59
+ // Compatibility: comparison profiles retain their existing clipboard path.
60
+ this.#pendingWrite = write().catch(() => { });
61
+ return undefined;
62
+ }
63
+ /** Copies a selection after any pending comparison write has settled. */
64
+ copySelection(snapshot) {
65
+ return this.responseCopy?.submit(snapshot, this.#pendingWrite);
66
+ }
67
+ /** Reads the clipboard once any write this shell started has settled. */
68
+ async readContent(signal = new AbortController().signal) {
69
+ await this.#pendingWrite;
70
+ return this.#read(signal);
71
+ }
72
+ capturePaste() {
73
+ const before = this.responseCopy?.capturePasteBarrier() ?? (async () => true);
74
+ if (this.#options.clipboard !== undefined)
75
+ return { kind: "provided", read: signal => this.#read(signal), before };
76
+ if (responseCopyDestination(process.env) === "terminal")
77
+ return {
78
+ kind: "provided", before, read: async () => { throw new ImageAttachmentError("paste-unavailable"); },
79
+ };
80
+ return { kind: "native", before };
81
+ }
82
+ /** The extension clipboard writer: true once delivered, false when submitted without confirmation. */
83
+ async writeForExtension(text) {
84
+ const result = await this.responseCopy.submitText(text);
85
+ if (result.outcome === "delivered")
86
+ return true;
87
+ if (result.outcome === "submitted-unverified")
88
+ return false;
89
+ throw new Error(result.outcome === "timed-out" ? "Clipboard delivery timed out" : "Clipboard delivery could not be completed");
90
+ }
91
+ warm() { this.#executor?.warm(); }
92
+ reset() { this.responseCopy?.reset(); }
93
+ dispose() {
94
+ this.responseCopy?.dispose();
95
+ this.#executor?.dispose();
96
+ }
97
+ async #read(signal) {
98
+ if (signal.aborted)
99
+ throw new ImageAttachmentError("image-canceled");
100
+ const { clipboard } = this.#options;
101
+ if (clipboard === undefined)
102
+ return runImageWorker({ kind: "clipboard" }, signal);
103
+ try {
104
+ const image = await clipboard.readImage?.(signal);
105
+ if (image !== null && image !== undefined) {
106
+ const canonical = await runImageWorker({ kind: "canonicalize", source: image }, signal);
107
+ if (canonical !== null)
108
+ return { kind: "image", ...canonical };
109
+ }
110
+ }
111
+ catch (error) {
112
+ if (error instanceof ImageAttachmentError)
113
+ throw error;
114
+ // Compatibility: an unavailable image reader can still provide text.
115
+ }
116
+ const text = await clipboard.readText(signal);
117
+ return text === null ? null : { kind: "text", text };
118
+ }
119
+ }
@@ -1,14 +1,17 @@
1
- import { type PiShellEditorOptions, type PiShellEditorPort } from "../../integrations/pi/components/shell-shared-facade.js";
1
+ import type { OwnedUiShellComponents, OwnedUiShellEditorOptions, OwnedUiShellEditorPort } from "../../contracts/owned-ui/index.js";
2
+ import type { ThemeSource } from "../../contracts/presentation/index.js";
2
3
  import type { PasteEvent, PasteSource } from "./paste-protocol.js";
3
4
  import type { PromptChipStore } from "./prompt-chips.js";
4
5
  /** The shell callbacks the editor is wired to; the editor receives the whole object as its own options. */
5
- export type ShellEditorHandlers = Omit<PiShellEditorOptions, "onChange" | "onToolsExpand" | "keybindingProfile"> & {
6
+ type ShellEditorHandlers = Omit<OwnedUiShellEditorOptions, "onChange" | "onToolsExpand" | "keybindingProfile"> & {
6
7
  readonly requestRender: (force?: boolean) => void;
7
8
  readonly onEditorChange?: (text: string) => void;
8
9
  readonly captureClipboardPaste?: () => PasteSource;
9
10
  readonly pasteDiagnostics?: (event: PasteEvent) => void;
10
11
  };
11
- export interface ShellEditorPorts {
12
+ interface ShellEditorPorts {
13
+ readonly theme: ThemeSource;
14
+ readonly components: Pick<OwnedUiShellComponents, "editor" | "text">;
12
15
  readonly handlers: ShellEditorHandlers;
13
16
  readonly customViewport: boolean;
14
17
  readonly cwd: string;
@@ -24,4 +27,5 @@ export interface ShellEditorPorts {
24
27
  * Builds the session's prompt editor: chip-aware paste, selection, and hyperlink decoration over
25
28
  * the Pi editor, plus bare A1's prompt presentation and terminal paste diagnostics.
26
29
  */
27
- export declare function createShellEditor(ports: ShellEditorPorts): PiShellEditorPort;
30
+ export declare function createShellEditor(ports: ShellEditorPorts): OwnedUiShellEditorPort;
31
+ export {};
@@ -3,9 +3,6 @@ import { PromptInput } from "../../ui/components/prompt-input.js";
3
3
  import { backgroundSgrSpan, hyperlinkSgrSpan } from "../../ui/components/spans.js";
4
4
  import { faint, stripAnsi } from "../../ui/components/text.js";
5
5
  import { paintTextSelection } from "../../ui/components/text-selection.js";
6
- import { createPiShellEditor } from "../../integrations/pi/components/shell-editor-autocomplete.js";
7
- import { piTheme } from "../../integrations/pi/components/upstream/theme/theme.js";
8
- import { piShellTruncateToWidth, piShellVisibleWidth, } from "../../integrations/pi/components/shell-shared-facade.js";
9
6
  import { EditorHyperlinkBudget } from "./editor-hyperlink-budget.js";
10
7
  import { canPreparePasteInline } from "./paste-text-preparation.js";
11
8
  /**
@@ -15,13 +12,14 @@ import { canPreparePasteInline } from "./paste-text-preparation.js";
15
12
  export function createShellEditor(ports) {
16
13
  const { handlers, customViewport, promptChips } = ports;
17
14
  let terminalPasteDiagnosticRequest;
18
- const editor = createPiShellEditor({
15
+ const { visibleWidth, truncateToWidth } = ports.components.text;
16
+ const editor = ports.components.editor.create({
19
17
  ...handlers,
20
18
  keybindingProfile: customViewport ? "a1" : "pi",
21
19
  ...(handlers.keybindings === undefined ? {} : { keybindings: handlers.keybindings }),
22
20
  paintEditorSelection: (line, from, to, atomic) => atomic
23
- ? backgroundSgrSpan(line, from, to, "\u001b[7m", "\u001b[27m", piShellVisibleWidth)
24
- : paintTextSelection(line, from, to, piShellVisibleWidth, true),
21
+ ? backgroundSgrSpan(line, from, to, "\u001b[7m", "\u001b[27m", visibleWidth)
22
+ : paintTextSelection(line, from, to, visibleWidth, true),
25
23
  transformPastedContent: content => {
26
24
  try {
27
25
  return promptChips.transformPastedContent(content, editor.getText());
@@ -50,16 +48,16 @@ export function createShellEditor(ports) {
50
48
  } : {}),
51
49
  editorAtomicRanges: line => promptChips.atomicRanges(line),
52
50
  editorHiddenRanges: line => promptChips.hiddenRanges(line),
53
- decorateEditorRow: editorRowDecorator(promptChips, ports.editorPointerSelecting),
51
+ decorateEditorRow: editorRowDecorator(promptChips, ports.editorPointerSelecting, visibleWidth),
54
52
  expandCopiedEditorText: text => promptChips.expandCopiedText(text),
55
53
  cwd: ports.cwd,
56
54
  ...(ports.agentDir === undefined ? {} : { agentDir: ports.agentDir }),
57
55
  onToolsExpand: () => ports.toggleToolsExpanded(),
58
56
  ...(customViewport ? {
59
57
  promptPresentation: {
60
- input: new PromptInput({ fg: (token, text) => piTheme().fg(token, text) }, {
61
- measure: piShellVisibleWidth,
62
- truncate: piShellTruncateToWidth,
58
+ input: new PromptInput({ fg: (token, text) => ports.theme.current().fg(token, text) }, {
59
+ measure: visibleWidth,
60
+ truncate: truncateToWidth,
63
61
  }),
64
62
  styleSuggestion: faint,
65
63
  styleSuggestionCaret: caretCell,
@@ -78,17 +76,17 @@ export function createShellEditor(ports) {
78
76
  * Dims unsent chips and turns the draft's URLs into terminal hyperlinks, each target once per
79
77
  * decoration pass; while a pointer drag selects, links paint as plain underlines instead.
80
78
  */
81
- function editorRowDecorator(promptChips, editorPointerSelecting) {
79
+ function editorRowDecorator(promptChips, editorPointerSelecting, visibleWidth) {
82
80
  const editorHyperlinks = new EditorHyperlinkBudget();
83
81
  return (row, width, rowIndex) => {
84
82
  if (rowIndex === 0)
85
83
  editorHyperlinks.reset();
86
84
  const plain = stripAnsi(row);
87
- const dimmed = promptChips.unsentRanges(plain).reduce((result, range) => backgroundSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), "\u001b[2m", "\u001b[22m", piShellVisibleWidth), row);
85
+ const dimmed = promptChips.unsentRanges(plain).reduce((result, range) => backgroundSgrSpan(result, visibleWidth(plain.slice(0, range.start)), visibleWidth(plain.slice(0, range.end)), "\u001b[2m", "\u001b[22m", visibleWidth), row);
88
86
  const ranges = promptChips.hyperlinkRanges(plain);
89
87
  if (ranges.length === 0 || !editorHyperlinks.takeCleanup())
90
88
  return dimmed;
91
- const linkResetAndTail = `\u001b]8;;\u001b\\\u001b[24m${" ".repeat(Math.max(0, width - piShellVisibleWidth(dimmed)))}`;
89
+ const linkResetAndTail = `\u001b]8;;\u001b\\\u001b[24m${" ".repeat(Math.max(0, width - visibleWidth(dimmed)))}`;
92
90
  // Platform: VS15 is zero-column and default-ignorable. It breaks Windows Terminal's
93
91
  // plain-text URL detector only in the held-button paint; semantic text stays exact.
94
92
  const selecting = editorPointerSelecting();
@@ -96,8 +94,8 @@ function editorRowDecorator(promptChips, editorPointerSelecting) {
96
94
  ? dimmed.replaceAll("https://", "https:︎//").replaceAll("http://", "http:︎//")
97
95
  : dimmed;
98
96
  const decorated = selecting
99
- ? ranges.reduce((result, range) => backgroundSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), "\u001b[4:4m", "\u001b[24m", piShellVisibleWidth), paintRow)
100
- : ranges.filter(range => editorHyperlinks.take(range.target)).reduce((result, range) => hyperlinkSgrSpan(result, piShellVisibleWidth(plain.slice(0, range.start)), piShellVisibleWidth(plain.slice(0, range.end)), range.target, piShellVisibleWidth), dimmed);
97
+ ? ranges.reduce((result, range) => backgroundSgrSpan(result, visibleWidth(plain.slice(0, range.start)), visibleWidth(plain.slice(0, range.end)), "\u001b[4:4m", "\u001b[24m", visibleWidth), paintRow)
98
+ : ranges.filter(range => editorHyperlinks.take(range.target)).reduce((result, range) => hyperlinkSgrSpan(result, visibleWidth(plain.slice(0, range.start)), visibleWidth(plain.slice(0, range.end)), range.target, visibleWidth), dimmed);
101
99
  // Platform: Windows Terminal can retain stale native dotted-link cells when a mutable
102
100
  // prompt replaces a longer URL. Explicit non-link spaces overwrite that tail.
103
101
  return `${decorated}${linkResetAndTail}`;
@@ -1,20 +1,18 @@
1
+ import type { TerminalComponentPort, TerminalInputListener, TerminalInputListenerResult, TerminalInputSurfaceKind, TerminalOverlayHandle, TerminalOverlayOptions, TerminalPointerSurface, TerminalPort } from "../../contracts/presentation/index.js";
1
2
  import type { OwnedUiBackgroundSettingsPort, OwnedUiPresenterHost, OwnedUiSessionPresenter, OwnedUiTerminalHost as OwnedUiTerminalHostContract } from "../../contracts/owned-ui/index.js";
2
3
  import type { UiRouteSurface } from "../../ui/apps/contracts.js";
3
- import { type PiTuiDamageDecision, type PiTuiDamageFrameDescriptor, type PiTuiDamageFrameSafety } from "../../integrations/pi/tui-runtime/damage-aware-terminal.js";
4
- import { PiTuiRuntimeAdapter } from "../../integrations/pi/tui-runtime/adapter.js";
5
- import type { PiTuiInputSurfaceKind } from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
6
- import type { PiTuiComponentPort, PiTuiInputListener, PiTuiInputListenerResult, PiTuiOverlayHandle, PiTuiOverlayOptions, PiTuiPointerSurface, PiTuiTerminalPort } from "../../integrations/pi/tui-runtime/contracts.js";
4
+ import type { ShellPresentationPorts, TerminalDamageDecision, TerminalDamageFrameDescriptor, TerminalDamageFrameSafety, TerminalProgramStatus, TerminalRuntimePort, TerminalWheelScrollLines } from "../../contracts/presentation/index.js";
7
5
  import { type OwnedUiSessionShellOptions, type OwnedUiShellPresentationOptions, type PinnedLayoutPart } from "./session-shell-root.js";
8
- type ProgramStatus = Parameters<PiTuiRuntimeAdapter["setProgramStatus"]>[0];
9
- type WheelScrollLines = Parameters<PiTuiRuntimeAdapter["setWheelScrollLines"]>[0];
6
+ type ProgramStatus = TerminalProgramStatus;
7
+ type WheelScrollLines = TerminalWheelScrollLines;
10
8
  /** What the terminal host needs from a presenter beyond the neutral contract: the Pi runtime's per-session seams. */
11
9
  export interface OwnedUiHostedPresenter extends OwnedUiSessionPresenter {
12
- inputCoordinationSurface(): PiTuiInputSurfaceKind;
13
- setOverlaySurfaces(surfaces: readonly PiTuiPointerSurface[] | null): void;
10
+ inputCoordinationSurface(): TerminalInputSurfaceKind;
11
+ setOverlaySurfaces(surfaces: readonly TerminalPointerSurface[] | null): void;
14
12
  noteImmediatePresentation(): void;
15
13
  /** The bare-A1 viewport's pre-input routing; returns what the runtime's pre-input listener returns. */
16
- handleViewportInput(data: string): PiTuiInputListenerResult | undefined;
17
- layoutPart(part: PinnedLayoutPart): PiTuiComponentPort;
14
+ handleViewportInput(data: string): TerminalInputListenerResult | undefined;
15
+ layoutPart(part: PinnedLayoutPart): TerminalComponentPort;
18
16
  /** Called after the host makes this presenter active, to republish its terminal-wide state. */
19
17
  activated(): void;
20
18
  /**
@@ -45,9 +43,9 @@ export interface OwnedUiPresenterTerminal extends OwnedUiPresenterHost {
45
43
  writeControl(data: string): void;
46
44
  hasOverlay(): boolean;
47
45
  hasFocusedOverlay(): boolean;
48
- showOverlay(component: PiTuiComponentPort, options?: PiTuiOverlayOptions): PiTuiOverlayHandle;
49
- addInputListener(listener: PiTuiInputListener): () => void;
50
- armFrame(descriptor: PiTuiDamageFrameDescriptor, safety: PiTuiDamageFrameSafety): void;
46
+ showOverlay(component: TerminalComponentPort, options?: TerminalOverlayOptions): TerminalOverlayHandle;
47
+ addInputListener(listener: TerminalInputListener): () => void;
48
+ armFrame(descriptor: TerminalDamageFrameDescriptor, safety: TerminalDamageFrameSafety): void;
51
49
  requestHyperlinkCleanup(rows?: readonly number[]): void;
52
50
  openRoute(surface: UiRouteSurface, callbacks: OwnedUiRouteCallbacks): {
53
51
  hide(): void;
@@ -61,19 +59,20 @@ export interface OwnedUiPresenterTerminal extends OwnedUiPresenterHost {
61
59
  setClearOnShrink(enabled: boolean): void;
62
60
  setWheelScrollLines(lines: WheelScrollLines): void;
63
61
  }
64
- export interface OwnedUiRouteCallbacks {
62
+ interface OwnedUiRouteCallbacks {
65
63
  /** The route, or the interrupt chord over it, asked to leave A1. */
66
64
  exit(): void;
67
65
  /** The route closed itself; `rendered` is true when it was ever painted. */
68
66
  closed(rendered: boolean): void;
69
67
  }
70
68
  export interface OwnedUiTerminalHostOptions {
69
+ readonly presentation: ShellPresentationPorts;
71
70
  /** Bare A1 owns a bounded custom viewport; comparison profiles keep the pinned layout. */
72
71
  readonly layout: "pinned" | "custom-viewport";
73
72
  readonly mode: "regular" | "fullscreen";
74
73
  readonly hardwareCursor: boolean;
75
74
  readonly wheelScrollLines?: WheelScrollLines;
76
- readonly terminal?: PiTuiTerminalPort;
75
+ readonly terminal?: TerminalPort;
77
76
  readonly input?: OwnedUiShellPresentationOptions["input"];
78
77
  readonly backgroundSettings?: OwnedUiBackgroundSettingsPort;
79
78
  readonly quitOutro?: OwnedUiShellPresentationOptions["quitOutro"];
@@ -93,9 +92,9 @@ export declare function sessionTerminalHostOptions(options: OwnedUiSessionShellO
93
92
  */
94
93
  export declare class OwnedUiTerminalHost implements OwnedUiTerminalHostContract<OwnedUiHostedPresenter> {
95
94
  #private;
96
- readonly runtime: PiTuiRuntimeAdapter;
95
+ readonly runtime: TerminalRuntimePort;
97
96
  constructor(options: OwnedUiTerminalHostOptions);
98
- get damagePresentationDecision(): PiTuiDamageDecision | null;
97
+ get damagePresentationDecision(): TerminalDamageDecision | null;
99
98
  /** Registers a presenter and returns its terminal handle. The presenter paints only once attached. */
100
99
  connect(presenter: OwnedUiHostedPresenter): OwnedUiPresenterTerminal;
101
100
  /** Forgets a presenter whose construction failed before it could be attached or released. */
@@ -1,9 +1,5 @@
1
1
  import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../../ui/components/mouse.js";
2
2
  import { readVisibleHyperlinks } from "../../ui/components/visible-hyperlinks.js";
3
- import { piCanvasBackgroundAnsi } from "../../integrations/pi/components/theme.js";
4
- import { DamageAwareTerminalAdapter, } from "../../integrations/pi/tui-runtime/damage-aware-terminal.js";
5
- import { PiTuiRuntimeAdapter, legacyKeyInput } from "../../integrations/pi/tui-runtime/adapter.js";
6
- import { classifyPiTuiInput } from "../../integrations/pi/tui-runtime/input-presentation-coordinator.js";
7
3
  import { pinnedLayoutRoot, } from "./session-shell-root.js";
8
4
  /** The size and render request a Pi component reads from the terminal it is shown on, read on every call. */
9
5
  export function componentRuntime(terminal) {
@@ -20,6 +16,7 @@ export function sessionTerminalHostOptions(options) {
20
16
  const customViewport = sessionLayout === "custom-viewport";
21
17
  const settings = backend.settings.snapshot();
22
18
  return {
19
+ presentation: options.presenters,
23
20
  layout: customViewport ? "custom-viewport" : "pinned",
24
21
  // Invariant: bare A1 owns a bounded viewport and therefore always runs on the alternate
25
22
  // fullscreen surface. The pinned comparison profiles still honor Pi's mode.
@@ -40,12 +37,13 @@ export function sessionTerminalHostOptions(options) {
40
37
  export class OwnedUiTerminalHost {
41
38
  runtime;
42
39
  #customViewport;
40
+ #runtimeFactory;
43
41
  #damageTerminal;
44
42
  #quitOutro;
45
43
  #presenters = new Set();
46
44
  #overlays = new Map();
47
- #removeViewportPreInput;
48
- #unsubscribeBackground;
45
+ // Invariant: one release removes both the viewport pre-input listener and the background subscription.
46
+ #release;
49
47
  #active = null;
50
48
  #focused = false;
51
49
  #pointerReporting = false;
@@ -53,6 +51,7 @@ export class OwnedUiTerminalHost {
53
51
  #ending;
54
52
  constructor(options) {
55
53
  this.#customViewport = options.layout === "custom-viewport";
54
+ this.#runtimeFactory = options.presentation.terminalRuntime;
56
55
  this.#quitOutro = options.quitOutro;
57
56
  const input = options.input;
58
57
  let damageTerminal;
@@ -75,7 +74,7 @@ export class OwnedUiTerminalHost {
75
74
  consumeUnhandledMouse: true,
76
75
  onOverlayGeometry: surfaces => this.#active?.setOverlaySurfaces(surfaces),
77
76
  decorateTerminal: (terminal) => {
78
- damageTerminal = new DamageAwareTerminalAdapter(terminal, {
77
+ damageTerminal = this.#runtimeFactory.createDamageTerminal(terminal, {
79
78
  regionalScroll: process.env.TERM !== "dumb",
80
79
  inspectHyperlinks: readVisibleHyperlinks,
81
80
  onResize: () => this.#active?.noteImmediatePresentation(),
@@ -85,12 +84,12 @@ export class OwnedUiTerminalHost {
85
84
  },
86
85
  ...(input?.coordination === false ? {} : {
87
86
  inputCoordination: {
88
- classify: (data, focusedOverlay) => classifyPiTuiInput(data, focusedOverlay ?? this.#active?.inputCoordinationSurface() ?? "opaque"),
87
+ classify: (data, focusedOverlay) => this.#runtimeFactory.classifyInput(data, focusedOverlay ?? this.#active?.inputCoordinationSurface() ?? "opaque"),
89
88
  onReceipt: () => this.#active?.noteImmediatePresentation(),
90
89
  ...(input?.scheduler === undefined ? {} : { scheduler: input.scheduler }),
91
90
  },
92
91
  }),
93
- } : { layoutRoot: pinnedLayoutRoot(this.#delegatingLayoutParts()) }),
92
+ } : { layoutRoot: pinnedLayoutRoot(this.#delegatingLayoutParts(), options.presentation.theme) }),
94
93
  ...(input?.onEvent === undefined ? {} : {
95
94
  inputDiagnostics: {
96
95
  onEvent: input.onEvent,
@@ -101,18 +100,19 @@ export class OwnedUiTerminalHost {
101
100
  hardwareCursor: options.hardwareCursor,
102
101
  ...(options.wheelScrollLines === undefined ? {} : { wheelScrollLines: options.wheelScrollLines }),
103
102
  };
104
- this.runtime = new PiTuiRuntimeAdapter(runtimeOptions);
103
+ this.runtime = this.#runtimeFactory.createRuntime(runtimeOptions);
105
104
  this.#damageTerminal = damageTerminal ?? null;
106
105
  const background = this.#customViewport ? options.backgroundSettings : undefined;
107
106
  if (background !== undefined)
108
- this.#damageTerminal?.setCanvasBackground(piCanvasBackgroundAnsi(background.snapshot()));
109
- this.#unsubscribeBackground = background === undefined || this.#damageTerminal === null ? () => { } : background.onChange(style => {
110
- if (this.#damageTerminal?.setCanvasBackground(piCanvasBackgroundAnsi(style)))
107
+ this.#damageTerminal?.setCanvasBackground(options.presentation.canvasBackground(background.snapshot()));
108
+ const unsubscribeBackground = background === undefined || this.#damageTerminal === null ? () => { } : background.onChange(style => {
109
+ if (this.#damageTerminal?.setCanvasBackground(options.presentation.canvasBackground(style)))
111
110
  this.runtime.requestRender(true);
112
111
  });
113
- this.#removeViewportPreInput = this.#customViewport
112
+ const removeViewportPreInput = this.#customViewport
114
113
  ? this.runtime.addPreInputListener(data => this.#active?.handleViewportInput(data))
115
114
  : () => { };
115
+ this.#release = () => { removeViewportPreInput(); unsubscribeBackground(); };
116
116
  }
117
117
  get damagePresentationDecision() {
118
118
  return this.#damageTerminal?.lastDecision ?? null;
@@ -199,8 +199,7 @@ export class OwnedUiTerminalHost {
199
199
  // Invariant: the outro frame is what the terminal shows now, before any cleanup writes.
200
200
  const outroFrame = this.#captureQuitOutroFrame();
201
201
  attempt(() => this.#setPointerReporting(false, true));
202
- attempt(() => this.#removeViewportPreInput());
203
- attempt(() => this.#unsubscribeBackground());
202
+ attempt(() => this.#release());
204
203
  const terminal = { mode: this.runtime.mode, columns: this.runtime.viewport().columns };
205
204
  const active = this.#active;
206
205
  const releases = [];
@@ -344,7 +343,7 @@ export class OwnedUiTerminalHost {
344
343
  };
345
344
  removeInterruptWatch = runtime.addInputListener(data => {
346
345
  // Compatibility: a kitty-keyboard child reports Ctrl+C as a CSI-u sequence rather than the byte.
347
- if (!attached() || !data.includes(INTERRUPT) && legacyKeyInput(data) !== INTERRUPT)
346
+ if (!attached() || !data.includes(INTERRUPT) && this.#runtimeFactory.legacyKeyInput(data) !== INTERRUPT)
348
347
  return undefined;
349
348
  // Protocol: route hosts decide whether one interrupt closes their app. Raw-input forwarding
350
349
  // is required because Pi handles Ctrl+C before the fullscreen overlay receives normal input.
@@ -403,7 +402,7 @@ export class OwnedUiTerminalHost {
403
402
  for (const event of events)
404
403
  surface.handleMouse(event);
405
404
  // Invariant: owned screens see legacy key bytes whether the terminal reports legacy or kitty keys.
406
- const key = rest.length > 0 ? legacyKeyInput(rest) : "";
405
+ const key = rest.length > 0 ? this.#runtimeFactory.legacyKeyInput(rest) : "";
407
406
  if (key.length > 0)
408
407
  surface.handleInput(key);
409
408
  if (surface.isClosed()) {
@@ -471,7 +470,7 @@ export class OwnedUiTerminalHost {
471
470
  write: data => this.runtime.writeControl(data),
472
471
  ...(capture.settings.canvasBackgroundAnsi === null
473
472
  ? {}
474
- : { canvasBackgroundAnsi: capture.settings.canvasBackgroundAnsi }),
473
+ : { canvas: { ansi: capture.settings.canvasBackgroundAnsi, rebase: this.#runtimeFactory.rebaseDefaultBackground } }),
475
474
  ...(outro.now === undefined ? {} : { now: outro.now }),
476
475
  ...(outro.sleep === undefined ? {} : { sleep: outro.sleep }),
477
476
  ...(outro.seed === undefined ? {} : { seed: outro.seed }),