@swifttui/web 0.3.3 → 0.3.8
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
CHANGED
|
@@ -121,7 +121,7 @@ not load a terminal emulator and does not depend on `ghostty-web` or
|
|
|
121
121
|
`ghostty-vt.wasm`.
|
|
122
122
|
|
|
123
123
|
`web-surface` is the default `SwiftTUIWASI` browser transport. WebHost still
|
|
124
|
-
sets `
|
|
124
|
+
sets `SWIFTTUI_TRANSPORT=surface` explicitly so generated app environments are
|
|
125
125
|
self-describing.
|
|
126
126
|
|
|
127
127
|
## Notes
|
|
@@ -131,7 +131,7 @@ self-describing.
|
|
|
131
131
|
one active palette/theme pair plus the runtime payload sent into SwiftTUI.
|
|
132
132
|
- Hosts that want multiple themes swap entire `WebHostTerminalStyle` objects;
|
|
133
133
|
the library does not provide a built-in mode switcher.
|
|
134
|
-
- `BrowserWASIBridge` sets `
|
|
134
|
+
- `BrowserWASIBridge` sets `SWIFTTUI_TRANSPORT=surface` and decodes surface
|
|
135
135
|
frames before handing them to the canvas runtime.
|
|
136
136
|
- Hyperlink cells published by the app (`links`/`linkTargets` on the frame)
|
|
137
137
|
are clickable: a click opens the target in a new tab (`http(s)` only) or
|
|
@@ -169,7 +169,7 @@ published `dist/` library output.
|
|
|
169
169
|
|
|
170
170
|
The demo/app build flow is intentionally small:
|
|
171
171
|
|
|
172
|
-
1. `build:manifest` captures `
|
|
172
|
+
1. `build:manifest` captures `SWIFTTUI_MODE=manifest` output from the Swift app by invoking `swiftly run swift`.
|
|
173
173
|
2. `build:wasm` copies the app's wasm artifact into `dist-demo/assets/app.wasm`,
|
|
174
174
|
validates it with the browser `WebAssembly` API, then keeps the stripped
|
|
175
175
|
artifact only if stripping still produces browser-parseable wasm.
|
|
@@ -14,16 +14,16 @@ var BrowserWASIBridge = class {
|
|
|
14
14
|
latestResize;
|
|
15
15
|
constructor(options) {
|
|
16
16
|
this.environment = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
SWIFTTUI_MODE: "browser",
|
|
18
|
+
SWIFTTUI_TRANSPORT: "surface",
|
|
19
|
+
SWIFTTUI_SURFACE_DELTA: "1",
|
|
20
|
+
SWIFTTUI_SCENE: options.sceneId,
|
|
21
|
+
SWIFTTUI_COLUMNS: String(Math.max(1, options.columns)),
|
|
22
|
+
SWIFTTUI_ROWS: String(Math.max(1, options.rows)),
|
|
23
|
+
SWIFTTUI_RENDER_MODE: "async-no-cancel",
|
|
24
24
|
...stackProfileEnvironmentDefaults(options.engineCapabilities ?? resolveWasmEngineCapabilities()),
|
|
25
25
|
...options.environment,
|
|
26
|
-
...options.renderStyle ? {
|
|
26
|
+
...options.renderStyle ? { SWIFTTUI_RENDER_STYLE: encodeWebHostTerminalRenderStyleBase64(options.renderStyle) } : {}
|
|
27
27
|
};
|
|
28
28
|
this.latestResize = {
|
|
29
29
|
columns: Math.max(1, options.columns),
|
|
@@ -60,8 +60,8 @@ var BrowserWASIBridge = class {
|
|
|
60
60
|
resize(columns, rows, cellWidth, cellHeight) {
|
|
61
61
|
const normalizedColumns = Math.max(1, columns);
|
|
62
62
|
const normalizedRows = Math.max(1, rows);
|
|
63
|
-
this.environment.
|
|
64
|
-
this.environment.
|
|
63
|
+
this.environment.SWIFTTUI_COLUMNS = String(normalizedColumns);
|
|
64
|
+
this.environment.SWIFTTUI_ROWS = String(normalizedRows);
|
|
65
65
|
this.latestResize = {
|
|
66
66
|
columns: normalizedColumns,
|
|
67
67
|
rows: normalizedRows,
|
|
@@ -72,7 +72,7 @@ var BrowserWASIBridge = class {
|
|
|
72
72
|
for (const listener of this.resizeListeners) listener(normalizedColumns, normalizedRows, cellWidth, cellHeight);
|
|
73
73
|
}
|
|
74
74
|
updateRenderStyle(style) {
|
|
75
|
-
this.environment.
|
|
75
|
+
this.environment.SWIFTTUI_RENDER_STYLE = encodeWebHostTerminalRenderStyleBase64(style);
|
|
76
76
|
this.stdin.write(encodeRenderStyleControlMessage(style));
|
|
77
77
|
}
|
|
78
78
|
sendInput(chunk) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrowserWASIBridge.js","names":[],"sources":["../../../src/wasi/BrowserWASIBridge.ts"],"sourcesContent":["import { StdIOPipe } from \"./StdIOPipe.ts\";\nimport {\n resolveWasmEngineCapabilities,\n stackProfileEnvironmentDefaults,\n type WasmEngineCapabilities,\n} from \"./WasmEngineCapabilities.ts\";\nimport {\n encodeWebHostTerminalRenderStyleBase64,\n type WebHostTerminalStyle,\n} from \"../WebHostTerminalStyle.ts\";\nimport {\n WebHostOutputDecoder,\n encodeRenderStyleControlMessage,\n encodeResizeControlMessage,\n type WebHostOutputSink,\n} from \"../WebHostSurfaceTransport.ts\";\n\nexport interface BrowserWASIBridgeOptions {\n sceneId: string;\n columns: number;\n rows: number;\n environment?: Record<string, string>;\n renderStyle?: WebHostTerminalStyle;\n /**\n * Detected engine capabilities driving engine-conditional environment\n * defaults (e.g. disabling the stack-lean resolve profile on V8). Defaults\n * to probing the current engine; injectable for tests and embedders that\n * want to force a profile.\n */\n engineCapabilities?: WasmEngineCapabilities;\n}\n\nexport type BrowserWASIOutputSink = WebHostOutputSink;\n\nexport class BrowserWASIBridge {\n readonly stdin = new StdIOPipe();\n readonly stdout = new StdIOPipe();\n readonly stderr = new StdIOPipe();\n readonly environment: Record<string, string>;\n\n private detachStdout?: () => void;\n private detachStderr?: () => void;\n private readonly resizeListeners = new Set<(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n ) => void>();\n private latestResize: {\n columns: number;\n rows: number;\n cellWidth?: number;\n cellHeight?: number;\n };\n\n constructor(options: BrowserWASIBridgeOptions) {\n this.environment = {\n
|
|
1
|
+
{"version":3,"file":"BrowserWASIBridge.js","names":[],"sources":["../../../src/wasi/BrowserWASIBridge.ts"],"sourcesContent":["import { StdIOPipe } from \"./StdIOPipe.ts\";\nimport {\n resolveWasmEngineCapabilities,\n stackProfileEnvironmentDefaults,\n type WasmEngineCapabilities,\n} from \"./WasmEngineCapabilities.ts\";\nimport {\n encodeWebHostTerminalRenderStyleBase64,\n type WebHostTerminalStyle,\n} from \"../WebHostTerminalStyle.ts\";\nimport {\n WebHostOutputDecoder,\n encodeRenderStyleControlMessage,\n encodeResizeControlMessage,\n type WebHostOutputSink,\n} from \"../WebHostSurfaceTransport.ts\";\n\nexport interface BrowserWASIBridgeOptions {\n sceneId: string;\n columns: number;\n rows: number;\n environment?: Record<string, string>;\n renderStyle?: WebHostTerminalStyle;\n /**\n * Detected engine capabilities driving engine-conditional environment\n * defaults (e.g. disabling the stack-lean resolve profile on V8). Defaults\n * to probing the current engine; injectable for tests and embedders that\n * want to force a profile.\n */\n engineCapabilities?: WasmEngineCapabilities;\n}\n\nexport type BrowserWASIOutputSink = WebHostOutputSink;\n\nexport class BrowserWASIBridge {\n readonly stdin = new StdIOPipe();\n readonly stdout = new StdIOPipe();\n readonly stderr = new StdIOPipe();\n readonly environment: Record<string, string>;\n\n private detachStdout?: () => void;\n private detachStderr?: () => void;\n private readonly resizeListeners = new Set<(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n ) => void>();\n private latestResize: {\n columns: number;\n rows: number;\n cellWidth?: number;\n cellHeight?: number;\n };\n\n constructor(options: BrowserWASIBridgeOptions) {\n this.environment = {\n SWIFTTUI_MODE: \"browser\",\n SWIFTTUI_TRANSPORT: \"surface\",\n SWIFTTUI_SURFACE_DELTA: \"1\",\n SWIFTTUI_SCENE: options.sceneId,\n SWIFTTUI_COLUMNS: String(Math.max(1, options.columns)),\n SWIFTTUI_ROWS: String(Math.max(1, options.rows)),\n // Browser default (2026-07, engine-blind): the single-threaded WASI\n // drive surfaces tick invalidations exactly where the async driver's\n // supersession predicate samples, so the default `async` disposal\n // (completed-frame visual-only drops + pre-start cancels) coalesces\n // steady scenes to ~1 wire frame per 3+ generations — the 0.1.9 live\n // regression. `async-no-cancel` keeps scheduler intent-merging but\n // commits every completed frame; measured 0.22 → 0.86 distinct-\n // generation coverage on the deployed Life scene, both execution\n // modes, with per-frame cost unchanged. Live lean sessions measure\n // zero drops/cancels, so the default is safe engine-blind. Callers\n // (and the `?renderMode=` page seam) override via\n // `options.environment`; rollback is this one line.\n SWIFTTUI_RENDER_MODE: \"async-no-cancel\",\n ...stackProfileEnvironmentDefaults(\n options.engineCapabilities ?? resolveWasmEngineCapabilities()\n ),\n ...options.environment,\n ...(options.renderStyle\n ? {\n SWIFTTUI_RENDER_STYLE: encodeWebHostTerminalRenderStyleBase64(\n options.renderStyle\n ),\n }\n : {}),\n };\n this.latestResize = {\n columns: Math.max(1, options.columns),\n rows: Math.max(1, options.rows),\n };\n }\n\n bindOutput(\n sink: BrowserWASIOutputSink\n ): void {\n this.detachStdout?.();\n this.detachStderr?.();\n const decoder = new WebHostOutputDecoder();\n this.detachStdout = this.stdout.subscribe((chunk) => {\n for (const record of decoder.feed(chunk)) {\n switch (record.type) {\n case \"surface\":\n sink.presentSurface(record.frame);\n break;\n case \"clipboard\":\n void sink.writeClipboard?.(record.text);\n break;\n case \"runtimeIssue\":\n sink.notifyRuntimeIssue?.(record.issue);\n break;\n case \"frameDiagnostic\":\n sink.recordFrameDiagnostic?.(record.diagnostic);\n break;\n case \"text\":\n sink.writeOutput?.(record.text);\n break;\n }\n }\n });\n this.detachStderr = this.stderr.subscribe((chunk) => {\n sink.writeError?.(new TextDecoder().decode(chunk));\n });\n }\n\n resize(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n ): void {\n const normalizedColumns = Math.max(1, columns);\n const normalizedRows = Math.max(1, rows);\n this.environment.SWIFTTUI_COLUMNS = String(normalizedColumns);\n this.environment.SWIFTTUI_ROWS = String(normalizedRows);\n this.latestResize = {\n columns: normalizedColumns,\n rows: normalizedRows,\n cellWidth,\n cellHeight,\n };\n this.stdin.write(encodeResizeControlMessage(columns, rows, cellWidth, cellHeight));\n for (const listener of this.resizeListeners) {\n listener(normalizedColumns, normalizedRows, cellWidth, cellHeight);\n }\n }\n\n updateRenderStyle(\n style: WebHostTerminalStyle\n ): void {\n this.environment.SWIFTTUI_RENDER_STYLE = encodeWebHostTerminalRenderStyleBase64(style);\n this.stdin.write(encodeRenderStyleControlMessage(style));\n }\n\n sendInput(\n chunk: Uint8Array\n ): void {\n this.stdin.write(chunk);\n }\n\n subscribeResize(\n listener: (\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n ) => void\n ): () => void {\n this.resizeListeners.add(listener);\n listener(\n this.latestResize.columns,\n this.latestResize.rows,\n this.latestResize.cellWidth,\n this.latestResize.cellHeight\n );\n return () => {\n this.resizeListeners.delete(listener);\n };\n }\n\n dispose(): void {\n this.detachStdout?.();\n this.detachStderr?.();\n this.resizeListeners.clear();\n this.stdin.close();\n this.stdout.close();\n this.stderr.close();\n }\n}\n\nexport {\n encodeRenderStyleControlMessage,\n encodeResizeControlMessage,\n};\n"],"mappings":";;;;;AAkCA,IAAa,oBAAb,MAA+B;CAC7B,QAAiB,IAAI,UAAU;CAC/B,SAAkB,IAAI,UAAU;CAChC,SAAkB,IAAI,UAAU;CAChC;CAEA;CACA;CACA,kCAAmC,IAAI,IAK5B;CACX;CAOA,YAAY,SAAmC;EAC7C,KAAK,cAAc;GACjB,eAAe;GACf,oBAAoB;GACpB,wBAAwB;GACxB,gBAAgB,QAAQ;GACxB,kBAAkB,OAAO,KAAK,IAAI,GAAG,QAAQ,OAAO,CAAC;GACrD,eAAe,OAAO,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC;GAa/C,sBAAsB;GACtB,GAAG,gCACD,QAAQ,sBAAsB,8BAA8B,CAC9D;GACA,GAAG,QAAQ;GACX,GAAI,QAAQ,cACR,EACE,uBAAuB,uCACrB,QAAQ,WACV,EACF,IACA,CAAC;EACP;EACA,KAAK,eAAe;GAClB,SAAS,KAAK,IAAI,GAAG,QAAQ,OAAO;GACpC,MAAM,KAAK,IAAI,GAAG,QAAQ,IAAI;EAChC;CACF;CAEA,WACE,MACM;EACN,KAAK,eAAe;EACpB,KAAK,eAAe;EACpB,MAAM,UAAU,IAAI,qBAAqB;EACzC,KAAK,eAAe,KAAK,OAAO,WAAW,UAAU;GACnD,KAAK,MAAM,UAAU,QAAQ,KAAK,KAAK,GACrC,QAAQ,OAAO,MAAf;IACA,KAAK;KACH,KAAK,eAAe,OAAO,KAAK;KAChC;IACF,KAAK;KACH,KAAU,iBAAiB,OAAO,IAAI;KACtC;IACF,KAAK;KACH,KAAK,qBAAqB,OAAO,KAAK;KACtC;IACF,KAAK;KACH,KAAK,wBAAwB,OAAO,UAAU;KAC9C;IACF,KAAK;KACH,KAAK,cAAc,OAAO,IAAI;KAC9B;GACF;EAEJ,CAAC;EACD,KAAK,eAAe,KAAK,OAAO,WAAW,UAAU;GACnD,KAAK,aAAa,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,CAAC;EACnD,CAAC;CACH;CAEA,OACE,SACA,MACA,WACA,YACM;EACN,MAAM,oBAAoB,KAAK,IAAI,GAAG,OAAO;EAC7C,MAAM,iBAAiB,KAAK,IAAI,GAAG,IAAI;EACvC,KAAK,YAAY,mBAAmB,OAAO,iBAAiB;EAC5D,KAAK,YAAY,gBAAgB,OAAO,cAAc;EACtD,KAAK,eAAe;GAClB,SAAS;GACT,MAAM;GACN;GACA;EACF;EACA,KAAK,MAAM,MAAM,2BAA2B,SAAS,MAAM,WAAW,UAAU,CAAC;EACjF,KAAK,MAAM,YAAY,KAAK,iBAC1B,SAAS,mBAAmB,gBAAgB,WAAW,UAAU;CAErE;CAEA,kBACE,OACM;EACN,KAAK,YAAY,wBAAwB,uCAAuC,KAAK;EACrF,KAAK,MAAM,MAAM,gCAAgC,KAAK,CAAC;CACzD;CAEA,UACE,OACM;EACN,KAAK,MAAM,MAAM,KAAK;CACxB;CAEA,gBACE,UAMY;EACZ,KAAK,gBAAgB,IAAI,QAAQ;EACjC,SACE,KAAK,aAAa,SAClB,KAAK,aAAa,MAClB,KAAK,aAAa,WAClB,KAAK,aAAa,UACpB;EACA,aAAa;GACX,KAAK,gBAAgB,OAAO,QAAQ;EACtC;CACF;CAEA,UAAgB;EACd,KAAK,eAAe;EACpB,KAAK,eAAe;EACpB,KAAK,gBAAgB,MAAM;EAC3B,KAAK,MAAM,MAAM;EACjB,KAAK,OAAO,MAAM;EAClB,KAAK,OAAO,MAAM;CACpB;AACF"}
|
|
@@ -90,8 +90,8 @@ var WasmSceneRuntime = class extends WebHostSceneRuntime {
|
|
|
90
90
|
cellHeight
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
|
-
const initialColumns = Number(this.bridge?.environment.
|
|
94
|
-
const initialRows = Number(this.bridge?.environment.
|
|
93
|
+
const initialColumns = Number(this.bridge?.environment.SWIFTTUI_COLUMNS ?? "0") || 0;
|
|
94
|
+
const initialRows = Number(this.bridge?.environment.SWIFTTUI_ROWS ?? "0") || 0;
|
|
95
95
|
if (!this.bridge && initialColumns > 0 && initialRows > 0) this.onSceneResize?.({
|
|
96
96
|
sceneId: this.descriptor.id,
|
|
97
97
|
columns: initialColumns,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WasmSceneRuntime.js","names":[],"sources":["../../../src/wasi/WasmSceneRuntime.ts"],"sourcesContent":["import {\n WebHostSceneRuntime,\n type WebHostSceneRuntimeOptions,\n} from \"../WebHostSceneRuntime.ts\";\nimport {\n encodeResizeControlMessage,\n type BrowserWASIBridge,\n} from \"./BrowserWASIBridge.ts\";\n\nimport { MainThreadWasmExecutor } from \"./MainThreadWasmExecutor.ts\";\nimport {\n SharedInputQueueWriter,\n createSharedInputQueue,\n type SharedInputQueueBuffers,\n} from \"./SharedInputQueue.ts\";\nimport {\n mainThreadStackProfileEnvironmentDefaults,\n resolveWasmEngineCapabilities,\n type WasmEngineCapabilities,\n} from \"./WasmEngineCapabilities.ts\";\nimport { createWasmPauseCell, setWasmPauseCellPaused } from \"./WasmRuntimePause.ts\";\n\nconst workerModuleURL = new URL(\"./wasm-scene-worker.js\", import.meta.url);\n\ninterface WorkerStartMessage {\n type: \"start\";\n wasmURL: string;\n environment: Record<string, string>;\n inputQueue: SharedInputQueueBuffers;\n pauseCell?: SharedArrayBuffer;\n}\n\ninterface WorkerOutputMessage {\n type: \"stdout\" | \"stderr\";\n chunk: Uint8Array;\n}\n\ninterface WorkerExitMessage {\n type: \"exit\";\n code: number;\n}\n\ninterface WorkerErrorMessage {\n type: \"error\";\n message: string;\n}\n\ntype WorkerMessage = WorkerOutputMessage | WorkerExitMessage | WorkerErrorMessage;\n\nexport interface WasmSceneResizeEvent {\n sceneId: string;\n columns: number;\n rows: number;\n cellWidth?: number;\n cellHeight?: number;\n}\n\nexport interface WasmSceneRuntimeHandle {\n readonly descriptor: WebHostSceneRuntime[\"descriptor\"];\n sendInput(chunk: Uint8Array): void;\n}\n\nexport type WasmExecutionMode = \"worker\" | \"main-thread\";\nexport type WasmExecutionModePreference = WasmExecutionMode | \"auto\";\n\nexport interface WasmSceneRuntimeFactoryOptions {\n onSceneResize?(event: WasmSceneResizeEvent): void;\n onRuntimeCreated?(runtime: WasmSceneRuntimeHandle): void;\n workerModuleURL?: string | URL;\n /**\n * How to execute the wasm app. \"worker\" is the classic path\n * (`Atomics.wait` stdin, needs SharedArrayBuffer/COOP/COEP). \"main-thread\"\n * runs on the page's thread via WebAssembly JSPI — larger stack budget (no\n * stack-lean profile on measured engines), no COOP/COEP requirement, at\n * the cost of sharing the main thread. \"auto\" (default) picks main-thread\n * only where workers cannot run (SharedArrayBuffer unavailable and JSPI\n * present); workers everywhere else.\n */\n executionMode?: WasmExecutionModePreference;\n}\n\nexport function resolveWasmExecutionMode(\n preference: WasmExecutionModePreference,\n capabilities: WasmEngineCapabilities,\n sharedInputQueueAvailable: boolean\n): WasmExecutionMode {\n if (preference !== \"auto\") {\n return preference;\n }\n if (!capabilities.supportsJSPI) {\n return \"worker\";\n }\n // Workers stay the auto default even on JSPI-capable engines: main-thread\n // execution shares the page's thread, and its stack-budget advantage only\n // pays off once the non-lean profile is production-ready (see\n // `stackProfileEnvironmentDefaults`). JSPI's auto role today is running\n // where workers cannot — pages without cross-origin isolation.\n if (!sharedInputQueueAvailable) {\n return \"main-thread\";\n }\n return \"worker\";\n}\n\nexport function createWasmSceneRuntimeFactory(\n wasmURL: URL,\n factoryOptions: WasmSceneRuntimeFactoryOptions = {}\n): (options: WebHostSceneRuntimeOptions) => WebHostSceneRuntime {\n return (options) => {\n const runtime = new WasmSceneRuntime(options, wasmURL, factoryOptions);\n factoryOptions.onRuntimeCreated?.(runtime);\n return runtime;\n };\n}\n\nclass WasmSceneRuntime extends WebHostSceneRuntime {\n private readonly bridge?: BrowserWASIBridge;\n private readonly wasmURL: URL;\n private readonly onSceneResize?: (event: WasmSceneResizeEvent) => void;\n private readonly workerModuleURL: string | URL;\n private readonly executionModePreference: WasmExecutionModePreference;\n private readonly inputQueue?: SharedInputQueueBuffers;\n private readonly inputWriter?: SharedInputQueueWriter;\n private readonly inputRouter: { route(chunk: Uint8Array): void };\n private readonly sharedQueueError?: unknown;\n private readonly pauseCell?: SharedArrayBuffer;\n\n private detachBridgeInputListener?: () => void;\n private detachResizeListener?: () => void;\n private worker?: Worker;\n private executor?: MainThreadWasmExecutor;\n private didMount = false;\n private suspended = false;\n\n constructor(\n options: WebHostSceneRuntimeOptions,\n wasmURL: URL,\n factoryOptions: WasmSceneRuntimeFactoryOptions\n ) {\n let inputQueue: SharedInputQueueBuffers | undefined;\n let inputWriter: SharedInputQueueWriter | undefined;\n let sharedQueueError: unknown;\n let pauseCell: SharedArrayBuffer | undefined;\n\n try {\n inputQueue = createSharedInputQueue();\n inputWriter = new SharedInputQueueWriter(inputQueue);\n pauseCell = createWasmPauseCell();\n } catch (error) {\n // Not fatal here: the main-thread (JSPI) mode runs without\n // SharedArrayBuffer. Surfaced at mount if the worker mode needs it.\n sharedQueueError = error;\n }\n\n const inputRouter = {\n route: (chunk: Uint8Array): void => {\n try {\n inputWriter?.write(chunk);\n } catch (error) {\n console.error(\"[SwiftTUIWeb] failed to enqueue terminal input\", error);\n }\n },\n };\n\n super({\n ...options,\n onInput: (chunk) => inputRouter.route(chunk),\n });\n\n this.bridge = options.bridge;\n this.wasmURL = wasmURL;\n this.onSceneResize = factoryOptions.onSceneResize;\n this.workerModuleURL = factoryOptions.workerModuleURL ?? workerModuleURL;\n this.executionModePreference = factoryOptions.executionMode ?? \"auto\";\n this.inputQueue = inputQueue;\n this.inputWriter = inputWriter;\n this.inputRouter = inputRouter;\n this.sharedQueueError = sharedQueueError;\n this.pauseCell = pauseCell;\n }\n\n protected override onRuntimeSuspensionChange(\n suspended: boolean\n ): void {\n this.suspended = suspended;\n if (this.pauseCell) {\n setWasmPauseCellPaused(this.pauseCell, suspended);\n }\n this.executor?.setSuspended(suspended);\n }\n\n override async mount(): Promise<void> {\n await super.mount();\n if (this.didMount) {\n return;\n }\n\n this.didMount = true;\n this.detachBridgeInputListener = this.bridge?.stdin.subscribe((chunk) => {\n this.inputRouter.route(chunk);\n });\n this.detachResizeListener = this.bridge?.subscribeResize((columns, rows, cellWidth, cellHeight) => {\n this.onSceneResize?.({\n sceneId: this.descriptor.id,\n columns,\n rows,\n cellWidth,\n cellHeight,\n });\n });\n\n const initialColumns = Number(this.bridge?.environment.TUIGUI_COLUMNS ?? \"0\") || 0;\n const initialRows = Number(this.bridge?.environment.TUIGUI_ROWS ?? \"0\") || 0;\n if (!this.bridge && initialColumns > 0 && initialRows > 0) {\n this.onSceneResize?.({\n sceneId: this.descriptor.id,\n columns: initialColumns,\n rows: initialRows,\n });\n }\n\n if (!this.bridge) {\n this.writeOutput(\n \"\\r\\nSwiftTUI WASI browser runtime requires a WASI bridge.\\r\\n\"\n );\n return;\n }\n\n const mode = resolveWasmExecutionMode(\n this.executionModePreference,\n resolveWasmEngineCapabilities(),\n this.inputQueue !== undefined && this.inputWriter !== undefined\n );\n if (mode === \"main-thread\") {\n this.startMainThreadExecutor();\n return;\n }\n\n if (!this.inputQueue || !this.inputWriter) {\n if (this.sharedQueueError !== undefined) {\n console.error(\n \"[SwiftTUIWeb] failed to create shared stdin queue\",\n this.sharedQueueError\n );\n }\n this.writeOutput(\n \"\\r\\nSwiftTUI WASI browser runtime requires SharedArrayBuffer-backed stdin. Serve the app with COOP/COEP headers.\\r\\n\"\n );\n return;\n }\n\n this.worker = new Worker(this.workerModuleURL, { type: \"module\" });\n this.worker.addEventListener(\"message\", (event: MessageEvent<WorkerMessage>) => {\n this.handleWorkerMessage(event.data);\n });\n this.worker.addEventListener(\"error\", (event) => {\n this.bridge?.stderr.write(\n `\\nSwiftTUI WASI worker failed: ${event.message || \"unknown worker error\"}\\n`\n );\n });\n\n const environment = { ...this.bridge.environment };\n\n const message: WorkerStartMessage = {\n type: \"start\",\n wasmURL: this.wasmURL.href,\n environment,\n inputQueue: this.inputQueue,\n pauseCell: this.pauseCell,\n };\n this.worker.postMessage(message);\n }\n\n override dispose(): void {\n this.detachBridgeInputListener?.();\n this.detachResizeListener?.();\n this.inputWriter?.close();\n this.worker?.terminate();\n this.executor?.dispose();\n super.dispose();\n }\n\n private startMainThreadExecutor(): void {\n const bridge = this.bridge;\n if (!bridge) {\n return;\n }\n const executor = new MainThreadWasmExecutor({\n wasmURL: this.wasmURL.href,\n environment: {\n ...mainThreadStackProfileEnvironmentDefaults(resolveWasmEngineCapabilities()),\n ...bridge.environment,\n },\n onStdout: (chunk) => bridge.stdout.write(chunk),\n onStderr: (chunk) => bridge.stderr.write(chunk),\n onExit: (code) => {\n if (code !== 0) {\n bridge.stderr.write(`\\nSwiftTUI WASI app exited with code ${code}.\\n`);\n }\n },\n onError: (message) => {\n bridge.stderr.write(`\\nFailed to start SwiftTUI WASI app: ${message}\\n`);\n },\n });\n this.executor = executor;\n this.inputRouter.route = (chunk) => executor.sendInput(chunk);\n executor.setSuspended(this.suspended);\n executor.start();\n }\n\n private handleWorkerMessage(\n message: WorkerMessage\n ): void {\n switch (message.type) {\n case \"stdout\":\n this.bridge?.stdout.write(message.chunk);\n break;\n case \"stderr\":\n this.bridge?.stderr.write(message.chunk);\n break;\n case \"exit\":\n if (message.code !== 0) {\n this.bridge?.stderr.write(`\\nSwiftTUI WASI app exited with code ${message.code}.\\n`);\n }\n break;\n case \"error\":\n this.bridge?.stderr.write(`\\nFailed to start SwiftTUI WASI app: ${message.message}\\n`);\n break;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WasmSceneRuntime.js","names":[],"sources":["../../../src/wasi/WasmSceneRuntime.ts"],"sourcesContent":["import {\n WebHostSceneRuntime,\n type WebHostSceneRuntimeOptions,\n} from \"../WebHostSceneRuntime.ts\";\nimport {\n encodeResizeControlMessage,\n type BrowserWASIBridge,\n} from \"./BrowserWASIBridge.ts\";\n\nimport { MainThreadWasmExecutor } from \"./MainThreadWasmExecutor.ts\";\nimport {\n SharedInputQueueWriter,\n createSharedInputQueue,\n type SharedInputQueueBuffers,\n} from \"./SharedInputQueue.ts\";\nimport {\n mainThreadStackProfileEnvironmentDefaults,\n resolveWasmEngineCapabilities,\n type WasmEngineCapabilities,\n} from \"./WasmEngineCapabilities.ts\";\nimport { createWasmPauseCell, setWasmPauseCellPaused } from \"./WasmRuntimePause.ts\";\n\nconst workerModuleURL = new URL(\"./wasm-scene-worker.js\", import.meta.url);\n\ninterface WorkerStartMessage {\n type: \"start\";\n wasmURL: string;\n environment: Record<string, string>;\n inputQueue: SharedInputQueueBuffers;\n pauseCell?: SharedArrayBuffer;\n}\n\ninterface WorkerOutputMessage {\n type: \"stdout\" | \"stderr\";\n chunk: Uint8Array;\n}\n\ninterface WorkerExitMessage {\n type: \"exit\";\n code: number;\n}\n\ninterface WorkerErrorMessage {\n type: \"error\";\n message: string;\n}\n\ntype WorkerMessage = WorkerOutputMessage | WorkerExitMessage | WorkerErrorMessage;\n\nexport interface WasmSceneResizeEvent {\n sceneId: string;\n columns: number;\n rows: number;\n cellWidth?: number;\n cellHeight?: number;\n}\n\nexport interface WasmSceneRuntimeHandle {\n readonly descriptor: WebHostSceneRuntime[\"descriptor\"];\n sendInput(chunk: Uint8Array): void;\n}\n\nexport type WasmExecutionMode = \"worker\" | \"main-thread\";\nexport type WasmExecutionModePreference = WasmExecutionMode | \"auto\";\n\nexport interface WasmSceneRuntimeFactoryOptions {\n onSceneResize?(event: WasmSceneResizeEvent): void;\n onRuntimeCreated?(runtime: WasmSceneRuntimeHandle): void;\n workerModuleURL?: string | URL;\n /**\n * How to execute the wasm app. \"worker\" is the classic path\n * (`Atomics.wait` stdin, needs SharedArrayBuffer/COOP/COEP). \"main-thread\"\n * runs on the page's thread via WebAssembly JSPI — larger stack budget (no\n * stack-lean profile on measured engines), no COOP/COEP requirement, at\n * the cost of sharing the main thread. \"auto\" (default) picks main-thread\n * only where workers cannot run (SharedArrayBuffer unavailable and JSPI\n * present); workers everywhere else.\n */\n executionMode?: WasmExecutionModePreference;\n}\n\nexport function resolveWasmExecutionMode(\n preference: WasmExecutionModePreference,\n capabilities: WasmEngineCapabilities,\n sharedInputQueueAvailable: boolean\n): WasmExecutionMode {\n if (preference !== \"auto\") {\n return preference;\n }\n if (!capabilities.supportsJSPI) {\n return \"worker\";\n }\n // Workers stay the auto default even on JSPI-capable engines: main-thread\n // execution shares the page's thread, and its stack-budget advantage only\n // pays off once the non-lean profile is production-ready (see\n // `stackProfileEnvironmentDefaults`). JSPI's auto role today is running\n // where workers cannot — pages without cross-origin isolation.\n if (!sharedInputQueueAvailable) {\n return \"main-thread\";\n }\n return \"worker\";\n}\n\nexport function createWasmSceneRuntimeFactory(\n wasmURL: URL,\n factoryOptions: WasmSceneRuntimeFactoryOptions = {}\n): (options: WebHostSceneRuntimeOptions) => WebHostSceneRuntime {\n return (options) => {\n const runtime = new WasmSceneRuntime(options, wasmURL, factoryOptions);\n factoryOptions.onRuntimeCreated?.(runtime);\n return runtime;\n };\n}\n\nclass WasmSceneRuntime extends WebHostSceneRuntime {\n private readonly bridge?: BrowserWASIBridge;\n private readonly wasmURL: URL;\n private readonly onSceneResize?: (event: WasmSceneResizeEvent) => void;\n private readonly workerModuleURL: string | URL;\n private readonly executionModePreference: WasmExecutionModePreference;\n private readonly inputQueue?: SharedInputQueueBuffers;\n private readonly inputWriter?: SharedInputQueueWriter;\n private readonly inputRouter: { route(chunk: Uint8Array): void };\n private readonly sharedQueueError?: unknown;\n private readonly pauseCell?: SharedArrayBuffer;\n\n private detachBridgeInputListener?: () => void;\n private detachResizeListener?: () => void;\n private worker?: Worker;\n private executor?: MainThreadWasmExecutor;\n private didMount = false;\n private suspended = false;\n\n constructor(\n options: WebHostSceneRuntimeOptions,\n wasmURL: URL,\n factoryOptions: WasmSceneRuntimeFactoryOptions\n ) {\n let inputQueue: SharedInputQueueBuffers | undefined;\n let inputWriter: SharedInputQueueWriter | undefined;\n let sharedQueueError: unknown;\n let pauseCell: SharedArrayBuffer | undefined;\n\n try {\n inputQueue = createSharedInputQueue();\n inputWriter = new SharedInputQueueWriter(inputQueue);\n pauseCell = createWasmPauseCell();\n } catch (error) {\n // Not fatal here: the main-thread (JSPI) mode runs without\n // SharedArrayBuffer. Surfaced at mount if the worker mode needs it.\n sharedQueueError = error;\n }\n\n const inputRouter = {\n route: (chunk: Uint8Array): void => {\n try {\n inputWriter?.write(chunk);\n } catch (error) {\n console.error(\"[SwiftTUIWeb] failed to enqueue terminal input\", error);\n }\n },\n };\n\n super({\n ...options,\n onInput: (chunk) => inputRouter.route(chunk),\n });\n\n this.bridge = options.bridge;\n this.wasmURL = wasmURL;\n this.onSceneResize = factoryOptions.onSceneResize;\n this.workerModuleURL = factoryOptions.workerModuleURL ?? workerModuleURL;\n this.executionModePreference = factoryOptions.executionMode ?? \"auto\";\n this.inputQueue = inputQueue;\n this.inputWriter = inputWriter;\n this.inputRouter = inputRouter;\n this.sharedQueueError = sharedQueueError;\n this.pauseCell = pauseCell;\n }\n\n protected override onRuntimeSuspensionChange(\n suspended: boolean\n ): void {\n this.suspended = suspended;\n if (this.pauseCell) {\n setWasmPauseCellPaused(this.pauseCell, suspended);\n }\n this.executor?.setSuspended(suspended);\n }\n\n override async mount(): Promise<void> {\n await super.mount();\n if (this.didMount) {\n return;\n }\n\n this.didMount = true;\n this.detachBridgeInputListener = this.bridge?.stdin.subscribe((chunk) => {\n this.inputRouter.route(chunk);\n });\n this.detachResizeListener = this.bridge?.subscribeResize((columns, rows, cellWidth, cellHeight) => {\n this.onSceneResize?.({\n sceneId: this.descriptor.id,\n columns,\n rows,\n cellWidth,\n cellHeight,\n });\n });\n\n const initialColumns = Number(this.bridge?.environment.SWIFTTUI_COLUMNS ?? \"0\") || 0;\n const initialRows = Number(this.bridge?.environment.SWIFTTUI_ROWS ?? \"0\") || 0;\n if (!this.bridge && initialColumns > 0 && initialRows > 0) {\n this.onSceneResize?.({\n sceneId: this.descriptor.id,\n columns: initialColumns,\n rows: initialRows,\n });\n }\n\n if (!this.bridge) {\n this.writeOutput(\n \"\\r\\nSwiftTUI WASI browser runtime requires a WASI bridge.\\r\\n\"\n );\n return;\n }\n\n const mode = resolveWasmExecutionMode(\n this.executionModePreference,\n resolveWasmEngineCapabilities(),\n this.inputQueue !== undefined && this.inputWriter !== undefined\n );\n if (mode === \"main-thread\") {\n this.startMainThreadExecutor();\n return;\n }\n\n if (!this.inputQueue || !this.inputWriter) {\n if (this.sharedQueueError !== undefined) {\n console.error(\n \"[SwiftTUIWeb] failed to create shared stdin queue\",\n this.sharedQueueError\n );\n }\n this.writeOutput(\n \"\\r\\nSwiftTUI WASI browser runtime requires SharedArrayBuffer-backed stdin. Serve the app with COOP/COEP headers.\\r\\n\"\n );\n return;\n }\n\n this.worker = new Worker(this.workerModuleURL, { type: \"module\" });\n this.worker.addEventListener(\"message\", (event: MessageEvent<WorkerMessage>) => {\n this.handleWorkerMessage(event.data);\n });\n this.worker.addEventListener(\"error\", (event) => {\n this.bridge?.stderr.write(\n `\\nSwiftTUI WASI worker failed: ${event.message || \"unknown worker error\"}\\n`\n );\n });\n\n const environment = { ...this.bridge.environment };\n\n const message: WorkerStartMessage = {\n type: \"start\",\n wasmURL: this.wasmURL.href,\n environment,\n inputQueue: this.inputQueue,\n pauseCell: this.pauseCell,\n };\n this.worker.postMessage(message);\n }\n\n override dispose(): void {\n this.detachBridgeInputListener?.();\n this.detachResizeListener?.();\n this.inputWriter?.close();\n this.worker?.terminate();\n this.executor?.dispose();\n super.dispose();\n }\n\n private startMainThreadExecutor(): void {\n const bridge = this.bridge;\n if (!bridge) {\n return;\n }\n const executor = new MainThreadWasmExecutor({\n wasmURL: this.wasmURL.href,\n environment: {\n ...mainThreadStackProfileEnvironmentDefaults(resolveWasmEngineCapabilities()),\n ...bridge.environment,\n },\n onStdout: (chunk) => bridge.stdout.write(chunk),\n onStderr: (chunk) => bridge.stderr.write(chunk),\n onExit: (code) => {\n if (code !== 0) {\n bridge.stderr.write(`\\nSwiftTUI WASI app exited with code ${code}.\\n`);\n }\n },\n onError: (message) => {\n bridge.stderr.write(`\\nFailed to start SwiftTUI WASI app: ${message}\\n`);\n },\n });\n this.executor = executor;\n this.inputRouter.route = (chunk) => executor.sendInput(chunk);\n executor.setSuspended(this.suspended);\n executor.start();\n }\n\n private handleWorkerMessage(\n message: WorkerMessage\n ): void {\n switch (message.type) {\n case \"stdout\":\n this.bridge?.stdout.write(message.chunk);\n break;\n case \"stderr\":\n this.bridge?.stderr.write(message.chunk);\n break;\n case \"exit\":\n if (message.code !== 0) {\n this.bridge?.stderr.write(`\\nSwiftTUI WASI app exited with code ${message.code}.\\n`);\n }\n break;\n case \"error\":\n this.bridge?.stderr.write(`\\nFailed to start SwiftTUI WASI app: ${message.message}\\n`);\n break;\n }\n }\n}\n"],"mappings":"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"}
|