@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,9 @@
1
+ import type { AgentSession, AgentSessionRuntime } from "../startup-public.js";
2
+ import type { PiWorkflowHost, PiWorkflowRequest, PiWorkflowResult } from "./workflows.js";
3
+ export interface PiShareCall {
4
+ readonly request: PiWorkflowRequest;
5
+ readonly session: AgentSession;
6
+ readonly runtime: AgentSessionRuntime;
7
+ readonly host: PiWorkflowHost;
8
+ }
9
+ export declare function runShareWorkflow({ request, session, runtime, host }: PiShareCall): Promise<PiWorkflowResult>;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Pinned Pi's `/share` workflow: the Radius artifact route with a gist fallback. The runner loads
3
+ * this module on the first `/share`, so its network clients stay off the startup path.
4
+ */
5
+ import { randomUUID } from "node:crypto";
6
+ import { readFile, rm } from "node:fs/promises";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { PRODUCT_IDENTITY } from "../../../product-identity.js";
10
+ import { isRecord, requireCapability, stringProperty } from "./message-values.js";
11
+ import { commandMissing, errorMessage, isAbortError, workflowResult } from "./workflow-support.js";
12
+ // Provenance: the pinned engine's modes/interactive/session-share.ts Radius route and the model
13
+ // runtime's DEFAULT_RADIUS_GATEWAY; neither is in the public API, so each upgrade re-checks them.
14
+ const RADIUS_SHARE_GATEWAY = "https://radius.pi.dev";
15
+ const RADIUS_AUTH_MIN_VALIDITY_MS = 5 * 60_000;
16
+ export async function runShareWorkflow({ request, session, runtime, host }) {
17
+ const signal = request.signal;
18
+ if (signal?.aborted)
19
+ return shareCancelled(request);
20
+ const destination = runtime.services.settingsManager?.getSessionShareDestination?.() ?? "auto";
21
+ if (destination !== "gist") {
22
+ const radius = await shareViaRadius({ request, session, runtime, host });
23
+ if (radius !== undefined)
24
+ return radius;
25
+ if (destination === "radius")
26
+ return workflowResult(request.command, "failed", "Radius authentication is not configured. Run '/login radius' first.");
27
+ }
28
+ request.onProgress?.("Creating gist…");
29
+ try {
30
+ await host.runCommand("gh", ["auth", "status"], signal === undefined ? undefined : { signal });
31
+ }
32
+ catch (error) {
33
+ if (signal?.aborted || isAbortError(error))
34
+ return shareCancelled(request);
35
+ const message = commandMissing(error)
36
+ ? "GitHub CLI (gh) is not installed. Install it from https://cli.github.com/"
37
+ : "GitHub CLI is not logged in. Run 'gh auth login' first.";
38
+ return workflowResult(request.command, "failed", message);
39
+ }
40
+ const temporary = join(tmpdir(), `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}pi-session-${process.pid}.html`);
41
+ try {
42
+ try {
43
+ await requireCapability(session.exportToHtml, "exportToHtml").call(session, temporary);
44
+ }
45
+ catch (error) {
46
+ return workflowResult(request.command, "failed", `Failed to export session: ${errorMessage(error, "Unknown error")}`);
47
+ }
48
+ if (signal?.aborted)
49
+ return shareCancelled(request);
50
+ let gist;
51
+ try {
52
+ gist = await host.runCommand("gh", ["gist", "create", "--public=false", temporary], signal === undefined ? undefined : { signal });
53
+ }
54
+ catch (error) {
55
+ if (signal?.aborted || isAbortError(error))
56
+ return shareCancelled(request);
57
+ return workflowResult(request.command, "failed", `Failed to create gist: ${shareCommandFailureDetail(error)}`);
58
+ }
59
+ if (signal?.aborted)
60
+ return shareCancelled(request);
61
+ const gistUrl = gist.stdout.trim();
62
+ const gistId = gistUrl.split("/").at(-1);
63
+ if (!gistId)
64
+ return workflowResult(request.command, "failed", "Failed to parse gist ID from gh output");
65
+ return workflowResult(request.command, "completed", `Share URL: ${shareViewerUrl(gistId)}`, gistUrl);
66
+ }
67
+ finally {
68
+ await rm(temporary, { force: true });
69
+ }
70
+ }
71
+ function shareCancelled(request) {
72
+ return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
73
+ }
74
+ // Protocol: the pinned Radius `/share` route. Undefined means Radius has no provider or credential,
75
+ // so the caller may fall back to a gist; any other outcome, including an upload failure, is final.
76
+ async function shareViaRadius({ request, session, runtime, host }) {
77
+ const signal = request.signal;
78
+ const models = runtime.services.modelRuntime;
79
+ if (models.getProvider?.("radius") === undefined)
80
+ return undefined;
81
+ const token = radiusAuthCredential(await models.getAuth("radius", { minOAuthValidityMs: RADIUS_AUTH_MIN_VALIDITY_MS }));
82
+ if (!token)
83
+ return undefined;
84
+ if (signal?.aborted)
85
+ return shareCancelled(request);
86
+ request.onProgress?.("Uploading to Radius…");
87
+ const temporary = join(tmpdir(), `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}pi-session-${process.pid}.jsonl`);
88
+ let body;
89
+ try {
90
+ requireCapability(session.exportToJsonl, "exportToJsonl").call(session, temporary);
91
+ body = withRadiusShareTrailingEntry(await readFile(temporary, "utf8"), session.state ?? {}, randomUUID().slice(0, 8));
92
+ }
93
+ catch (error) {
94
+ return workflowResult(request.command, "failed", `Failed to export session: ${errorMessage(error, "Unknown error")}`);
95
+ }
96
+ finally {
97
+ await rm(temporary, { force: true });
98
+ }
99
+ const url = new URL("/v1/artifacts", RADIUS_SHARE_GATEWAY);
100
+ url.searchParams.set("visibility", "organization");
101
+ url.searchParams.set("title", "Pi session");
102
+ try {
103
+ const response = await (host.fetch ?? fetch)(url, {
104
+ method: "POST",
105
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
106
+ body,
107
+ ...(signal === undefined ? {} : { signal }),
108
+ });
109
+ const json = await response.json().catch(() => null);
110
+ if (signal?.aborted)
111
+ return shareCancelled(request);
112
+ const artifact = isRecord(json) && isRecord(json.artifact) ? json.artifact : undefined;
113
+ const shareUrl = artifact === undefined ? undefined : stringProperty(artifact, "canonical_url");
114
+ if (!response.ok || shareUrl === undefined) {
115
+ const detail = (isRecord(json) ? stringProperty(json, "error") : undefined) || response.statusText || String(response.status);
116
+ return workflowResult(request.command, "failed", `Failed to upload Radius artifact: ${detail}`);
117
+ }
118
+ return workflowResult(request.command, "completed", `Share URL: ${shareUrl}`);
119
+ }
120
+ catch (error) {
121
+ if (signal?.aborted || isAbortError(error))
122
+ return shareCancelled(request);
123
+ return workflowResult(request.command, "failed", `Failed to upload Radius artifact: ${errorMessage(error, "Unknown error")}`);
124
+ }
125
+ }
126
+ function shareCommandFailureDetail(error) {
127
+ // Compatibility: Pi reports a failed child's stderr, not execFile's command wrapper.
128
+ if (isRecord(error) && (typeof error.code === "number" || typeof error.signal === "string") && typeof error.stderr === "string") {
129
+ return error.stderr.trim() || "Unknown error";
130
+ }
131
+ return errorMessage(error, "Unknown error");
132
+ }
133
+ function shareViewerUrl(gistId) {
134
+ const baseUrl = process.env.PI_SHARE_VIEWER_URL || "https://pi.dev/session/";
135
+ return `${baseUrl}#${gistId}`;
136
+ }
137
+ /** The bearer token in a model-runtime auth result: an API key or an `Authorization: Bearer` header. */
138
+ function radiusAuthCredential(auth) {
139
+ const resolved = isRecord(auth) && isRecord(auth.auth) ? auth.auth : undefined;
140
+ if (resolved === undefined)
141
+ return undefined;
142
+ if (typeof resolved.apiKey === "string" && resolved.apiKey)
143
+ return resolved.apiKey;
144
+ const headers = isRecord(resolved.headers) ? resolved.headers : {};
145
+ const authorization = Object.entries(headers).find(([name]) => name.toLowerCase() === "authorization")?.[1];
146
+ return typeof authorization === "string" ? /^Bearer\s+(.+)$/iu.exec(authorization)?.[1] : undefined;
147
+ }
148
+ /**
149
+ * Append the pinned `pi.share` trailing entry (system prompt and tool schemas for the session viewer)
150
+ * to a branch JSONL export. It chains to the last exported entry and reuses the header timestamp.
151
+ */
152
+ function withRadiusShareTrailingEntry(jsonl, state, id) {
153
+ const lines = jsonl.split("\n").filter(line => line.length > 0);
154
+ const header = lines[0] === undefined ? undefined : JSON.parse(lines[0]);
155
+ const last = lines.length > 1 ? JSON.parse(lines.at(-1)) : undefined;
156
+ const entry = {
157
+ type: "custom",
158
+ customType: "pi.share",
159
+ id,
160
+ parentId: isRecord(last) && typeof last.id === "string" ? last.id : null,
161
+ timestamp: isRecord(header) && typeof header.timestamp === "string" ? header.timestamp : new Date().toISOString(),
162
+ data: {
163
+ systemPrompt: state.systemPrompt,
164
+ tools: (state.tools ?? []).filter(isRecord).map(tool => ({ name: tool.name, description: tool.description, parameters: tool.parameters })),
165
+ },
166
+ };
167
+ return `${[...lines, JSON.stringify(entry)].join("\n")}\n`;
168
+ }
@@ -12,22 +12,8 @@ export declare function workflowLoginNotification(event: unknown): PiWorkflowLog
12
12
  export declare function workflowResult(command: PiWorkflowRequest["command"], outcome: PiWorkflowResult["outcome"], message: string, detail?: string, messageKind?: PiWorkflowResult["messageKind"], messages?: readonly PiWorkflowMessage[]): PiWorkflowResult;
