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

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 (81) hide show
  1. package/dist/app/session-shell/prompt-submission.d.ts +1 -1
  2. package/dist/app/session-shell/prompt-submission.js +9 -9
  3. package/dist/app/session-shell/resource-labels.d.ts +2 -0
  4. package/dist/app/session-shell/resource-labels.js +158 -0
  5. package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
  6. package/dist/app/session-shell/session-prompt-history.js +80 -0
  7. package/dist/app/session-shell/session-selectors.d.ts +46 -0
  8. package/dist/app/session-shell/session-selectors.js +269 -0
  9. package/dist/app/session-shell/session-shell-root.d.ts +6 -28
  10. package/dist/app/session-shell/session-shell-root.js +26 -220
  11. package/dist/app/session-shell/session-shell.d.ts +3 -23
  12. package/dist/app/session-shell/session-shell.js +70 -388
  13. package/dist/app/session-shell/session-terminal-presentation.d.ts +16 -0
  14. package/dist/app/session-shell/session-terminal-presentation.js +35 -0
  15. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -10
  16. package/dist/app/session-shell/session-viewport-controller.js +40 -318
  17. package/dist/app/session-shell/settings-bindings.d.ts +1 -1
  18. package/dist/app/session-shell/settings-bindings.js +2 -2
  19. package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
  20. package/dist/app/session-shell/viewport-pointer.js +361 -0
  21. package/dist/composition/owned-ui.js +63 -59
  22. package/dist/contracts/owned-ui/index.d.ts +1 -1
  23. package/dist/contracts/owned-ui/index.js +1 -1
  24. package/dist/contracts/owned-ui/validation.d.ts +1 -11
  25. package/dist/contracts/owned-ui/validation.js +8 -104
  26. package/dist/contracts/presentation/index.d.ts +1 -1
  27. package/dist/contracts/presentation/terminal.d.ts +0 -6
  28. package/dist/features/owned-ui/settings-app.js +13 -208
  29. package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
  30. package/dist/features/owned-ui/settings-model-dialog.js +194 -0
  31. package/dist/foundation/release/automatic-update.js +1 -8
  32. package/dist/foundation/release/dependency-layer.js +3 -3
  33. package/dist/foundation/release/latest-release.js +1 -1
  34. package/dist/foundation/release/managed-path.d.ts +10 -0
  35. package/dist/foundation/release/managed-path.js +26 -0
  36. package/dist/foundation/release/release-gc.js +6 -8
  37. package/dist/foundation/release/restart-certification.js +3 -4
  38. package/dist/foundation/release/update-activation.js +1 -1
  39. package/dist/foundation/release/update-execution-lease.js +1 -7
  40. package/dist/foundation/release/update-installer.d.ts +45 -0
  41. package/dist/foundation/release/update-installer.js +57 -0
  42. package/dist/foundation/release/update-recovery.js +2 -9
  43. package/dist/foundation/release/update.d.ts +4 -23
  44. package/dist/foundation/release/update.js +160 -179
  45. package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
  46. package/dist/integrations/pi/components/bracketed-paste.js +123 -0
  47. package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
  48. package/dist/integrations/pi/engine/adapter.d.ts +1 -8
  49. package/dist/integrations/pi/engine/adapter.js +6 -49
  50. package/dist/integrations/pi/engine/host.js +1 -1
  51. package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
  52. package/dist/integrations/pi/engine/repository-metadata.js +189 -0
  53. package/dist/integrations/pi/engine/session-events.js +8 -6
  54. package/dist/integrations/pi/engine/session-runtime.d.ts +6 -12
  55. package/dist/integrations/pi/engine/session-runtime.js +21 -178
  56. package/dist/integrations/pi/engine/view-state.d.ts +21 -2
  57. package/dist/integrations/pi/engine/view-state.js +129 -1
  58. package/dist/integrations/pi/engine/workflow-runner.d.ts +0 -1
  59. package/dist/integrations/pi/engine/workflow-runner.js +254 -365
  60. package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
  61. package/dist/integrations/pi/engine/workflow-share.js +168 -0
  62. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -14
  63. package/dist/integrations/pi/engine/workflow-support.js +0 -47
  64. package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
  65. package/dist/integrations/pi/tui-runtime/adapter.js +35 -144
  66. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +4 -4
  67. package/dist/integrations/pi/tui-runtime/index.d.ts +0 -1
  68. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -2
  69. package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
  70. package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
  71. package/dist/ui/components/transcript-viewport-frame.js +314 -0
  72. package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
  73. package/dist/ui/components/transcript-viewport.d.ts +2 -147
  74. package/dist/ui/components/transcript-viewport.js +87 -315
  75. package/package.json +3 -3
  76. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
  77. package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
  78. package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
  79. package/dist/integrations/pi/engine/usage-view.js +0 -56
  80. package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -22
  81. /package/dist/{integrations/pi/tui-runtime/contracts.js → ui/components/transcript-viewport-types.js} +0 -0
