@timurproko/a1 0.1.8-dev.157 → 0.1.8-dev.214
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.
- package/README.md +32 -0
- package/bin/cli.js +2 -2
- package/bin/sync-pi-tui-proxy.js +1 -1
- package/bin/ui.js +18 -3
- package/dist/cli/dispatch.js +3 -3
- package/dist/cli/packages.js +1 -1
- package/dist/cli/version-stats.js +3 -3
- package/dist/composition/owned-ui.d.ts +4 -1
- package/dist/composition/owned-ui.js +9 -6
- package/dist/composition/settings-route-host.js +6 -4
- package/dist/contracts/agent-engine/capability-ports.d.ts +2 -2
- package/dist/contracts/agent-engine/domain-validation.js +22 -4
- package/dist/contracts/agent-engine/domain.d.ts +17 -0
- package/dist/contracts/owned-ui/model.d.ts +8 -0
- package/dist/contracts/owned-ui/validation.js +11 -0
- package/dist/features/launch/development-launch.js +1 -1
- package/dist/features/owned-ui/customization.d.ts +2 -0
- package/dist/features/owned-ui/customization.js +2 -0
- package/dist/features/owned-ui/diagnostics.d.ts +1 -0
- package/dist/features/owned-ui/diagnostics.js +1 -0
- package/dist/features/owned-ui/index.d.ts +1 -0
- package/dist/features/owned-ui/index.js +1 -0
- package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
- package/dist/features/owned-ui/project-trust-prompt.js +149 -0
- package/dist/features/owned-ui/settings-app.js +69 -44
- package/dist/features/workspace/reducer.d.ts +1 -0
- package/dist/features/workspace/reducer.js +1 -0
- package/dist/features/workspace/router.d.ts +1 -0
- package/dist/features/workspace/router.js +1 -0
- package/dist/features/workspace/store.d.ts +1 -0
- package/dist/features/workspace/store.js +1 -0
- package/dist/foundation/launch-guardian/main.js +1 -1
- package/dist/foundation/native-host-protocol/codec.d.ts +2 -0
- package/dist/foundation/native-host-protocol/codec.js +2 -0
- package/dist/foundation/process-containment/linux-process-inspector.d.ts +1 -0
- package/dist/foundation/process-containment/linux-process-inspector.js +1 -0
- package/dist/foundation/process-containment/native-guardian-containment.d.ts +1 -0
- package/dist/foundation/process-containment/native-guardian-containment.js +1 -0
- package/dist/foundation/process-containment/windows-process-inspector.d.ts +1 -0
- package/dist/foundation/process-containment/windows-process-inspector.js +1 -0
- package/dist/foundation/protocol/client.d.ts +1 -0
- package/dist/foundation/protocol/client.js +1 -0
- package/dist/foundation/protocol/messages.d.ts +1 -0
- package/dist/foundation/protocol/messages.js +1 -0
- package/dist/foundation/release/bootstrap.js +6 -6
- package/dist/foundation/release/cohort-state.d.ts +1 -0
- package/dist/foundation/release/cohort-state.js +1 -0
- package/dist/foundation/release/release-gc.js +1 -1
- package/dist/foundation/release/release.js +1 -1
- package/dist/foundation/release/update-transaction.d.ts +1 -0
- package/dist/foundation/release/update-transaction.js +1 -0
- package/dist/foundation/release/update.js +12 -12
- package/dist/foundation/storage/control-store.d.ts +1 -0
- package/dist/foundation/storage/control-store.js +1 -0
- package/dist/foundation/structured-agent-runtime/backpressure.d.ts +1 -0
- package/dist/foundation/structured-agent-runtime/backpressure.js +1 -0
- package/dist/foundation/structured-agent-runtime/commands.d.ts +1 -0
- package/dist/foundation/structured-agent-runtime/commands.js +1 -0
- package/dist/foundation/structured-agent-runtime/reconnection.d.ts +1 -0
- package/dist/foundation/structured-agent-runtime/reconnection.js +1 -0
- package/dist/foundation/structured-agent-runtime/state.d.ts +1 -0
- package/dist/foundation/structured-agent-runtime/state.js +1 -0
- package/dist/foundation/supervision/main.js +1 -1
- package/dist/foundation/supervision/server.d.ts +1 -0
- package/dist/foundation/supervision/server.js +4 -6
- package/dist/integrations/pi/components/conformance.d.ts +1 -0
- package/dist/integrations/pi/components/conformance.js +1 -0
- package/dist/integrations/pi/components/shell-components.d.ts +1 -0
- package/dist/integrations/pi/components/shell-components.js +1 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +10 -0
- package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
- package/dist/integrations/pi/components/shell-footer-status.js +19 -13
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
- package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +3 -36
- package/dist/integrations/pi/components/shell-presenters-transcript.js +88 -128
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +11 -1
- package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
- package/dist/integrations/pi/engine/adapter.d.ts +22 -4
- package/dist/integrations/pi/engine/adapter.js +220 -70
- package/dist/integrations/pi/engine/conformance.d.ts +2 -0
- package/dist/integrations/pi/engine/conformance.js +2 -0
- package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
- package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
- package/dist/integrations/pi/engine/index.d.ts +3 -0
- package/dist/integrations/pi/engine/index.js +3 -0
- package/dist/integrations/pi/engine/model-auth-integration.d.ts +1 -0
- package/dist/integrations/pi/engine/model-auth-integration.js +1 -0
- package/dist/integrations/pi/engine/package-integration.js +1 -1
- package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
- package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
- package/dist/integrations/pi/engine/resource-extension-integration.d.ts +1 -0
- package/dist/integrations/pi/engine/resource-extension-integration.js +1 -0
- package/dist/integrations/pi/engine/runtime-integration.d.ts +16 -1
- package/dist/integrations/pi/engine/runtime-integration.js +34 -4
- package/dist/integrations/pi/engine/session-integration.d.ts +1 -0
- package/dist/integrations/pi/engine/session-integration.js +2 -1
- package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
- package/dist/integrations/pi/engine/settings-effects.js +249 -0
- package/dist/integrations/pi/engine/settings-integration.d.ts +14 -26
- package/dist/integrations/pi/engine/settings-integration.js +102 -108
- package/dist/integrations/pi/engine/workflow-controllers.d.ts +2 -1
- package/dist/integrations/pi/engine/workflow-controllers.js +1 -0
- package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
- package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
- package/dist/integrations/pi/session-ui/index.d.ts +1 -0
- package/dist/integrations/pi/session-ui/index.js +1 -0
- package/dist/integrations/pi/session-ui/prompt-chips.js +6 -2
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +39 -4
- package/dist/integrations/pi/session-ui/session-shell-root.js +215 -80
- package/dist/integrations/pi/session-ui/session-shell.d.ts +10 -1
- package/dist/integrations/pi/session-ui/session-shell.js +256 -45
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +76 -11
- package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
- package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
- package/dist/integrations/pi/session-ui/system-clipboard.d.ts +9 -0
- package/dist/integrations/pi/session-ui/system-clipboard.js +38 -9
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +166 -8
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +25 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +63 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +240 -0
- package/dist/integrations/pi/tui-runtime/index.d.ts +2 -0
- package/dist/integrations/pi/tui-runtime/index.js +2 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
- package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/ui/apps/host.js +5 -9
- package/dist/ui/apps/registry.d.ts +1 -0
- package/dist/ui/apps/registry.js +1 -0
- package/dist/ui/components/dialog-panel.js +5 -3
- package/dist/ui/components/frame.d.ts +1 -0
- package/dist/ui/components/frame.js +1 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/line-input.d.ts +1 -6
- package/dist/ui/components/line-input.js +4 -9
- package/dist/ui/components/list-view.d.ts +0 -6
- package/dist/ui/components/list-view.js +14 -7
- package/dist/ui/components/progress-status.d.ts +2 -0
- package/dist/ui/components/progress-status.js +4 -0
- package/dist/ui/components/scrollbar.js +1 -1
- package/dist/ui/components/shortcuts.d.ts +1 -0
- package/dist/ui/components/shortcuts.js +1 -0
- package/dist/ui/components/spans.d.ts +2 -5
- package/dist/ui/components/spans.js +28 -8
- package/dist/ui/components/surface.d.ts +0 -5
- package/dist/ui/components/surface.js +1 -5
- package/dist/ui/components/text-selection.d.ts +10 -1
- package/dist/ui/components/text-selection.js +96 -29
- package/dist/ui/components/text.js +3 -3
- package/dist/ui/components/transcript-viewport.d.ts +42 -0
- package/dist/ui/components/transcript-viewport.js +287 -39
- package/dist/ui/settings/sections.d.ts +6 -19
- package/dist/ui/settings/sections.js +17 -15
- package/dist/ui/settings/session.d.ts +8 -18
- package/dist/ui/settings/session.js +60 -52
- package/dist/ui/settings/store.d.ts +1 -0
- package/dist/ui/settings/store.js +2 -1
- package/docs/architecture/code-documentation.md +77 -0
- package/docs/architecture/project-structure.md +15 -1
- package/docs/architecture/ui-reference-provenance.md +23 -3
- package/docs/ci-release-runbook.md +98 -7
- package/docs/manual-owned-ui-checkpoint.md +28 -1
- package/docs/manual-terminal-colour-check.md +4 -3
- package/docs/repository-governance-live-acceptance.md +77 -0
- package/package.json +11 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TranscriptViewport, routeMouseInput, scrollForTrackPage, scrollForThumbRow, scrollbarSelectionRows, scrollbarWheelRows, } from "../../../ui/components/index.js";
|
|
1
|
+
import { TranscriptViewport, hyperlinkTargetAtColumn, routeMouseInput, scrollForTrackPage, scrollForThumbRow, scrollbarSelectionRows, scrollbarWheelRows, } from "../../../ui/components/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Owns the stateful interaction policy of the bare-A1 session viewport. The
|
|
4
4
|
* shell root supplies semantic rows and dock geometry; this controller owns
|
|
@@ -16,19 +16,26 @@ export class SessionViewportController {
|
|
|
16
16
|
scrollbarSpeed: "normal",
|
|
17
17
|
};
|
|
18
18
|
#dragGrabOffset = null;
|
|
19
|
-
|
|
19
|
+
// Invariant: a left-button sequence begun in dock chrome remains owned by the dock.
|
|
20
20
|
#dockPointerSuppressed = false;
|
|
21
|
-
|
|
21
|
+
// Invariant: editor selection remains true only while its left button is held.
|
|
22
22
|
#editorPointerSelecting = false;
|
|
23
|
-
|
|
23
|
+
// Invariant: pointer routing uses the most recently composed editor rows.
|
|
24
24
|
#editorPointerFrame;
|
|
25
25
|
#selectionAutoScrollTimer;
|
|
26
26
|
#selectionAutoScrollPointer;
|
|
27
|
+
#pointerPosition;
|
|
28
|
+
#hoveredHyperlinkKey;
|
|
29
|
+
#lastRequestedSelectionRevision = -1;
|
|
30
|
+
#presentationRevision = 0;
|
|
27
31
|
#activityTimer;
|
|
28
32
|
constructor(options) {
|
|
29
33
|
this.#enabled = options.enabled;
|
|
30
34
|
this.#editor = options.editor;
|
|
31
|
-
this.#requestRender =
|
|
35
|
+
this.#requestRender = force => {
|
|
36
|
+
this.#presentationRevision += 1;
|
|
37
|
+
options.requestRender(force);
|
|
38
|
+
};
|
|
32
39
|
this.#hasEditorLinks = options.hasEditorLinks ?? (() => false);
|
|
33
40
|
}
|
|
34
41
|
get config() {
|
|
@@ -40,12 +47,45 @@ export class SessionViewportController {
|
|
|
40
47
|
get transcriptPointerSelecting() {
|
|
41
48
|
return this.#viewport.selectionActive;
|
|
42
49
|
}
|
|
50
|
+
get hasSelection() {
|
|
51
|
+
return this.#viewport.hasSelection;
|
|
52
|
+
}
|
|
53
|
+
get selectionRevision() {
|
|
54
|
+
return this.#viewport.selectionRevision;
|
|
55
|
+
}
|
|
56
|
+
get presentationRevision() {
|
|
57
|
+
return this.#presentationRevision;
|
|
58
|
+
}
|
|
59
|
+
get frame() {
|
|
60
|
+
return this.#viewport.frame;
|
|
61
|
+
}
|
|
43
62
|
setEditorPointerFrame(frame) {
|
|
44
63
|
this.#editorPointerFrame = frame;
|
|
45
64
|
}
|
|
46
65
|
compose(input) {
|
|
47
66
|
this.#viewport.setConfig(this.#config);
|
|
48
|
-
|
|
67
|
+
const selectionRevision = this.#viewport.selectionRevision;
|
|
68
|
+
const frame = this.#viewport.compose(input);
|
|
69
|
+
// Concurrency: if selection changed during composition, this frame truthfully carries
|
|
70
|
+
// the older revision and exactly one ordinary follow-up render publishes the latest.
|
|
71
|
+
if (this.#viewport.selectionRevision !== selectionRevision
|
|
72
|
+
&& this.#lastRequestedSelectionRevision < this.#viewport.selectionRevision) {
|
|
73
|
+
this.#lastRequestedSelectionRevision = this.#viewport.selectionRevision;
|
|
74
|
+
this.#requestRender();
|
|
75
|
+
}
|
|
76
|
+
if (this.#pointerPosition !== undefined && !this.#viewport.selectionActive && !this.#editorPointerSelecting) {
|
|
77
|
+
const next = this.#hyperlinkKeyAt(frame, this.#pointerPosition.column, this.#pointerPosition.row);
|
|
78
|
+
if (this.#hoveredHyperlinkKey !== undefined && next !== this.#hoveredHyperlinkKey) {
|
|
79
|
+
// Platform: Windows Terminal can leave its solid native-hover underline behind
|
|
80
|
+
// when a following viewport moves the link away from a stationary pointer.
|
|
81
|
+
this.#requestRender(true);
|
|
82
|
+
}
|
|
83
|
+
this.#hoveredHyperlinkKey = next;
|
|
84
|
+
}
|
|
85
|
+
return frame;
|
|
86
|
+
}
|
|
87
|
+
composeDockOnly(dockRows, width, height) {
|
|
88
|
+
return this.#viewport.composeDockOnly(dockRows, width, height);
|
|
49
89
|
}
|
|
50
90
|
/** Returns true when document wrapping may have changed. */
|
|
51
91
|
setConfig(config) {
|
|
@@ -72,18 +112,24 @@ export class SessionViewportController {
|
|
|
72
112
|
this.#requestRender();
|
|
73
113
|
}
|
|
74
114
|
reset() {
|
|
115
|
+
this.#presentationRevision += 1;
|
|
75
116
|
this.#clearActivityTimer();
|
|
76
117
|
this.#stopSelectionAutoScroll();
|
|
77
118
|
this.#editorPointerSelecting = false;
|
|
78
119
|
this.#editorPointerFrame = undefined;
|
|
120
|
+
this.#pointerPosition = undefined;
|
|
121
|
+
this.#hoveredHyperlinkKey = undefined;
|
|
79
122
|
this.#viewport.reset();
|
|
80
123
|
}
|
|
81
124
|
clearPointerState() {
|
|
125
|
+
this.#presentationRevision += 1;
|
|
82
126
|
this.#clearActivityTimer();
|
|
83
127
|
this.#dragGrabOffset = null;
|
|
84
128
|
this.#dockPointerSuppressed = false;
|
|
85
129
|
this.#editorPointerSelecting = false;
|
|
86
130
|
this.#editorPointerFrame = undefined;
|
|
131
|
+
this.#pointerPosition = undefined;
|
|
132
|
+
this.#hoveredHyperlinkKey = undefined;
|
|
87
133
|
this.#stopSelectionAutoScroll();
|
|
88
134
|
this.#viewport.clearSelection();
|
|
89
135
|
this.#viewport.clearTransient();
|
|
@@ -91,15 +137,15 @@ export class SessionViewportController {
|
|
|
91
137
|
handlePreInput(data, allowWheel = true, now = Date.now()) {
|
|
92
138
|
if (!this.#enabled)
|
|
93
139
|
return { data, consumed: false };
|
|
94
|
-
// Pi components share one keybinding manager. Restore bare A1's aliases
|
|
140
|
+
// Compatibility: Pi components share one keybinding manager. Restore bare A1's aliases
|
|
95
141
|
// before the focused vanilla editor handles this input.
|
|
96
142
|
this.#editor.activateKeybindings();
|
|
97
|
-
//
|
|
143
|
+
// Platform: handle the physical paste chord at the pre-input boundary. Windows
|
|
98
144
|
// terminals vary between forwarding Ctrl+V and terminal-owned bracketed paste.
|
|
99
145
|
if (this.#editor.matchesTerminalKey(data, "ctrl+v") && this.#editor.pasteClipboard()) {
|
|
100
146
|
return { data: "", consumed: true };
|
|
101
147
|
}
|
|
102
|
-
// URL chip deletion must overwrite terminal link cells in the same frame.
|
|
148
|
+
// Platform: URL chip deletion must overwrite terminal link cells in the same frame.
|
|
103
149
|
if (EDITOR_LINK_DELETION_INPUTS.has(data) && this.#hasEditorLinks())
|
|
104
150
|
this.#requestRender(true);
|
|
105
151
|
if (this.#viewport.frame === null)
|
|
@@ -160,6 +206,16 @@ export class SessionViewportController {
|
|
|
160
206
|
const overBottom = hits.bottom !== null && event.row === hits.bottom.row
|
|
161
207
|
&& event.column >= hits.bottom.columnStart && event.column <= hits.bottom.columnEnd;
|
|
162
208
|
if (event.kind === "motion") {
|
|
209
|
+
this.#pointerPosition = { column: event.column, row: event.row };
|
|
210
|
+
if (!this.#viewport.selectionActive && !this.#editorPointerSelecting) {
|
|
211
|
+
const nextHyperlink = this.#hyperlinkKeyAt(frame, event.column, event.row);
|
|
212
|
+
if (this.#hoveredHyperlinkKey !== undefined && nextHyperlink !== this.#hoveredHyperlinkKey) {
|
|
213
|
+
// Platform: a forced redraw overwrites Windows Terminal's cached solid hover
|
|
214
|
+
// underline exactly once when the pointer leaves or changes links.
|
|
215
|
+
forceRepaint = true;
|
|
216
|
+
}
|
|
217
|
+
this.#hoveredHyperlinkKey = nextHyperlink;
|
|
218
|
+
}
|
|
163
219
|
this.#viewport.setRailHovered(overRail);
|
|
164
220
|
this.#viewport.setStickyHovered(overSticky);
|
|
165
221
|
this.#viewport.setBottomHovered(overBottom);
|
|
@@ -282,7 +338,7 @@ export class SessionViewportController {
|
|
|
282
338
|
}
|
|
283
339
|
if (this.#viewport.releaseSelection()) {
|
|
284
340
|
this.#stopSelectionAutoScroll();
|
|
285
|
-
//
|
|
341
|
+
// Platform: restore OSC 8 links only after the held-button selection paint has
|
|
286
342
|
// ended, then overwrite any terminal-cached hover cells immediately.
|
|
287
343
|
forceRepaint = true;
|
|
288
344
|
repaint = true;
|
|
@@ -303,10 +359,18 @@ export class SessionViewportController {
|
|
|
303
359
|
});
|
|
304
360
|
if (activity)
|
|
305
361
|
this.#scheduleActivityExpiry();
|
|
306
|
-
if (repaint)
|
|
362
|
+
if (repaint) {
|
|
363
|
+
this.#lastRequestedSelectionRevision = this.#viewport.selectionRevision;
|
|
307
364
|
this.#requestRender(forceRepaint);
|
|
365
|
+
}
|
|
308
366
|
return routed;
|
|
309
367
|
}
|
|
368
|
+
#hyperlinkKeyAt(frame, column, row) {
|
|
369
|
+
if (row < 1 || row > frame.rows.length || column < 1)
|
|
370
|
+
return undefined;
|
|
371
|
+
const target = hyperlinkTargetAtColumn(frame.rows[row - 1] ?? "", column - 1);
|
|
372
|
+
return target === undefined ? undefined : `${row}\u0000${target}`;
|
|
373
|
+
}
|
|
310
374
|
#updateSelectionAutoScroll(column, row, viewportHeight) {
|
|
311
375
|
const beyondEdge = row <= 1 || row > viewportHeight;
|
|
312
376
|
if (!beyondEdge) {
|
|
@@ -338,6 +402,7 @@ export class SessionViewportController {
|
|
|
338
402
|
this.#stopSelectionAutoScroll();
|
|
339
403
|
return;
|
|
340
404
|
}
|
|
405
|
+
this.#lastRequestedSelectionRevision = this.#viewport.selectionRevision;
|
|
341
406
|
this.#requestRender();
|
|
342
407
|
this.#scheduleActivityExpiry();
|
|
343
408
|
this.#selectionAutoScrollTimer = setTimeout(() => this.#selectionAutoScrollTick(), SELECTION_AUTO_SCROLL_INTERVAL_MS);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 33 ms (30.3 fps): baseline chunks arrive every 5 ms, while this cadence
|
|
3
|
+
* retains immediate first paint/input and explicit final-state flushing.
|
|
4
|
+
*/
|
|
5
|
+
export declare const STREAM_PRESENTATION_INTERVAL_MS = 33;
|
|
6
|
+
export interface StreamPresentationScheduler {
|
|
7
|
+
now(): number;
|
|
8
|
+
setTimeout(callback: () => void, delayMs: number): ReturnType<typeof setTimeout>;
|
|
9
|
+
clearTimeout(timer: ReturnType<typeof setTimeout>): void;
|
|
10
|
+
}
|
|
11
|
+
/** Bounds terminal presentation requests while semantic root state remains current. */
|
|
12
|
+
export declare class StreamPresentationCoalescer {
|
|
13
|
+
#private;
|
|
14
|
+
readonly present: () => void;
|
|
15
|
+
readonly intervalMs: number;
|
|
16
|
+
readonly scheduler: StreamPresentationScheduler;
|
|
17
|
+
constructor(present: () => void, intervalMs?: number, scheduler?: StreamPresentationScheduler);
|
|
18
|
+
get pending(): boolean;
|
|
19
|
+
request(): void;
|
|
20
|
+
/** Cancels a superseded stream frame; the immediate input/overlay/resize path renders current state. */
|
|
21
|
+
preempt(): void;
|
|
22
|
+
/** Records an immediate input/status/overlay frame and cancels an obsolete stream timer. */
|
|
23
|
+
noteImmediatePresentation(): void;
|
|
24
|
+
/** Presents current semantic state immediately regardless of pending stream work. */
|
|
25
|
+
presentImmediate(): void;
|
|
26
|
+
/** Presents the newest semantic state immediately only when a stream frame is pending. */
|
|
27
|
+
flush(): void;
|
|
28
|
+
dispose(): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 33 ms (30.3 fps): baseline chunks arrive every 5 ms, while this cadence
|
|
3
|
+
* retains immediate first paint/input and explicit final-state flushing.
|
|
4
|
+
*/
|
|
5
|
+
export const STREAM_PRESENTATION_INTERVAL_MS = 33;
|
|
6
|
+
const SYSTEM_SCHEDULER = {
|
|
7
|
+
now: Date.now,
|
|
8
|
+
setTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
9
|
+
clearTimeout: timer => clearTimeout(timer),
|
|
10
|
+
};
|
|
11
|
+
/** Bounds terminal presentation requests while semantic root state remains current. */
|
|
12
|
+
export class StreamPresentationCoalescer {
|
|
13
|
+
present;
|
|
14
|
+
intervalMs;
|
|
15
|
+
scheduler;
|
|
16
|
+
#timer;
|
|
17
|
+
#lastPresentationAt = Number.NEGATIVE_INFINITY;
|
|
18
|
+
#disposed = false;
|
|
19
|
+
constructor(present, intervalMs = STREAM_PRESENTATION_INTERVAL_MS, scheduler = SYSTEM_SCHEDULER) {
|
|
20
|
+
this.present = present;
|
|
21
|
+
this.intervalMs = intervalMs;
|
|
22
|
+
this.scheduler = scheduler;
|
|
23
|
+
if (!Number.isSafeInteger(intervalMs) || intervalMs < 1)
|
|
24
|
+
throw new RangeError("stream presentation interval must be positive");
|
|
25
|
+
}
|
|
26
|
+
get pending() { return this.#timer !== undefined; }
|
|
27
|
+
request() {
|
|
28
|
+
if (this.#disposed || this.#timer !== undefined)
|
|
29
|
+
return;
|
|
30
|
+
const elapsed = this.scheduler.now() - this.#lastPresentationAt;
|
|
31
|
+
if (elapsed >= this.intervalMs) {
|
|
32
|
+
this.#presentNow();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.#timer = this.scheduler.setTimeout(() => {
|
|
36
|
+
this.#timer = undefined;
|
|
37
|
+
this.#presentNow();
|
|
38
|
+
}, Math.max(0, this.intervalMs - elapsed));
|
|
39
|
+
}
|
|
40
|
+
/** Cancels a superseded stream frame; the immediate input/overlay/resize path renders current state. */
|
|
41
|
+
preempt() {
|
|
42
|
+
if (this.#timer === undefined)
|
|
43
|
+
return;
|
|
44
|
+
this.scheduler.clearTimeout(this.#timer);
|
|
45
|
+
this.#timer = undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Records an immediate input/status/overlay frame and cancels an obsolete stream timer. */
|
|
48
|
+
noteImmediatePresentation() {
|
|
49
|
+
if (this.#disposed)
|
|
50
|
+
return;
|
|
51
|
+
this.preempt();
|
|
52
|
+
this.#lastPresentationAt = this.scheduler.now();
|
|
53
|
+
}
|
|
54
|
+
/** Presents current semantic state immediately regardless of pending stream work. */
|
|
55
|
+
presentImmediate() {
|
|
56
|
+
if (this.#disposed)
|
|
57
|
+
return;
|
|
58
|
+
this.noteImmediatePresentation();
|
|
59
|
+
this.present();
|
|
60
|
+
}
|
|
61
|
+
/** Presents the newest semantic state immediately only when a stream frame is pending. */
|
|
62
|
+
flush() {
|
|
63
|
+
if (this.#disposed || this.#timer === undefined)
|
|
64
|
+
return;
|
|
65
|
+
this.scheduler.clearTimeout(this.#timer);
|
|
66
|
+
this.#timer = undefined;
|
|
67
|
+
this.#presentNow();
|
|
68
|
+
}
|
|
69
|
+
dispose() {
|
|
70
|
+
this.preempt();
|
|
71
|
+
this.#disposed = true;
|
|
72
|
+
}
|
|
73
|
+
#presentNow() {
|
|
74
|
+
if (this.#disposed)
|
|
75
|
+
return;
|
|
76
|
+
this.#lastPresentationAt = this.scheduler.now();
|
|
77
|
+
this.present();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { PiShellClipboardContent } from "../components/index.js";
|
|
2
|
+
export interface SystemClipboardImageReader {
|
|
3
|
+
hasImage(): boolean;
|
|
4
|
+
getImageBinary?(): Promise<readonly number[]>;
|
|
5
|
+
getImageBase64?(): Promise<string>;
|
|
6
|
+
}
|
|
2
7
|
/** Load the native adapter during shell startup so the first paste is warm. */
|
|
3
8
|
export declare function preloadSystemClipboard(): void;
|
|
4
9
|
/**
|
|
@@ -7,5 +12,9 @@ export declare function preloadSystemClipboard(): void;
|
|
|
7
12
|
* unavailable clipboard simply makes the paste action a no-op.
|
|
8
13
|
*/
|
|
9
14
|
export declare function readSystemClipboardContent(): Promise<PiShellClipboardContent | null>;
|
|
15
|
+
export declare function readSystemClipboardImage(reader: SystemClipboardImageReader): Promise<{
|
|
16
|
+
readonly data: string;
|
|
17
|
+
readonly mimeType: string;
|
|
18
|
+
} | null>;
|
|
10
19
|
export declare function readSystemClipboardText(): Promise<string | null>;
|
|
11
20
|
export declare function writeSystemClipboardText(text: string): Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
+
import { canonicalizeClipboardImage } from "./clipboard-image.js";
|
|
2
3
|
const MAX_CLIPBOARD_BYTES = 16 * 1024 * 1024;
|
|
3
4
|
let nativeClipboardPromise;
|
|
4
5
|
let pendingClipboardWrite = Promise.resolve();
|
|
@@ -22,21 +23,49 @@ export async function readSystemClipboardContent() {
|
|
|
22
23
|
if (files !== null)
|
|
23
24
|
return { kind: "text", text: files };
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return { kind: "image", data, mimeType: "image/png" };
|
|
29
|
-
}
|
|
26
|
+
const image = await readSystemClipboardImage(native);
|
|
27
|
+
if (image !== null)
|
|
28
|
+
return { kind: "image", ...image };
|
|
30
29
|
}
|
|
31
30
|
catch {
|
|
32
|
-
//
|
|
31
|
+
// Compatibility: fall through to text and platform readers.
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
const text = await readSystemClipboardText();
|
|
36
35
|
return text === null ? null : { kind: "text", text };
|
|
37
36
|
}
|
|
37
|
+
export async function readSystemClipboardImage(reader) {
|
|
38
|
+
if (!reader.hasImage())
|
|
39
|
+
return null;
|
|
40
|
+
if (reader.getImageBinary !== undefined) {
|
|
41
|
+
try {
|
|
42
|
+
const bytes = await reader.getImageBinary();
|
|
43
|
+
if (bytes.length > 0 && bytes.every(byte => Number.isInteger(byte) && byte >= 0 && byte <= 255)) {
|
|
44
|
+
return canonicalizeClipboardImage({
|
|
45
|
+
data: Buffer.from(bytes).toString("base64"),
|
|
46
|
+
mimeType: "image/png",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Compatibility: older or partially available native bindings may still expose base64.
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (reader.getImageBase64 !== undefined) {
|
|
55
|
+
try {
|
|
56
|
+
return canonicalizeClipboardImage({
|
|
57
|
+
data: await reader.getImageBase64(),
|
|
58
|
+
mimeType: "image/png",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Compatibility: fall through to the caller's text path.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
38
67
|
export async function readSystemClipboardText() {
|
|
39
|
-
// Ctrl+C/Ctrl+X and Ctrl+V can arrive in adjacent input turns. Serialize the
|
|
68
|
+
// Concurrency: Ctrl+C/Ctrl+X and Ctrl+V can arrive in adjacent input turns. Serialize the
|
|
40
69
|
// read behind our native write so paste never observes the previous value.
|
|
41
70
|
await pendingClipboardWrite;
|
|
42
71
|
if (process.platform === "win32" || process.platform === "darwin") {
|
|
@@ -48,7 +77,7 @@ export async function readSystemClipboardText() {
|
|
|
48
77
|
return text;
|
|
49
78
|
}
|
|
50
79
|
catch {
|
|
51
|
-
//
|
|
80
|
+
// Compatibility: fall through to the platform command.
|
|
52
81
|
}
|
|
53
82
|
}
|
|
54
83
|
}
|
|
@@ -74,7 +103,7 @@ export function writeSystemClipboardText(text) {
|
|
|
74
103
|
return;
|
|
75
104
|
}
|
|
76
105
|
catch {
|
|
77
|
-
//
|
|
106
|
+
// Compatibility: the caller still emits its terminal clipboard fallback.
|
|
78
107
|
}
|
|
79
108
|
}
|
|
80
109
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { PiTuiComponentPort, PiTuiInputListener, PiTuiOverlayHandle, PiTuiOverlayOptions, PiTuiPreInputListener, PiTuiRuntimeAdapterOptions, PiTuiRuntimeState, PiTuiScrollState, PiTuiStopOptions, PiTuiViewport } from "./contracts.js";
|
|
2
2
|
export type PiTuiRuntimeErrorStage = "construction" | "start" | "input-drain" | "restoration";
|
|
3
|
+
/** Identifies the Pi TUI lifecycle stage that failed while preserving the original cause. */
|
|
3
4
|
export declare class PiTuiRuntimeError extends Error {
|
|
4
5
|
readonly stage: PiTuiRuntimeErrorStage;
|
|
5
6
|
constructor(stage: PiTuiRuntimeErrorStage, cause: unknown);
|
|
6
7
|
}
|
|
8
|
+
/** Bridges neutral presentation ports to one Pi TUI instance and owns terminal restoration on stop. */
|
|
7
9
|
export declare class PiTuiRuntimeAdapter {
|
|
8
10
|
#private;
|
|
9
11
|
constructor(options: PiTuiRuntimeAdapterOptions);
|
|
@@ -31,7 +33,11 @@ export declare class PiTuiRuntimeAdapter {
|
|
|
31
33
|
switchMode(mode: "regular" | "fullscreen"): boolean;
|
|
32
34
|
setHardwareCursor(enabled: boolean): void;
|
|
33
35
|
getHardwareCursor(): boolean;
|
|
36
|
+
setClearOnShrink(enabled: boolean): void;
|
|
37
|
+
getClearOnShrink(): boolean;
|
|
38
|
+
setTerminalProgress(active: boolean): void;
|
|
34
39
|
setTitle(title: string): void;
|
|
40
|
+
writeAfterStop(text: string): void;
|
|
35
41
|
invalidate(): void;
|
|
36
42
|
requestRender(force?: boolean): void;
|
|
37
43
|
renderNow(force?: boolean): void;
|