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

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 (296) 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 +13 -14
  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/resource-labels.d.ts +2 -0
  39. package/dist/app/session-shell/resource-labels.js +158 -0
  40. package/dist/app/session-shell/response-copy-protocol.d.ts +3 -2
  41. package/dist/app/session-shell/response-copy-transport.d.ts +2 -14
  42. package/dist/app/session-shell/response-copy-transport.js +1 -1
  43. package/dist/app/session-shell/session-lifecycle.d.ts +2 -1
  44. package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
  45. package/dist/app/session-shell/session-prompt-history.js +80 -0
  46. package/dist/app/session-shell/session-selectors.d.ts +46 -0
  47. package/dist/app/session-shell/session-selectors.js +269 -0
  48. package/dist/app/session-shell/session-shell-root.d.ts +76 -113
  49. package/dist/app/session-shell/session-shell-root.js +103 -329
  50. package/dist/app/session-shell/session-shell.d.ts +14 -37
  51. package/dist/app/session-shell/session-shell.js +177 -612
  52. package/dist/app/session-shell/session-viewport-controller.d.ts +9 -16
  53. package/dist/app/session-shell/session-viewport-controller.js +42 -320
  54. package/dist/app/session-shell/settings-bindings.d.ts +25 -3
  55. package/dist/app/session-shell/settings-bindings.js +26 -2
  56. package/dist/app/session-shell/share-operation.d.ts +3 -3
  57. package/dist/app/session-shell/share-operation.js +4 -6
  58. package/dist/app/session-shell/shell-clipboard.d.ts +59 -0
  59. package/dist/app/session-shell/shell-clipboard.js +119 -0
  60. package/dist/app/session-shell/shell-editor-factory.d.ts +8 -4
  61. package/dist/app/session-shell/shell-editor-factory.js +13 -15
  62. package/dist/app/session-shell/shell-host.d.ts +16 -17
  63. package/dist/app/session-shell/shell-host.js +18 -19
  64. package/dist/app/session-shell/shell-layout.d.ts +2 -2
  65. package/dist/app/session-shell/shell-prompt-suggestions.d.ts +26 -0
  66. package/dist/app/session-shell/shell-prompt-suggestions.js +48 -0
  67. package/dist/app/session-shell/suggestion-surface.d.ts +1 -6
  68. package/dist/app/session-shell/suggestion-surface.js +1 -1
  69. package/dist/app/session-shell/system-clipboard.d.ts +2 -14
  70. package/dist/app/session-shell/system-clipboard.js +2 -7
  71. package/dist/app/session-shell/transcript-document.d.ts +11 -8
  72. package/dist/app/session-shell/transcript-document.js +13 -14
  73. package/dist/app/session-shell/viewport-layout.d.ts +2 -2
  74. package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
  75. package/dist/app/session-shell/viewport-pointer.js +361 -0
  76. package/dist/app/session-shell/workflow-output.d.ts +5 -3
  77. package/dist/app/session-shell/workflow-output.js +13 -15
  78. package/dist/cli/package-messages.d.ts +0 -1
  79. package/dist/cli/package-messages.js +1 -1
  80. package/dist/cli/session-context.d.ts +1 -1
  81. package/dist/cli/uninstall.d.ts +3 -2
  82. package/dist/composition/owned-settings.d.ts +2 -1
  83. package/dist/composition/owned-settings.js +0 -3
  84. package/dist/composition/owned-ui.d.ts +1 -1
  85. package/dist/composition/owned-ui.js +77 -73
  86. package/dist/composition/reference-selection-options.d.ts +1 -1
  87. package/dist/composition/reference-selection-options.js +1 -1
  88. package/dist/composition/session-runtime-registry.d.ts +2 -1
  89. package/dist/composition/settings-route-host.js +3 -10
  90. package/dist/composition/settings-search-input.d.ts +2 -1
  91. package/dist/composition/startup-route.d.ts +2 -2
  92. package/dist/composition/startup-route.js +2 -2
  93. package/dist/contracts/owned-ui/extension-ui.d.ts +2 -1
  94. package/dist/contracts/owned-ui/index.d.ts +2 -1
  95. package/dist/contracts/owned-ui/index.js +1 -1
  96. package/dist/contracts/owned-ui/session-presenters.d.ts +3 -0
  97. package/dist/contracts/owned-ui/shell-components.d.ts +543 -0
  98. package/dist/contracts/owned-ui/validation.d.ts +1 -11
  99. package/dist/contracts/owned-ui/validation.js +8 -104
  100. package/dist/contracts/presentation/component.d.ts +17 -0
  101. package/dist/contracts/presentation/component.js +8 -0
  102. package/dist/contracts/presentation/index.d.ts +6 -15
  103. package/dist/contracts/presentation/index.js +2 -0
  104. package/dist/contracts/presentation/terminal.d.ts +284 -0
  105. package/dist/contracts/presentation/terminal.js +1 -0
  106. package/dist/contracts/presentation/theme.d.ts +46 -0
  107. package/dist/contracts/presentation/theme.js +1 -0
  108. package/dist/features/launch/development-launch.js +1 -1
  109. package/dist/features/launch/runtime-selection.d.ts +2 -2
  110. package/dist/features/launch/runtime-selection.js +2 -2
  111. package/dist/features/owned-ui/project-trust-prompt.d.ts +2 -2
  112. package/dist/features/owned-ui/project-trust-prompt.js +1 -1
  113. package/dist/features/owned-ui/release-note-state.d.ts +2 -1
  114. package/dist/features/owned-ui/settings-app.js +49 -425
  115. package/dist/features/owned-ui/settings-edits.d.ts +31 -0
  116. package/dist/features/owned-ui/settings-edits.js +139 -0
  117. package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
  118. package/dist/features/owned-ui/settings-model-dialog.js +194 -0
  119. package/dist/features/owned-ui/settings-scroll-rail.d.ts +29 -0
  120. package/dist/features/owned-ui/settings-scroll-rail.js +79 -0
  121. package/dist/features/owned-ui/settings-search-input.d.ts +2 -1
  122. package/dist/features/prompt-history/service.d.ts +3 -2
  123. package/dist/foundation/lifecycle/claim-gate.d.ts +2 -1
  124. package/dist/foundation/release/automatic-update.d.ts +5 -14
  125. package/dist/foundation/release/automatic-update.js +15 -22
  126. package/dist/foundation/release/bootstrap.js +4 -4
  127. package/dist/foundation/release/dependency-certification.d.ts +0 -1
  128. package/dist/foundation/release/dependency-certification.js +1 -1
  129. package/dist/foundation/release/dependency-layer.js +3 -3
  130. package/dist/foundation/release/early-accent.d.ts +2 -2
  131. package/dist/foundation/release/early-accent.js +1 -1
  132. package/dist/foundation/release/index.d.ts +2 -2
  133. package/dist/foundation/release/index.js +1 -1
  134. package/dist/foundation/release/latest-release.d.ts +3 -18
  135. package/dist/foundation/release/latest-release.js +11 -11
  136. package/dist/foundation/release/managed-path.d.ts +10 -0
  137. package/dist/foundation/release/managed-path.js +26 -0
  138. package/dist/foundation/release/ordinary-launch-selection.d.ts +1 -1
  139. package/dist/foundation/release/ordinary-launch-selection.js +1 -1
  140. package/dist/foundation/release/release-gc.d.ts +3 -3
  141. package/dist/foundation/release/release-gc.js +10 -12
  142. package/dist/foundation/release/{cohort-state.d.ts → release-state.d.ts} +23 -23
  143. package/dist/foundation/release/{cohort-state.js → release-state.js} +8 -8
  144. package/dist/foundation/release/restart-certification.js +3 -4
  145. package/dist/foundation/release/update-activation.js +3 -3
  146. package/dist/foundation/release/update-execution-lease.d.ts +2 -3
  147. package/dist/foundation/release/update-execution-lease.js +2 -8
  148. package/dist/foundation/release/update-installer.d.ts +45 -0
  149. package/dist/foundation/release/update-installer.js +57 -0
  150. package/dist/foundation/release/update-launch.d.ts +2 -2
  151. package/dist/foundation/release/update-recovery.js +2 -9
  152. package/dist/foundation/release/update.d.ts +4 -25
  153. package/dist/foundation/release/update.js +164 -183
  154. package/dist/foundation/startup/startup-budget.d.ts +5 -5
  155. package/dist/foundation/startup/startup-budget.js +2 -2
  156. package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
  157. package/dist/integrations/pi/components/bracketed-paste.js +123 -0
  158. package/dist/integrations/pi/components/editor-interaction.d.ts +4 -12
  159. package/dist/integrations/pi/components/index.d.ts +15 -9
  160. package/dist/integrations/pi/components/index.js +9 -5
  161. package/dist/integrations/pi/components/lazy-selectors.d.ts +2 -10
  162. package/dist/integrations/pi/components/lazy-selectors.js +1 -1
  163. package/dist/integrations/pi/components/modal-frame.d.ts +5 -22
  164. package/dist/integrations/pi/components/modal-frame.js +3 -23
  165. package/dist/integrations/pi/components/owned-editor-ux.d.ts +4 -3
  166. package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
  167. package/dist/integrations/pi/components/prompt-input-port.d.ts +2 -19
  168. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -1
  169. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +3 -2
  170. package/dist/integrations/pi/components/shell-footer-status.d.ts +2 -2
  171. package/dist/integrations/pi/components/shell-footer-status.js +3 -2
  172. package/dist/integrations/pi/components/shell-presenters-info.d.ts +6 -5
  173. package/dist/integrations/pi/components/shell-presenters-info.js +2 -1
  174. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +2 -1
  175. package/dist/integrations/pi/components/shell-shared-facade.d.ts +33 -227
  176. package/dist/integrations/pi/components/shell-shared-facade.js +8 -1
  177. package/dist/integrations/pi/components/skills-command.d.ts +1 -9
  178. package/dist/integrations/pi/components/skills-command.js +5 -5
  179. package/dist/integrations/pi/components/theme.d.ts +9 -2
  180. package/dist/integrations/pi/components/theme.js +17 -2
  181. package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +2 -1
  182. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  183. package/dist/integrations/pi/engine/adapter.d.ts +4 -13
  184. package/dist/integrations/pi/engine/adapter.js +9 -61
  185. package/dist/integrations/pi/engine/changelog.d.ts +2 -2
  186. package/dist/integrations/pi/engine/changelog.js +3 -3
  187. package/dist/integrations/pi/engine/compaction-progress.d.ts +2 -3
  188. package/dist/integrations/pi/engine/compaction-progress.js +1 -1
  189. package/dist/integrations/pi/engine/conformance.d.ts +2 -1
  190. package/dist/integrations/pi/engine/default-model-setting.d.ts +0 -2
  191. package/dist/integrations/pi/engine/default-model-setting.js +1 -2
  192. package/dist/integrations/pi/engine/event-delivery.d.ts +2 -1
  193. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -1
  194. package/dist/integrations/pi/engine/host.js +3 -3
  195. package/dist/integrations/pi/engine/message-values.d.ts +0 -1
  196. package/dist/integrations/pi/engine/message-values.js +1 -1
  197. package/dist/integrations/pi/engine/pending-delivery.d.ts +0 -4
  198. package/dist/integrations/pi/engine/pending-delivery.js +3 -3
  199. package/dist/integrations/pi/engine/ports.d.ts +7 -1
  200. package/dist/integrations/pi/engine/ports.js +4 -0
  201. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +2 -1
  202. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +3 -2
  203. package/dist/integrations/pi/engine/provider-authentication.d.ts +1 -1
  204. package/dist/integrations/pi/engine/public-main-entry.d.ts +1 -5
  205. package/dist/integrations/pi/engine/public-main-entry.js +1 -1
  206. package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
  207. package/dist/integrations/pi/engine/repository-metadata.js +189 -0
  208. package/dist/integrations/pi/engine/repository-pr-rest.d.ts +1 -9
  209. package/dist/integrations/pi/engine/repository-pr-rest.js +2 -2
  210. package/dist/integrations/pi/engine/repository-pr.d.ts +1 -3
  211. package/dist/integrations/pi/engine/repository-pr.js +1 -1
  212. package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
  213. package/dist/integrations/pi/engine/session-integration.d.ts +1 -1
  214. package/dist/integrations/pi/engine/session-runtime.d.ts +7 -12
  215. package/dist/integrations/pi/engine/session-runtime.js +21 -178
  216. package/dist/integrations/pi/engine/settings-bridge.d.ts +0 -2
  217. package/dist/integrations/pi/engine/settings-bridge.js +1 -1
  218. package/dist/integrations/pi/engine/settings-port.d.ts +3 -2
  219. package/dist/integrations/pi/engine/transcript-projection.d.ts +2 -1
  220. package/dist/integrations/pi/engine/view-state.d.ts +21 -2
  221. package/dist/integrations/pi/engine/view-state.js +129 -1
  222. package/dist/integrations/pi/engine/workflow-contexts.d.ts +1 -1
  223. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -2
  224. package/dist/integrations/pi/engine/workflow-runner.js +254 -365
  225. package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
  226. package/dist/integrations/pi/engine/workflow-share.js +168 -0
  227. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -20
  228. package/dist/integrations/pi/engine/workflow-support.js +4 -65
  229. package/dist/integrations/pi/engine/workflows.d.ts +5 -5
  230. package/dist/integrations/pi/session-presenters/components.d.ts +3 -0
  231. package/dist/integrations/pi/session-presenters/components.js +75 -0
  232. package/dist/integrations/pi/session-presenters/index.d.ts +1 -0
  233. package/dist/integrations/pi/session-presenters/index.js +1 -0
  234. package/dist/integrations/pi/session-presenters/presentation.d.ts +3 -0
  235. package/dist/integrations/pi/session-presenters/presentation.js +10 -0
  236. package/dist/integrations/pi/session-presenters/presenters.d.ts +2 -1
  237. package/dist/integrations/pi/session-presenters/presenters.js +3 -1
  238. package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
  239. package/dist/integrations/pi/tui-runtime/adapter.js +56 -174
  240. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +9 -44
  241. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
  242. package/dist/integrations/pi/tui-runtime/index.js +1 -0
  243. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +4 -6
  244. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +4 -10
  245. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +3 -9
  246. package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
  247. package/dist/integrations/pi/tui-runtime/runtime-factory.d.ts +3 -0
  248. package/dist/integrations/pi/tui-runtime/runtime-factory.js +11 -0
  249. package/dist/ui/components/index.d.ts +2 -2
  250. package/dist/ui/components/index.js +1 -1
  251. package/dist/ui/components/selection-copy.d.ts +0 -1
  252. package/dist/ui/components/selection-copy.js +0 -12
  253. package/dist/ui/components/theme.d.ts +4 -19
  254. package/dist/ui/components/theme.js +6 -0
  255. package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
  256. package/dist/ui/components/transcript-viewport-frame.js +314 -0
  257. package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
  258. package/dist/ui/components/transcript-viewport-types.js +1 -0
  259. package/dist/ui/components/transcript-viewport.d.ts +2 -147
  260. package/dist/ui/components/transcript-viewport.js +87 -315
  261. package/dist/ui/settings/declarations.d.ts +2 -3
  262. package/dist/ui/settings/declarations.js +2 -3
  263. package/dist/ui/settings/early-accent.js +1 -1
  264. package/dist/ui/settings/index.d.ts +3 -5
  265. package/dist/ui/settings/index.js +2 -3
  266. package/dist/ui/settings/manager.d.ts +0 -4
  267. package/dist/ui/settings/manager.js +6 -62
  268. package/dist/ui/settings/resolution.d.ts +6 -18
  269. package/dist/ui/settings/resolution.js +4 -103
  270. package/dist/ui/settings/sections.d.ts +0 -4
  271. package/dist/ui/settings/sections.js +1 -1
  272. package/docs/architecture/boundaries.md +2 -2
  273. package/docs/architecture/internal-naming.md +2 -0
  274. package/docs/architecture/project-structure.md +4 -2
  275. package/docs/validation.md +1 -1
  276. package/package.json +3 -4
  277. package/dist/integrations/pi/components/components.d.ts +0 -4
  278. package/dist/integrations/pi/components/components.js +0 -48
  279. package/dist/integrations/pi/components/conformance.d.ts +0 -18
  280. package/dist/integrations/pi/components/conformance.js +0 -70
  281. package/dist/integrations/pi/components/shell-components.d.ts +0 -7
  282. package/dist/integrations/pi/components/shell-components.js +0 -7
  283. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +0 -33
  284. package/dist/integrations/pi/components/upstream/theme/theme-controller.js +0 -114
  285. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
  286. package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
  287. package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
  288. package/dist/integrations/pi/engine/usage-view.js +0 -56
  289. package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -166
  290. package/dist/ui/settings/migrations.d.ts +0 -19
  291. package/dist/ui/settings/migrations.js +0 -225
  292. package/dist/ui/settings/progress-palette.generated.d.ts +0 -139
  293. package/dist/ui/settings/progress-palette.generated.js +0 -139
  294. /package/dist/{integrations/pi/tui-runtime/contracts.js → contracts/owned-ui/shell-components.js} +0 -0
  295. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.d.ts +0 -0
  296. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.js +0 -0