@@ -0,0 +1,83 @@
1
+ import type { OwnedUiShellEditorPort, OwnedUiViewportSettings } from "../../contracts/owned-ui/index.js";
2
+ import type { TerminalPointerSurface } from "../../contracts/presentation/index.js";
3
+ import { type RoutedMouseInput } from "../../ui/components/mouse.js";
4
+ import type { SelectionCopySnapshot } from "../../ui/components/selection-copy.js";
5
+ import type { TranscriptViewport, TranscriptViewportFrame } from "../../ui/components/transcript-viewport.js";
6
+ /** One-based, inclusive terminal cells of a clickable viewport-tail control. */
7
+ export interface TailControlRegion {
8
+ readonly rowStart: number;
9
+ readonly rowEnd: number;
10
+ readonly columnStart: number;
11
+ readonly columnEnd: number;
12
+ }
13
+ export type TailControl = TailControlRegion & {
14
+ readonly activate: () => void;
15
+ };
16
+ export declare function withinTailControl(region: TailControlRegion, column: number, row: number): boolean;
17
+ export interface ViewportPointerPorts {
18
+ readonly viewport: TranscriptViewport;
19
+ readonly editor: OwnedUiShellEditorPort;
20
+ config(): OwnedUiViewportSettings;
21
+ copyOnSelect(): boolean;
22
+ /** False while replacement input or overlay bounds have not been painted yet. */
23
+ inputGeometryReady(): boolean;
24
+ /** The topmost overlay or input surface under a cell. */
25
+ modalAt(column: number, row: number): TerminalPointerSurface | undefined;
26
+ /** The editor rows of the most recent composition. */
27
+ editorFrame(): {
28
+ readonly rowStart: number;
29
+ readonly rowEnd: number;
30
+ } | undefined;
31
+ tailControl(): TailControl | undefined;
32
+ clearTailControl(): void;
33
+ requestRender(): void;
34
+ requestHyperlinkCleanup(rows?: readonly number[]): void;
35
+ updateSelectionAutoScroll(column: number, row: number, viewportHeight: number, frameHeight: number): void;
36
+ stopSelectionAutoScroll(): void;
37
+ /** A right-click on the editor pastes the clipboard. */
38
+ pasteFromPointer(): void;
39
+ }
40
+ /** How one input chunk's mouse reports changed the viewport. */
41
+ export interface ViewportPointerOutcome {
42
+ repaint: boolean;
43
+ forceRepaint: boolean;
44
+ activity: boolean;
45
+ completedCopy: SelectionCopySnapshot | undefined;
46
+ }
47
+ interface PointerCall {
48
+ readonly allowWheel: boolean;
49
+ readonly now: number;
50
+ readonly editorActive: boolean;
51
+ }
52
+ /**
53
+ * The pointer gestures of the session viewport: which surface a gesture is latched to, the
54
+ * suppression of a gesture begun on the dock or a control, the editor click that waits for drag
55
+ * or release, the rail drag offset, the pointer position and the hovered link. Each report is
56
+ * routed in the same order of checks, and its effect is reported as an outcome.
57
+ */
58
+ export declare class ViewportPointerGestures {
59
+ #private;
60
+ constructor(ports: ViewportPointerPorts);
61
+ get position(): {
62
+ readonly column: number;
63
+ readonly row: number;
64
+ } | undefined;
65
+ get editorSelecting(): boolean;
66
+ /** A gesture is latched to a surface or a selection is being dragged. */
67
+ get held(): boolean;
68
+ /** No pointer position or hovered link is remembered. */
69
+ get idle(): boolean;
70
+ /** Forget every latch. */
71
+ clear(): void;
72
+ /** Swallow the rest of a held gesture until its release. */
73
+ drain(): void;
74
+ /** Before the first paint, a left press and its motion and release belong to no surface. */
75
+ routeUnpainted(data: string): RoutedMouseInput;
76
+ route(data: string, frame: TranscriptViewportFrame, call: PointerCall): {
77
+ readonly routed: RoutedMouseInput;
78
+ readonly outcome: ViewportPointerOutcome;
79
+ };
80
+ /** A newly composed frame may move a link away from a stationary pointer. */
81
+ refreshHover(frame: TranscriptViewportFrame): void;
82
+ }
83
+ export {};
@@ -0,0 +1,361 @@
1
+ import { routeMouseInput } from "../../ui/components/mouse.js";
2
+ import { scrollForThumbRow, scrollForTrackPage, scrollbarWheelRows } from "../../ui/components/scrollbar.js";
3
+ import { readVisibleHyperlinks } from "../../ui/components/visible-hyperlinks.js";
4
+ export function withinTailControl(region, column, row) {
5
+ return row >= region.rowStart && row <= region.rowEnd && column >= region.columnStart && column <= region.columnEnd;
6
+ }
7
+ /**
8
+ * The pointer gestures of the session viewport: which surface a gesture is latched to, the
9
+ * suppression of a gesture begun on the dock or a control, the editor click that waits for drag
10
+ * or release, the rail drag offset, the pointer position and the hovered link. Each report is
11
+ * routed in the same order of checks, and its effect is reported as an outcome.
12
+ */
13
+ export class ViewportPointerGestures {
14
+ #ports;
15
+ #dragGrabOffset = null;
16
+ #owner;
17
+ // Invariant: a right-click editor gesture remains owned by the dock.
18
+ #dockSuppressed = false;
19
+ // Invariant: a gesture begun on an explicit control remains owned by that control.
20
+ #tailSuppressed = false;
21
+ // Invariant: editor selection remains true only while its left button is held.
22
+ #editorSelecting = false;
23
+ // Invariant: no-drag editor presses replay on release; distinct motion promotes frame selection.
24
+ #pendingEditorClick;
25
+ #position;
26
+ #hoveredHyperlinkKey;
27
+ constructor(ports) {
28
+ this.#ports = ports;
29
+ }
30
+ get position() {
31
+ return this.#position;
32
+ }
33
+ get editorSelecting() {
34
+ return this.#editorSelecting;
35
+ }
36
+ /** A gesture is latched to a surface or a selection is being dragged. */
37
+ get held() {
38
+ return this.#owner !== undefined || this.#ports.viewport.selectionActive || this.#editorSelecting;
39
+ }
40
+ /** No pointer position or hovered link is remembered. */
41
+ get idle() {
42
+ return this.#position === undefined && this.#hoveredHyperlinkKey === undefined;
43
+ }
44
+ /** Forget every latch. */
45
+ clear() {
46
+ this.#dragGrabOffset = null;
47
+ this.#dockSuppressed = false;
48
+ this.#tailSuppressed = false;
49
+ this.#editorSelecting = false;
50
+ this.#pendingEditorClick = undefined;
51
+ this.#position = undefined;
52
+ this.#hoveredHyperlinkKey = undefined;
53
+ this.#owner = undefined;
54
+ }
55
+ /** Swallow the rest of a held gesture until its release. */
56
+ drain() {
57
+ this.#owner = "drain";
58
+ }
59
+ /** Before the first paint, a left press and its motion and release belong to no surface. */
60
+ routeUnpainted(data) {
61
+ // Invariant: a not-yet-painted A1 screen is not Pi's selection surface.
62
+ return routeMouseInput(data, event => {
63
+ if (event.kind === "press" && event.button === 0)
64
+ this.#tailSuppressed = true;
65
+ if (event.kind === "release")
66
+ this.#tailSuppressed = false;
67
+ return event.kind === "motion" || event.kind === "release" || (event.kind === "press" && event.button === 0);
68
+ });
69
+ }
70
+ route(data, frame, call) {
71
+ const outcome = { repaint: false, forceRepaint: false, activity: false, completedCopy: undefined };
72
+ const routed = routeMouseInput(data, (event, report) => this.#report(event, report, frame, call, outcome));
73
+ return { routed, outcome };
74
+ }
75
+ /** A newly composed frame may move a link away from a stationary pointer. */
76
+ refreshHover(frame) {
77
+ const pointer = this.#position;
78
+ if (pointer === undefined || this.#ports.viewport.selectionActive || this.#editorSelecting)
79
+ return;
80
+ const next = hyperlinkKeyAt(frame, pointer.column, pointer.row);
81
+ if (this.#hoveredHyperlinkKey !== undefined && next !== this.#hoveredHyperlinkKey) {
82
+ // Platform: Windows Terminal can leave its solid native-hover underline behind
83
+ // when a following viewport moves the link away from a stationary pointer.
84
+ this.#ports.requestHyperlinkCleanup([pointer.row]);
85
+ this.#ports.requestRender();
86
+ }
87
+ this.#hoveredHyperlinkKey = next;
88
+ }
89
+ #report(event, report, frame, call, outcome) {
90
+ const viewport = this.#ports.viewport;
91
+ const hits = frame.hits;
92
+ const overModal = this.#ports.modalAt(event.column, event.row);
93
+ const wheel = event.kind === "wheel-up" || event.kind === "wheel-down";
94
+ // Invariant: latch each gesture to one surface.
95
+ if (this.#owner === "drain" || !this.#ports.inputGeometryReady()) {
96
+ if (event.kind === "release")
97
+ this.#owner = undefined;
98
+ else if (event.kind === "press")
99
+ this.#owner = "drain";
100
+ return true;
101
+ }
102
+ const owner = !wheel && this.#owner !== undefined ? this.#owner : overModal;
103
+ if (owner !== undefined && owner !== "viewport") {
104
+ if (event.kind === "press")
105
+ this.#owner = owner;
106
+ if (event.kind === "release")
107
+ this.#owner = undefined;
108
+ if (event.kind === "press" && viewport.clearSelection())
109
+ outcome.repaint = true;
110
+ viewport.setRailHovered(false);
111
+ viewport.setStickyHovered(false);
112
+ this.#position = undefined;
113
+ owner.component.handleInput?.(report);
114
+ outcome.repaint = true;
115
+ return true;
116
+ }
117
+ if (event.kind === "press")
118
+ this.#owner = "viewport";
119
+ if (event.kind === "release")
120
+ this.#owner = undefined;
121
+ const tailControl = this.#ports.tailControl();
122
+ const target = {
123
+ frame,
124
+ overRail: overModal === undefined && hits.rail !== null
125
+ && event.column === hits.rail.column
126
+ && event.row >= hits.rail.rowStart
127
+ && event.row < hits.rail.rowStart + hits.rail.trackHeight,
128
+ overSticky: overModal === undefined && hits.sticky !== null && event.row === hits.sticky.row && event.column <= hits.sticky.width,
129
+ overBottom: overModal === undefined && hits.bottom !== null && event.row === hits.bottom.row
130
+ && event.column >= hits.bottom.columnStart && event.column <= hits.bottom.columnEnd,
131
+ tailControl,
132
+ overTailControl: overModal === undefined && tailControl !== undefined
133
+ && withinTailControl(tailControl, event.column, event.row),
134
+ };
135
+ this.#track(event, target, outcome);
136
+ switch (event.kind) {
137
+ case "motion": return this.#motion(event, target, call, outcome);
138
+ case "wheel-up":
139
+ case "wheel-down": return this.#wheel(event, frame, call, outcome);
140
+ case "press": return this.#press(event, target, call, outcome);
141
+ case "release": return this.#release(event, call, outcome);
142
+ }
143
+ }
144
+ // Rationale: every report updates the pointer position, the hover of the bottom and tail controls, and the hovered link.
145
+ #track(event, target, outcome) {
146
+ const hits = target.frame.hits;
147
+ const tailControl = target.tailControl;
148
+ const previousPointer = this.#position;
149
+ const wasOverBottom = hits.bottom !== null && previousPointer !== undefined
150
+ && previousPointer.row === hits.bottom.row
151
+ && previousPointer.column >= hits.bottom.columnStart && previousPointer.column <= hits.bottom.columnEnd;
152
+ // Invariant: every report updates the pointer position.
153
+ this.#position = { column: event.column, row: event.row };
154
+ outcome.repaint ||= wasOverBottom !== target.overBottom;
155
+ const wasOverTailControl = tailControl !== undefined && previousPointer !== undefined
156
+ && withinTailControl(tailControl, previousPointer.column, previousPointer.row);
157
+ outcome.repaint ||= wasOverTailControl !== target.overTailControl;
158
+ if (!this.#ports.viewport.selectionActive && !this.#editorSelecting) {
159
+ const nextHyperlink = hyperlinkKeyAt(target.frame, event.column, event.row);
160
+ if (this.#hoveredHyperlinkKey !== undefined && nextHyperlink !== this.#hoveredHyperlinkKey) {
161
+ // Platform: overwrite Windows Terminal's cached native-hover underline exactly once.
162
+ this.#ports.requestHyperlinkCleanup(previousPointer === undefined ? undefined : [previousPointer.row]);
163
+ outcome.repaint = true;
164
+ }
165
+ this.#hoveredHyperlinkKey = nextHyperlink;
166
+ }
167
+ }
168
+ #motion(event, target, call, outcome) {
169
+ const viewport = this.#ports.viewport;
170
+ const { frame } = target;
171
+ const hits = frame.hits;
172
+ viewport.setRailHovered(target.overRail);
173
+ viewport.setStickyHovered(target.overSticky);
174
+ outcome.repaint = true;
175
+ const editorFrame = this.#ports.editorFrame();
176
+ if (call.editorActive && this.#editorSelecting && editorFrame !== undefined) {
177
+ this.#ports.editor.handlePointer({
178
+ kind: "motion",
179
+ button: event.button,
180
+ column: event.column,
181
+ row: event.row - editorFrame.rowStart + 1,
182
+ });
183
+ return true;
184
+ }
185
+ if (this.#dockSuppressed || this.#tailSuppressed)
186
+ return true;
187
+ const pendingEditorClick = this.#pendingEditorClick;
188
+ if (pendingEditorClick !== undefined) {
189
+ if (event.column === pendingEditorClick.column && event.row === pendingEditorClick.row)
190
+ return true;
191
+ this.#pendingEditorClick = undefined;
192
+ const activeEditorFrame = call.editorActive ? editorFrame : undefined;
193
+ if (activeEditorFrame !== undefined && this.#ports.editor.handlePointer({ kind: "press", button: 0, column: pendingEditorClick.column, row: pendingEditorClick.row - activeEditorFrame.rowStart + 1 })) {
194
+ this.#editorSelecting = true;
195
+ this.#ports.editor.handlePointer({ kind: "motion", button: event.button, column: event.column, row: event.row - activeEditorFrame.rowStart + 1 });
196
+ return true;
197
+ }
198
+ viewport.pressSelection(pendingEditorClick.column, pendingEditorClick.row, call.now, { continueMultiClick: false });
199
+ viewport.extendSelection(event.column, event.row, call.now, false);
200
+ this.#ports.updateSelectionAutoScroll(event.column, event.row, hits.viewportHeight, frame.rows.length);
201
+ outcome.activity = true;
202
+ return true;
203
+ }
204
+ if (viewport.selectionActive) {
205
+ viewport.extendSelection(event.column, event.row, call.now, false);
206
+ this.#ports.updateSelectionAutoScroll(event.column, event.row, hits.viewportHeight, frame.rows.length);
207
+ outcome.activity = true;
208
+ return true;
209
+ }
210
+ if (this.#dragGrabOffset !== null && hits.rail !== null) {
211
+ const scroll = scrollForThumbRow(hits.rail.geometry, event.row - hits.rail.rowStart - this.#dragGrabOffset);
212
+ viewport.scrollTo(scroll, call.now);
213
+ outcome.activity = true;
214
+ return true;
215
+ }
216
+ return target.overRail || target.overSticky || target.overBottom || target.overTailControl;
217
+ }
218
+ #wheel(event, frame, call, outcome) {
219
+ if (!call.allowWheel || event.row < 1 || event.row > frame.hits.viewportHeight)
220
+ return false;
221
+ const distance = scrollbarWheelRows(this.#ports.config().scrollbarSpeed);
222
+ this.#ports.viewport.scrollBy(event.kind === "wheel-up" ? -distance : distance, call.now);
223
+ outcome.activity = true;
224
+ outcome.repaint = true;
225
+ outcome.forceRepaint = true;
226
+ return true;
227
+ }
228
+ #press(event, target, call, outcome) {
229
+ const viewport = this.#ports.viewport;
230
+ const { frame } = target;
231
+ const hits = frame.hits;
232
+ const editorFrame = call.editorActive ? this.#ports.editorFrame() : undefined;
233
+ if (event.button === 2 && editorFrame !== undefined
234
+ && event.row >= editorFrame.rowStart && event.row <= editorFrame.rowEnd) {
235
+ this.#ports.stopSelectionAutoScroll();
236
+ if (viewport.clearSelection())
237
+ outcome.repaint = true;
238
+ this.#ports.pasteFromPointer();
239
+ this.#dockSuppressed = true;
240
+ outcome.repaint = true;
241
+ return true;
242
+ }
243
+ if (event.button !== 0)
244
+ return false;
245
+ this.#dockSuppressed = false;
246
+ this.#tailSuppressed = false;
247
+ this.#pendingEditorClick = undefined;
248
+ this.#ports.stopSelectionAutoScroll();
249
+ if (viewport.clearSelection())
250
+ outcome.repaint = true;
251
+ if (target.overTailControl && target.tailControl !== undefined) {
252
+ this.#ports.clearTailControl();
253
+ target.tailControl.activate();
254
+ this.#tailSuppressed = true;
255
+ outcome.repaint = true;
256
+ return true;
257
+ }
258
+ if (target.overBottom) {
259
+ viewport.scrollToEnd(call.now);
260
+ this.#tailSuppressed = true;
261
+ outcome.activity = true;
262
+ outcome.repaint = true;
263
+ return true;
264
+ }
265
+ if (target.overSticky && hits.sticky !== null) {
266
+ viewport.scrollTo(hits.sticky.target, call.now);
267
+ this.#tailSuppressed = true;
268
+ outcome.activity = true;
269
+ outcome.repaint = true;
270
+ return true;
271
+ }
272
+ if (target.overRail && hits.rail !== null) {
273
+ const trackRow = event.row - hits.rail.rowStart;
274
+ const geometry = hits.rail.geometry;
275
+ if (trackRow >= geometry.thumbTop && trackRow < geometry.thumbTop + geometry.thumbHeight) {
276
+ this.#dragGrabOffset = trackRow - geometry.thumbTop;
277
+ viewport.setRailDragging(true);
278
+ }
279
+ else {
280
+ viewport.scrollTo(scrollForTrackPage(geometry, trackRow, frame.scrollTop, hits.viewportHeight), call.now);
281
+ this.#tailSuppressed = true;
282
+ }
283
+ outcome.activity = true;
284
+ outcome.repaint = true;
285
+ return true;
286
+ }
287
+ if (event.row >= 1 && event.row <= frame.rows.length && event.column <= frame.descriptor.width) {
288
+ if (editorFrame !== undefined && event.row >= editorFrame.rowStart && event.row <= editorFrame.rowEnd) {
289
+ // Invariant: no-drag clicks remain editor-owned; first motion latches an editor drag.
290
+ this.#pendingEditorClick = { column: event.column, row: event.row };
291
+ }
292
+ else {
293
+ viewport.pressSelection(event.column, event.row, call.now);
294
+ }
295
+ outcome.repaint = true;
296
+ return true;
297
+ }
298
+ this.#tailSuppressed = true;
299
+ return true;
300
+ }
301
+ #release(event, call, outcome) {
302
+ const viewport = this.#ports.viewport;
303
+ const editorFrame = this.#ports.editorFrame();
304
+ if (call.editorActive && this.#editorSelecting && editorFrame !== undefined) {
305
+ this.#ports.editor.handlePointer({
306
+ kind: "release",
307
+ button: event.button,
308
+ column: event.column,
309
+ row: event.row - editorFrame.rowStart + 1,
310
+ });
311
+ this.#editorSelecting = false;
312
+ outcome.forceRepaint = true;
313
+ outcome.repaint = true;
314
+ return true;
315
+ }
316
+ const pendingEditorClick = this.#pendingEditorClick;
317
+ if (pendingEditorClick !== undefined) {
318
+ this.#pendingEditorClick = undefined;
319
+ if (editorFrame !== undefined) {
320
+ const editorRow = pendingEditorClick.row - editorFrame.rowStart + 1;
321
+ this.#ports.editor.handlePointer({ kind: "press", button: 0, column: pendingEditorClick.column, row: editorRow });
322
+ this.#ports.editor.handlePointer({ kind: "release", button: 0, column: pendingEditorClick.column, row: editorRow });
323
+ }
324
+ outcome.forceRepaint = true;
325
+ outcome.repaint = true;
326
+ return true;
327
+ }
328
+ if (viewport.releaseSelection()) {
329
+ this.#ports.stopSelectionAutoScroll();
330
+ if (this.#ports.copyOnSelect())
331
+ outcome.completedCopy = viewport.captureSelectedText() ?? undefined;
332
+ // Platform: restore OSC 8 links only after the held-button selection paint has
333
+ // ended, then overwrite any terminal-cached hover cells immediately.
334
+ outcome.forceRepaint = true;
335
+ outcome.repaint = true;
336
+ return true;
337
+ }
338
+ if (this.#dragGrabOffset !== null) {
339
+ this.#dragGrabOffset = null;
340
+ viewport.setRailDragging(false);
341
+ outcome.repaint = true;
342
+ return true;
343
+ }
344
+ if (this.#dockSuppressed) {
345
+ this.#dockSuppressed = false;
346
+ return true;
347
+ }
348
+ if (this.#tailSuppressed) {
349
+ this.#tailSuppressed = false;
350
+ return true;
351
+ }
352
+ return false;
353
+ }
354
+ }
355
+ function hyperlinkKeyAt(frame, column, row) {
356
+ if (row < 1 || row > frame.rows.length || column < 1)
357
+ return undefined;
358
+ const range = readVisibleHyperlinks(frame.rows[row - 1] ?? "").ranges
359
+ .find(candidate => column - 1 >= candidate.from && column - 1 < candidate.to);
360
+ return range === undefined ? undefined : JSON.stringify([row, range]);
361
+ }
@@ -39,6 +39,8 @@ export async function composeOwnedUi(options = {}) {
39
39
  profileId: options.profileId,
40
40
  agentProvider: () => backend.settingsPort(),
41
41
  });