13
13
  export declare function workflowConfirmation(command: PiWorkflowRequest["command"], message: string, detail?: string): PiWorkflowResult;
14
14
  export declare function errorMessage(error: unknown, fallback: string): string;
15
- export declare function shareCommandFailureDetail(error: unknown): string;
16
15
  export declare function commandMissing(error: unknown): boolean;
17
16
  export declare function isAbortError(error: unknown): boolean;
18
- export declare function shareViewerUrl(gistId: string): string;
19
- export declare const RADIUS_SHARE_GATEWAY = "https://radius.pi.dev";
20
- export declare const RADIUS_AUTH_MIN_VALIDITY_MS: number;
21
- /** The bearer token in a model-runtime auth result: an API key or an `Authorization: Bearer` header. */
22
- export declare function radiusAuthCredential(auth: unknown): string | undefined;
23
- /**
24
- * Append the pinned `pi.share` trailing entry (system prompt and tool schemas for the session viewer)
25
- * to a branch JSONL export. It chains to the last exported entry and reuses the header timestamp.
26
- */
27
- export declare function withRadiusShareTrailingEntry(jsonl: string, state: {
28
- readonly systemPrompt?: unknown;
29
- readonly tools?: readonly unknown[];
30
- }, id: string): string;
31
17
  export declare function workflowOptions(value: unknown, idKey: string, labelKey: string): readonly PiWorkflowOption[];