@@ -1,151 +1,6 @@
1
1
  import { type SelectionCopySnapshot } from "./selection-copy.js";
2
- import { type ScrollbarAppearance, type ScrollbarGeometry, type ScrollbarStyle } from "./scrollbar.js";
3
- export interface TranscriptPromptAnchor {
4
- readonly id: string;
5
- readonly firstRow: number;
6
- readonly lastRow: number;
7
- /** The source first row, including its submitted-prompt styling. */
8
- readonly sourceRow: string;
9
- /** Optional source-styled row used only while prominent and not hovered. */
10
- readonly prominentSourceRow?: string;
11
- }
12
- export interface TranscriptViewportConfig {
13
- readonly scrollbarAppearance: ScrollbarAppearance;
14
- readonly scrollbarStyle: ScrollbarStyle;
15
- }
16
- export interface TranscriptViewportTheme {
17
- readonly track: (text: string) => string;
18
- readonly thumb: (text: string, active: boolean) => string;
19
- readonly sticky: (text: string, hovered: boolean) => string;
20
- readonly quietSticky: (text: string) => string;
21
- readonly bottomControl: (text: string, hovered: boolean) => string;
22
- readonly selection: (line: string, from: number, to: number) => string;
23
- }
24
- export interface TranscriptViewportFrameInput {
25
- readonly documentRows: readonly string[];
26
- /** Optional paint-only transform; semantic selection and copying keep documentRows exact. */
27
- readonly paintDocumentRow?: (row: string) => string;
28
- /** Leading persistent document rows; later transient rows remain selectable visible-frame content. */
29
- readonly selectableDocumentRowCount?: number;
30
- /**
31
- * First document row owned by the block that is currently streaming, if any. Rows from
32
- * here to the end of the selectable document reflow together whenever that block
33
- * re-renders, so the frame can declare how much damage is legitimately its own.
34
- */
35
- readonly liveTailStartRow?: number;
36
- /** Final transient rows to bottom-align with unused viewport space while content fits. */
37
- readonly bottomAlignedTailRowCount?: number;
38
- readonly dockRows: readonly string[];
39
- /** Zero-based dock rows admitted to frame copying, classified before dock composition. */
40
- readonly copyableDockRows?: readonly {
41
- readonly row: number;
42
- readonly prompt?: "first" | "continuation";
43
- }[];
44
- readonly promptAnchors: readonly TranscriptPromptAnchor[];
45
- readonly width: number;
46
- readonly height: number;
47
- /**
48
- * Optional zero-based terminal row for the floating bottom control. The shell
49
- * supplies a stable anchor above its editor group so transient status and
50
- * notification rows cannot make the control jump vertically.
51
- */
52
- readonly bottomControlRow?: number;
53
- /** Latest reported one-based terminal coordinates; bottom hover uses this frame's hit region. */
54
- readonly pointerPosition?: {
55
- readonly column: number;
56
- readonly row: number;
57
- };
58
- /** Base-frame cells hidden by modal/replacement surfaces and therefore excluded from visual copy. */
59
- readonly coveredCells?: readonly {
60
- readonly row: number;
61
- readonly columnStart: number;
62
- readonly columnEnd: number;
63
- }[];
64
- /** Transient paint-only text that must not participate in frame allocation or copying. */
65
- readonly frameOverlay?: {
66
- /** One-based terminal row. */
67
- readonly row: number;
68
- readonly text: string;
69
- };
70
- readonly now?: number;
71
- readonly theme?: TranscriptViewportTheme;
72
- }
73
- export interface TranscriptViewportHitRegions {
74
- readonly viewportHeight: number;
75
- readonly rail: {
76
- readonly column: number;
77
- /** One-based terminal row. The one-row top inset is intentionally excluded. */
78
- readonly rowStart: number;
79
- readonly trackHeight: number;
80
- readonly geometry: ScrollbarGeometry;
81
- } | null;
82
- readonly sticky: {
83
- readonly row: number;
84
- readonly target: number;
85
- readonly width: number;
86
- } | null;
87
- readonly bottom: {
88
- readonly row: number;
89
- readonly columnStart: number;
90
- readonly columnEnd: number;
91
- } | null;
92
- /** Transient non-persistent document-tail rows currently visible in the viewport. */
93
- readonly transientTail: readonly number[];
94
- }
95
- export interface TranscriptViewportFrameDescriptor {
96
- readonly frameId: number;
97
- readonly width: number;
98
- readonly height: number;
99
- readonly transcript: {
100
- readonly rowStart: number;
101
- readonly rowEnd: number;
102
- } | null;
103
- readonly dock: {
104
- readonly rowStart: number;
105
- readonly rowEnd: number;
106
- } | null;
107
- readonly previousDocumentRange: {
108
- readonly start: number;
109
- readonly end: number;
110
- } | null;
111
- readonly nextDocumentRange: {
112
- readonly start: number;
113
- readonly end: number;
114
- };
115
- readonly previousFollowingEnd: boolean | null;
116
- readonly followingEnd: boolean;
117
- /** Complete non-persistent suffix, including pending steering, alignment, and status rows. */
118
- readonly transientRowCount: number;
119
- /** Flexible rows inserted before the bottom-aligned live status while content fits. */
120
- readonly transientAlignmentGapRows: number;
121
- /** Bottom-aligned transient rows. */
122
- readonly bottomAlignedTailRowCount: number;
123
- /** Visible rows owned by the currently streaming transcript block. */
124
- readonly liveTailRows: number;
125
- readonly verticalShiftRows: number;
126
- readonly safeVerticalShift: boolean;
127
- /** Monotonic interaction revision used to reject stale selection evidence. */
128
- readonly selectionRevision: number;
129
- /** One-based terminal rows whose source or normalized selection range changed. */
130
- readonly selectionDamagedRows: readonly number[];
131
- readonly cause: "initial" | "steady" | "dock-input" | "follow-shift" | "detached" | "geometry-change";
132
- }
133
- export interface TranscriptViewportFrame {
134
- readonly rows: readonly string[];
135
- readonly contentWidth: number;
136
- readonly scrollTop: number;
137
- readonly maxScroll: number;
138
- readonly followingEnd: boolean;
139
- readonly hits: TranscriptViewportHitRegions;
140
- readonly descriptor: TranscriptViewportFrameDescriptor;
141
- /** Deterministic evidence for bounded selection-only row composition. */
142
- readonly selectionDamage: {
143
- readonly revision: number;
144
- readonly recomputedRows: readonly number[];
145
- readonly reusedRows: readonly number[];
146
- readonly cacheEntries: number;
147
- };
148
- }
2
+ import type { TranscriptViewportConfig, TranscriptViewportFrameInput, TranscriptViewportFrameDescriptor, TranscriptViewportFrame } from "./transcript-viewport-types.js";
3
+ export type { TranscriptPromptAnchor, TranscriptViewportConfig, TranscriptViewportTheme, TranscriptViewportFrameInput, TranscriptViewportHitRegions, TranscriptViewportFrameDescriptor, TranscriptViewportFrame, } from "./transcript-viewport-types.js";
149
4
  /**
150
5
  * A1-owned viewport state. It knows only rows and semantic prompt anchors; Pi
151
6
  * components and transcript payloads remain outside this neutral boundary.
@@ -1,16 +1,7 @@
1
1
  import { MAX_COPY_SOURCE_UNITS, selectionCopyLineContent } from "./selection-copy.js";
2
- import { isThumbRow, scrollbarGeometry, scrollbarPresentation, } from "./scrollbar.js";
3
- import { overlaySpan } from "./spans.js";
4
- import { faint, truncateToWidth, displayWidth, stripAnsi } from "./text.js";
2
+ import { faint, truncateToWidth, stripAnsi } from "./text.js";
3
+ import { alignTailRows, frameDescriptor, hitRegions, layoutScrollbar, maskSelectionRows, placeBottomControl, sameRectangle, VIEWPORT_PROMPT_ZONES, ViewportRowCaches, } from "./transcript-viewport-frame.js";
5
4
  import { extendTextSelection, orderedTextSelection, paintTextSelection, pressTextSelection, releaseTextSelection, textSelectionLineExtendColumn, textSelectionPointAt, textSelectionText, usefulTextLineContent, } from "./text-selection.js";
6
- const CONTROL_STYLE_RESET = "\u001b]8;;\u001b\\\u001b[0m";
7
- // Protocol: upstream OSC 133 zones are layout annotations, not fullscreen cursor commands.
8
- // Styling/padding can move them away from the row prefix Pi strips; 133;A then advances a real
9
- // terminal parser to a fresh line. The custom viewport owns prompt anchors separately.
10
- const VIEWPORT_PROMPT_ZONES = /\u001b\]133;[ABC](?:\u0007|\u001b\\)/gu;
11
- // Invariant: scrollbar chrome closes source links and decoration without clearing
12
- // the row surface painted beneath its dedicated, non-semantic gutter.
13
- const GUTTER_DECORATION_RESET = "\u001b]8;;\u001b\\\u001b[22;23;24;25;27;28;29;39;54;55m";
14
5
  const IDENTITY_ROW = (row) => row;
15
6
  function documentSelectionAnchor(row, source, liveFromBottom) {
16
7
  return { kind: "document", row, source, identity: stripAnsi(source).trimEnd(), liveFromBottom };
@@ -66,12 +57,7 @@ export class TranscriptViewport {
66
57
  #copyableSelection = false;
67
58
  // Invariant: exact unselected rows cover the latest visible base frame and dock.
68
59
  #selectionRows = [];
69
- #paintedRowCache = new Map();
70
- #baseRowCache = new Map();
71
- #selectedRowCache = new Map();
72
- #finalRowCache = new Map();
73
- #functionIds = new WeakMap();
74
- #nextFunctionId = 1;
60
+ #rowCaches = new ViewportRowCaches();
75
61
  #previousVisibleStates = [];
76
62
  #documentRows = [];
77
63
  #selectableDocumentRowCount = 0;
@@ -369,10 +355,7 @@ export class TranscriptViewport {
369
355
  this.#selectionRows = [];
370
356
  this.#selectionRowAnchors = [];
371
357
  this.#selectionClick = undefined;
372
- this.#paintedRowCache.clear();
373
- this.#baseRowCache.clear();
374
- this.#selectedRowCache.clear();
375
- this.#finalRowCache.clear();
358
+ this.#rowCaches.clear();
376
359
  this.#previousVisibleStates = [];
377
360
  this.#documentRows = [];
378
361
  this.#selectableDocumentRowCount = 0;
@@ -399,18 +382,7 @@ export class TranscriptViewport {
399
382
  const dock = input.dockRows.length > height ? input.dockRows.slice(-height) : [...input.dockRows];
400
383
  this.#copyableDockRows = copyableDockRowsBySuffix(input.dockRows, input.copyableDockRows);
401
384
  const viewportHeight = Math.max(0, height - dock.length);
402
- const bottomAlignedTailRowCount = clamp(input.bottomAlignedTailRowCount ?? 0, 0, input.documentRows.length);
403
- const transientAlignmentGapRows = bottomAlignedTailRowCount > 0
404
- ? Math.max(0, viewportHeight - input.documentRows.length)
405
- : 0;
406
- const tailStart = input.documentRows.length - bottomAlignedTailRowCount;
407
- const documentRows = transientAlignmentGapRows === 0
408
- ? input.documentRows
409
- : [
410
- ...input.documentRows.slice(0, tailStart),
411
- ...Array.from({ length: transientAlignmentGapRows }, () => ""),
412
- ...input.documentRows.slice(tailStart),
413
- ];
385
+ const { documentRows, bottomAlignedTailRowCount, transientAlignmentGapRows } = alignTailRows(input.documentRows, input.bottomAlignedTailRowCount, viewportHeight);
414
386
  this.#maxScroll = Math.max(0, documentRows.length - viewportHeight);
415
387
  if (this.#followingEnd)
416
388
  this.#scrollTop = this.#maxScroll;
@@ -420,21 +392,9 @@ export class TranscriptViewport {
420
392
  this.#followingEnd = true;
421
393
  if (this.#followingEnd)
422
394
  this.#newMessages = 0;
423
- const geometry = scrollbarGeometry({
424
- contentLength: documentRows.length,
425
- viewportHeight,
426
- scroll: this.#scrollTop,
427
- // Invariant: the rail keeps its top gap; the shell owns the spacing above the dock.
428
- trackHeight: Math.max(0, viewportHeight - 1),
429
- });
430
- const presentation = scrollbarPresentation({
431
- geometry,
432
- appearance: this.#config.scrollbarAppearance,
433
- style: this.#config.scrollbarStyle,
434
- hovered: this.#railHovered,
435
- dragging: this.#railDragging,
436
- activeUntil: this.#activeUntil,
437
- now: input.now ?? Date.now(),
395
+ const { geometry, presentation } = layoutScrollbar({
396
+ contentLength: documentRows.length, viewportHeight, scroll: this.#scrollTop, config: this.#config,
397
+ hovered: this.#railHovered, dragging: this.#railDragging, activeUntil: this.#activeUntil, now: input.now ?? Date.now(),
438
398
  });
439
399
  const contentWidth = presentation.reservesSpace ? Math.max(1, width - 1) : width;
440
400
  this.#documentRows = documentRows;
@@ -448,212 +408,58 @@ export class TranscriptViewport {
448
408
  // use contentWidth and leave the gutter outside semantic selection.
449
409
  this.#selectionWidth = width;
450
410
  this.#viewportHeight = viewportHeight;
451
- const paintDocumentRow = input.paintDocumentRow ?? IDENTITY_ROW;
452
411
  const cacheLimit = Math.max(32, viewportHeight * 6);
453
- trimCache(this.#paintedRowCache, cacheLimit);
454
- trimCache(this.#baseRowCache, cacheLimit);
455
- trimCache(this.#selectedRowCache, cacheLimit);
456
- trimCache(this.#finalRowCache, cacheLimit);
457
- const paintId = this.#functionId(paintDocumentRow);
458
- const paintRecomputedRows = new Set();
459
- const visibleSource = documentRows.slice(this.#scrollTop, this.#scrollTop + viewportHeight);
460
- while (visibleSource.length < viewportHeight)
461
- visibleSource.push("");
462
- const visible = visibleSource.map((row, index) => {
463
- const painted = cachedString(this.#paintedRowCache, `${paintId}\u0000${row}`, cacheLimit, () => paintDocumentRow(row));
464
- if (!painted.reused)
465
- paintRecomputedRows.add(index);
466
- return painted.value;
467
- });
468
- const governing = governingPrompt(input.promptAnchors, this.#scrollTop);
469
- const stickyActive = governing !== null && governing.firstRow < this.#scrollTop;
470
- if (stickyActive && visible.length > 0) {
471
- const quiet = this.#scrollTop > governing.lastRow;
472
- // Compatibility: only non-hovered prominent rows may retain source metadata roles.
473
- // Quiet and hover keep the established full-row styling; anchors without a
474
- // variant retain their existing presentation in every state.
475
- const sourceRow = !quiet && !this.#stickyHovered
476
- ? governing.prominentSourceRow ?? governing.sourceRow
477
- : governing.sourceRow;
478
- const source = cachedString(this.#paintedRowCache, `${paintId}\u0000${sourceRow}`, cacheLimit, () => paintDocumentRow(sourceRow));
479
- if (!source.reused)
480
- paintRecomputedRows.add(0);
481
- const sticky = theme.sticky(source.value, this.#stickyHovered);
482
- visible[0] = quiet && !this.#stickyHovered ? theme.quietSticky(sticky) : sticky;
483
- visibleSource[0] = sourceRow;
484
- }
485
- this.#selectionRows = [
486
- ...visibleSource.map(row => truncateToWidth(row, contentWidth)),
487
- ...dock,
488
- ].slice(0, height);
489
- const visibleAnchors = Array.from({ length: viewportHeight }, (_value, row) => {
490
- const documentRow = this.#scrollTop + row;
491
- return documentRow < documentRows.length
492
- ? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", this.#selectableDocumentRowCount, documentRows.length)
493
- : { kind: "screen", row, source: this.#selectionRows[row] ?? "" };
494
- });
495
- this.#firstSelectableRow = stickyActive && viewportHeight > 0 ? 1 : 0;
412
+ this.#rowCaches.trim(cacheLimit);
413
+ const { visible, visibleSource, paintRecomputedRows, governing } = this.#paintVisibleRows(input, theme, cacheLimit);
414
+ this.#selectionRows = [...visibleSource.map(row => truncateToWidth(row, contentWidth)), ...dock].slice(0, height);
415
+ this.#firstSelectableRow = governing !== null && viewportHeight > 0 ? 1 : 0;
496
416
  this.#selectionRowAnchors = [
497
- ...visibleAnchors,
417
+ ...visibleSelectionAnchors(documentRows, this.#scrollTop, viewportHeight, this.#selectableDocumentRowCount, this.#selectionRows),
498
418
  ...dock.map((_row, index) => ({ kind: "dock", fromBottom: dock.length - index - 1 })),
499
419
  ].slice(0, height);
500
420
  this.#projectSelection(documentRows, dock, height);
501
- const selectionPainterId = this.#functionId(theme.selection);
502
421
  // Concurrency: a painter may synchronously route input in tests or host integrations.
503
422
  // Label this frame with the exact selection snapshot it began composing.
504
423
  const composingSelectionRevision = this.#selectionRevision;
505
424
  const orderedSelection = this.#visibleSelection();
425
+ const frameRows = [...visible, ...dock].slice(0, height);
426
+ const control = placeBottomControl({
427
+ detached: this.#maxScroll > 0 && !this.#followingEnd, newMessages: this.#newMessages, contentWidth, viewportHeight,
428
+ frameRowCount: frameRows.length, anchorRow: input.bottomControlRow, pointer: input.pointerPosition, theme,
429
+ });
430
+ this.#selectionRows = maskSelectionRows(this.#selectionRows, width, control, input.coveredCells);
431
+ this.#updateCopyableSelection();
432
+ const rowPaint = {
433
+ width, contentWidth, viewportHeight, cacheLimit, theme, selectionPainterId: this.#rowCaches.functionId(theme.selection),
434
+ geometry, presentation, railActive: this.#railHovered || this.#railDragging, overlay: input.frameOverlay, control,
435
+ };
506
436
  const recomputedRows = [];
507
437
  const reusedRows = [];
508
438
  const selectionDamagedRows = [];
509
439
  const visibleStates = [];
510
- const frameRows = [...visible, ...dock].slice(0, height);
511
- const selectionRows = [...this.#selectionRows];
512
- let bottomHit = null;
513
- let control;
514
- if (this.#maxScroll > 0 && !this.#followingEnd && frameRows.length > 0) {
515
- const genericLabel = " Jump to bottom (Ctrl+End) ↓ ";
516
- const countedLabel = this.#newMessages > 0
517
- ? ` ${this.#newMessages} new message${this.#newMessages === 1 ? "" : "s"} (Ctrl+End) ↓ `
518
- : genericLabel;
519
- const label = displayWidth(countedLabel) <= contentWidth ? countedLabel : genericLabel;
520
- const labelWidth = displayWidth(label);
521
- if (labelWidth <= contentWidth) {
522
- const fallbackRow = Math.max(0, viewportHeight - 1);
523
- const row = clamp(input.bottomControlRow ?? fallbackRow, 0, frameRows.length - 1);
524
- const left = Math.floor((contentWidth - labelWidth) / 2);
525
- bottomHit = { row: row + 1, columnStart: left + 1, columnEnd: left + labelWidth };
526
- // Invariant: appearance and hit testing share current geometry, even when no mouse moves.
527
- const pointer = input.pointerPosition;
528
- const bottomHovered = pointer !== undefined && pointer.row === bottomHit.row
529
- && pointer.column >= bottomHit.columnStart && pointer.column <= bottomHit.columnEnd;
530
- control = { row, left, text: `${CONTROL_STYLE_RESET}${theme.bottomControl(label, bottomHovered)}` };
531
- // Invariant: the control floats above selection; copy restores the text beneath it.
532
- selectionRows[row] = overlaySpan(padRowPreservingBackground(selectionRows[row] ?? "", width), left, left + labelWidth, " ".repeat(labelWidth));
533
- }
534
- }
535
- for (const covered of input.coveredCells ?? []) {
536
- const row = covered.row - 1;
537
- if (row < 0 || row >= selectionRows.length)
538
- continue;
539
- const from = clamp(covered.columnStart - 1, 0, width);
540
- const to = clamp(covered.columnEnd, from, width);
541
- selectionRows[row] = overlaySpan(padRowPreservingBackground(selectionRows[row] ?? "", width), from, to, " ".repeat(to - from));
542
- }
543
- this.#selectionRows = selectionRows;
544
- this.#updateCopyableSelection();
545
440
  for (let row = 0; row < frameRows.length; row += 1) {
546
- const painted = frameRows[row] ?? "";
547
- const rowSelectionWidth = row < viewportHeight ? contentWidth : width;
548
- const range = selectionRangeForLine(orderedSelection, row, this.#selectionRows.length, rowSelectionWidth);
549
- // Invariant: the reserved rail gutter is not a semantic selection or copy cell,
550
- // but a range that reaches the content boundary paints through it so the rail
551
- // floats above one continuous selection surface.
552
- const paintRange = range !== null && row < viewportHeight
553
- && contentWidth < width && range.to === contentWidth
554
- ? { from: range.from, to: width }
555
- : range;
556
- const padded = row < viewportHeight || paintRange !== null;
557
- const base = cachedString(this.#baseRowCache, `${width}\u0000${contentWidth}\u0000${row < viewportHeight}\u0000${padded}\u0000${painted}`, cacheLimit, () => row < viewportHeight
558
- ? padViewportRowWithGutter(painted, contentWidth, width)
559
- : padded ? padRowPreservingBackground(painted, width) : painted);
560
- let rowRecomputed = row < viewportHeight && paintRecomputedRows.has(row) || !base.reused;
561
- const rangeKey = paintRange === null ? "-" : `${paintRange.from}:${paintRange.to}`;
562
- const selected = paintRange === null
563
- ? { value: base.value, reused: true }
564
- : cachedString(this.#selectedRowCache, `${selectionPainterId}\u0000${rangeKey}\u0000${base.value}`, cacheLimit, () => theme.selection(base.value, paintRange.from, paintRange.to));
565
- rowRecomputed ||= !selected.reused;
566
- // Invariant: paint the rail after selection over the gutter's continued row
567
- // background. Source content and selection still end at the preceding cell.
568
- let railCell = "";
569
- if (row > 0 && row < viewportHeight && presentation.visible && geometry !== null) {
570
- const trackRow = row - 1;
571
- const thumb = isThumbRow(geometry, trackRow);
572
- const glyph = thumb ? presentation.thumbGlyph : presentation.trackGlyph;
573
- railCell = thumb ? theme.thumb(glyph, this.#railHovered || this.#railDragging) : theme.track(glyph);
574
- }
575
- const overlayLimit = row < viewportHeight ? contentWidth : width;
576
- const overlayText = input.frameOverlay?.row === row + 1
577
- ? truncateToWidth(input.frameOverlay.text, overlayLimit)
578
- : "";
579
- const overlayWidth = displayWidth(overlayText);
580
- const rowControl = control?.row === row && bottomHit !== null
581
- ? { left: control.left, right: bottomHit.columnEnd, text: control.text }
582
- : null;
583
- const final = cachedString(this.#finalRowCache, `${width}\u0000${railCell}\u0000${overlayText}\u0000${rowControl?.text ?? ""}\u0000${selected.value}`, cacheLimit, () => {
584
- const withRail = railCell.length === 0 ? selected.value : overlaySpan(selected.value, width - 1, width, `${GUTTER_DECORATION_RESET}${railCell}`, { inheritStartStyle: true });
585
- // Invariant: floating control styling is applied only after the row and
586
- // gutter surfaces are resolved, so its inline background cannot leak.
587
- const withControl = rowControl === null ? withRail : overlaySpan(withRail, rowControl.left, rowControl.right, rowControl.text);
588
- return overlayWidth === 0 ? withControl : overlaySpan(padRowPreservingBackground(withControl, width), overlayLimit - overlayWidth, overlayLimit, `${GUTTER_DECORATION_RESET}${overlayText}`, { inheritStartStyle: true });
589
- });
590
- rowRecomputed ||= !final.reused;
591
- frameRows[row] = final.value.replace(VIEWPORT_PROMPT_ZONES, "");
592
- (rowRecomputed ? recomputedRows : reusedRows).push(row + 1);
593
- const state = `${width}\u0000${row}\u0000${painted}\u0000${rangeKey}\u0000${range === null ? "" : selectionPainterId}\u0000${railCell}\u0000${overlayText}\u0000${rowControl?.text ?? ""}`;
594
- visibleStates.push(state);
595
- if (this.#previousVisibleStates[row] !== state)
441
+ const range = selectionRangeForLine(orderedSelection, row, this.#selectionRows.length, row < viewportHeight ? contentWidth : width);
442
+ const painted = this.#rowCaches.paintRow(rowPaint, row, frameRows[row] ?? "", range, paintRecomputedRows.has(row));
443
+ frameRows[row] = painted.text;
444
+ (painted.recomputed ? recomputedRows : reusedRows).push(row + 1);
445
+ visibleStates.push(painted.state);
446
+ if (this.#previousVisibleStates[row] !== painted.state)
596
447
  selectionDamagedRows.push(row + 1);
597
448
  }
598
449
  this.#previousVisibleStates = visibleStates;
599
- const nextDocumentRange = {
600
- start: this.#scrollTop,
601
- end: Math.min(documentRows.length, this.#scrollTop + viewportHeight),
602
- };
603
- const previousDocumentRange = previousDescriptor?.nextDocumentRange ?? null;
604
- const sameGeometry = previousDescriptor !== null
605
- && previousDescriptor.width === width
606
- && previousDescriptor.height === height
607
- && sameRectangle(previousDescriptor.transcript, viewportHeight === 0 ? null : { rowStart: 1, rowEnd: viewportHeight })
608
- && sameRectangle(previousDescriptor.dock, dock.length === 0 ? null : { rowStart: viewportHeight + 1, rowEnd: height });
609
- const verticalShiftRows = previousDocumentRange === null ? 0 : nextDocumentRange.start - previousDocumentRange.start;
610
- const safeVerticalShift = sameGeometry
611
- && previousDescriptor?.followingEnd === true
612
- && this.#followingEnd
613
- && this.#selection === undefined
614
- && verticalShiftRows > 0
615
- && verticalShiftRows < viewportHeight;
616
- const descriptor = {
617
- frameId: ++this.#frameId,
618
- width,
619
- height,
620
- transcript: viewportHeight === 0 ? null : { rowStart: 1, rowEnd: viewportHeight },
621
- dock: dock.length === 0 ? null : { rowStart: viewportHeight + 1, rowEnd: height },
622
- previousDocumentRange,
623
- nextDocumentRange,
624
- previousFollowingEnd: previousDescriptor?.followingEnd ?? null,
625
- followingEnd: this.#followingEnd,
626
- transientRowCount: Math.max(0, documentRows.length - this.#selectableDocumentRowCount),
627
- transientAlignmentGapRows,
628
- bottomAlignedTailRowCount,
629
- liveTailRows: visibleLiveTailRows(input.liveTailStartRow, this.#selectableDocumentRowCount, this.#scrollTop, viewportHeight),
630
- verticalShiftRows,
631
- safeVerticalShift,
632
- selectionRevision: composingSelectionRevision,
633
- selectionDamagedRows,
634
- cause: previousDescriptor === null
635
- ? "initial"
636
- : !sameGeometry
637
- ? "geometry-change"
638
- : !this.#followingEnd
639
- ? "detached"
640
- : safeVerticalShift
641
- ? "follow-shift"
642
- : "steady",
643
- };
450
+ const descriptor = frameDescriptor({
451
+ frameId: ++this.#frameId, previous: previousDescriptor, width, height, viewportHeight, dockRowCount: dock.length,
452
+ documentRowCount: documentRows.length, selectableDocumentRowCount: this.#selectableDocumentRowCount,
453
+ scrollTop: this.#scrollTop, followingEnd: this.#followingEnd, selecting: this.#selection !== undefined,
454
+ transientAlignmentGapRows, bottomAlignedTailRowCount, liveTailStartRow: input.liveTailStartRow,
455
+ selectionRevision: composingSelectionRevision, selectionDamagedRows,
456
+ });
644
457
  assertTranscriptViewportFrameDescriptor(descriptor);
645
- const hits = {
646
- viewportHeight,
647
- rail: geometry === null || this.#config.scrollbarAppearance === "hidden" ? null : {
648
- column: width,
649
- rowStart: 2,
650
- trackHeight: Math.max(0, viewportHeight - 1),
651
- geometry,
652
- },
653
- sticky: stickyActive ? { row: 1, target: governing.firstRow, width: contentWidth } : null,
654
- bottom: bottomHit,
655
- transientTail: Array.from({ length: Math.max(0, Math.min(documentRows.length, this.#scrollTop + viewportHeight) - Math.max(this.#scrollTop, this.#selectableDocumentRowCount)) }, (_row, index) => Math.max(this.#scrollTop, this.#selectableDocumentRowCount) - this.#scrollTop + index + 1),
656
- };
458
+ const hits = hitRegions({
459
+ width, contentWidth, viewportHeight, documentRowCount: documentRows.length,
460
+ selectableDocumentRowCount: this.#selectableDocumentRowCount, scrollTop: this.#scrollTop, geometry,
461
+ railHidden: this.#config.scrollbarAppearance === "hidden", stickyTarget: governing?.firstRow ?? null, bottom: control?.hit ?? null,
462
+ });
657
463
  const frame = {
658
464
  rows: frameRows,
659
465
  contentWidth,
@@ -662,17 +468,48 @@ export class TranscriptViewport {
662
468
  followingEnd: this.#followingEnd,
663
469
  hits,
664
470
  descriptor,
665
- selectionDamage: {
666
- revision: composingSelectionRevision,
667
- recomputedRows,
668
- reusedRows,
669
- cacheEntries: this.#paintedRowCache.size + this.#baseRowCache.size
670
- + this.#selectedRowCache.size + this.#finalRowCache.size,
671
- },
471
+ selectionDamage: { revision: composingSelectionRevision, recomputedRows, reusedRows, cacheEntries: this.#rowCaches.entries },
672
472
  };
673
473
  this.#frame = frame;
674
474
  return frame;
675
475
  }
476
+ // Rationale: the visible document rows go through the paint transform, with the governing prompt
477
+ // pinned as a sticky header once its first row has scrolled away. `governing` is that header's
478
+ // anchor, or null while the prompt's first row is still on screen.
479
+ #paintVisibleRows(input, theme, cacheLimit) {
480
+ const paintDocumentRow = input.paintDocumentRow ?? IDENTITY_ROW;
481
+ const paintId = this.#rowCaches.functionId(paintDocumentRow);
482
+ const paintRecomputedRows = new Set();
483
+ const visibleSource = this.#documentRows.slice(this.#scrollTop, this.#scrollTop + this.#viewportHeight);
484
+ while (visibleSource.length < this.#viewportHeight)
485
+ visibleSource.push("");
486
+ const visible = visibleSource.map((row, index) => {
487
+ const painted = this.#rowCaches.painted(paintId, row, cacheLimit, paintDocumentRow);
488
+ if (!painted.reused)
489
+ paintRecomputedRows.add(index);
490
+ return painted.value;
491
+ });
492
+ const governing = governingPrompt(input.promptAnchors, this.#scrollTop);
493
+ const stickyActive = governing !== null && governing.firstRow < this.#scrollTop;
494
+ if (!stickyActive)
495
+ return { visible, visibleSource, paintRecomputedRows, governing: null };
496
+ if (visible.length === 0)
497
+ return { visible, visibleSource, paintRecomputedRows, governing };
498
+ const quiet = this.#scrollTop > governing.lastRow;
499
+ // Compatibility: only non-hovered prominent rows may retain source metadata roles.
500
+ // Quiet and hover keep the established full-row styling; anchors without a
501
+ // variant retain their existing presentation in every state.
502
+ const sourceRow = !quiet && !this.#stickyHovered
503
+ ? governing.prominentSourceRow ?? governing.sourceRow
504
+ : governing.sourceRow;
505
+ const source = this.#rowCaches.painted(paintId, sourceRow, cacheLimit, paintDocumentRow);
506
+ if (!source.reused)
507
+ paintRecomputedRows.add(0);
508
+ const sticky = theme.sticky(source.value, this.#stickyHovered);
509
+ visible[0] = quiet && !this.#stickyHovered ? theme.quietSticky(sticky) : sticky;
510
+ visibleSource[0] = sourceRow;
511
+ return { visible, visibleSource, paintRecomputedRows, governing };
512
+ }
676
513
  /** Reuses an established transcript frame when only same-height dock rows changed. */