42
+ // Invariant: bare A1 owns the settings-driven ports; the comparison profile receives none of them.
43
+ const bareSettings = bare ? settings : null;
42
44
  const projectTrustPrompt = options.projectTrustPrompt;
43
45
  // Invariant: process-wide engine state has one owner. The host installs the HTTP dispatcher, applies
44
46
  // the theme singletons, and announces startup notices once; every session is created through it.
@@ -48,7 +50,7 @@ export async function composeOwnedUi(options = {}) {
48
50
  announceStartupChangelog: !bare,
49
51
  theme: {
50
52
  base: bare ? "dark" : "engine-configured",
51
- accentColor: settings !== null && bare ? settings.value("accentColor") : "purple",
53
+ accentColor: bareSettings?.value("accentColor") ?? "purple",
52
54
  packageBorderProjection: bare,
53
55
  apply: { base: applyConfiguredPiTheme, accentColor: setPiAccentColor, packageBorderProjection: setPiPackageBorderProjectionEnabled },
54
56
  },
@@ -75,21 +77,20 @@ export async function composeOwnedUi(options = {}) {
75
77
  ...(options.packageVersion === undefined ? {} : { packageVersion: options.packageVersion }),
76
78
  ...(options.profileId === undefined ? {} : { profileId: options.profileId }),
77
79
  });