32
18
  export declare function pathArgument(value: string): string | undefined;
33
19
  export declare function pinnedHotkeySummary(): string;
@@ -262,59 +262,12 @@ export function workflowConfirmation(command, message, detail) {
262
262
  export function errorMessage(error, fallback) {
263
263
  return error instanceof Error ? error.message : fallback;
264
264
  }
265
- export function shareCommandFailureDetail(error) {
266
- // Compatibility: Pi reports a failed child's stderr, not execFile's command wrapper.
267
- if (isRecord(error) && (typeof error.code === "number" || typeof error.signal === "string") && typeof error.stderr === "string") {
268
- return error.stderr.trim() || "Unknown error";
269
- }
270
- return errorMessage(error, "Unknown error");
271
- }
272
265
  export function commandMissing(error) {
273
266
  return isRecord(error) && error.code === "ENOENT";
274
267
  }
275
268
  export function isAbortError(error) {
276
269
  return isRecord(error) && (error.name === "AbortError" || error.code === "ABORT_ERR");
277
270
  }
278
- export function shareViewerUrl(gistId) {
279
- const baseUrl = process.env.PI_SHARE_VIEWER_URL || "https://pi.dev/session/";
280
- return `${baseUrl}#${gistId}`;
281
- }
282
- // Provenance: the pinned engine's modes/interactive/session-share.ts Radius route and the model
283
- // runtime's DEFAULT_RADIUS_GATEWAY; neither is in the public API, so each upgrade re-checks them.
284
- export const RADIUS_SHARE_GATEWAY = "https://radius.pi.dev";
285
- export const RADIUS_AUTH_MIN_VALIDITY_MS = 5 * 60_000;
286
- /** The bearer token in a model-runtime auth result: an API key or an `Authorization: Bearer` header. */
287
- export function radiusAuthCredential(auth) {
288
- const resolved = isRecord(auth) && isRecord(auth.auth) ? auth.auth : undefined;
289
- if (resolved === undefined)
290
- return undefined;
291
- if (typeof resolved.apiKey === "string" && resolved.apiKey)
292
- return resolved.apiKey;
293
- const headers = isRecord(resolved.headers) ? resolved.headers : {};
294
- const authorization = Object.entries(headers).find(([name]) => name.toLowerCase() === "authorization")?.[1];
295
- return typeof authorization === "string" ? /^Bearer\s+(.+)$/iu.exec(authorization)?.[1] : undefined;
296
- }
297
- /**
298
- * Append the pinned `pi.share` trailing entry (system prompt and tool schemas for the session viewer)
299
- * to a branch JSONL export. It chains to the last exported entry and reuses the header timestamp.
300
- */
301
- export function withRadiusShareTrailingEntry(jsonl, state, id) {
302
- const lines = jsonl.split("\n").filter(line => line.length > 0);
303
- const header = lines[0] === undefined ? undefined : JSON.parse(lines[0]);
304
- const last = lines.length > 1 ? JSON.parse(lines.at(-1)) : undefined;
305
- const entry = {
306
- type: "custom",
307
- customType: "pi.share",
308
- id,
309
- parentId: isRecord(last) && typeof last.id === "string" ? last.id : null,
310
- timestamp: isRecord(header) && typeof header.timestamp === "string" ? header.timestamp : new Date().toISOString(),
311
- data: {
312
- systemPrompt: state.systemPrompt,
313
- tools: (state.tools ?? []).filter(isRecord).map(tool => ({ name: tool.name, description: tool.description, parameters: tool.parameters })),
314
- },
315
- };
316
- return `${[...lines, JSON.stringify(entry)].join("\n")}\n`;
317
- }
318
271
  export function workflowOptions(value, idKey, labelKey) {
319
272
  if (!Array.isArray(value))
320
273
  return [];
@@ -1,5 +1,5 @@
1
1
  import { type ProgramStatus, type WheelScrollLines } from "@earendil-works/pi-tui";
2
- import type { PiTuiComponentPort, PiTuiInputListener, PiTuiOverlayHandle, PiTuiOverlayOptions, PiTuiPreInputListener, PiTuiRuntimeAdapterOptions, PiTuiRuntimeState, PiTuiScrollState, PiTuiStopOptions, PiTuiViewport } from "./contracts.js";
2
+ import type { TerminalComponentPort, TerminalInputListener, TerminalOverlayHandle, TerminalOverlayOptions, TerminalPreInputListener, TerminalRuntimeOptions, TerminalRuntimeState, TerminalStopOptions, TerminalViewport } from "../../../contracts/presentation/index.js";
3
3
  /**
4
4
  * Re-encodes one key report decoded by pi-tui in the legacy form, so a kitty-keyboard terminal
5
5
  * and a legacy terminal produce the same owned-screen action. Releases decode
@@ -15,19 +15,14 @@ export declare class PiTuiRuntimeError extends Error {
15
15
  /** Bridges neutral presentation ports to one Pi TUI instance and owns terminal restoration on stop. */
16
16
  export declare class PiTuiRuntimeAdapter {
17
17
  #private;
18
- constructor(options: PiTuiRuntimeAdapterOptions);
19
- get state(): PiTuiRuntimeState;
18
+ constructor(options: TerminalRuntimeOptions);
19
+ get state(): TerminalRuntimeState;
20
20
  get active(): boolean;
21
21
  get mode(): "regular" | "fullscreen";
22
22
  get fullRedraws(): number;
23
- viewport(): PiTuiViewport;
24
- scrollState(id?: string): PiTuiScrollState;
25
- scrollBy(lines: number, id?: string): void;
26
- scrollToTop(id?: string): void;
27
- scrollToBottom(id?: string): void;
23
+ viewport(): TerminalViewport;
28
24
  start(): void;
29
- setFocus(component: PiTuiComponentPort | null): void;
30
- showOverlay(component: PiTuiComponentPort, options?: PiTuiOverlayOptions): PiTuiOverlayHandle;
25
+ showOverlay(component: TerminalComponentPort, options?: TerminalOverlayOptions): TerminalOverlayHandle;
31
26
  hasOverlay(): boolean;
32
27
  /** Keyboard capture is independent of a passive overlay's visible pointer region. */
33
28
  hasFocusedOverlay(): boolean;
@@ -44,30 +39,25 @@ export declare class PiTuiRuntimeAdapter {
44
39
  * renderer keeps scheduling but nothing it paints reaches the terminal.
45
40
  */
46
41
  freezePresentation(): void;
47
- get presentationFrozen(): boolean;
48
42
  /**
49
43
  * Keeps asynchronous replacement-surface construction atomic at the terminal. Renderers may
50
44
  * compose while held, but their writes are dropped until the final forced repaint on release.
51
45
  */
52
46
  beginPresentationHold(): () => void;
53
47
  get presentationHeld(): boolean;
54
- addPreInputListener(listener: PiTuiPreInputListener): () => void;
55
- addInputListener(listener: PiTuiInputListener): () => void;
48
+ addPreInputListener(listener: TerminalPreInputListener): () => void;
49
+ addInputListener(listener: TerminalInputListener): () => void;
56
50
  switchMode(mode: "regular" | "fullscreen"): boolean;
57
51
  setHardwareCursor(enabled: boolean): void;
58
- getHardwareCursor(): boolean;
59
52
  setClearOnShrink(enabled: boolean): void;
60
53
  setWheelScrollLines(lines: WheelScrollLines): void;
61
- getClearOnShrink(): boolean;
62
54
  setTerminalProgress(active: boolean): void;
63
55
  setProgramStatus(status: ProgramStatus): void;
64
56
  setTitle(title: string): void;
65
- /** Shows a bounded fullscreen acknowledgement without changing root layout geometry. */
66
- showFlash(message: string, durationMs?: number): void;
67
57
  writeAfterStop(text: string): void;
68
58
  invalidate(): void;
69
59
  requestRender(force?: boolean): void;
70
60
  renderNow(force?: boolean): void;
71
- stop(options?: PiTuiStopOptions): Promise<void>;
72
- dispose(options?: PiTuiStopOptions): Promise<void>;
61
+ stop(options?: TerminalStopOptions): Promise<void>;
62
+ dispose(options?: TerminalStopOptions): Promise<void>;
73
63
  }
@@ -128,7 +128,7 @@ export class PiTuiRuntimeAdapter {
128
128
  #rootBridge;
129
129
  #bridges = new WeakMap();
130
130
  #mountedComponents = new Set();
131
- #scrollViews = new Map();
131
+ #scrollIds = new Set();
132
132
  #overlayGeometry;
133
133
  #overlayDisposers = new Set();
134
134
  #overlayInputCoordination = new Map();
@@ -222,53 +222,6 @@ export class PiTuiRuntimeAdapter {
222
222
  rows: Math.max(1, this.#terminal.rows),
223
223
  };
224
224
  }
225
- scrollState(id) {
226
- if (id === undefined) {
227
- if (!(this.#tui instanceof TuiAltScreen)) {
228
- throw new Error("Pi TUI application scrolling is available only in fullscreen mode");
229
- }
230
- return {
231
- scrollTop: this.#tui.viewportTop,
232
- viewportHeight: this.viewport().rows,
233
- followingEnd: this.#tui.isFollowingOutput,
234
- scrollbarVisible: false,
235
- };
236
- }
237
- const scrollView = this.#requireScrollView(id);
238
- return {
239
- scrollTop: scrollView.scrollTop,
240
- viewportHeight: scrollView.viewportHeight,
241
- followingEnd: scrollView.isFollowingEnd,
242
- scrollbarVisible: scrollView.isScrollbarVisible,
243
- };
244
- }
245
- scrollBy(lines, id) {
246
- this.#assertRunning("scroll");
247
- if (id === undefined)
248
- this.#requireFullscreen().scrollBy(lines);
249
- else {
250
- this.#requireScrollView(id).scrollBy(lines);
251
- this.#tui.requestRender();
252
- }
253
- }
254
- scrollToTop(id) {
255
- this.#assertRunning("scroll");
256
- if (id === undefined)
257
- this.#requireFullscreen().scrollToTop();
258
- else {
259
- this.#requireScrollView(id).scrollToStart();
260
- this.#tui.requestRender();
261
- }
262
- }
263
- scrollToBottom(id) {
264
- this.#assertRunning("scroll");
265
- if (id === undefined)
266
- this.#requireFullscreen().scrollToBottom();
267
- else {
268
- this.#requireScrollView(id).scrollToEnd();
269
- this.#tui.requestRender();
270
- }
271
- }
272
225
  start() {
273
226
  if (this.#state === "running")
274
227
  return;
@@ -286,12 +239,6 @@ export class PiTuiRuntimeAdapter {
286
239
  throw new PiTuiRuntimeError("start", error);
287
240
  }
288
241
  }
289
- setFocus(component) {
290
- this.#assertRunning("focus");
291
- if (this.#overlayInputCoordination.size > 0)
292
- this.#overlayGeometry?.invalidate();
293
- this.#tui.setFocus(this.#bridgeFor(component));
294
- }
295
242
  showOverlay(component, options) {
296
243
  this.#assertRunning("overlay");
297
244
  if (this.#bridges.has(component))
@@ -340,9 +287,6 @@ export class PiTuiRuntimeAdapter {
340
287
  this.#assertRunning("presentation freeze");
341
288
  this.#presentationFrozen = true;
342
289
  }
343
- get presentationFrozen() {
344
- return this.#presentationFrozen;
345
- }
346
290
  /**
347
291
  * Keeps asynchronous replacement-surface construction atomic at the terminal. Renderers may
348
292
  * compose while held, but their writes are dropped until the final forced repaint on release.
@@ -394,7 +338,7 @@ export class PiTuiRuntimeAdapter {
394
338
  previous.clear();
395
339
  if (previous instanceof TuiAltScreen)
396
340
  previous.setLayoutRoot(undefined);
397
- this.#scrollViews.clear();
341
+ this.#scrollIds.clear();
398
342
  const next = this.#createTui(mode, hardwareCursor);
399
343
  next.setClearOnShrink(clearOnShrink);
400
344
  this.#mountTui(next);
@@ -417,9 +361,6 @@ export class PiTuiRuntimeAdapter {
417
361
  if (this.active)
418
362
  this.#tui.requestRender();
419
363
  }
420
- getHardwareCursor() {
421
- return this.#tui.getShowHardwareCursor();
422
- }
423
364
  setClearOnShrink(enabled) {
424
365
  this.#tui.setClearOnShrink(enabled);
425
366
  }
@@ -428,9 +369,6 @@ export class PiTuiRuntimeAdapter {
428
369
  if (this.#tui instanceof TuiAltScreen)
429
370
  this.#tui.setWheelScrollLines(lines);
430
371
  }
431
- getClearOnShrink() {
432
- return this.#tui.getClearOnShrink();
433
- }
434
372
  setTerminalProgress(active) {
435
373
  if (this.#terminalProgress === active)
436
374
  return;
@@ -443,11 +381,6 @@ export class PiTuiRuntimeAdapter {
443
381
  setTitle(title) {
444
382
  this.#terminal.setTitle(title);
445
383
  }
446
- /** Shows a bounded fullscreen acknowledgement without changing root layout geometry. */
447
- showFlash(message, durationMs) {
448
- this.#assertRunning("flash");
449
- this.#requireFullscreen().flash(message, durationMs);
450
- }
451
384
  writeAfterStop(text) {
452
385
  if (this.#state !== "stopped")
453
386
  throw new Error("terminal output requires a stopped Pi TUI runtime");
@@ -580,7 +513,7 @@ export class PiTuiRuntimeAdapter {
580
513
  if (node.type === "component")
581
514
  return this.#mount(node.component);
582
515
  if (node.type === "scroll") {
583
- if (this.#scrollViews.has(node.id))
516
+ if (this.#scrollIds.has(node.id))
584
517
  throw new TypeError(`duplicate Pi TUI scroll layout id: ${node.id}`);
585
518
  const options = {
586
519
  ...(node.follow === undefined ? {} : { follow: node.follow }),
@@ -591,9 +524,8 @@ export class PiTuiRuntimeAdapter {
591
524
  ...(node.scrollbarThumbStyle === undefined ? {} : { scrollbarThumbStyle: node.scrollbarThumbStyle }),
592
525
  ...(node.scrollbarHideDelayMs === undefined ? {} : { scrollbarHideDelayMs: node.scrollbarHideDelayMs }),
593
526
  };
594
- const scrollView = new ScrollView(this.#buildLayout(node.child), options);
595
- this.#scrollViews.set(node.id, scrollView);
596
- return scrollView;
527
+ this.#scrollIds.add(node.id);
528
+ return new ScrollView(this.#buildLayout(node.child), options);
597
529
  }
598
530
  const children = node.children.map(entry => ({
599
531
  component: this.#buildLayout(entry.node),
@@ -610,19 +542,6 @@ export class PiTuiRuntimeAdapter {
610
542
  };
611
543
  return node.direction === "vertical" ? new VStack(children, options) : new HStack(children, options);
612
544
  }
613
- #requireFullscreen() {
614
- if (!(this.#tui instanceof TuiAltScreen)) {
615
- throw new Error("Pi TUI application scrolling is available only in fullscreen mode");
616
- }
617
- return this.#tui;
618
- }
619
- #requireScrollView(id) {
620
- this.#requireFullscreen();
621
- const scrollView = this.#scrollViews.get(id);
622
- if (!scrollView)
623
- throw new TypeError(`unknown Pi TUI scroll layout id: ${id}`);
624
- return scrollView;
625
- }
626
545
  #assertRunning(operation) {
627
546
  if (!this.active)
628
547
  throw new Error(`Pi TUI ${operation} requires a running runtime`);
@@ -683,12 +602,33 @@ export class PiTuiRuntimeAdapter {
683
602
  this.#disposeRoot();
684
603
  }
685
604
  }
686
- function coordinatedInputTerminal(terminal, coordinator, setSink, frameMouse) {
687
- const mouse = frameMouse ? new MouseReportInput(data => coordinator.accept(data)) : undefined;
605
+ /**
606
+ * Forwards every terminal member to `terminal` unless `overrides` replaces it.
607
+ * Invariant: the size and keyboard getters stay live; spreading the inner terminal would freeze them.
608
+ */
609
+ function forwardingTerminal(terminal, overrides = {}) {
688
610
  return {
689
611
  get columns() { return terminal.columns; },
690
612
  get rows() { return terminal.rows; },
691
613
  get kittyProtocolActive() { return terminal.kittyProtocolActive; },
614
+ start: overrides.start ?? ((onInput, onResize) => terminal.start(onInput, onResize)),
615
+ stop: overrides.stop ?? (() => terminal.stop()),
616
+ drainInput: overrides.drainInput ?? ((maxMs, idleMs) => terminal.drainInput(maxMs, idleMs)),
617
+ write: overrides.write ?? (data => terminal.write(data)),
618
+ moveBy: overrides.moveBy ?? (lines => terminal.moveBy(lines)),
619
+ hideCursor: overrides.hideCursor ?? (() => terminal.hideCursor()),
620
+ showCursor: overrides.showCursor ?? (() => terminal.showCursor()),
621
+ clearLine: overrides.clearLine ?? (() => terminal.clearLine()),
622
+ clearFromCursor: overrides.clearFromCursor ?? (() => terminal.clearFromCursor()),
623
+ clearScreen: overrides.clearScreen ?? (() => terminal.clearScreen()),
624
+ setTitle: overrides.setTitle ?? (title => terminal.setTitle(title)),
625
+ setProgress: overrides.setProgress ?? (active => terminal.setProgress(active)),
626
+ setProgramStatus: overrides.setProgramStatus ?? (status => terminal.setProgramStatus?.(status)),
627
+ };
628
+ }
629
+ function coordinatedInputTerminal(terminal, coordinator, setSink, frameMouse) {
630
+ const mouse = frameMouse ? new MouseReportInput(data => coordinator.accept(data)) : undefined;
631
+ return forwardingTerminal(terminal, {
692
632
  start(onInput, onResize) {
693
633
  setSink(onInput);
694
634
  terminal.start(data => mouse === undefined ? coordinator.accept(data) : mouse.accept(data), () => {
@@ -703,27 +643,10 @@ function coordinatedInputTerminal(terminal, coordinator, setSink, frameMouse) {
703
643
  setSink(undefined);
704
644
  terminal.stop();
705
645
  },
706
- drainInput: (maxMs, idleMs) => terminal.drainInput(maxMs, idleMs),
707
- write: data => terminal.write(data),
708
- moveBy: lines => terminal.moveBy(lines),
709
- hideCursor: () => terminal.hideCursor(),
710
- showCursor: () => terminal.showCursor(),
711
- clearLine: () => terminal.clearLine(),
712
- clearFromCursor: () => terminal.clearFromCursor(),
713
- clearScreen: () => terminal.clearScreen(),
714
- setTitle: title => terminal.setTitle(title),
715
- setProgress: active => terminal.setProgress(active),
716
- setProgramStatus: status => terminal.setProgramStatus?.(status),
717
- };
646
+ });
718
647
  }
719
648
  function diagnosticTerminal(terminal, trace) {
720
- return {
721
- get columns() { return terminal.columns; },
722
- get rows() { return terminal.rows; },
723
- get kittyProtocolActive() { return terminal.kittyProtocolActive; },
724
- start: (onInput, onResize) => terminal.start(onInput, onResize),
725
- stop: () => terminal.stop(),
726
- drainInput: (maxMs, idleMs) => terminal.drainInput(maxMs, idleMs),
649
+ return forwardingTerminal(terminal, {
727
650
  write(data) {
728
651
  trace("write-start");
729
652
  try {
@@ -733,25 +656,10 @@ function diagnosticTerminal(terminal, trace) {
733
656
  trace("write-end");
734
657
  }
735
658
  },
736
- moveBy: lines => terminal.moveBy(lines),
737
- hideCursor: () => terminal.hideCursor(),
738
- showCursor: () => terminal.showCursor(),
739
- clearLine: () => terminal.clearLine(),
740
- clearFromCursor: () => terminal.clearFromCursor(),
741
- clearScreen: () => terminal.clearScreen(),
742
- setTitle: title => terminal.setTitle(title),
743
- setProgress: active => terminal.setProgress(active),
744
- setProgramStatus: status => terminal.setProgramStatus?.(status),
745
- };
659
+ });
746
660
  }
747
661
  function frozenGateTerminal(terminal, frozen) {
748
- return {
749
- get columns() { return terminal.columns; },
750
- get rows() { return terminal.rows; },
751
- get kittyProtocolActive() { return terminal.kittyProtocolActive; },
752
- start: (onInput, onResize) => terminal.start(onInput, onResize),
753
- stop: () => terminal.stop(),
754
- drainInput: (maxMs, idleMs) => terminal.drainInput(maxMs, idleMs),
662
+ return forwardingTerminal(terminal, {
755
663
  write: data => { if (!frozen())
756
664
  terminal.write(data); },
757
665
  moveBy: lines => { if (!frozen())
@@ -766,17 +674,11 @@ function frozenGateTerminal(terminal, frozen) {
766
674
  terminal.clearFromCursor(); },
767
675
  clearScreen: () => { if (!frozen())
768
676
  terminal.clearScreen(); },
769
- setTitle: title => terminal.setTitle(title),
770
- setProgress: active => terminal.setProgress(active),
771
- setProgramStatus: status => terminal.setProgramStatus?.(status),
772
- };
677
+ });
773
678
  }
774
679
  function preInputTerminal(terminal, route, frameMouse) {
775
680
  let mouse;
776
- return {
777
- get columns() { return terminal.columns; },
778
- get rows() { return terminal.rows; },
779
- get kittyProtocolActive() { return terminal.kittyProtocolActive; },
681
+ return forwardingTerminal(terminal, {
780
682
  start(onInput, onResize) {
781
683
  const deliver = (data) => {
782
684
  const routed = route(data);
@@ -790,18 +692,7 @@ function preInputTerminal(terminal, route, frameMouse) {
790
692
  });
791
693
  },
792
694
  stop: () => { mouse?.reset(); terminal.stop(); },
793
- drainInput: (maxMs, idleMs) => terminal.drainInput(maxMs, idleMs),
794
- write: data => terminal.write(data),
795
- moveBy: lines => terminal.moveBy(lines),
796
- hideCursor: () => terminal.hideCursor(),
797
- showCursor: () => terminal.showCursor(),
798
- clearLine: () => terminal.clearLine(),
799
- clearFromCursor: () => terminal.clearFromCursor(),
800
- clearScreen: () => terminal.clearScreen(),
801
- setTitle: title => terminal.setTitle(title),
802
- setProgress: active => terminal.setProgress(active),
803
- setProgramStatus: status => terminal.setProgramStatus?.(status),
804
- };
695
+ });
805
696
  }
806
697
  function toOverlayOptions(options) {
807
698
  if (options === undefined)
@@ -1,4 +1,4 @@
1
- import type { PiTuiTerminalPort } from "./contracts.js";
1
+ import type { TerminalPort } from "../../../contracts/presentation/index.js";
2
2
  export declare const PINNED_PI_TUI_DAMAGE_GRAMMAR = "@earendil-works/pi-tui@1.5.0:tui-alt-screen-one-write-v1";
3
3
  import type { TerminalDamageDecision, TerminalDamageDecisionReason, TerminalDamageFrameDescriptor, TerminalDamageFrameSafety } from "../../../contracts/presentation/index.js";
4
4
  export type PiTuiDamageFrameDescriptor = TerminalDamageFrameDescriptor;
@@ -34,11 +34,11 @@ export interface DamageAwareTerminalOptions {
34
34
  * viewport metadata authorizes a shift; bytes are inspected only to validate
35
35
  * and preserve Pi's complete row paint and cursor syntax.
36
36
  */
37
- export declare class DamageAwareTerminalAdapter implements PiTuiTerminalPort {
37
+ export declare class DamageAwareTerminalAdapter implements TerminalPort {
38
38
  #private;
39
- readonly inner: PiTuiTerminalPort;
39
+ readonly inner: TerminalPort;
40
40
  readonly options: DamageAwareTerminalOptions;
41
- constructor(inner: PiTuiTerminalPort, options: DamageAwareTerminalOptions);
41
+ constructor(inner: TerminalPort, options: DamageAwareTerminalOptions);
42
42
  get columns(): number;
43
43
  get rows(): number;
44
44
  get kittyProtocolActive(): boolean;
@@ -1,4 +1,3 @@
1
- export type { PiTuiComponentPort, PiTuiInputDiagnosticsEvent, PiTuiInputDiagnosticsPhase, PiTuiInputListener, PiTuiInputListenerResult, PiTuiLayoutEntry, PiTuiLayoutNode, PiTuiOverlayAnchor, PiTuiOverlayHandle, PiTuiOverlayMargin, PiTuiOverlayOptions, PiTuiOverlayUnfocusOptions, PiTuiPointerSurface, PiTuiPreInputListener, PiTuiRuntimeAdapterOptions, PiTuiRuntimeState, PiTuiScrollState, PiTuiSizeValue, PiTuiStopOptions, PiTuiTerminalPort, PiTuiViewport, } from "./contracts.js";
2
1
  export { samePointerSurfaces } from "./overlay-geometry.js";
3
2
  export { PI_TERMINAL_RUNTIME } from "./runtime-factory.js";
4
3
  export { DamageAwareTerminalAdapter, PINNED_PI_TUI_DAMAGE_GRAMMAR, paintTerminalCanvasFrame, rebaseTerminalDefaultBackground, } from "./damage-aware-terminal.js";