677
514
  composeDockOnly(dockRows, width, height, copyableDockRows = []) {
678
515
  const previous = this.#frame;
@@ -716,15 +553,6 @@ export class TranscriptViewport {
716
553
  this.#frame = frame;
717
554
  return frame;
718
555
  }
719
- #functionId(value) {
720
- const existing = this.#functionIds.get(value);
721
- if (existing !== undefined)
722
- return existing;
723
- const id = this.#nextFunctionId;
724
- this.#nextFunctionId += 1;
725
- this.#functionIds.set(value, id);
726
- return id;
727
- }
728
556
  #selectionAnchorAt(line) {
729
557
  if (line < 0 || line >= this.#selectionRows.length)
730
558
  return undefined;
@@ -935,34 +763,13 @@ function selectionRangeForLine(selection, documentLine, selectableRowCount, widt
935
763
  const boundedTo = clamp(to, boundedFrom, width);
936
764
  return boundedTo > boundedFrom ? { from: boundedFrom, to: boundedTo } : null;
937
765
  }
938
- function cachedString(cache, key, limit, create) {
939
- const existing = cache.get(key);
940
- if (existing !== undefined) {
941
- cache.delete(key);
942
- cache.set(key, existing);
943
- return { value: existing, reused: true };
944
- }
945
- const value = create();
946
- cache.set(key, value);
947
- while (cache.size > limit)
948
- cache.delete(cache.keys().next().value);
949
- return { value, reused: false };
950
- }
951
- function trimCache(cache, limit) {
952
- while (cache.size > limit)
953
- cache.delete(cache.keys().next().value);
954
- }
955
- /**
956
- * Rows of the streaming block that the visible window actually shows. Gap insertion happens
957
- * after the selectable document, so live-block indices need no adjustment here.
958
- */
959
- function visibleLiveTailRows(liveTailStartRow, selectableDocumentRowCount, scrollTop, viewportHeight) {
960
- if (liveTailStartRow === undefined || !Number.isSafeInteger(liveTailStartRow))
961
- return 0;
962
- const start = clamp(liveTailStartRow, 0, selectableDocumentRowCount);
963
- const visibleStart = Math.max(start, scrollTop);
964
- const visibleEnd = Math.min(selectableDocumentRowCount, scrollTop + viewportHeight);
965
- return Math.max(0, visibleEnd - visibleStart);
766
+ function visibleSelectionAnchors(documentRows, scrollTop, viewportHeight, selectableDocumentRowCount, selectionRows) {
767
+ return Array.from({ length: viewportHeight }, (_value, row) => {
768
+ const documentRow = scrollTop + row;
769
+ return documentRow < documentRows.length
770
+ ? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", selectableDocumentRowCount, documentRows.length)
771
+ : { kind: "screen", row, source: selectionRows[row] ?? "" };
772
+ });
966
773
  }
967
774
  function governingPrompt(anchors, scrollTop) {
968
775
  let result = null;
@@ -973,38 +780,6 @@ function governingPrompt(anchors, scrollTop) {
973
780
  }
974
781
  return result;
975
782
  }
976
- function padViewportRowWithGutter(line, contentWidth, frameWidth) {
977
- const boundedContentWidth = Math.min(Math.max(1, contentWidth), frameWidth);
978
- const content = padRowPreservingBackground(line, boundedContentWidth);
979
- const gutterWidth = frameWidth - boundedContentWidth;
980
- if (gutterWidth === 0)
981
- return content;
982
- // Invariant: render no source glyph beyond contentWidth, but reuse the established
983
- // row-padding background before stripping source foreground, links, and emphasis.
984
- const gutterStyle = explicitBackgroundPaddingStyle(line);
985
- return `${content}${GUTTER_DECORATION_RESET}${gutterStyle?.on ?? ""}${" ".repeat(gutterWidth)}${gutterStyle?.off ?? ""}`;
986
- }
987
- function explicitBackgroundPaddingStyle(line) {
988
- const background = /\u001b\[(?:4[0-8]|10[0-7]|48;(?:2;\d+;\d+;\d+|5;\d+))m/.exec(line)?.[0];
989
- return background === undefined ? null : { on: background, off: "\u001b[49m" };
990
- }
991
- function rowPaddingStyle(line) {
992
- const background = explicitBackgroundPaddingStyle(line);
993
- if (background !== null)
994
- return background;
995
- const reverse = /\u001b\[(?:7(?:;[0-9]+)*|[0-9;]*;7(?:;[0-9]+)*)m/.exec(line)?.[0];
996
- return reverse === undefined ? null : { on: reverse, off: "\u001b[27m" };
997
- }
998
- function padRowPreservingBackground(line, width) {
999
- const shown = displayWidth(line) > width ? truncateToWidth(line, width) : line;
1000
- const padding = Math.max(0, width - displayWidth(shown));
1001
- if (padding === 0)
1002
- return shown;
1003
- const style = rowPaddingStyle(line);
1004
- return style === null
1005
- ? shown + " ".repeat(padding)
1006
- : `${shown}${style.on}${" ".repeat(padding)}${style.off}`;
1007
- }
1008
783
  export function assertTranscriptViewportFrameDescriptor(descriptor) {
1009
784
  if (!Number.isSafeInteger(descriptor.frameId) || descriptor.frameId < 1)
1010
785
  throw new TypeError("viewport frame descriptor identity is invalid");
@@ -1069,9 +844,6 @@ function assertDocumentRange(range, label) {
1069
844
  throw new TypeError(`viewport frame descriptor ${label} document range is invalid`);
1070
845
  }
1071
846
  }
1072
- function sameRectangle(left, right) {
1073
- return left === null ? right === null : right !== null && left.rowStart === right.rowStart && left.rowEnd === right.rowEnd;
1074
- }
1075
847
  function copyableDockRowsBySuffix(dockRows, declarations) {
1076
848
  const result = new Map();
1077
849
  for (const declaration of declarations ?? []) {