78
- const unsubscribeAccentChange = settings === null || !bare
80
+ const unsubscribeAccentChange = bareSettings === null
79
81
  ? () => { }
80
- : settings.onChange(current => {
82
+ : bareSettings.onChange(current => {
81
83
  const accent = current.value("accentColor");
82
84
  host.setAccentColor(accent);
83
85
  });
84
86
  // Rationale: separate A1 processes share one settings document; a live scope change saved in
85
87
  // one reaches the others only by rereading that document.
86
- const unwatchSharedSettings = settings === null || !bare
88
+ const unwatchSharedSettings = bareSettings === null
87
89
  ? () => { }
88
- : watchSharedSettings(settings);
90
+ : watchSharedSettings(bareSettings);
89
91
  const unsubscribeAccent = () => { unwatchSharedSettings(); unsubscribeAccentChange(); };
90
92
  // Invariant: the Settings search and agent prompt borrow the same resolved Pi keybindings.
91
93
  const keybindingsHost = createPiKeybindingsHost({ profile: bare ? "a1" : "pi", agentDir: backend.identity.agentDir });
92
- const keybindings = keybindingsHost;
93
94
  // Rationale: Routes open only after shell construction.
94
95
  const references = {
95
96
  changelog: async (input) => {
@@ -105,41 +106,29 @@ export async function composeOwnedUi(options = {}) {
105
106
  },
106
107
  session: () => import("./session-info-reference.js").then(m => m.loadSessionInfoReference(backend)),
107
108
  };
108
- const routeHost = settings === null || !bare ? null
109
- : createOwnedRouteHost(settings, references, settingsRouteOptions(keybindings, backend.identity.agentDir, cwd));
110
- const viewportSettings = settings === null || !bare ? null : {
111
- snapshot: () => viewportSettingsSnapshot(settings),
112
- onChange: listener => settings.onChange(() => listener(viewportSettingsSnapshot(settings))),
113
- };
114
- const backgroundSettings = settings === null || !bare ? null : {
115
- snapshot: () => settings.value("backgroundStyle"),
116
- // Rationale: one manager notification also recomputes an accent-derived canvas after accentColor changes.
117
- onChange: listener => settings.onChange(current => listener(current.value("backgroundStyle"))),
118
- };
109
+ const routeHost = bareSettings === null ? null
110
+ : createOwnedRouteHost(bareSettings, references, settingsRouteOptions(keybindingsHost, backend.identity.agentDir, cwd));
119
111
  const diagnosticDestination = options.suggestionDiagnosticsPath ?? process.env[PRODUCT_IDENTITY.environment.suggestionDiagnostics];
120
- const suggestionDiagnostics = settings !== null && bare && diagnosticDestination?.trim()
112
+ const suggestionDiagnostics = bareSettings !== null && diagnosticDestination?.trim()
121
113
  ? await import("../features/prompt-suggestions/diagnostics.js").then(module => new module.SuggestionDiagnosticCapture({ enabled: true, destination: diagnosticDestination })) : null;
122
- const promptSuggestions = settings === null || !bare ? null : {
123
- ...(suggestionDiagnostics === null ? {} : { diagnostics: suggestionDiagnostics }),
124
- generator: backend,
125
- enabled: () => settings.value("promptSuggestions"),
126
- onChange: (listener) => settings.onChange(() => listener(settings.value("promptSuggestions"))),
127
- };
128
- const promptImages = settings === null || !bare ? null : {
129
- limit: () => settings.value("promptImageLimit"),
130
- onChange: (listener) => settings.onChange(listener),
131
- };
132
- const skills = settings === null || !bare ? null : {
133
- presentation: () => settings.value("skillsPresentation"),
134
- // Rationale: one listener covers both the A1 presentation choice and the engine skill-command toggle.
135
- onChange: (listener) => settings.onChange(() => listener()),
136
- };
114
+ const barePorts = bareSettings === null ? null : bareShellPorts(bareSettings, backend, suggestionDiagnostics);
137
115
  const promptHistory = createPromptHistoryConfiguration(settings, backend, bare);
138
116
  const clipboardDestination = options.clipboardDiagnosticsPath ?? process.env[PRODUCT_IDENTITY.environment.clipboardDiagnostics];
139
- const clipboardDiagnostics = settings !== null && bare && clipboardDestination?.trim()
117
+ const clipboardDiagnostics = bareSettings !== null && clipboardDestination?.trim()
140
118
  ? new ClipboardDiagnosticCapture(clipboardDestination) : null;
141
119
  // Invariant: process-wide services are created once here and borrowed by every session shell.
142
120
  const clipboardServices = bare ? createOwnedUiClipboardServices() : null;
121
+ // Invariant: one release order for a failed construction and for dispose. The shared spares outlive every
122
+ // shell and stop only after the last one is disposed; the host outlives the application and is released last.
123
+ const releaseShared = async () => {
124
+ clipboardServices?.dispose();
125
+ unsubscribeAccent();
126
+ await sessionRuntimes.dispose();
127
+ await releaseNote.releaseClaim();
128
+ suggestionDiagnostics?.dispose();
129
+ clipboardDiagnostics?.dispose();
130
+ await host.dispose();
131
+ };
143
132
  let createdTerminalHost;
144
133
  let terminalHost;
145
134
  let presenter;
@@ -156,20 +145,14 @@ export async function composeOwnedUi(options = {}) {
156
145
  presentation: {
157
146
  ...(options.terminal === undefined ? {} : { terminal: createPiTerminalBridge(options.terminal) }),
158
147
  ...(clipboardDiagnostics === null ? {} : { input: { onEvent: event => clipboardDiagnostics.runtime(event) } }),
159
- ...(viewportSettings === null ? {} : { viewportSettings }),
160
- ...(backgroundSettings === null ? {} : { backgroundSettings }),
161
- ...(settings === null || !bare ? {} : {
162
- quitOutro: { snapshot: () => quitOutroSettingsSnapshot(settings), interactive: process.stdout.isTTY === true },
163
- }),
148
+ ...barePorts?.presentation,
164
149
  },
165
150
  ...(clipboardDiagnostics === null ? {} : { diagnostics: {
166
151
  responseCopy: { onEvent: event => clipboardDiagnostics.copy(event) },
167
152
  paste: event => clipboardDiagnostics.paste(event),
168
153
  } }),
169
- ...(promptSuggestions === null ? {} : { suggestions: promptSuggestions }),
170
- ...(promptImages === null ? {} : { promptImages }),
171
- ...(skills === null ? {} : { skills }),
172
- shared: { keybindings, ...(clipboardServices === null ? {} : { clipboard: clipboardServices }) },
154
+ ...(barePorts === null ? {} : { suggestions: barePorts.suggestions, promptImages: barePorts.promptImages, skills: barePorts.skills }),
155
+ shared: { keybindings: keybindingsHost, ...(clipboardServices === null ? {} : { clipboard: clipboardServices }) },
173
156
  ...(promptHistory === null ? {} : { history: {
174
157
  ...promptHistory,
175
158
  editor: await import("../integrations/pi/components/history-editor-loader.js").then(module => module.loadHistoryEditor()),
@@ -183,12 +166,7 @@ export async function composeOwnedUi(options = {}) {
183
166
  }
184
167
  catch (error) {
185
168
  await createdTerminalHost?.dispose().catch(() => undefined);
186
- unsubscribeAccent();
187
- await releaseNote.releaseClaim();
188
- clipboardDiagnostics?.dispose();
189
- suggestionDiagnostics?.dispose();
190
- clipboardServices?.dispose();
191
- await host.dispose();
169
+ await releaseShared();
192
170
  throw error;
193
171
  }
194
172
  // Rationale: pinned Pi's version check is unreachable because the owned shell never runs its
@@ -197,7 +175,7 @@ export async function composeOwnedUi(options = {}) {
197
175
  const releaseUpdates = deferStartupReleaseUpdateObservation({
198
176
  backend,
199
177
  configDir: productPaths.configDir,
200
- mode: settings === null || !bare ? "auto" : settings.value("updateCheck"),
178
+ mode: bareSettings === null ? "auto" : bareSettings.value("updateCheck"),
201
179
  packageVersion: releaseNote.packageVersion, readPackageVersion,
202
180
  checkForNewerRelease: options.checkForNewerRelease,
203
181
  });
@@ -226,20 +204,46 @@ export async function composeOwnedUi(options = {}) {
226
204
  await releaseNote.settleAcknowledgement();
227
205
  }
228
206
  finally {
229
- // Invariant: the shared spares outlive every shell and stop only after the last one is disposed.
230
- clipboardServices?.dispose();
231
- unsubscribeAccent();
232
- await sessionRuntimes.dispose();
233
- await releaseNote.releaseClaim();
234
- suggestionDiagnostics?.dispose();
235
- clipboardDiagnostics?.dispose();
236
- // Invariant: the host outlives the application; disposing it cancels host-level probes last.
237
- await host.dispose();
207
+ await releaseShared();
238
208
  }
239
209
  },
240
210
  };
241
211
  return { application, settings };
242
212
  }
213
+ /** The settings-driven ports only bare A1 receives: viewport, background, quit outro, suggestions, image limit, and skills. */
214
+ function bareShellPorts(settings, generator, diagnostics) {
215
+ const viewportSettings = {
216
+ snapshot: () => viewportSettingsSnapshot(settings),
217
+ onChange: listener => settings.onChange(() => listener(viewportSettingsSnapshot(settings))),
218
+ };
219
+ const backgroundSettings = {
220
+ snapshot: () => settings.value("backgroundStyle"),
221
+ // Rationale: one manager notification also recomputes an accent-derived canvas after accentColor changes.
222
+ onChange: listener => settings.onChange(current => listener(current.value("backgroundStyle"))),
223
+ };
224
+ return {
225
+ presentation: {
226
+ viewportSettings,
227
+ backgroundSettings,
228
+ quitOutro: { snapshot: () => quitOutroSettingsSnapshot(settings), interactive: process.stdout.isTTY === true },
229
+ },
230
+ suggestions: {
231
+ ...(diagnostics === null || diagnostics === undefined ? {} : { diagnostics }),
232
+ generator,
233
+ enabled: () => settings.value("promptSuggestions"),
234
+ onChange: (listener) => settings.onChange(() => listener(settings.value("promptSuggestions"))),
235
+ },
236
+ promptImages: {
237
+ limit: () => settings.value("promptImageLimit"),
238
+ onChange: (listener) => settings.onChange(listener),
239
+ },
240
+ skills: {
241
+ presentation: () => settings.value("skillsPresentation"),
242
+ // Rationale: one listener covers both the A1 presentation choice and the engine skill-command toggle.
243
+ onChange: (listener) => settings.onChange(() => listener()),
244
+ },
245
+ };
246
+ }
243
247
  function createPromptHistoryConfiguration(settings, backend, bare) {
244
248
  const limit = settings?.value("promptHistoryMaxItems") ?? 100;
245
249
  const scope = settings?.value("promptHistoryScope") ?? "unique";
@@ -17,5 +17,5 @@ export { SUGGESTION_ATTEMPT_TRIGGERS, SUGGESTION_DECISION_REASONS, SUGGESTION_DI
17
17
  export type { SuggestionAttemptTrigger, SuggestionDecision, SuggestionDecisionReason, SuggestionDiagnosticEvent, SuggestionDiagnosticObserver, SuggestionDiagnosticRecord, } from "./suggestion-diagnostics.js";
18
18
  export { PROMPT_HISTORY_EDITOR_REPLACEMENT, PROMPT_HISTORY_MAX_ENTRY_BYTES, PROMPT_HISTORY_MAX_PENDING, PROMPT_HISTORY_MAX_TEXT_BYTES, assertPromptHistorySubmission, } from "./prompt-history.js";
19
19
  export type { PromptHistoryFailure, PromptHistoryImageSidecarAttachment, PromptHistoryImageSidecarPort, PromptHistoryImageSidecarRecord, PromptHistoryKind, PromptHistoryPort, PromptHistoryResult, PromptHistorySnapshot, PromptHistorySubmission, } from "./prompt-history.js";
20
- export { assertOwnedUiCommand, assertOwnedUiCustomization, assertOwnedUiDiagnostics, assertOwnedUiEditorState, assertOwnedUiEvent, assertOwnedUiPromptSuggestionIdentity, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiPromptSuggestionState, assertOwnedUiSessionViewModel, assertOwnedUiSnapshot, assertOwnedUiStatusView, assertOwnedUiTerminalSurface, assertOwnedUiTranscriptBlock, } from "./validation.js";
20
+ export { assertOwnedUiCommand, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiSnapshot, } from "./validation.js";
21
21
  export type { OwnedUiShellAuthProviderOption, OwnedUiShellAutocompleteCommand, OwnedUiShellClipboardContent, OwnedUiShellCommandMessage, OwnedUiShellComponentPort, OwnedUiShellComponents, OwnedUiShellEditorBodyGeometry, OwnedUiShellEditorOptions, OwnedUiShellEditorPointerEvent, OwnedUiShellEditorPort, OwnedUiShellEditorRecall, OwnedUiShellEditorTextRange, OwnedUiShellExtensionRendererResolver, OwnedUiShellExtensionUiBridge, OwnedUiShellExtensionUiBridgeHost, OwnedUiShellHeaderOptions, OwnedUiShellHeaderPort, OwnedUiShellHistoryEditor, OwnedUiShellHotkeysPresentation, OwnedUiShellImageAssetResolver, OwnedUiShellKeybindings, OwnedUiShellKeybindingsHost, OwnedUiShellLazySelectorLoader, OwnedUiShellLoadedResourcesPort, OwnedUiShellLoginDialogPort, OwnedUiShellMermaidRenderingMode, OwnedUiShellOperationLoaderPort, OwnedUiShellPasteReservation, OwnedUiShellProgramStatus, OwnedUiShellProgramStatusBlocked, OwnedUiShellProgramStatusReporter, OwnedUiShellProgressPresentationMode, OwnedUiShellProgressStatusPresentation, OwnedUiShellPromptInputBody, OwnedUiShellPromptInputGeometry, OwnedUiShellPromptInputPresentation, OwnedUiShellQueuedInputPort, OwnedUiShellReleaseUpdate, OwnedUiShellReleaseUpdateBanner, OwnedUiShellResourceEntry, OwnedUiShellResourceSection, OwnedUiShellRuntime, OwnedUiShellSelectorOption, OwnedUiShellSelectorOptions, OwnedUiShellSkillSummary, OwnedUiShellStartupNotice, OwnedUiShellStatusPlacement, OwnedUiShellStatusPort, OwnedUiShellSubmittedPromptComposer, OwnedUiShellThinkingLevel, OwnedUiShellThinkingSelectorOptions, OwnedUiShellTranscriptComponentPort, OwnedUiShellTreeFilterMode, OwnedUiShellTreeSelectorOptions, OwnedUiShellTrustDecision, OwnedUiShellTrustOption, OwnedUiShellTrustSelectorOptions, OwnedUiShellTrustUpdate, OwnedUiShellViewComponentPort, } from "./shell-components.js";
@@ -7,4 +7,4 @@ export { CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, normalizePromptSuggestionCand
7
7
  export { canonicalPromptChipMatches, protectPromptChipWrapping, replaceCanonicalPromptChips } from "./prompt-chips.js";
8
8
  export { SUGGESTION_ATTEMPT_TRIGGERS, SUGGESTION_DECISION_REASONS, SUGGESTION_DIAGNOSTIC_EVENTS } from "./suggestion-diagnostics.js";
9
9
  export { PROMPT_HISTORY_EDITOR_REPLACEMENT, PROMPT_HISTORY_MAX_ENTRY_BYTES, PROMPT_HISTORY_MAX_PENDING, PROMPT_HISTORY_MAX_TEXT_BYTES, assertPromptHistorySubmission, } from "./prompt-history.js";
10
- export { assertOwnedUiCommand, assertOwnedUiCustomization, assertOwnedUiDiagnostics, assertOwnedUiEditorState, assertOwnedUiEvent, assertOwnedUiPromptSuggestionIdentity, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiPromptSuggestionState, assertOwnedUiSessionViewModel, assertOwnedUiSnapshot, assertOwnedUiStatusView, assertOwnedUiTerminalSurface, assertOwnedUiTranscriptBlock, } from "./validation.js";
10
+ export { assertOwnedUiCommand, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiSnapshot, } from "./validation.js";