@swifttui/web 0.4.0 → 0.4.2

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 (41) hide show
  1. package/dist/index.d.ts +3 -3
  2. package/dist/index.js +2 -2
  3. package/dist/src/AccessibilityTree.js +10 -2
  4. package/dist/src/AccessibilityTree.js.map +1 -1
  5. package/dist/src/CanvasSurfacePainter.d.ts +16 -0
  6. package/dist/src/CanvasSurfacePainter.js +162 -16
  7. package/dist/src/CanvasSurfacePainter.js.map +1 -1
  8. package/dist/src/DomSurfacePainter.d.ts +17 -3
  9. package/dist/src/DomSurfacePainter.js +63 -10
  10. package/dist/src/DomSurfacePainter.js.map +1 -1
  11. package/dist/src/SurfacePainterConformanceControl.js +13 -0
  12. package/dist/src/SurfacePainterConformanceControl.js.map +1 -0
  13. package/dist/src/SurfaceRenderer.d.ts +3 -2
  14. package/dist/src/SurfaceRenderer.js.map +1 -1
  15. package/dist/src/WebHostSceneRuntime.d.ts +3 -1
  16. package/dist/src/WebHostSceneRuntime.js +16 -7
  17. package/dist/src/WebHostSceneRuntime.js.map +1 -1
  18. package/dist/src/WebHostSurfaceTransport.d.ts +63 -6
  19. package/dist/src/WebHostSurfaceTransport.js +186 -9
  20. package/dist/src/WebHostSurfaceTransport.js.map +1 -1
  21. package/dist/src/WebSocketSceneBridge.d.ts +2 -0
  22. package/dist/src/WebSocketSceneBridge.js +30 -5
  23. package/dist/src/WebSocketSceneBridge.js.map +1 -1
  24. package/dist/src/normalizeWireTokens.d.ts +5 -0
  25. package/dist/src/normalizeWireTokens.js +41 -0
  26. package/dist/src/normalizeWireTokens.js.map +1 -0
  27. package/dist/src/wasi/BrowserWASIBridge.d.ts +8 -0
  28. package/dist/src/wasi/BrowserWASIBridge.js +31 -4
  29. package/dist/src/wasi/BrowserWASIBridge.js.map +1 -1
  30. package/dist/src/wasi/SharedInputQueue.d.ts +2 -0
  31. package/dist/src/wasi/SharedInputQueue.js +22 -0
  32. package/dist/src/wasi/SharedInputQueue.js.map +1 -1
  33. package/dist/src/wasi/StdIOPipe.d.ts +2 -2
  34. package/dist/src/wasi/StdIOPipe.js +5 -3
  35. package/dist/src/wasi/StdIOPipe.js.map +1 -1
  36. package/dist/src/wasi/WasmSceneRuntime.js +24 -4
  37. package/dist/src/wasi/WasmSceneRuntime.js.map +1 -1
  38. package/dist/src/wasi/WasmSceneWorker.js +1 -1
  39. package/dist/testing.d.ts +2 -1
  40. package/dist/wasi.js +1 -1
  41. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"WebHostSurfaceTransport.js","names":[],"sources":["../../src/WebHostSurfaceTransport.ts"],"sourcesContent":["import {\n encodeWebHostTerminalRenderStyleBase64,\n type WebHostTerminalStyle,\n} from \"./WebHostTerminalStyle.ts\";\n\nexport interface WebHostSurfaceStyle {\n fg?: string;\n bg?: string;\n em?: number;\n underline?: WebHostSurfaceLineStyle;\n strikethrough?: WebHostSurfaceLineStyle;\n opacity?: number;\n}\n\nexport interface WebHostSurfaceLineStyle {\n pattern: \"solid\" | \"dot\" | \"dash\" | \"dashDot\" | \"dashDotDot\" | \"double\" | \"curly\";\n color?: string;\n}\n\nexport type WebHostSurfaceCell = [\n x: number,\n text: string,\n span: number,\n styleIndex: number,\n];\n\nexport type WebHostSurfaceRect = [\n x: number,\n y: number,\n width: number,\n height: number,\n];\n\nexport type WebHostSurfaceSize = [\n width: number,\n height: number,\n];\n\nexport type WebHostAccessibilityPoint = [\n x: number,\n y: number,\n];\n\nexport type WebHostAccessibilityLiveRegion = \"off\" | \"polite\" | \"assertive\";\n\nexport interface WebHostAccessibilityNode {\n id: string;\n parentId?: string;\n rect: WebHostSurfaceRect;\n role: string;\n label?: string;\n hint?: string;\n /** omitted when false */\n hidden?: boolean;\n liveRegion?: WebHostAccessibilityLiveRegion;\n cursorAnchor?: WebHostAccessibilityPoint;\n isFocused?: boolean;\n}\n\n/**\n * One hyperlink run within a row: [x, spanWidth, linkTargetIndex]. The index\n * points into the frame's deduplicated `linkTargets` table.\n */\nexport type WebHostSurfaceLinkRun = [\n x: number,\n span: number,\n targetIndex: number,\n];\n\n/** Hyperlink runs for one row: [rowIndex, runs]. */\nexport type WebHostSurfaceLinkRow = [\n row: number,\n runs: WebHostSurfaceLinkRun[],\n];\n\nexport type WebHostFocusSemantics = \"none\" | \"automatic\" | \"activate\" | \"edit\";\n\n/**\n * The settled focus presentation for a committed frame — the same derivation\n * the Android host consumes (`prefersTextInput` gates its IME).\n */\nexport interface WebHostFocusPresentation {\n focusedIdentity?: string;\n semantics: WebHostFocusSemantics;\n prefersTextInput: boolean;\n hasFocusedRegion: boolean;\n}\n\nexport interface WebHostAccessibilityAnnouncement {\n message: string;\n politeness: WebHostAccessibilityLiveRegion;\n}\n\nexport type WebHostSurfaceImageFormat = \"png\" | \"jpeg\" | \"gif\";\n\nexport interface WebHostSurfaceImage {\n id: string;\n format: WebHostSurfaceImageFormat;\n bounds: WebHostSurfaceRect;\n visibleBounds: WebHostSurfaceRect;\n scalingMode: \"stretch\" | \"fit\" | \"fill\";\n pixelSize?: WebHostSurfaceSize;\n dataBase64?: string;\n}\n\nexport type WebHostSurfaceDamageRange = [\n start: number,\n end: number,\n];\n\nexport type WebHostSurfaceDamageTextRow = [\n row: number,\n ranges: WebHostSurfaceDamageRange[],\n];\n\nexport interface WebHostSurfaceDamage {\n textRows: WebHostSurfaceDamageTextRow[];\n requiresFullTextRepaint: boolean;\n requiresFullGraphicsReplay: boolean;\n}\n\n/**\n * Per-region scroll extent published with each frame so the host can implement\n * scroll-chaining: capture the wheel only while the region under the pointer can\n * still scroll in the wheel's direction, otherwise let it fall through to the\n * page. The host recomputes the per-direction headroom from `offset`/`content`/\n * the viewport `rect`, mirroring SwiftTUI's\n * `min(max(0, offset), max(0, content - viewport))` clamp.\n */\nexport interface WebHostScrollRegion {\n /** identity path — same key space as accessibility node ids */\n id: string;\n /** viewport rect in cells: [x, y, width, height] */\n rect: WebHostSurfaceRect;\n /** current clamped scroll offset in cells: [x, y] */\n offset: WebHostAccessibilityPoint;\n /** total content size in cells: [width, height] */\n content: WebHostSurfaceSize;\n}\n\nexport interface WebHostSurfaceFrame {\n version: 1 | 2;\n sequence?: number;\n width: number;\n height: number;\n styles: Array<WebHostSurfaceStyle | null>;\n rows: WebHostSurfaceCell[][];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport type WebHostSurfaceDeltaRow = [\n row: number,\n cells: WebHostSurfaceCell[],\n];\n\nexport interface WebHostSurfaceDeltaFrame {\n version: 3;\n encoding: \"delta\";\n sequence?: number;\n width: number;\n height: number;\n styles: Array<WebHostSurfaceStyle | null>;\n deltaRows: WebHostSurfaceDeltaRow[];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport interface WebHostRuntimeIssue {\n severity: \"warning\" | \"error\";\n code: string;\n message: string;\n description: string;\n identity?: string;\n source?: string;\n}\n\nexport interface WebHostFrameDiagnosticRecord {\n format: \"swift-tui-frame-diagnostics-v1\";\n header: string[];\n fields: string[];\n}\n\nexport type WebHostOutputRecord =\n | { type: \"surface\"; frame: WebHostSurfaceFrame }\n | { type: \"clipboard\"; text: string }\n | { type: \"runtimeIssue\"; issue: WebHostRuntimeIssue }\n | { type: \"frameDiagnostic\"; diagnostic: WebHostFrameDiagnosticRecord }\n | { type: \"text\"; text: string };\n\nexport interface WebHostOutputSink {\n presentSurface(frame: WebHostSurfaceFrame): void;\n writeClipboard?(text: string): void | Promise<void>;\n notifyRuntimeIssue?(issue: WebHostRuntimeIssue): void;\n recordFrameDiagnostic?(diagnostic: WebHostFrameDiagnosticRecord): void;\n writeOutput?(text: string): void;\n writeError?(text: string): void;\n}\n\nexport interface WebHostKeyInput {\n key:\n | \"return\"\n | \"space\"\n | \"tab\"\n | \"arrowLeft\"\n | \"arrowRight\"\n | \"arrowUp\"\n | \"arrowDown\"\n | \"backspace\"\n | \"escape\"\n | \"home\"\n | \"end\"\n | \"character\";\n character?: string;\n modifiers?: number;\n}\n\nexport interface WebHostMouseInput {\n kind: \"down\" | \"up\" | \"moved\" | \"dragged\" | \"scrolled\";\n x: number;\n y: number;\n button?: \"primary\" | \"middle\" | \"secondary\";\n deltaX?: number;\n deltaY?: number;\n modifiers?: number;\n}\n\nconst recordPrefix = \"\\u001E\";\nconst textEncoder = new TextEncoder();\n\n/**\n * The newest `surface` record version this runtime understands. Unknown\n * additive object keys are ignored by design (older runtimes render newer\n * frames), but a frame declaring a NEWER version than this is surfaced as an\n * error-severity runtime issue instead of silently degrading to a text\n * diagnostic — silent version skew is the failure mode this guards against\n * (F57).\n */\nexport const SUPPORTED_SURFACE_VERSION = 3;\n\nexport class WebHostOutputDecoder {\n private readonly textDecoder = new TextDecoder();\n private bufferedText = \"\";\n private lastSurfaceFrame?: WebHostSurfaceFrame;\n\n feed(\n chunk: Uint8Array\n ): WebHostOutputRecord[] {\n this.bufferedText += this.textDecoder.decode(chunk, { stream: true });\n const records: WebHostOutputRecord[] = [];\n\n while (true) {\n const newlineIndex = this.bufferedText.indexOf(\"\\n\");\n if (newlineIndex < 0) {\n break;\n }\n\n const line = this.bufferedText.slice(0, newlineIndex);\n this.bufferedText = this.bufferedText.slice(newlineIndex + 1);\n records.push(this.decodeLine(line));\n }\n\n if (this.bufferedText.length > 4096 && !this.bufferedText.startsWith(recordPrefix)) {\n records.push({ type: \"text\", text: this.bufferedText });\n this.bufferedText = \"\";\n }\n\n return records;\n }\n\n flush(): WebHostOutputRecord[] {\n if (!this.bufferedText) {\n return [];\n }\n const text = this.bufferedText;\n this.bufferedText = \"\";\n return [this.decodeLine(text)];\n }\n\n private decodeLine(\n line: string\n ): WebHostOutputRecord {\n if (line.startsWith(`${recordPrefix}clipboard:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}clipboard:`.length));\n if (isWebHostClipboardRecord(record)) {\n return { type: \"clipboard\", text: record.text };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}runtimeIssue:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}runtimeIssue:`.length));\n if (isWebHostRuntimeIssue(record)) {\n return { type: \"runtimeIssue\", issue: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}frameDiagnostic:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}frameDiagnostic:`.length));\n if (isWebHostFrameDiagnosticRecord(record)) {\n return { type: \"frameDiagnostic\", diagnostic: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (!line.startsWith(`${recordPrefix}surface:`)) {\n return { type: \"text\", text: `${line}\\n` };\n }\n\n try {\n const frame = JSON.parse(line.slice(`${recordPrefix}surface:`.length));\n if (declaresNewerSurfaceVersion(frame)) {\n return {\n type: \"runtimeIssue\",\n issue: {\n severity: \"error\",\n code: \"surface.unsupportedVersion\",\n message: `SwiftTUI surface version ${frame.version} is newer than the supported ${SUPPORTED_SURFACE_VERSION}`,\n description: \"The app emitted a surface record with version \"\n + `${frame.version}, but this @swifttui/web runtime understands `\n + `versions up to ${SUPPORTED_SURFACE_VERSION}. Update @swifttui/web `\n + \"to render it.\",\n },\n };\n }\n if (isWebHostSurfaceFrame(frame)) {\n this.lastSurfaceFrame = frame;\n return { type: \"surface\", frame };\n }\n if (isWebHostSurfaceDeltaFrame(frame)) {\n const materialized = this.materializeDeltaFrame(frame);\n if (materialized) {\n this.lastSurfaceFrame = materialized;\n return { type: \"surface\", frame: materialized };\n }\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n private materializeDeltaFrame(\n frame: WebHostSurfaceDeltaFrame\n ): WebHostSurfaceFrame | undefined {\n const baseline = this.lastSurfaceFrame;\n if (!baseline || baseline.width !== frame.width || baseline.height !== frame.height) {\n return undefined;\n }\n\n const rows = baseline.rows.slice();\n for (const [row, cells] of frame.deltaRows) {\n if (!Number.isSafeInteger(row) || row < 0 || row >= frame.height) {\n return undefined;\n }\n rows[row] = cells;\n }\n\n return {\n version: baseline.version,\n sequence: frame.sequence,\n width: frame.width,\n height: frame.height,\n styles: frame.styles,\n rows,\n images: frame.images,\n damage: frame.damage,\n accessibilityTree: frame.accessibilityTree,\n accessibilityAnnouncements: frame.accessibilityAnnouncements,\n scrollRegions: frame.scrollRegions,\n links: frame.links,\n linkTargets: frame.linkTargets,\n focusPresentation: frame.focusPresentation,\n preferredGridWidth: frame.preferredGridWidth,\n preferredGridHeight: frame.preferredGridHeight,\n };\n }\n}\n\nfunction declaresNewerSurfaceVersion(\n value: unknown\n): value is { version: number } {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const version = (value as { version?: unknown }).version;\n return typeof version === \"number\"\n && Number.isSafeInteger(version)\n && version > SUPPORTED_SURFACE_VERSION;\n}\n\nfunction isWebHostClipboardRecord(\n value: unknown\n): value is { text: string } {\n return !!value && typeof value === \"object\" && typeof (value as { text?: unknown }).text === \"string\";\n}\n\nfunction isWebHostRuntimeIssue(\n value: unknown\n): value is WebHostRuntimeIssue {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostRuntimeIssue>;\n return (record.severity === \"warning\" || record.severity === \"error\")\n && typeof record.code === \"string\"\n && typeof record.message === \"string\"\n && typeof record.description === \"string\"\n && (record.identity === undefined || typeof record.identity === \"string\")\n && (record.source === undefined || typeof record.source === \"string\");\n}\n\nfunction isWebHostFrameDiagnosticRecord(\n value: unknown\n): value is WebHostFrameDiagnosticRecord {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostFrameDiagnosticRecord>;\n return record.format === \"swift-tui-frame-diagnostics-v1\"\n && Array.isArray(record.header)\n && record.header.every((field) => typeof field === \"string\")\n && Array.isArray(record.fields)\n && record.fields.every((field) => typeof field === \"string\");\n}\n\nexport function encodeResizeControlMessage(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n): Uint8Array {\n const normalizedColumns = Math.max(1, Math.round(columns));\n const normalizedRows = Math.max(1, Math.round(rows));\n if (cellWidth && cellHeight) {\n return textEncoder.encode(\n `${recordPrefix}resize:${normalizedColumns}:${normalizedRows}:${Math.max(1, Math.round(cellWidth))}:${Math.max(1, Math.round(cellHeight))}\\n`\n );\n }\n\n return textEncoder.encode(`${recordPrefix}resize:${normalizedColumns}:${normalizedRows}\\n`);\n}\n\nexport function encodeRenderStyleControlMessage(\n style: WebHostTerminalStyle\n): Uint8Array {\n const encoded = encodeWebHostTerminalRenderStyleBase64(style);\n return textEncoder.encode(`${recordPrefix}style:${encoded}\\n`);\n}\n\nexport function encodeCapabilitiesControlMessage(): Uint8Array {\n // The client's wire-capability declaration, sent once after the socket\n // opens. The declaration is truthful today: this decoder materializes v3\n // delta frames (`materializeDeltaFrame`). Byte shape and key order are\n // pinned by the cross-repo fixture Fixtures/Transport/web-caps-record.txt\n // — swift-tui's input parser consumes the identical bytes, and the\n // coordination root's transport_fixture_sync gate keeps the copies in\n // lockstep. Servers that predate the record drop it silently, so the\n // session degrades to today's full-frame defaults.\n //\n // Capabilities are named feature bits. The retired `maxWebSurfaceVersion`\n // key declared a decoder version ceiling, which was only ever read as\n // \"accepts delta or not\" and duplicated — more weakly — the version check\n // this decoder already performs on every record (SUPPORTED_SURFACE_VERSION).\n // Servers still expecting it skip unknown keys, so dropping it is safe in\n // both directions.\n return textEncoder.encode(\n `${recordPrefix}caps:{\"acceptsDeltaFrames\":true}\\n`\n );\n}\n\nexport function encodeKeyInputMessage(\n input: WebHostKeyInput\n): Uint8Array {\n const modifiers = Math.max(0, Math.round(input.modifiers ?? 0));\n if (input.key === \"character\") {\n return textEncoder.encode(\n `${recordPrefix}key:character:${encodeURIComponent(input.character ?? \"\")}:${modifiers}\\n`\n );\n }\n return textEncoder.encode(`${recordPrefix}key:${input.key}:${modifiers}\\n`);\n}\n\nexport function encodePasteInputMessage(\n text: string\n): Uint8Array {\n return textEncoder.encode(`${recordPrefix}paste:${encodeURIComponent(text)}\\n`);\n}\n\nexport function encodeMouseInputMessage(\n input: WebHostMouseInput\n): Uint8Array {\n return textEncoder.encode(\n recordPrefix + [\n \"mouse\",\n input.kind,\n formatCellCoordinate(input.x),\n formatCellCoordinate(input.y),\n input.button ?? \"none\",\n Math.round(input.deltaX ?? 0),\n Math.round(input.deltaY ?? 0),\n Math.max(0, Math.round(input.modifiers ?? 0)),\n ].join(\":\") + \"\\n\"\n );\n}\n\nfunction formatCellCoordinate(\n value: number\n): string {\n return Number.isFinite(value) ? String(value) : \"0\";\n}\n\nfunction isWebHostSurfaceFrame(\n value: unknown\n): value is WebHostSurfaceFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceFrame>;\n return (frame.version === 1 || frame.version === 2)\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.rows)\n && frame.rows.every(isWebHostSurfaceRow)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\nfunction isWebHostSurfaceDeltaFrame(\n value: unknown\n): value is WebHostSurfaceDeltaFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceDeltaFrame>;\n return frame.version === 3\n && frame.encoding === \"delta\"\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.deltaRows)\n && frame.deltaRows.every(isWebHostSurfaceDeltaRow)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\n/**\n * The F19 additive fields shared by the full and delta record shapes. Absent\n * means \"feature not present\" — servers older than the field omit it.\n */\nfunction hasValidAdditiveFrameFields(\n frame: Partial<WebHostSurfaceFrame> | Partial<WebHostSurfaceDeltaFrame>\n): boolean {\n return (frame.links === undefined || isWebHostSurfaceLinks(frame.links))\n && (frame.linkTargets === undefined || isWebHostSurfaceLinkTargets(frame.linkTargets))\n && (\n frame.focusPresentation === undefined\n || isWebHostFocusPresentation(frame.focusPresentation)\n )\n && (\n frame.preferredGridWidth === undefined\n || (Number.isSafeInteger(frame.preferredGridWidth) && frame.preferredGridWidth >= 0)\n )\n && (\n frame.preferredGridHeight === undefined\n || (Number.isSafeInteger(frame.preferredGridHeight) && frame.preferredGridHeight >= 0)\n );\n}\n\nfunction isWebHostSurfaceLinks(\n value: unknown\n): value is WebHostSurfaceLinkRow[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceLinkRow);\n}\n\nfunction isWebHostSurfaceLinkRow(\n value: unknown\n): value is WebHostSurfaceLinkRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceLinkRun);\n}\n\nfunction isWebHostSurfaceLinkRun(\n value: unknown\n): value is WebHostSurfaceLinkRun {\n if (!Array.isArray(value) || value.length !== 3) {\n return false;\n }\n const [x, span, targetIndex] = value as number[];\n return Number.isSafeInteger(x)\n && x >= 0\n && Number.isSafeInteger(span)\n && span >= 1\n && Number.isSafeInteger(targetIndex)\n && targetIndex >= 0;\n}\n\nfunction isWebHostSurfaceLinkTargets(\n value: unknown\n): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isWebHostFocusPresentation(\n value: unknown\n): value is WebHostFocusPresentation {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const presentation = value as Partial<WebHostFocusPresentation>;\n return (\n presentation.focusedIdentity === undefined\n || typeof presentation.focusedIdentity === \"string\"\n )\n && (\n presentation.semantics === \"none\"\n || presentation.semantics === \"automatic\"\n || presentation.semantics === \"activate\"\n || presentation.semantics === \"edit\"\n )\n && typeof presentation.prefersTextInput === \"boolean\"\n && typeof presentation.hasFocusedRegion === \"boolean\";\n}\n\nfunction isWebHostSurfaceDeltaRow(\n value: unknown\n): value is WebHostSurfaceDeltaRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && isWebHostSurfaceRow(value[1]);\n}\n\nfunction isWebHostSurfaceRow(\n value: unknown\n): value is WebHostSurfaceCell[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceCell);\n}\n\nfunction isWebHostSurfaceCell(\n value: unknown\n): value is WebHostSurfaceCell {\n return Array.isArray(value)\n && value.length === 4\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && typeof value[1] === \"string\"\n && Number.isSafeInteger(value[2])\n && value[2] >= 1\n && Number.isSafeInteger(value[3])\n && value[3] >= 0;\n}\n\nfunction isWebHostAccessibilityNodes(\n value: unknown\n): value is WebHostAccessibilityNode[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityNode);\n}\n\nfunction isWebHostAccessibilityNode(\n value: unknown\n): value is WebHostAccessibilityNode {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const node = value as Partial<WebHostAccessibilityNode>;\n return typeof node.id === \"string\"\n && (node.parentId === undefined || typeof node.parentId === \"string\")\n && isWebHostSurfaceRect(node.rect)\n && typeof node.role === \"string\"\n && (node.label === undefined || typeof node.label === \"string\")\n && (node.hint === undefined || typeof node.hint === \"string\")\n && (node.hidden === undefined || typeof node.hidden === \"boolean\")\n && (\n node.liveRegion === undefined\n || node.liveRegion === \"off\"\n || node.liveRegion === \"polite\"\n || node.liveRegion === \"assertive\"\n )\n && (node.cursorAnchor === undefined || isWebHostAccessibilityPoint(node.cursorAnchor))\n && (node.isFocused === undefined || typeof node.isFocused === \"boolean\");\n}\n\nfunction isWebHostAccessibilityPoint(\n value: unknown\n): value is WebHostAccessibilityPoint {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostAccessibilityAnnouncements(\n value: unknown\n): value is WebHostAccessibilityAnnouncement[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityAnnouncement);\n}\n\nfunction isWebHostAccessibilityAnnouncement(\n value: unknown\n): value is WebHostAccessibilityAnnouncement {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const announcement = value as Partial<WebHostAccessibilityAnnouncement>;\n return typeof announcement.message === \"string\"\n && (\n announcement.politeness === \"off\"\n || announcement.politeness === \"polite\"\n || announcement.politeness === \"assertive\"\n );\n}\n\nfunction isWebHostSurfaceImages(\n value: unknown\n): value is WebHostSurfaceImage[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceImage);\n}\n\nfunction isWebHostSurfaceImage(\n value: unknown\n): value is WebHostSurfaceImage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const image = value as Partial<WebHostSurfaceImage>;\n return typeof image.id === \"string\"\n && isWebHostSurfaceImageFormat(image.format)\n && isWebHostSurfaceRect(image.bounds)\n && isWebHostSurfaceRect(image.visibleBounds)\n && isWebHostSurfaceScalingMode(image.scalingMode)\n && (image.pixelSize === undefined || isWebHostSurfaceSize(image.pixelSize))\n && (image.dataBase64 === undefined || typeof image.dataBase64 === \"string\");\n}\n\nfunction isWebHostSurfaceDamage(\n value: unknown\n): value is WebHostSurfaceDamage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const damage = value as Partial<WebHostSurfaceDamage>;\n return Array.isArray(damage.textRows)\n && damage.textRows.every(isWebHostSurfaceDamageTextRow)\n && typeof damage.requiresFullTextRepaint === \"boolean\"\n && typeof damage.requiresFullGraphicsReplay === \"boolean\";\n}\n\nfunction isWebHostSurfaceDamageTextRow(\n value: unknown\n): value is WebHostSurfaceDamageTextRow {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceDamageRange);\n}\n\nfunction isWebHostSurfaceDamageRange(\n value: unknown\n): value is WebHostSurfaceDamageRange {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && typeof value[1] === \"number\";\n}\n\nfunction isWebHostSurfaceImageFormat(\n value: unknown\n): value is WebHostSurfaceImageFormat {\n return value === \"png\" || value === \"jpeg\" || value === \"gif\";\n}\n\nfunction isWebHostScrollRegions(\n value: unknown\n): value is WebHostScrollRegion[] {\n return Array.isArray(value) && value.every(isWebHostScrollRegion);\n}\n\nfunction isWebHostScrollRegion(\n value: unknown\n): value is WebHostScrollRegion {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const region = value as Partial<WebHostScrollRegion>;\n return typeof region.id === \"string\"\n && isWebHostSurfaceRect(region.rect)\n && isWebHostSurfaceSize(region.offset)\n && isWebHostSurfaceSize(region.content);\n}\n\nfunction isWebHostSurfaceRect(\n value: unknown\n): value is WebHostSurfaceRect {\n return Array.isArray(value)\n && value.length === 4\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceSize(\n value: unknown\n): value is WebHostSurfaceSize {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceScalingMode(\n value: unknown\n): value is WebHostSurfaceImage[\"scalingMode\"] {\n return value === \"stretch\" || value === \"fit\" || value === \"fill\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WebHostSurfaceTransport.js","names":[],"sources":["../../src/WebHostSurfaceTransport.ts"],"sourcesContent":["/**\n * Browser consumer for SwiftTUI's host wire. The normative cross-host\n * contract lives upstream:\n * https://github.com/SwiftTUI/swift-tui/blob/main/docs/HOST-WIRE-CONTRACT.md\n */\nimport {\n encodeWebHostTerminalRenderStyleBase64,\n type WebHostTerminalStyle,\n} from \"./WebHostTerminalStyle.ts\";\n\nexport interface WebHostSurfaceStyle {\n fg?: string;\n bg?: string;\n em?: number;\n underline?: WebHostSurfaceLineStyle;\n strikethrough?: WebHostSurfaceLineStyle;\n opacity?: number;\n}\n\nexport interface WebHostSurfaceLineStyle {\n pattern: \"solid\" | \"dot\" | \"dash\" | \"dashDot\" | \"dashDotDot\" | \"double\" | \"curly\";\n color?: string;\n}\n\nexport type WebHostSurfaceCell = [\n x: number,\n text: string,\n span: number,\n styleIndex: number,\n];\n\nexport type WebHostSurfaceRect = [\n x: number,\n y: number,\n width: number,\n height: number,\n];\n\nexport type WebHostSurfaceSize = [\n width: number,\n height: number,\n];\n\nexport type WebHostAccessibilityPoint = [\n x: number,\n y: number,\n];\n\nexport type WebHostAccessibilityLiveRegion = string;\n\nexport interface WebHostAccessibilityNode {\n id: string;\n parentId?: string;\n rect: WebHostSurfaceRect;\n role: string;\n label?: string;\n hint?: string;\n /** omitted when false */\n hidden?: boolean;\n liveRegion?: WebHostAccessibilityLiveRegion;\n cursorAnchor?: WebHostAccessibilityPoint;\n isFocused?: boolean;\n}\n\n/**\n * One hyperlink run within a row: [x, spanWidth, linkTargetIndex]. The index\n * points into the frame's deduplicated `linkTargets` table.\n */\nexport type WebHostSurfaceLinkRun = [\n x: number,\n span: number,\n targetIndex: number,\n];\n\n/** Hyperlink runs for one row: [rowIndex, runs]. */\nexport type WebHostSurfaceLinkRow = [\n row: number,\n runs: WebHostSurfaceLinkRun[],\n];\n\nexport type WebHostFocusSemantics = string;\n\n/**\n * The settled focus presentation for a committed frame — the same derivation\n * the Android host consumes (`prefersTextInput` gates its IME).\n */\nexport interface WebHostFocusPresentation {\n focusedIdentity?: string;\n semantics: WebHostFocusSemantics;\n prefersTextInput: boolean;\n hasFocusedRegion: boolean;\n}\n\nexport interface WebHostAccessibilityAnnouncement {\n message: string;\n politeness: WebHostAccessibilityLiveRegion;\n}\n\nexport type WebHostSurfaceImageFormat = string;\n\n/**\n * Reports locally unresolved image IDs to a recovery-capable host. The first\n * branch lets bounded hosts return the admitted subset; the explicit legacy\n * void branch preserves contextual typing for concise callbacks that return\n * incidental values such as `array.push(...)`.\n */\nexport type WebHostImagePayloadRequestHandler =\n | ((ids: readonly string[]) => readonly string[])\n | ((ids: readonly string[]) => void);\n\nexport interface WebHostSurfaceImage {\n id: string;\n format: WebHostSurfaceImageFormat;\n bounds: WebHostSurfaceRect;\n visibleBounds: WebHostSurfaceRect;\n scalingMode: string;\n pixelSize?: WebHostSurfaceSize;\n dataBase64?: string;\n}\n\nexport type WebHostSurfaceDamageRange = [\n start: number,\n end: number,\n];\n\nexport type WebHostSurfaceDamageTextRow = [\n row: number,\n ranges: WebHostSurfaceDamageRange[],\n];\n\nexport interface WebHostSurfaceDamage {\n textRows: WebHostSurfaceDamageTextRow[];\n requiresFullTextRepaint: boolean;\n requiresFullGraphicsReplay: boolean;\n}\n\n/**\n * Per-region scroll extent published with each frame so the host can implement\n * scroll-chaining: capture the wheel only while the region under the pointer can\n * still scroll in the wheel's direction, otherwise let it fall through to the\n * page. The host recomputes the per-direction headroom from `offset`/`content`/\n * the viewport `rect`, mirroring SwiftTUI's\n * `min(max(0, offset), max(0, content - viewport))` clamp.\n */\nexport interface WebHostScrollRegion {\n /** identity path — same key space as accessibility node ids */\n id: string;\n /** viewport rect in cells: [x, y, width, height] */\n rect: WebHostSurfaceRect;\n /** current clamped scroll offset in cells: [x, y] */\n offset: WebHostAccessibilityPoint;\n /** total content size in cells: [width, height] */\n content: WebHostSurfaceSize;\n}\n\nexport interface WebHostSurfaceFrame {\n version: 1 | 2;\n epoch?: number;\n gen?: number;\n sequence?: number;\n width: number;\n height: number;\n styles: Array<WebHostSurfaceStyle | null>;\n rows: WebHostSurfaceCell[][];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport type WebHostSurfaceDeltaRow = [\n row: number,\n cells: WebHostSurfaceCell[],\n];\n\nexport interface WebHostSurfaceDeltaFrame {\n version: 3;\n encoding: \"delta\";\n epoch?: number;\n gen?: number;\n baselineGen?: number;\n sequence?: number;\n width: number;\n height: number;\n styles: Array<WebHostSurfaceStyle | null>;\n deltaRows: WebHostSurfaceDeltaRow[];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport interface WebHostRuntimeIssue {\n severity: \"warning\" | \"error\";\n code: string;\n message: string;\n description: string;\n identity?: string;\n source?: string;\n}\n\nexport interface WebHostFrameDiagnosticRecord {\n format: \"swift-tui-frame-diagnostics-v1\";\n header: string[];\n fields: string[];\n}\n\nexport type WebHostOutputRecord =\n | { type: \"surface\"; frame: WebHostSurfaceFrame }\n | { type: \"clipboard\"; text: string }\n | { type: \"runtimeIssue\"; issue: WebHostRuntimeIssue }\n | { type: \"frameDiagnostic\"; diagnostic: WebHostFrameDiagnosticRecord }\n | { type: \"surfaceDropped\"; reason: \"noBaseline\" | \"staleBaseline\" }\n | { type: \"text\"; text: string };\n\nexport type WebHostResyncRequest =\n | { scope: \"keyframe\" }\n | { scope: \"images\"; ids?: string[] };\n\nexport interface WebHostOutputSink {\n presentSurface(\n frame: WebHostSurfaceFrame,\n recoveredImagePayloadIds?: readonly string[]\n ): void;\n writeClipboard?(text: string): void | Promise<void>;\n notifyRuntimeIssue?(issue: WebHostRuntimeIssue): void;\n recordFrameDiagnostic?(diagnostic: WebHostFrameDiagnosticRecord): void;\n writeOutput?(text: string): void;\n writeError?(text: string): void;\n}\n\nexport interface WebHostKeyInput {\n key:\n | \"return\"\n | \"space\"\n | \"tab\"\n | \"arrowLeft\"\n | \"arrowRight\"\n | \"arrowUp\"\n | \"arrowDown\"\n | \"backspace\"\n | \"escape\"\n | \"home\"\n | \"end\"\n | \"character\";\n character?: string;\n modifiers?: number;\n}\n\nexport interface WebHostMouseInput {\n kind: \"down\" | \"up\" | \"moved\" | \"dragged\" | \"scrolled\";\n x: number;\n y: number;\n button?: \"primary\" | \"middle\" | \"secondary\";\n deltaX?: number;\n deltaY?: number;\n modifiers?: number;\n}\n\nconst recordPrefix = \"\\u001E\";\nconst textEncoder = new TextEncoder();\n\n/** Limits wire-derived recovery state and keeps every single-ID WASI request bounded. */\nexport const MAX_IMAGE_RECOVERY_ID_BYTES = 1_024;\nexport const MAX_OUTSTANDING_IMAGE_RECOVERY_IDS = 1_024;\n\nexport function isWebHostImageRecoveryId(\n id: string\n): boolean {\n return id.length > 0\n && id.length <= MAX_IMAGE_RECOVERY_ID_BYTES\n && textEncoder.encode(id).byteLength <= MAX_IMAGE_RECOVERY_ID_BYTES;\n}\n\n/**\n * The newest `surface` record version this runtime understands. Unknown\n * additive object keys are ignored by design (older runtimes render newer\n * frames), but a frame declaring a NEWER version than this is surfaced as an\n * error-severity runtime issue instead of silently degrading to a text\n * diagnostic — silent version skew is the failure mode this guards against\n * (F57).\n */\nexport const SUPPORTED_SURFACE_VERSION = 3;\n\nexport class WebHostOutputDecoder {\n private readonly textDecoder = new TextDecoder();\n private bufferedText = \"\";\n private lastSurfaceFrame?: WebHostSurfaceFrame;\n private lastEpoch?: number;\n private lastGen?: number;\n private lastPresentedEpoch?: number;\n private keyframeResyncOutstanding = false;\n private keyframeResyncPending = false;\n private readonly imageResyncOutstandingIds = new Set<string>();\n private readonly imageResyncPendingIds = new Set<string>();\n\n feed(\n chunk: Uint8Array\n ): WebHostOutputRecord[] {\n this.bufferedText += this.textDecoder.decode(chunk, { stream: true });\n const records: WebHostOutputRecord[] = [];\n\n while (true) {\n const newlineIndex = this.bufferedText.indexOf(\"\\n\");\n if (newlineIndex < 0) {\n break;\n }\n\n const line = this.bufferedText.slice(0, newlineIndex);\n this.bufferedText = this.bufferedText.slice(newlineIndex + 1);\n records.push(this.decodeLine(line));\n }\n\n if (this.bufferedText.length > 4096 && !this.bufferedText.startsWith(recordPrefix)) {\n records.push({ type: \"text\", text: this.bufferedText });\n this.bufferedText = \"\";\n }\n\n return records;\n }\n\n flush(): WebHostOutputRecord[] {\n if (!this.bufferedText) {\n return [];\n }\n const text = this.bufferedText;\n this.bufferedText = \"\";\n return [this.decodeLine(text)];\n }\n\n takeResyncRequest(\n maximumEncodedBytes?: number\n ): WebHostResyncRequest | undefined {\n if (this.keyframeResyncPending) {\n this.keyframeResyncPending = false;\n return { scope: \"keyframe\" };\n }\n if (this.imageResyncPendingIds.size === 0) {\n return undefined;\n }\n\n const sortedIds = [...this.imageResyncPendingIds].sort();\n const { ids, rejectedIds } = boundedImageResyncIds(\n sortedIds,\n maximumEncodedBytes\n );\n for (const id of rejectedIds) {\n this.imageResyncPendingIds.delete(id);\n this.imageResyncOutstandingIds.delete(id);\n }\n for (const id of ids) {\n this.imageResyncPendingIds.delete(id);\n }\n if (ids.length === 0) {\n return undefined;\n }\n return { scope: \"images\", ids };\n }\n\n /**\n * Adds locally unresolved image IDs to this epoch's recovery set. IDs remain\n * outstanding after delivery, so repeat painter misses cannot create storms;\n * a payload-bearing record or epoch re-anchor releases them. Returns the IDs\n * now tracked (new or already outstanding); callers should suppress repeats\n * only for this admitted subset.\n */\n requestImagePayloads(\n ids: Iterable<string>\n ): readonly string[] {\n const acceptedIds = new Set<string>();\n for (const id of ids) {\n if (!isWebHostImageRecoveryId(id)) {\n continue;\n }\n if (this.imageResyncOutstandingIds.has(id)) {\n acceptedIds.add(id);\n continue;\n }\n if (\n this.imageResyncOutstandingIds.size\n >= MAX_OUTSTANDING_IMAGE_RECOVERY_IDS\n ) {\n continue;\n }\n this.imageResyncOutstandingIds.add(id);\n this.imageResyncPendingIds.add(id);\n acceptedIds.add(id);\n }\n return [...acceptedIds];\n }\n\n /**\n * Advances image recovery immediately before one decoded surface reaches its\n * presenter. Payload arrival and epoch reset therefore follow delivery order\n * rather than `feed`'s parse-ahead order across a multi-record chunk. The\n * return value identifies payloads that answer an outstanding image request,\n * so presenters can open exactly one fresh local decode generation even when\n * content-addressed retransmission uses identical bytes.\n */\n prepareToPresentSurface(\n frame: WebHostSurfaceFrame\n ): readonly string[] {\n const recoveredImagePayloadIds = this.recoveredImagePayloadIds(frame.images);\n this.resetImageResyncForEpoch(frame.epoch);\n this.sweepImageResyncForPresentedImages(frame.images);\n this.clearArrivedImagePayloads(frame.images);\n if (frame.epoch !== undefined) {\n this.lastPresentedEpoch = frame.epoch;\n }\n return recoveredImagePayloadIds;\n }\n\n resyncRequestDeliveryFailed(\n request: WebHostResyncRequest\n ): void {\n if (request.scope === \"keyframe\") {\n if (this.keyframeResyncOutstanding) {\n this.keyframeResyncPending = true;\n }\n return;\n }\n\n for (const id of request.ids ?? []) {\n if (this.imageResyncOutstandingIds.has(id)) {\n this.imageResyncPendingIds.add(id);\n }\n }\n }\n\n private decodeLine(\n line: string\n ): WebHostOutputRecord {\n if (line.startsWith(`${recordPrefix}clipboard:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}clipboard:`.length));\n if (isWebHostClipboardRecord(record)) {\n return { type: \"clipboard\", text: record.text };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}runtimeIssue:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}runtimeIssue:`.length));\n if (isWebHostRuntimeIssue(record)) {\n return { type: \"runtimeIssue\", issue: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}frameDiagnostic:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}frameDiagnostic:`.length));\n if (isWebHostFrameDiagnosticRecord(record)) {\n return { type: \"frameDiagnostic\", diagnostic: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (!line.startsWith(`${recordPrefix}surface:`)) {\n return { type: \"text\", text: `${line}\\n` };\n }\n\n try {\n const frame = JSON.parse(line.slice(`${recordPrefix}surface:`.length));\n if (declaresNewerSurfaceVersion(frame)) {\n return {\n type: \"runtimeIssue\",\n issue: {\n severity: \"error\",\n code: \"surface.unsupportedVersion\",\n message: `SwiftTUI surface version ${frame.version} is newer than the supported ${SUPPORTED_SURFACE_VERSION}`,\n description: \"The app emitted a surface record with version \"\n + `${frame.version}, but this @swifttui/web runtime understands `\n + `versions up to ${SUPPORTED_SURFACE_VERSION}. Update @swifttui/web `\n + \"to render it.\",\n },\n };\n }\n if (isWebHostSurfaceFrame(frame)) {\n this.lastSurfaceFrame = frame;\n this.lastEpoch = frame.epoch;\n this.lastGen = frame.gen;\n this.keyframeResyncOutstanding = false;\n this.keyframeResyncPending = false;\n return { type: \"surface\", frame };\n }\n if (isWebHostSurfaceDeltaFrame(frame)) {\n const carriesDeliveryStamps = frame.epoch !== undefined\n || frame.gen !== undefined\n || frame.baselineGen !== undefined;\n if (\n !this.lastSurfaceFrame\n || this.lastSurfaceFrame.width !== frame.width\n || this.lastSurfaceFrame.height !== frame.height\n ) {\n if (carriesDeliveryStamps) {\n this.requestKeyframeResync();\n }\n return { type: \"surfaceDropped\", reason: \"noBaseline\" };\n }\n if (\n carriesDeliveryStamps\n && (\n frame.epoch === undefined\n || frame.gen === undefined\n || frame.baselineGen === undefined\n || frame.epoch !== this.lastEpoch\n || frame.baselineGen !== this.lastGen\n )\n ) {\n this.requestKeyframeResync();\n return { type: \"surfaceDropped\", reason: \"staleBaseline\" };\n }\n const materialized = this.materializeDeltaFrame(frame);\n if (materialized) {\n this.lastSurfaceFrame = materialized;\n this.lastEpoch = frame.epoch;\n this.lastGen = frame.gen;\n return { type: \"surface\", frame: materialized };\n }\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n private requestKeyframeResync(): void {\n if (this.keyframeResyncOutstanding) {\n return;\n }\n this.keyframeResyncOutstanding = true;\n this.keyframeResyncPending = true;\n }\n\n private resetImageResyncForEpoch(\n epoch: number | undefined\n ): void {\n if (epoch === undefined || epoch === this.lastPresentedEpoch) {\n return;\n }\n this.imageResyncOutstandingIds.clear();\n this.imageResyncPendingIds.clear();\n }\n\n private clearArrivedImagePayloads(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n for (const image of images ?? []) {\n if (image.dataBase64 === undefined) {\n continue;\n }\n this.imageResyncOutstandingIds.delete(image.id);\n this.imageResyncPendingIds.delete(image.id);\n }\n }\n\n private recoveredImagePayloadIds(\n images: WebHostSurfaceImage[] | undefined\n ): string[] {\n const recoveredIds = new Set<string>();\n for (const image of images ?? []) {\n if (\n image.dataBase64 !== undefined\n && this.imageResyncOutstandingIds.has(image.id)\n ) {\n recoveredIds.add(image.id);\n }\n }\n return [...recoveredIds].sort();\n }\n\n private sweepImageResyncForPresentedImages(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n const presentedIds = new Set<string>();\n for (const image of images ?? []) {\n if (this.imageResyncOutstandingIds.has(image.id)) {\n presentedIds.add(image.id);\n }\n }\n for (const id of this.imageResyncOutstandingIds) {\n if (presentedIds.has(id)) {\n continue;\n }\n this.imageResyncOutstandingIds.delete(id);\n this.imageResyncPendingIds.delete(id);\n }\n }\n\n private materializeDeltaFrame(\n frame: WebHostSurfaceDeltaFrame\n ): WebHostSurfaceFrame | undefined {\n const baseline = this.lastSurfaceFrame;\n if (!baseline) {\n return undefined;\n }\n\n const rows = baseline.rows.slice();\n for (const [row, cells] of frame.deltaRows) {\n if (!Number.isSafeInteger(row) || row < 0 || row >= frame.height) {\n return undefined;\n }\n rows[row] = cells;\n }\n\n return {\n version: baseline.version,\n epoch: frame.epoch,\n gen: frame.gen,\n sequence: frame.sequence,\n width: frame.width,\n height: frame.height,\n styles: frame.styles,\n rows,\n images: frame.images,\n damage: frame.damage,\n accessibilityTree: frame.accessibilityTree,\n accessibilityAnnouncements: frame.accessibilityAnnouncements,\n scrollRegions: frame.scrollRegions,\n links: frame.links,\n linkTargets: frame.linkTargets,\n focusPresentation: frame.focusPresentation,\n preferredGridWidth: frame.preferredGridWidth,\n preferredGridHeight: frame.preferredGridHeight,\n };\n }\n}\n\nfunction boundedImageResyncIds(\n sortedIds: string[],\n maximumEncodedBytes: number | undefined\n): {\n ids: string[];\n rejectedIds: string[];\n} {\n if (\n maximumEncodedBytes === undefined\n || !Number.isFinite(maximumEncodedBytes)\n ) {\n return { ids: sortedIds, rejectedIds: [] };\n }\n\n const emptyRequestBytes = encodeResyncControlMessage({\n scope: \"images\",\n ids: [],\n }).byteLength;\n const normalizedMaximum = Math.max(0, Math.floor(maximumEncodedBytes));\n const ids: string[] = [];\n const rejectedIds: string[] = [];\n let encodedBytes = emptyRequestBytes;\n for (const id of sortedIds) {\n const separatorBytes = ids.length === 0 ? 0 : 1;\n const idBytes = textEncoder.encode(JSON.stringify(id)).byteLength;\n if (encodedBytes + separatorBytes + idBytes > normalizedMaximum) {\n if (ids.length === 0) {\n rejectedIds.push(id);\n continue;\n }\n break;\n }\n ids.push(id);\n encodedBytes += separatorBytes + idBytes;\n if (encodedBytes >= normalizedMaximum) {\n break;\n }\n }\n return { ids, rejectedIds };\n}\n\nfunction declaresNewerSurfaceVersion(\n value: unknown\n): value is { version: number } {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const version = (value as { version?: unknown }).version;\n return typeof version === \"number\"\n && Number.isSafeInteger(version)\n && version > SUPPORTED_SURFACE_VERSION;\n}\n\nfunction isWebHostClipboardRecord(\n value: unknown\n): value is { text: string } {\n return !!value && typeof value === \"object\" && typeof (value as { text?: unknown }).text === \"string\";\n}\n\nfunction isWebHostRuntimeIssue(\n value: unknown\n): value is WebHostRuntimeIssue {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostRuntimeIssue>;\n return (record.severity === \"warning\" || record.severity === \"error\")\n && typeof record.code === \"string\"\n && typeof record.message === \"string\"\n && typeof record.description === \"string\"\n && (record.identity === undefined || typeof record.identity === \"string\")\n && (record.source === undefined || typeof record.source === \"string\");\n}\n\nfunction isWebHostFrameDiagnosticRecord(\n value: unknown\n): value is WebHostFrameDiagnosticRecord {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostFrameDiagnosticRecord>;\n return record.format === \"swift-tui-frame-diagnostics-v1\"\n && Array.isArray(record.header)\n && record.header.every((field) => typeof field === \"string\")\n && Array.isArray(record.fields)\n && record.fields.every((field) => typeof field === \"string\");\n}\n\nexport function encodeResizeControlMessage(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n): Uint8Array {\n const normalizedColumns = Math.max(1, Math.round(columns));\n const normalizedRows = Math.max(1, Math.round(rows));\n if (cellWidth && cellHeight) {\n return textEncoder.encode(\n `${recordPrefix}resize:${normalizedColumns}:${normalizedRows}:${Math.max(1, Math.round(cellWidth))}:${Math.max(1, Math.round(cellHeight))}\\n`\n );\n }\n\n return textEncoder.encode(`${recordPrefix}resize:${normalizedColumns}:${normalizedRows}\\n`);\n}\n\nexport function encodeRenderStyleControlMessage(\n style: WebHostTerminalStyle\n): Uint8Array {\n const encoded = encodeWebHostTerminalRenderStyleBase64(style);\n return textEncoder.encode(`${recordPrefix}style:${encoded}\\n`);\n}\n\nexport function encodeCapabilitiesControlMessage(): Uint8Array {\n // The client's wire-capability declaration, sent once after the socket\n // opens. The declaration is truthful today: this decoder materializes v3\n // delta frames (`materializeDeltaFrame`). Byte shape and key order are\n // pinned by the cross-repo fixture Fixtures/Transport/web-caps-record.txt\n // — swift-tui's input parser consumes the identical bytes, and the\n // coordination root's transport_fixture_sync gate keeps the copies in\n // lockstep. Servers that predate the record drop it silently, so the\n // session degrades to today's full-frame defaults.\n //\n // Capabilities are named feature bits. The retired `maxWebSurfaceVersion`\n // key declared a decoder version ceiling, which was only ever read as\n // \"accepts delta or not\" and duplicated — more weakly — the version check\n // this decoder already performs on every record (SUPPORTED_SURFACE_VERSION).\n // Servers still expecting it skip unknown keys, so dropping it is safe in\n // both directions.\n return textEncoder.encode(\n `${recordPrefix}caps:{\"acceptsDeltaFrames\":true}\\n`\n );\n}\n\nexport function encodeResyncControlMessage(\n request: WebHostResyncRequest\n): Uint8Array {\n const payload = request.scope === \"keyframe\"\n ? { scope: \"keyframe\" }\n : {\n scope: \"images\",\n ...(request.ids === undefined ? {} : { ids: request.ids }),\n };\n return textEncoder.encode(\n `${recordPrefix}resync:${JSON.stringify(payload)}\\n`\n );\n}\n\nexport function encodeKeyInputMessage(\n input: WebHostKeyInput\n): Uint8Array {\n const modifiers = Math.max(0, Math.round(input.modifiers ?? 0));\n if (input.key === \"character\") {\n return textEncoder.encode(\n `${recordPrefix}key:character:${encodeURIComponent(input.character ?? \"\")}:${modifiers}\\n`\n );\n }\n return textEncoder.encode(`${recordPrefix}key:${input.key}:${modifiers}\\n`);\n}\n\nexport function encodePasteInputMessage(\n text: string\n): Uint8Array {\n return textEncoder.encode(`${recordPrefix}paste:${encodeURIComponent(text)}\\n`);\n}\n\nexport function encodeMouseInputMessage(\n input: WebHostMouseInput\n): Uint8Array {\n return textEncoder.encode(\n recordPrefix + [\n \"mouse\",\n input.kind,\n formatCellCoordinate(input.x),\n formatCellCoordinate(input.y),\n input.button ?? \"none\",\n Math.round(input.deltaX ?? 0),\n Math.round(input.deltaY ?? 0),\n Math.max(0, Math.round(input.modifiers ?? 0)),\n ].join(\":\") + \"\\n\"\n );\n}\n\nfunction formatCellCoordinate(\n value: number\n): string {\n return Number.isFinite(value) ? String(value) : \"0\";\n}\n\nfunction isWebHostSurfaceFrame(\n value: unknown\n): value is WebHostSurfaceFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceFrame>;\n return (frame.version === 1 || frame.version === 2)\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.rows)\n && frame.rows.every(isWebHostSurfaceRow)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\nfunction isWebHostSurfaceDeltaFrame(\n value: unknown\n): value is WebHostSurfaceDeltaFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceDeltaFrame>;\n return frame.version === 3\n && frame.encoding === \"delta\"\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.deltaRows)\n && frame.deltaRows.every(isWebHostSurfaceDeltaRow)\n && isOptionalSafeInteger(frame.baselineGen)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\n/**\n * The F19 additive fields shared by the full and delta record shapes. Absent\n * means \"feature not present\" — servers older than the field omit it.\n */\nfunction hasValidAdditiveFrameFields(\n frame: Partial<WebHostSurfaceFrame> | Partial<WebHostSurfaceDeltaFrame>\n): boolean {\n return isOptionalSafeInteger(frame.epoch)\n && isOptionalSafeInteger(frame.gen)\n && (frame.links === undefined || isWebHostSurfaceLinks(frame.links))\n && (frame.linkTargets === undefined || isWebHostSurfaceLinkTargets(frame.linkTargets))\n && (\n frame.focusPresentation === undefined\n || isWebHostFocusPresentation(frame.focusPresentation)\n )\n && (\n frame.preferredGridWidth === undefined\n || (Number.isSafeInteger(frame.preferredGridWidth) && frame.preferredGridWidth >= 0)\n )\n && (\n frame.preferredGridHeight === undefined\n || (Number.isSafeInteger(frame.preferredGridHeight) && frame.preferredGridHeight >= 0)\n );\n}\n\nfunction isOptionalSafeInteger(\n value: unknown\n): boolean {\n return value === undefined || Number.isSafeInteger(value);\n}\n\nfunction isWebHostSurfaceLinks(\n value: unknown\n): value is WebHostSurfaceLinkRow[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceLinkRow);\n}\n\nfunction isWebHostSurfaceLinkRow(\n value: unknown\n): value is WebHostSurfaceLinkRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceLinkRun);\n}\n\nfunction isWebHostSurfaceLinkRun(\n value: unknown\n): value is WebHostSurfaceLinkRun {\n if (!Array.isArray(value) || value.length !== 3) {\n return false;\n }\n const [x, span, targetIndex] = value as number[];\n return Number.isSafeInteger(x)\n && x >= 0\n && Number.isSafeInteger(span)\n && span >= 1\n && Number.isSafeInteger(targetIndex)\n && targetIndex >= 0;\n}\n\nfunction isWebHostSurfaceLinkTargets(\n value: unknown\n): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isWebHostFocusPresentation(\n value: unknown\n): value is WebHostFocusPresentation {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const presentation = value as Partial<WebHostFocusPresentation>;\n return (\n presentation.focusedIdentity === undefined\n || typeof presentation.focusedIdentity === \"string\"\n )\n && typeof presentation.semantics === \"string\"\n && typeof presentation.prefersTextInput === \"boolean\"\n && typeof presentation.hasFocusedRegion === \"boolean\";\n}\n\nfunction isWebHostSurfaceDeltaRow(\n value: unknown\n): value is WebHostSurfaceDeltaRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && isWebHostSurfaceRow(value[1]);\n}\n\nfunction isWebHostSurfaceRow(\n value: unknown\n): value is WebHostSurfaceCell[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceCell);\n}\n\nfunction isWebHostSurfaceCell(\n value: unknown\n): value is WebHostSurfaceCell {\n return Array.isArray(value)\n && value.length === 4\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && typeof value[1] === \"string\"\n && Number.isSafeInteger(value[2])\n && value[2] >= 1\n && Number.isSafeInteger(value[3])\n && value[3] >= 0;\n}\n\nfunction isWebHostAccessibilityNodes(\n value: unknown\n): value is WebHostAccessibilityNode[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityNode);\n}\n\nfunction isWebHostAccessibilityNode(\n value: unknown\n): value is WebHostAccessibilityNode {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const node = value as Partial<WebHostAccessibilityNode>;\n return typeof node.id === \"string\"\n && (node.parentId === undefined || typeof node.parentId === \"string\")\n && isWebHostSurfaceRect(node.rect)\n && typeof node.role === \"string\"\n && (node.label === undefined || typeof node.label === \"string\")\n && (node.hint === undefined || typeof node.hint === \"string\")\n && (node.hidden === undefined || typeof node.hidden === \"boolean\")\n && (node.liveRegion === undefined || typeof node.liveRegion === \"string\")\n && (node.cursorAnchor === undefined || isWebHostAccessibilityPoint(node.cursorAnchor))\n && (node.isFocused === undefined || typeof node.isFocused === \"boolean\");\n}\n\nfunction isWebHostAccessibilityPoint(\n value: unknown\n): value is WebHostAccessibilityPoint {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostAccessibilityAnnouncements(\n value: unknown\n): value is WebHostAccessibilityAnnouncement[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityAnnouncement);\n}\n\nfunction isWebHostAccessibilityAnnouncement(\n value: unknown\n): value is WebHostAccessibilityAnnouncement {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const announcement = value as Partial<WebHostAccessibilityAnnouncement>;\n return typeof announcement.message === \"string\"\n && typeof announcement.politeness === \"string\";\n}\n\nfunction isWebHostSurfaceImages(\n value: unknown\n): value is WebHostSurfaceImage[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceImage);\n}\n\nfunction isWebHostSurfaceImage(\n value: unknown\n): value is WebHostSurfaceImage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const image = value as Partial<WebHostSurfaceImage>;\n return typeof image.id === \"string\"\n && isWebHostSurfaceImageFormat(image.format)\n && isWebHostSurfaceRect(image.bounds)\n && isWebHostSurfaceRect(image.visibleBounds)\n && isWebHostSurfaceScalingMode(image.scalingMode)\n && (image.pixelSize === undefined || isWebHostSurfaceSize(image.pixelSize))\n && (image.dataBase64 === undefined || typeof image.dataBase64 === \"string\");\n}\n\nfunction isWebHostSurfaceDamage(\n value: unknown\n): value is WebHostSurfaceDamage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const damage = value as Partial<WebHostSurfaceDamage>;\n return Array.isArray(damage.textRows)\n && damage.textRows.every(isWebHostSurfaceDamageTextRow)\n && typeof damage.requiresFullTextRepaint === \"boolean\"\n && typeof damage.requiresFullGraphicsReplay === \"boolean\";\n}\n\nfunction isWebHostSurfaceDamageTextRow(\n value: unknown\n): value is WebHostSurfaceDamageTextRow {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceDamageRange);\n}\n\nfunction isWebHostSurfaceDamageRange(\n value: unknown\n): value is WebHostSurfaceDamageRange {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && typeof value[1] === \"number\";\n}\n\nfunction isWebHostSurfaceImageFormat(\n value: unknown\n): value is WebHostSurfaceImageFormat {\n return typeof value === \"string\";\n}\n\nfunction isWebHostScrollRegions(\n value: unknown\n): value is WebHostScrollRegion[] {\n return Array.isArray(value) && value.every(isWebHostScrollRegion);\n}\n\nfunction isWebHostScrollRegion(\n value: unknown\n): value is WebHostScrollRegion {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const region = value as Partial<WebHostScrollRegion>;\n return typeof region.id === \"string\"\n && isWebHostSurfaceRect(region.rect)\n && isWebHostSurfaceSize(region.offset)\n && isWebHostSurfaceSize(region.content);\n}\n\nfunction isWebHostSurfaceRect(\n value: unknown\n): value is WebHostSurfaceRect {\n return Array.isArray(value)\n && value.length === 4\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceSize(\n value: unknown\n): value is WebHostSurfaceSize {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceScalingMode(\n value: unknown\n): value is WebHostSurfaceImage[\"scalingMode\"] {\n return typeof value === \"string\";\n}\n"],"mappings":";;;;;;;AA+QA,MAAM,eAAe;AACrB,MAAM,cAAc,IAAI,YAAY;;AAGpC,MAAa,8BAA8B;AAC3C,MAAa,qCAAqC;AAElD,SAAgB,yBACd,IACS;CACT,OAAO,GAAG,SAAS,KACd,GAAG,UAAA,QACH,YAAY,OAAO,EAAE,CAAC,CAAC,cAAA;AAC9B;;;;;;;;;AAUA,MAAa,4BAA4B;AAEzC,IAAa,uBAAb,MAAkC;CAChC,cAA+B,IAAI,YAAY;CAC/C,eAAuB;CACvB;CACA;CACA;CACA;CACA,4BAAoC;CACpC,wBAAgC;CAChC,4CAA6C,IAAI,IAAY;CAC7D,wCAAyC,IAAI,IAAY;CAEzD,KACE,OACuB;EACvB,KAAK,gBAAgB,KAAK,YAAY,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;EACpE,MAAM,UAAiC,CAAC;EAExC,OAAO,MAAM;GACX,MAAM,eAAe,KAAK,aAAa,QAAQ,IAAI;GACnD,IAAI,eAAe,GACjB;GAGF,MAAM,OAAO,KAAK,aAAa,MAAM,GAAG,YAAY;GACpD,KAAK,eAAe,KAAK,aAAa,MAAM,eAAe,CAAC;GAC5D,QAAQ,KAAK,KAAK,WAAW,IAAI,CAAC;EACpC;EAEA,IAAI,KAAK,aAAa,SAAS,QAAQ,CAAC,KAAK,aAAa,WAAW,YAAY,GAAG;GAClF,QAAQ,KAAK;IAAE,MAAM;IAAQ,MAAM,KAAK;GAAa,CAAC;GACtD,KAAK,eAAe;EACtB;EAEA,OAAO;CACT;CAEA,QAA+B;EAC7B,IAAI,CAAC,KAAK,cACR,OAAO,CAAC;EAEV,MAAM,OAAO,KAAK;EAClB,KAAK,eAAe;EACpB,OAAO,CAAC,KAAK,WAAW,IAAI,CAAC;CAC/B;CAEA,kBACE,qBACkC;EAClC,IAAI,KAAK,uBAAuB;GAC9B,KAAK,wBAAwB;GAC7B,OAAO,EAAE,OAAO,WAAW;EAC7B;EACA,IAAI,KAAK,sBAAsB,SAAS,GACtC;EAIF,MAAM,EAAE,KAAK,gBAAgB,sBADX,CAAC,GAAG,KAAK,qBAAqB,CAAC,CAAC,KAExC,GACR,mBACF;EACA,KAAK,MAAM,MAAM,aAAa;GAC5B,KAAK,sBAAsB,OAAO,EAAE;GACpC,KAAK,0BAA0B,OAAO,EAAE;EAC1C;EACA,KAAK,MAAM,MAAM,KACf,KAAK,sBAAsB,OAAO,EAAE;EAEtC,IAAI,IAAI,WAAW,GACjB;EAEF,OAAO;GAAE,OAAO;GAAU;EAAI;CAChC;;;;;;;;CASA,qBACE,KACmB;EACnB,MAAM,8BAAc,IAAI,IAAY;EACpC,KAAK,MAAM,MAAM,KAAK;GACpB,IAAI,CAAC,yBAAyB,EAAE,GAC9B;GAEF,IAAI,KAAK,0BAA0B,IAAI,EAAE,GAAG;IAC1C,YAAY,IAAI,EAAE;IAClB;GACF;GACA,IACE,KAAK,0BAA0B,QAAA,MAG/B;GAEF,KAAK,0BAA0B,IAAI,EAAE;GACrC,KAAK,sBAAsB,IAAI,EAAE;GACjC,YAAY,IAAI,EAAE;EACpB;EACA,OAAO,CAAC,GAAG,WAAW;CACxB;;;;;;;;;CAUA,wBACE,OACmB;EACnB,MAAM,2BAA2B,KAAK,yBAAyB,MAAM,MAAM;EAC3E,KAAK,yBAAyB,MAAM,KAAK;EACzC,KAAK,mCAAmC,MAAM,MAAM;EACpD,KAAK,0BAA0B,MAAM,MAAM;EAC3C,IAAI,MAAM,UAAU,KAAA,GAClB,KAAK,qBAAqB,MAAM;EAElC,OAAO;CACT;CAEA,4BACE,SACM;EACN,IAAI,QAAQ,UAAU,YAAY;GAChC,IAAI,KAAK,2BACP,KAAK,wBAAwB;GAE/B;EACF;EAEA,KAAK,MAAM,MAAM,QAAQ,OAAO,CAAC,GAC/B,IAAI,KAAK,0BAA0B,IAAI,EAAE,GACvC,KAAK,sBAAsB,IAAI,EAAE;CAGvC;CAEA,WACE,MACqB;EACrB,IAAI,KAAK,WAAW,GAAG,aAAa,WAAW,GAAG;GAChD,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,GAAG,aAAa,YAAY,MAAM,CAAC;IACxE,IAAI,yBAAyB,MAAM,GACjC,OAAO;KAAE,MAAM;KAAa,MAAM,OAAO;IAAK;GAElD,QAAQ,CAER;GAEA,OAAO;IAAE,MAAM;IAAQ,MAAM,GAAG,KAAK;GAAI;EAC3C;EAEA,IAAI,KAAK,WAAW,GAAG,aAAa,cAAc,GAAG;GACnD,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,GAAG,aAAa,eAAe,MAAM,CAAC;IAC3E,IAAI,sBAAsB,MAAM,GAC9B,OAAO;KAAE,MAAM;KAAgB,OAAO;IAAO;GAEjD,QAAQ,CAER;GAEA,OAAO;IAAE,MAAM;IAAQ,MAAM,GAAG,KAAK;GAAI;EAC3C;EAEA,IAAI,KAAK,WAAW,GAAG,aAAa,iBAAiB,GAAG;GACtD,IAAI;IACF,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,GAAG,aAAa,kBAAkB,MAAM,CAAC;IAC9E,IAAI,+BAA+B,MAAM,GACvC,OAAO;KAAE,MAAM;KAAmB,YAAY;IAAO;GAEzD,QAAQ,CAER;GAEA,OAAO;IAAE,MAAM;IAAQ,MAAM,GAAG,KAAK;GAAI;EAC3C;EAEA,IAAI,CAAC,KAAK,WAAW,GAAG,aAAa,SAAS,GAC5C,OAAO;GAAE,MAAM;GAAQ,MAAM,GAAG,KAAK;EAAI;EAG3C,IAAI;GACF,MAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,GAAG,aAAa,UAAU,MAAM,CAAC;GACrE,IAAI,4BAA4B,KAAK,GACnC,OAAO;IACL,MAAM;IACN,OAAO;KACL,UAAU;KACV,MAAM;KACN,SAAS,4BAA4B,MAAM,QAAQ;KACnD,aAAa,iDACN,MAAM,QAAQ;IAGvB;GACF;GAEF,IAAI,sBAAsB,KAAK,GAAG;IAChC,KAAK,mBAAmB;IACxB,KAAK,YAAY,MAAM;IACvB,KAAK,UAAU,MAAM;IACrB,KAAK,4BAA4B;IACjC,KAAK,wBAAwB;IAC7B,OAAO;KAAE,MAAM;KAAW;IAAM;GAClC;GACA,IAAI,2BAA2B,KAAK,GAAG;IACrC,MAAM,wBAAwB,MAAM,UAAU,KAAA,KACzC,MAAM,QAAQ,KAAA,KACd,MAAM,gBAAgB,KAAA;IAC3B,IACE,CAAC,KAAK,oBACH,KAAK,iBAAiB,UAAU,MAAM,SACtC,KAAK,iBAAiB,WAAW,MAAM,QAC1C;KACA,IAAI,uBACF,KAAK,sBAAsB;KAE7B,OAAO;MAAE,MAAM;MAAkB,QAAQ;KAAa;IACxD;IACA,IACE,0BAEE,MAAM,UAAU,KAAA,KACb,MAAM,QAAQ,KAAA,KACd,MAAM,gBAAgB,KAAA,KACtB,MAAM,UAAU,KAAK,aACrB,MAAM,gBAAgB,KAAK,UAEhC;KACA,KAAK,sBAAsB;KAC3B,OAAO;MAAE,MAAM;MAAkB,QAAQ;KAAgB;IAC3D;IACA,MAAM,eAAe,KAAK,sBAAsB,KAAK;IACrD,IAAI,cAAc;KAChB,KAAK,mBAAmB;KACxB,KAAK,YAAY,MAAM;KACvB,KAAK,UAAU,MAAM;KACrB,OAAO;MAAE,MAAM;MAAW,OAAO;KAAa;IAChD;GACF;EACF,QAAQ,CAER;EAEA,OAAO;GAAE,MAAM;GAAQ,MAAM,GAAG,KAAK;EAAI;CAC3C;CAEA,wBAAsC;EACpC,IAAI,KAAK,2BACP;EAEF,KAAK,4BAA4B;EACjC,KAAK,wBAAwB;CAC/B;CAEA,yBACE,OACM;EACN,IAAI,UAAU,KAAA,KAAa,UAAU,KAAK,oBACxC;EAEF,KAAK,0BAA0B,MAAM;EACrC,KAAK,sBAAsB,MAAM;CACnC;CAEA,0BACE,QACM;EACN,KAAK,MAAM,SAAS,UAAU,CAAC,GAAG;GAChC,IAAI,MAAM,eAAe,KAAA,GACvB;GAEF,KAAK,0BAA0B,OAAO,MAAM,EAAE;GAC9C,KAAK,sBAAsB,OAAO,MAAM,EAAE;EAC5C;CACF;CAEA,yBACE,QACU;EACV,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,SAAS,UAAU,CAAC,GAC7B,IACE,MAAM,eAAe,KAAA,KAClB,KAAK,0BAA0B,IAAI,MAAM,EAAE,GAE9C,aAAa,IAAI,MAAM,EAAE;EAG7B,OAAO,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK;CAChC;CAEA,mCACE,QACM;EACN,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,SAAS,UAAU,CAAC,GAC7B,IAAI,KAAK,0BAA0B,IAAI,MAAM,EAAE,GAC7C,aAAa,IAAI,MAAM,EAAE;EAG7B,KAAK,MAAM,MAAM,KAAK,2BAA2B;GAC/C,IAAI,aAAa,IAAI,EAAE,GACrB;GAEF,KAAK,0BAA0B,OAAO,EAAE;GACxC,KAAK,sBAAsB,OAAO,EAAE;EACtC;CACF;CAEA,sBACE,OACiC;EACjC,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,UACH;EAGF,MAAM,OAAO,SAAS,KAAK,MAAM;EACjC,KAAK,MAAM,CAAC,KAAK,UAAU,MAAM,WAAW;GAC1C,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM,KAAK,OAAO,MAAM,QACxD;GAEF,KAAK,OAAO;EACd;EAEA,OAAO;GACL,SAAS,SAAS;GAClB,OAAO,MAAM;GACb,KAAK,MAAM;GACX,UAAU,MAAM;GAChB,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd;GACA,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,mBAAmB,MAAM;GACzB,4BAA4B,MAAM;GAClC,eAAe,MAAM;GACrB,OAAO,MAAM;GACb,aAAa,MAAM;GACnB,mBAAmB,MAAM;GACzB,oBAAoB,MAAM;GAC1B,qBAAqB,MAAM;EAC7B;CACF;AACF;AAEA,SAAS,sBACP,WACA,qBAIA;CACA,IACE,wBAAwB,KAAA,KACrB,CAAC,OAAO,SAAS,mBAAmB,GAEvC,OAAO;EAAE,KAAK;EAAW,aAAa,CAAC;CAAE;CAG3C,MAAM,oBAAoB,2BAA2B;EACnD,OAAO;EACP,KAAK,CAAC;CACR,CAAC,CAAC,CAAC;CACH,MAAM,oBAAoB,KAAK,IAAI,GAAG,KAAK,MAAM,mBAAmB,CAAC;CACrE,MAAM,MAAgB,CAAC;CACvB,MAAM,cAAwB,CAAC;CAC/B,IAAI,eAAe;CACnB,KAAK,MAAM,MAAM,WAAW;EAC1B,MAAM,iBAAiB,IAAI,WAAW,IAAI,IAAI;EAC9C,MAAM,UAAU,YAAY,OAAO,KAAK,UAAU,EAAE,CAAC,CAAC,CAAC;EACvD,IAAI,eAAe,iBAAiB,UAAU,mBAAmB;GAC/D,IAAI,IAAI,WAAW,GAAG;IACpB,YAAY,KAAK,EAAE;IACnB;GACF;GACA;EACF;EACA,IAAI,KAAK,EAAE;EACX,gBAAgB,iBAAiB;EACjC,IAAI,gBAAgB,mBAClB;CAEJ;CACA,OAAO;EAAE;EAAK;CAAY;AAC5B;AAEA,SAAS,4BACP,OAC8B;CAC9B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,UAAW,MAAgC;CACjD,OAAO,OAAO,YAAY,YACrB,OAAO,cAAc,OAAO,KAC5B,UAAA;AACP;AAEA,SAAS,yBACP,OAC2B;CAC3B,OAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,OAAQ,MAA6B,SAAS;AAC/F;AAEA,SAAS,sBACP,OAC8B;CAC9B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,SAAS;CACf,QAAQ,OAAO,aAAa,aAAa,OAAO,aAAa,YACxD,OAAO,OAAO,SAAS,YACvB,OAAO,OAAO,YAAY,YAC1B,OAAO,OAAO,gBAAgB,aAC7B,OAAO,aAAa,KAAA,KAAa,OAAO,OAAO,aAAa,cAC5D,OAAO,WAAW,KAAA,KAAa,OAAO,OAAO,WAAW;AAChE;AAEA,SAAS,+BACP,OACuC;CACvC,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,SAAS;CACf,OAAO,OAAO,WAAW,oCACpB,MAAM,QAAQ,OAAO,MAAM,KAC3B,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,QAAQ,KACxD,MAAM,QAAQ,OAAO,MAAM,KAC3B,OAAO,OAAO,OAAO,UAAU,OAAO,UAAU,QAAQ;AAC/D;AAEA,SAAgB,2BACd,SACA,MACA,WACA,YACY;CACZ,MAAM,oBAAoB,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;CACzD,MAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC;CACnD,IAAI,aAAa,YACf,OAAO,YAAY,OACjB,GAAG,aAAa,SAAS,kBAAkB,GAAG,eAAe,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,CAAC,EAAE,GAC5I;CAGF,OAAO,YAAY,OAAO,GAAG,aAAa,SAAS,kBAAkB,GAAG,eAAe,GAAG;AAC5F;AAEA,SAAgB,gCACd,OACY;CACZ,MAAM,UAAU,uCAAuC,KAAK;CAC5D,OAAO,YAAY,OAAO,GAAG,aAAa,QAAQ,QAAQ,GAAG;AAC/D;AAEA,SAAgB,mCAA+C;CAgB7D,OAAO,YAAY,OACjB,GAAG,aAAa,mCAClB;AACF;AAEA,SAAgB,2BACd,SACY;CACZ,MAAM,UAAU,QAAQ,UAAU,aAC9B,EAAE,OAAO,WAAW,IACpB;EACE,OAAO;EACP,GAAI,QAAQ,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK,QAAQ,IAAI;CAC1D;CACJ,OAAO,YAAY,OACjB,GAAG,aAAa,SAAS,KAAK,UAAU,OAAO,EAAE,GACnD;AACF;AAEA,SAAgB,sBACd,OACY;CACZ,MAAM,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,aAAa,CAAC,CAAC;CAC9D,IAAI,MAAM,QAAQ,aAChB,OAAO,YAAY,OACjB,GAAG,aAAa,gBAAgB,mBAAmB,MAAM,aAAa,EAAE,EAAE,GAAG,UAAU,GACzF;CAEF,OAAO,YAAY,OAAO,GAAG,aAAa,MAAM,MAAM,IAAI,GAAG,UAAU,GAAG;AAC5E;AAEA,SAAgB,wBACd,MACY;CACZ,OAAO,YAAY,OAAO,GAAG,aAAa,QAAQ,mBAAmB,IAAI,EAAE,GAAG;AAChF;AAEA,SAAgB,wBACd,OACY;CACZ,OAAO,YAAY,OACjB,eAAe;EACb;EACA,MAAM;EACN,qBAAqB,MAAM,CAAC;EAC5B,qBAAqB,MAAM,CAAC;EAC5B,MAAM,UAAU;EAChB,KAAK,MAAM,MAAM,UAAU,CAAC;EAC5B,KAAK,MAAM,MAAM,UAAU,CAAC;EAC5B,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,aAAa,CAAC,CAAC;CAC9C,CAAC,CAAC,KAAK,GAAG,IAAI,IAChB;AACF;AAEA,SAAS,qBACP,OACQ;CACR,OAAO,OAAO,SAAS,KAAK,IAAI,OAAO,KAAK,IAAI;AAClD;AAEA,SAAS,sBACP,OAC8B;CAC9B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,QAAQ;CACd,QAAQ,MAAM,YAAY,KAAK,MAAM,YAAY,OAE7C,MAAM,aAAa,KAAA,KACb,OAAO,cAAc,MAAM,QAAQ,KAAK,MAAM,YAAY,MAE/D,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW,YACxB,MAAM,QAAQ,MAAM,MAAM,KAC1B,MAAM,QAAQ,MAAM,IAAI,KACxB,MAAM,KAAK,MAAM,mBAAmB,MACnC,MAAM,WAAW,KAAA,KAAa,uBAAuB,MAAM,MAAM,OACjE,MAAM,WAAW,KAAA,KAAa,uBAAuB,MAAM,MAAM,OAEnE,MAAM,sBAAsB,KAAA,KACvB,4BAA4B,MAAM,iBAAiB,OAGxD,MAAM,+BAA+B,KAAA,KAChC,oCAAoC,MAAM,0BAA0B,OAEvE,MAAM,kBAAkB,KAAA,KAAa,uBAAuB,MAAM,aAAa,MAChF,4BAA4B,KAAK;AACxC;AAEA,SAAS,2BACP,OACmC;CACnC,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,QAAQ;CACd,OAAO,MAAM,YAAY,KACpB,MAAM,aAAa,YAEpB,MAAM,aAAa,KAAA,KACb,OAAO,cAAc,MAAM,QAAQ,KAAK,MAAM,YAAY,MAE/D,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW,YACxB,MAAM,QAAQ,MAAM,MAAM,KAC1B,MAAM,QAAQ,MAAM,SAAS,KAC7B,MAAM,UAAU,MAAM,wBAAwB,KAC9C,sBAAsB,MAAM,WAAW,MACtC,MAAM,WAAW,KAAA,KAAa,uBAAuB,MAAM,MAAM,OACjE,MAAM,WAAW,KAAA,KAAa,uBAAuB,MAAM,MAAM,OAEnE,MAAM,sBAAsB,KAAA,KACvB,4BAA4B,MAAM,iBAAiB,OAGxD,MAAM,+BAA+B,KAAA,KAChC,oCAAoC,MAAM,0BAA0B,OAEvE,MAAM,kBAAkB,KAAA,KAAa,uBAAuB,MAAM,aAAa,MAChF,4BAA4B,KAAK;AACxC;;;;;AAMA,SAAS,4BACP,OACS;CACT,OAAO,sBAAsB,MAAM,KAAK,KACnC,sBAAsB,MAAM,GAAG,MAC9B,MAAM,UAAU,KAAA,KAAa,sBAAsB,MAAM,KAAK,OAC9D,MAAM,gBAAgB,KAAA,KAAa,4BAA4B,MAAM,WAAW,OAElF,MAAM,sBAAsB,KAAA,KACvB,2BAA2B,MAAM,iBAAiB,OAGvD,MAAM,uBAAuB,KAAA,KACvB,OAAO,cAAc,MAAM,kBAAkB,KAAK,MAAM,sBAAsB,OAGpF,MAAM,wBAAwB,KAAA,KACxB,OAAO,cAAc,MAAM,mBAAmB,KAAK,MAAM,uBAAuB;AAE5F;AAEA,SAAS,sBACP,OACS;CACT,OAAO,UAAU,KAAA,KAAa,OAAO,cAAc,KAAK;AAC1D;AAEA,SAAS,sBACP,OACkC;CAClC,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,uBAAuB;AACpE;AAEA,SAAS,wBACP,OACgC;CAChC,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,OAAO,cAAc,MAAM,EAAE,KAC7B,MAAM,MAAM,KACZ,MAAM,QAAQ,MAAM,EAAE,KACtB,MAAM,EAAE,CAAC,MAAM,uBAAuB;AAC7C;AAEA,SAAS,wBACP,OACgC;CAChC,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAC5C,OAAO;CAET,MAAM,CAAC,GAAG,MAAM,eAAe;CAC/B,OAAO,OAAO,cAAc,CAAC,KACxB,KAAK,KACL,OAAO,cAAc,IAAI,KACzB,QAAQ,KACR,OAAO,cAAc,WAAW,KAChC,eAAe;AACtB;AAEA,SAAS,4BACP,OACmB;CACnB,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,UAAU,OAAO,UAAU,QAAQ;AACjF;AAEA,SAAS,2BACP,OACmC;CACnC,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,eAAe;CACrB,QACE,aAAa,oBAAoB,KAAA,KAC5B,OAAO,aAAa,oBAAoB,aAE1C,OAAO,aAAa,cAAc,YAClC,OAAO,aAAa,qBAAqB,aACzC,OAAO,aAAa,qBAAqB;AAChD;AAEA,SAAS,yBACP,OACiC;CACjC,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,OAAO,cAAc,MAAM,EAAE,KAC7B,MAAM,MAAM,KACZ,oBAAoB,MAAM,EAAE;AACnC;AAEA,SAAS,oBACP,OAC+B;CAC/B,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,oBAAoB;AACjE;AAEA,SAAS,qBACP,OAC6B;CAC7B,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,OAAO,cAAc,MAAM,EAAE,KAC7B,MAAM,MAAM,KACZ,OAAO,MAAM,OAAO,YACpB,OAAO,cAAc,MAAM,EAAE,KAC7B,MAAM,MAAM,KACZ,OAAO,cAAc,MAAM,EAAE,KAC7B,MAAM,MAAM;AACnB;AAEA,SAAS,4BACP,OACqC;CACrC,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,0BAA0B;AACvE;AAEA,SAAS,2BACP,OACmC;CACnC,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,OAAO;CACb,OAAO,OAAO,KAAK,OAAO,aACpB,KAAK,aAAa,KAAA,KAAa,OAAO,KAAK,aAAa,aACzD,qBAAqB,KAAK,IAAI,KAC9B,OAAO,KAAK,SAAS,aACpB,KAAK,UAAU,KAAA,KAAa,OAAO,KAAK,UAAU,cAClD,KAAK,SAAS,KAAA,KAAa,OAAO,KAAK,SAAS,cAChD,KAAK,WAAW,KAAA,KAAa,OAAO,KAAK,WAAW,eACpD,KAAK,eAAe,KAAA,KAAa,OAAO,KAAK,eAAe,cAC5D,KAAK,iBAAiB,KAAA,KAAa,4BAA4B,KAAK,YAAY,OAChF,KAAK,cAAc,KAAA,KAAa,OAAO,KAAK,cAAc;AAClE;AAEA,SAAS,4BACP,OACoC;CACpC,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,MAAM,OAAO,UAAU,OAAO,UAAU,QAAQ;AACvD;AAEA,SAAS,oCACP,OAC6C;CAC7C,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,kCAAkC;AAC/E;AAEA,SAAS,mCACP,OAC2C;CAC3C,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,eAAe;CACrB,OAAO,OAAO,aAAa,YAAY,YAClC,OAAO,aAAa,eAAe;AAC1C;AAEA,SAAS,uBACP,OACgC;CAChC,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,qBAAqB;AAClE;AAEA,SAAS,sBACP,OAC8B;CAC9B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,QAAQ;CACd,OAAO,OAAO,MAAM,OAAO,YACtB,4BAA4B,MAAM,MAAM,KACxC,qBAAqB,MAAM,MAAM,KACjC,qBAAqB,MAAM,aAAa,KACxC,4BAA4B,MAAM,WAAW,MAC5C,MAAM,cAAc,KAAA,KAAa,qBAAqB,MAAM,SAAS,OACrE,MAAM,eAAe,KAAA,KAAa,OAAO,MAAM,eAAe;AACtE;AAEA,SAAS,uBACP,OAC+B;CAC/B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,SAAS;CACf,OAAO,MAAM,QAAQ,OAAO,QAAQ,KAC/B,OAAO,SAAS,MAAM,6BAA6B,KACnD,OAAO,OAAO,4BAA4B,aAC1C,OAAO,OAAO,+BAA+B;AACpD;AAEA,SAAS,8BACP,OACsC;CACtC,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,OAAO,MAAM,OAAO,YACpB,MAAM,QAAQ,MAAM,EAAE,KACtB,MAAM,EAAE,CAAC,MAAM,2BAA2B;AACjD;AAEA,SAAS,4BACP,OACoC;CACpC,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,OAAO;AAC3B;AAEA,SAAS,4BACP,OACoC;CACpC,OAAO,OAAO,UAAU;AAC1B;AAEA,SAAS,uBACP,OACgC;CAChC,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,qBAAqB;AAClE;AAEA,SAAS,sBACP,OAC8B;CAC9B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CAET,MAAM,SAAS;CACf,OAAO,OAAO,OAAO,OAAO,YACvB,qBAAqB,OAAO,IAAI,KAChC,qBAAqB,OAAO,MAAM,KAClC,qBAAqB,OAAO,OAAO;AAC1C;AAEA,SAAS,qBACP,OAC6B;CAC7B,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,MAAM,OAAO,UAAU,OAAO,UAAU,QAAQ;AACvD;AAEA,SAAS,qBACP,OAC6B;CAC7B,OAAO,MAAM,QAAQ,KAAK,KACrB,MAAM,WAAW,KACjB,MAAM,OAAO,UAAU,OAAO,UAAU,QAAQ;AACvD;AAEA,SAAS,4BACP,OAC6C;CAC7C,OAAO,OAAO,UAAU;AAC1B"}
@@ -41,10 +41,12 @@ declare class WebSocketSceneBridge implements WebHostSceneBridge {
41
41
  resize(columns: number, rows: number, cellWidth?: number, cellHeight?: number): void;
42
42
  updateRenderStyle(style: WebHostTerminalStyle): void;
43
43
  sendInput(chunk: Uint8Array): void;
44
+ requestImagePayloads(ids: readonly string[]): readonly string[];
44
45
  dispose(): void;
45
46
  private receive;
46
47
  private deliver;
47
48
  private flushQueuedInput;
49
+ private sendPendingResyncRequests;
48
50
  }
49
51
  declare function webSocketSceneURL(options: Pick<WebSocketSceneBridgeOptions, "baseURL" | "webSocketURL" | "sceneId" | "token">): URL;
50
52
  //#endregion
@@ -1,4 +1,4 @@
1
- import { WebHostOutputDecoder, encodeCapabilitiesControlMessage, encodeRenderStyleControlMessage, encodeResizeControlMessage } from "./WebHostSurfaceTransport.js";
1
+ import { WebHostOutputDecoder, encodeCapabilitiesControlMessage, encodeRenderStyleControlMessage, encodeResizeControlMessage, encodeResyncControlMessage } from "./WebHostSurfaceTransport.js";
2
2
  //#region src/WebSocketSceneBridge.ts
3
3
  const socketOpenState = 1;
4
4
  const textEncoder = new TextEncoder();
@@ -43,8 +43,14 @@ var WebSocketSceneBridge = class {
43
43
  sendInput(chunk) {
44
44
  if (this.disposed) return;
45
45
  const copy = new Uint8Array(chunk);
46
- if (this.socket.readyState === socketOpenState) this.socket.send(copy);
47
- else this.queuedInput.push(copy);
46
+ this.queuedInput.push(copy);
47
+ if (this.socket.readyState === socketOpenState) this.flushQueuedInput();
48
+ }
49
+ requestImagePayloads(ids) {
50
+ if (this.disposed) return [];
51
+ const acceptedIds = this.decoder.requestImagePayloads(ids);
52
+ this.sendPendingResyncRequests();
53
+ return acceptedIds;
48
54
  }
49
55
  dispose() {
50
56
  if (this.disposed) return;
@@ -62,6 +68,7 @@ var WebSocketSceneBridge = class {
62
68
  const bytes = await bytesFromWebSocketMessage(message);
63
69
  if (!bytes) return;
64
70
  for (const record of this.decoder.feed(bytes)) this.deliver(record);
71
+ this.sendPendingResyncRequests();
65
72
  }
66
73
  deliver(record) {
67
74
  const sink = this.sink;
@@ -71,7 +78,7 @@ var WebSocketSceneBridge = class {
71
78
  }
72
79
  switch (record.type) {
73
80
  case "surface":
74
- sink.presentSurface(record.frame);
81
+ sink.presentSurface(record.frame, this.decoder.prepareToPresentSurface(record.frame));
75
82
  break;
76
83
  case "clipboard":
77
84
  sink.writeClipboard?.(record.text);
@@ -82,6 +89,7 @@ var WebSocketSceneBridge = class {
82
89
  case "frameDiagnostic":
83
90
  sink.recordFrameDiagnostic?.(record.diagnostic);
84
91
  break;
92
+ case "surfaceDropped": break;
85
93
  case "text":
86
94
  sink.writeOutput?.(record.text);
87
95
  break;
@@ -89,7 +97,24 @@ var WebSocketSceneBridge = class {
89
97
  }
90
98
  flushQueuedInput() {
91
99
  if (this.disposed || this.socket.readyState !== socketOpenState) return;
92
- while (this.queuedInput.length > 0) this.socket.send(this.queuedInput.shift());
100
+ while (this.queuedInput.length > 0) try {
101
+ this.socket.send(this.queuedInput[0]);
102
+ this.queuedInput.shift();
103
+ } catch {
104
+ return;
105
+ }
106
+ }
107
+ sendPendingResyncRequests() {
108
+ while (true) {
109
+ const request = this.decoder.takeResyncRequest();
110
+ if (!request) return;
111
+ try {
112
+ this.sendInput(encodeResyncControlMessage(request));
113
+ } catch {
114
+ this.decoder.resyncRequestDeliveryFailed(request);
115
+ return;
116
+ }
117
+ }
93
118
  }
94
119
  };
95
120
  function webSocketSceneURL(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketSceneBridge.js","names":[],"sources":["../../src/WebSocketSceneBridge.ts"],"sourcesContent":["import {\n WebHostOutputDecoder,\n encodeCapabilitiesControlMessage,\n encodeRenderStyleControlMessage,\n encodeResizeControlMessage,\n type WebHostOutputRecord,\n type WebHostOutputSink,\n} from \"./WebHostSurfaceTransport.ts\";\nimport type { WebHostTerminalStyle } from \"./WebHostTerminalStyle.ts\";\nimport type { WebHostSceneBridge } from \"./WebHostSceneRuntime.ts\";\n\nexport interface WebSocketSceneBridgeOptions {\n sceneId: string;\n token: string;\n baseURL?: string | URL;\n webSocketURL?: string | URL;\n webSocketFactory?: WebSocketSceneBridgeFactory;\n}\n\nexport type WebSocketSceneBridgeFactory = (url: string | URL) => WebSocketSceneSocket;\n\nexport interface WebSocketSceneSocket {\n binaryType: BinaryType;\n readonly readyState: number;\n send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: \"open\", listener: (event: Event) => void): void;\n addEventListener(type: \"message\", listener: (event: MessageEvent) => void): void;\n addEventListener(type: \"close\", listener: (event: CloseEvent) => void): void;\n addEventListener(type: \"error\", listener: (event: Event) => void): void;\n removeEventListener(type: \"open\", listener: (event: Event) => void): void;\n removeEventListener(type: \"message\", listener: (event: MessageEvent) => void): void;\n removeEventListener(type: \"close\", listener: (event: CloseEvent) => void): void;\n removeEventListener(type: \"error\", listener: (event: Event) => void): void;\n}\n\nconst socketOpenState = 1;\nconst textEncoder = new TextEncoder();\n\nexport class WebSocketSceneBridge implements WebHostSceneBridge {\n readonly url: URL;\n\n private readonly socket: WebSocketSceneSocket;\n private readonly decoder = new WebHostOutputDecoder();\n private readonly queuedInput: Uint8Array[] = [];\n private readonly queuedOutput: WebHostOutputRecord[] = [];\n private sink?: WebHostOutputSink;\n private disposed = false;\n\n private readonly handleOpen = () => {\n this.flushQueuedInput();\n };\n\n private readonly handleMessage = (event: MessageEvent) => {\n void this.receive(event.data);\n };\n\n private readonly handleClose = () => {\n for (const record of this.decoder.flush()) {\n this.deliver(record);\n }\n };\n\n private readonly handleError = () => {};\n\n constructor(options: WebSocketSceneBridgeOptions) {\n this.url = webSocketSceneURL(options);\n this.socket = (options.webSocketFactory ?? defaultWebSocketFactory)(this.url);\n this.socket.binaryType = \"arraybuffer\";\n this.socket.addEventListener(\"open\", this.handleOpen);\n this.socket.addEventListener(\"message\", this.handleMessage);\n this.socket.addEventListener(\"close\", this.handleClose);\n this.socket.addEventListener(\"error\", this.handleError);\n // Declare wire capabilities first: queued input flushes in order on\n // open, so the declaration reaches the server ahead of any\n // resize/style/input record.\n this.sendInput(encodeCapabilitiesControlMessage());\n }\n\n bindOutput(\n sink: WebHostOutputSink\n ): void {\n this.sink = sink;\n while (this.queuedOutput.length > 0) {\n this.deliver(this.queuedOutput.shift()!);\n }\n }\n\n resize(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n ): void {\n this.sendInput(encodeResizeControlMessage(columns, rows, cellWidth, cellHeight));\n }\n\n updateRenderStyle(\n style: WebHostTerminalStyle\n ): void {\n this.sendInput(encodeRenderStyleControlMessage(style));\n }\n\n sendInput(\n chunk: Uint8Array\n ): void {\n if (this.disposed) {\n return;\n }\n\n const copy = new Uint8Array(chunk);\n if (this.socket.readyState === socketOpenState) {\n this.socket.send(copy);\n } else {\n this.queuedInput.push(copy);\n }\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.socket.removeEventListener(\"open\", this.handleOpen);\n this.socket.removeEventListener(\"message\", this.handleMessage);\n this.socket.removeEventListener(\"close\", this.handleClose);\n this.socket.removeEventListener(\"error\", this.handleError);\n this.queuedInput.length = 0;\n this.queuedOutput.length = 0;\n this.socket.close(1000, \"WebHost scene disposed\");\n }\n\n private async receive(\n message: unknown\n ): Promise<void> {\n if (this.disposed) {\n return;\n }\n\n const bytes = await bytesFromWebSocketMessage(message);\n if (!bytes) {\n return;\n }\n\n for (const record of this.decoder.feed(bytes)) {\n this.deliver(record);\n }\n }\n\n private deliver(\n record: WebHostOutputRecord\n ): void {\n const sink = this.sink;\n if (!sink) {\n this.queuedOutput.push(record);\n return;\n }\n\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 private flushQueuedInput(): void {\n if (this.disposed || this.socket.readyState !== socketOpenState) {\n return;\n }\n while (this.queuedInput.length > 0) {\n this.socket.send(this.queuedInput.shift()!);\n }\n }\n}\n\nexport function webSocketSceneURL(\n options: Pick<WebSocketSceneBridgeOptions, \"baseURL\" | \"webSocketURL\" | \"sceneId\" | \"token\">\n): URL {\n if (options.webSocketURL) {\n const explicit = new URL(String(options.webSocketURL), currentPageURL());\n explicit.searchParams.set(\"token\", options.token);\n return explicit;\n }\n\n const url = new URL(String(options.baseURL ?? currentPageURL()), currentPageURL());\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const basePath = url.pathname.endsWith(\"/\") ? url.pathname.slice(0, -1) : url.pathname;\n url.pathname = `${basePath}/ws/scene/${encodeURIComponent(options.sceneId)}`;\n url.search = \"\";\n url.searchParams.set(\"token\", options.token);\n return url;\n}\n\nasync function bytesFromWebSocketMessage(\n message: unknown\n): Promise<Uint8Array | undefined> {\n if (typeof message === \"string\") {\n return textEncoder.encode(message);\n }\n if (message instanceof Uint8Array) {\n return message;\n }\n if (message instanceof ArrayBuffer) {\n return new Uint8Array(message);\n }\n if (ArrayBuffer.isView(message)) {\n return new Uint8Array(message.buffer, message.byteOffset, message.byteLength);\n }\n if (typeof Blob !== \"undefined\" && message instanceof Blob) {\n return new Uint8Array(await message.arrayBuffer());\n }\n return undefined;\n}\n\nfunction defaultWebSocketFactory(\n url: string | URL\n): WebSocketSceneSocket {\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\"WebSocket is not available\");\n }\n return new WebSocket(url) as WebSocketSceneSocket;\n}\n\nfunction currentPageURL(): string {\n return globalThis.location?.href ?? \"http://127.0.0.1/\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WebSocketSceneBridge.js","names":[],"sources":["../../src/WebSocketSceneBridge.ts"],"sourcesContent":["import {\n WebHostOutputDecoder,\n encodeCapabilitiesControlMessage,\n encodeResyncControlMessage,\n encodeRenderStyleControlMessage,\n encodeResizeControlMessage,\n type WebHostOutputRecord,\n type WebHostOutputSink,\n} from \"./WebHostSurfaceTransport.ts\";\nimport type { WebHostTerminalStyle } from \"./WebHostTerminalStyle.ts\";\nimport type { WebHostSceneBridge } from \"./WebHostSceneRuntime.ts\";\n\nexport interface WebSocketSceneBridgeOptions {\n sceneId: string;\n token: string;\n baseURL?: string | URL;\n webSocketURL?: string | URL;\n webSocketFactory?: WebSocketSceneBridgeFactory;\n}\n\nexport type WebSocketSceneBridgeFactory = (url: string | URL) => WebSocketSceneSocket;\n\nexport interface WebSocketSceneSocket {\n binaryType: BinaryType;\n readonly readyState: number;\n send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: \"open\", listener: (event: Event) => void): void;\n addEventListener(type: \"message\", listener: (event: MessageEvent) => void): void;\n addEventListener(type: \"close\", listener: (event: CloseEvent) => void): void;\n addEventListener(type: \"error\", listener: (event: Event) => void): void;\n removeEventListener(type: \"open\", listener: (event: Event) => void): void;\n removeEventListener(type: \"message\", listener: (event: MessageEvent) => void): void;\n removeEventListener(type: \"close\", listener: (event: CloseEvent) => void): void;\n removeEventListener(type: \"error\", listener: (event: Event) => void): void;\n}\n\nconst socketOpenState = 1;\nconst textEncoder = new TextEncoder();\n\nexport class WebSocketSceneBridge implements WebHostSceneBridge {\n readonly url: URL;\n\n private readonly socket: WebSocketSceneSocket;\n private readonly decoder = new WebHostOutputDecoder();\n private readonly queuedInput: Uint8Array[] = [];\n private readonly queuedOutput: WebHostOutputRecord[] = [];\n private sink?: WebHostOutputSink;\n private disposed = false;\n\n private readonly handleOpen = () => {\n this.flushQueuedInput();\n };\n\n private readonly handleMessage = (event: MessageEvent) => {\n void this.receive(event.data);\n };\n\n private readonly handleClose = () => {\n for (const record of this.decoder.flush()) {\n this.deliver(record);\n }\n };\n\n private readonly handleError = () => {};\n\n constructor(options: WebSocketSceneBridgeOptions) {\n this.url = webSocketSceneURL(options);\n this.socket = (options.webSocketFactory ?? defaultWebSocketFactory)(this.url);\n this.socket.binaryType = \"arraybuffer\";\n this.socket.addEventListener(\"open\", this.handleOpen);\n this.socket.addEventListener(\"message\", this.handleMessage);\n this.socket.addEventListener(\"close\", this.handleClose);\n this.socket.addEventListener(\"error\", this.handleError);\n // Declare wire capabilities first: queued input flushes in order on\n // open, so the declaration reaches the server ahead of any\n // resize/style/input record.\n this.sendInput(encodeCapabilitiesControlMessage());\n }\n\n bindOutput(\n sink: WebHostOutputSink\n ): void {\n this.sink = sink;\n while (this.queuedOutput.length > 0) {\n this.deliver(this.queuedOutput.shift()!);\n }\n }\n\n resize(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n ): void {\n this.sendInput(encodeResizeControlMessage(columns, rows, cellWidth, cellHeight));\n }\n\n updateRenderStyle(\n style: WebHostTerminalStyle\n ): void {\n this.sendInput(encodeRenderStyleControlMessage(style));\n }\n\n sendInput(\n chunk: Uint8Array\n ): void {\n if (this.disposed) {\n return;\n }\n\n const copy = new Uint8Array(chunk);\n this.queuedInput.push(copy);\n if (this.socket.readyState === socketOpenState) {\n this.flushQueuedInput();\n }\n }\n\n requestImagePayloads(\n ids: readonly string[]\n ): readonly string[] {\n if (this.disposed) {\n return [];\n }\n const acceptedIds = this.decoder.requestImagePayloads(ids);\n this.sendPendingResyncRequests();\n return acceptedIds;\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.socket.removeEventListener(\"open\", this.handleOpen);\n this.socket.removeEventListener(\"message\", this.handleMessage);\n this.socket.removeEventListener(\"close\", this.handleClose);\n this.socket.removeEventListener(\"error\", this.handleError);\n this.queuedInput.length = 0;\n this.queuedOutput.length = 0;\n this.socket.close(1000, \"WebHost scene disposed\");\n }\n\n private async receive(\n message: unknown\n ): Promise<void> {\n if (this.disposed) {\n return;\n }\n\n const bytes = await bytesFromWebSocketMessage(message);\n if (!bytes) {\n return;\n }\n\n for (const record of this.decoder.feed(bytes)) {\n this.deliver(record);\n }\n this.sendPendingResyncRequests();\n }\n\n private deliver(\n record: WebHostOutputRecord\n ): void {\n const sink = this.sink;\n if (!sink) {\n this.queuedOutput.push(record);\n return;\n }\n\n switch (record.type) {\n case \"surface\":\n sink.presentSurface(\n record.frame,\n this.decoder.prepareToPresentSurface(record.frame)\n );\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 \"surfaceDropped\":\n break;\n case \"text\":\n sink.writeOutput?.(record.text);\n break;\n }\n }\n\n private flushQueuedInput(): void {\n if (this.disposed || this.socket.readyState !== socketOpenState) {\n return;\n }\n while (this.queuedInput.length > 0) {\n try {\n this.socket.send(this.queuedInput[0]!);\n this.queuedInput.shift();\n } catch {\n return;\n }\n }\n }\n\n private sendPendingResyncRequests(): void {\n while (true) {\n const request = this.decoder.takeResyncRequest();\n if (!request) {\n return;\n }\n try {\n this.sendInput(encodeResyncControlMessage(request));\n } catch {\n this.decoder.resyncRequestDeliveryFailed(request);\n return;\n }\n }\n }\n}\n\nexport function webSocketSceneURL(\n options: Pick<WebSocketSceneBridgeOptions, \"baseURL\" | \"webSocketURL\" | \"sceneId\" | \"token\">\n): URL {\n if (options.webSocketURL) {\n const explicit = new URL(String(options.webSocketURL), currentPageURL());\n explicit.searchParams.set(\"token\", options.token);\n return explicit;\n }\n\n const url = new URL(String(options.baseURL ?? currentPageURL()), currentPageURL());\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const basePath = url.pathname.endsWith(\"/\") ? url.pathname.slice(0, -1) : url.pathname;\n url.pathname = `${basePath}/ws/scene/${encodeURIComponent(options.sceneId)}`;\n url.search = \"\";\n url.searchParams.set(\"token\", options.token);\n return url;\n}\n\nasync function bytesFromWebSocketMessage(\n message: unknown\n): Promise<Uint8Array | undefined> {\n if (typeof message === \"string\") {\n return textEncoder.encode(message);\n }\n if (message instanceof Uint8Array) {\n return message;\n }\n if (message instanceof ArrayBuffer) {\n return new Uint8Array(message);\n }\n if (ArrayBuffer.isView(message)) {\n return new Uint8Array(message.buffer, message.byteOffset, message.byteLength);\n }\n if (typeof Blob !== \"undefined\" && message instanceof Blob) {\n return new Uint8Array(await message.arrayBuffer());\n }\n return undefined;\n}\n\nfunction defaultWebSocketFactory(\n url: string | URL\n): WebSocketSceneSocket {\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\"WebSocket is not available\");\n }\n return new WebSocket(url) as WebSocketSceneSocket;\n}\n\nfunction currentPageURL(): string {\n return globalThis.location?.href ?? \"http://127.0.0.1/\";\n}\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ //#region src/normalizeWireTokens.d.ts
2
+ type NormalizedSurfaceImageFormat = "png" | "jpeg" | "gif";
3
+ //#endregion
4
+ export { NormalizedSurfaceImageFormat };
5
+ //# sourceMappingURL=normalizeWireTokens.d.ts.map
@@ -0,0 +1,41 @@
1
+ //#region src/normalizeWireTokens.ts
2
+ function normalizeSemantics(value) {
3
+ switch (value) {
4
+ case "none":
5
+ case "automatic":
6
+ case "activate":
7
+ case "edit": return value;
8
+ default: return "automatic";
9
+ }
10
+ }
11
+ function normalizePoliteness(value) {
12
+ switch (value) {
13
+ case "off":
14
+ case "polite":
15
+ case "assertive": return value;
16
+ default: return "polite";
17
+ }
18
+ }
19
+ function normalizeLiveRegion(value) {
20
+ switch (value) {
21
+ case "off":
22
+ case "polite":
23
+ case "assertive": return value;
24
+ default: return;
25
+ }
26
+ }
27
+ function normalizeScalingMode(value) {
28
+ switch (value) {
29
+ case "stretch":
30
+ case "fit":
31
+ case "fill": return value;
32
+ default: return "fit";
33
+ }
34
+ }
35
+ function isSupportedImageFormat(value) {
36
+ return value === "png" || value === "jpeg" || value === "gif";
37
+ }
38
+ //#endregion
39
+ export { isSupportedImageFormat, normalizeLiveRegion, normalizePoliteness, normalizeScalingMode, normalizeSemantics };
40
+
41
+ //# sourceMappingURL=normalizeWireTokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeWireTokens.js","names":[],"sources":["../../src/normalizeWireTokens.ts"],"sourcesContent":["export type NormalizedFocusSemantics =\n | \"none\"\n | \"automatic\"\n | \"activate\"\n | \"edit\";\n\nexport type NormalizedAccessibilityPoliteness =\n | \"off\"\n | \"polite\"\n | \"assertive\";\n\nexport type NormalizedSurfaceImageFormat = \"png\" | \"jpeg\" | \"gif\";\nexport type NormalizedSurfaceScalingMode = \"stretch\" | \"fit\" | \"fill\";\n\nexport function normalizeSemantics(\n value: string\n): NormalizedFocusSemantics {\n switch (value) {\n case \"none\":\n case \"automatic\":\n case \"activate\":\n case \"edit\":\n return value;\n default:\n return \"automatic\";\n }\n}\n\nexport function normalizePoliteness(\n value: string\n): NormalizedAccessibilityPoliteness {\n switch (value) {\n case \"off\":\n case \"polite\":\n case \"assertive\":\n return value;\n default:\n return \"polite\";\n }\n}\n\nexport function normalizeLiveRegion(\n value: string | undefined\n): NormalizedAccessibilityPoliteness | undefined {\n switch (value) {\n case \"off\":\n case \"polite\":\n case \"assertive\":\n return value;\n default:\n return undefined;\n }\n}\n\nexport function normalizeScalingMode(\n value: string\n): NormalizedSurfaceScalingMode {\n switch (value) {\n case \"stretch\":\n case \"fit\":\n case \"fill\":\n return value;\n default:\n return \"fit\";\n }\n}\n\nexport function isSupportedImageFormat(\n value: string\n): value is NormalizedSurfaceImageFormat {\n return value === \"png\" || value === \"jpeg\" || value === \"gif\";\n}\n"],"mappings":";AAcA,SAAgB,mBACd,OAC0B;CAC1B,QAAQ,OAAR;EACA,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO;EACT,SACE,OAAO;CACT;AACF;AAEA,SAAgB,oBACd,OACmC;CACnC,QAAQ,OAAR;EACA,KAAK;EACL,KAAK;EACL,KAAK,aACH,OAAO;EACT,SACE,OAAO;CACT;AACF;AAEA,SAAgB,oBACd,OAC+C;CAC/C,QAAQ,OAAR;EACA,KAAK;EACL,KAAK;EACL,KAAK,aACH,OAAO;EACT,SACE;CACF;AACF;AAEA,SAAgB,qBACd,OAC8B;CAC9B,QAAQ,OAAR;EACA,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO;EACT,SACE,OAAO;CACT;AACF;AAEA,SAAgB,uBACd,OACuC;CACvC,OAAO,UAAU,SAAS,UAAU,UAAU,UAAU;AAC1D"}
@@ -25,6 +25,7 @@ declare class BrowserWASIBridge {
25
25
  readonly environment: Record<string, string>;
26
26
  private detachStdout?;
27
27
  private detachStderr?;
28
+ private decoder;
28
29
  private readonly resizeListeners;
29
30
  private latestResize;
30
31
  constructor(options: BrowserWASIBridgeOptions);
@@ -32,7 +33,14 @@ declare class BrowserWASIBridge {
32
33
  resize(columns: number, rows: number, cellWidth?: number, cellHeight?: number): void;
33
34
  updateRenderStyle(style: WebHostTerminalStyle): void;
34
35
  sendInput(chunk: Uint8Array): void;
36
+ requestImagePayloads(ids: readonly string[]): readonly string[];
37
+ /**
38
+ * Called by the WASI runtime when its shared stdin queue has enough capacity
39
+ * for the delivery that previously failed.
40
+ */
41
+ notifyInputCapacityAvailable(): void;
35
42
  subscribeResize(listener: (columns: number, rows: number, cellWidth?: number, cellHeight?: number) => void): () => void;
43
+ private sendPendingResyncRequests;
36
44
  dispose(): void;
37
45
  }
38
46
  //#endregion
@@ -1,8 +1,10 @@
1
1
  import { StdIOPipe } from "./StdIOPipe.js";
2
2
  import { resolveWasmEngineCapabilities, stackProfileEnvironmentDefaults } from "./WasmEngineCapabilities.js";
3
3
  import { encodeWebHostTerminalRenderStyleBase64 } from "../WebHostTerminalStyle.js";
4
- import { WebHostOutputDecoder, encodeRenderStyleControlMessage, encodeResizeControlMessage } from "../WebHostSurfaceTransport.js";
4
+ import { WebHostOutputDecoder, encodeRenderStyleControlMessage, encodeResizeControlMessage, encodeResyncControlMessage } from "../WebHostSurfaceTransport.js";
5
+ import { sharedInputQueueDefaultCapacity } from "./SharedInputQueue.js";
5
6
  //#region src/wasi/BrowserWASIBridge.ts
7
+ const maximumWASIResyncControlBytes = sharedInputQueueDefaultCapacity - 1;
6
8
  var BrowserWASIBridge = class {
7
9
  stdin = new StdIOPipe();
8
10
  stdout = new StdIOPipe();
@@ -10,6 +12,7 @@ var BrowserWASIBridge = class {
10
12
  environment;
11
13
  detachStdout;
12
14
  detachStderr;
15
+ decoder = new WebHostOutputDecoder();
13
16
  resizeListeners = /* @__PURE__ */ new Set();
14
17
  latestResize;
15
18
  constructor(options) {
@@ -33,11 +36,11 @@ var BrowserWASIBridge = class {
33
36
  bindOutput(sink) {
34
37
  this.detachStdout?.();
35
38
  this.detachStderr?.();
36
- const decoder = new WebHostOutputDecoder();
39
+ this.decoder = new WebHostOutputDecoder();
37
40
  this.detachStdout = this.stdout.subscribe((chunk) => {
38
- for (const record of decoder.feed(chunk)) switch (record.type) {
41
+ for (const record of this.decoder.feed(chunk)) switch (record.type) {
39
42
  case "surface":
40
- sink.presentSurface(record.frame);
43
+ sink.presentSurface(record.frame, this.decoder.prepareToPresentSurface(record.frame));
41
44
  break;
42
45
  case "clipboard":
43
46
  sink.writeClipboard?.(record.text);
@@ -48,10 +51,12 @@ var BrowserWASIBridge = class {
48
51
  case "frameDiagnostic":
49
52
  sink.recordFrameDiagnostic?.(record.diagnostic);
50
53
  break;
54
+ case "surfaceDropped": break;
51
55
  case "text":
52
56
  sink.writeOutput?.(record.text);
53
57
  break;
54
58
  }
59
+ this.sendPendingResyncRequests();
55
60
  });
56
61
  this.detachStderr = this.stderr.subscribe((chunk) => {
57
62
  sink.writeError?.(new TextDecoder().decode(chunk));
@@ -78,6 +83,18 @@ var BrowserWASIBridge = class {
78
83
  sendInput(chunk) {
79
84
  this.stdin.write(chunk);
80
85
  }
86
+ requestImagePayloads(ids) {
87
+ const acceptedIds = this.decoder.requestImagePayloads(ids);
88
+ this.sendPendingResyncRequests();
89
+ return acceptedIds;
90
+ }
91
+ /**
92
+ * Called by the WASI runtime when its shared stdin queue has enough capacity
93
+ * for the delivery that previously failed.
94
+ */
95
+ notifyInputCapacityAvailable() {
96
+ this.sendPendingResyncRequests();
97
+ }
81
98
  subscribeResize(listener) {
82
99
  this.resizeListeners.add(listener);
83
100
  listener(this.latestResize.columns, this.latestResize.rows, this.latestResize.cellWidth, this.latestResize.cellHeight);
@@ -85,6 +102,16 @@ var BrowserWASIBridge = class {
85
102
  this.resizeListeners.delete(listener);
86
103
  };
87
104
  }
105
+ sendPendingResyncRequests() {
106
+ while (true) {
107
+ const request = this.decoder.takeResyncRequest(maximumWASIResyncControlBytes);
108
+ if (!request) return;
109
+ if (!this.stdin.write(encodeResyncControlMessage(request))) {
110
+ this.decoder.resyncRequestDeliveryFailed(request);
111
+ return;
112
+ }
113
+ }
114
+ }
88
115
  dispose() {
89
116
  this.detachStdout?.();
90
117
  this.detachStderr?.();
@@ -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 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"}
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 encodeResyncControlMessage,\n encodeRenderStyleControlMessage,\n encodeResizeControlMessage,\n type WebHostOutputSink,\n} from \"../WebHostSurfaceTransport.ts\";\nimport { sharedInputQueueDefaultCapacity } from \"./SharedInputQueue.ts\";\n\nconst maximumWASIResyncControlBytes = sharedInputQueueDefaultCapacity - 1;\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 decoder = new WebHostOutputDecoder();\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 this.decoder = new WebHostOutputDecoder();\n this.detachStdout = this.stdout.subscribe((chunk) => {\n for (const record of this.decoder.feed(chunk)) {\n switch (record.type) {\n case \"surface\":\n sink.presentSurface(\n record.frame,\n this.decoder.prepareToPresentSurface(record.frame)\n );\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 \"surfaceDropped\":\n break;\n case \"text\":\n sink.writeOutput?.(record.text);\n break;\n }\n }\n this.sendPendingResyncRequests();\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 requestImagePayloads(\n ids: readonly string[]\n ): readonly string[] {\n const acceptedIds = this.decoder.requestImagePayloads(ids);\n this.sendPendingResyncRequests();\n return acceptedIds;\n }\n\n /**\n * Called by the WASI runtime when its shared stdin queue has enough capacity\n * for the delivery that previously failed.\n */\n notifyInputCapacityAvailable(): void {\n this.sendPendingResyncRequests();\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 private sendPendingResyncRequests(): void {\n while (true) {\n const request = this.decoder.takeResyncRequest(\n maximumWASIResyncControlBytes\n );\n if (!request) {\n return;\n }\n const accepted = this.stdin.write(encodeResyncControlMessage(request));\n if (!accepted) {\n this.decoder.resyncRequestDeliveryFailed(request);\n return;\n }\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":"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"}
@@ -15,6 +15,8 @@ declare class SharedInputQueueWriter {
15
15
  private readonly queue;
16
16
  constructor(buffers: SharedInputQueueBuffers);
17
17
  write(chunk: Uint8Array | string): void;
18
+ availableCapacity(): number;
19
+ waitForCapacity(minimumBytes: number): Promise<boolean>;
18
20
  close(): void;
19
21
  }
20
22
  declare class SharedInputQueueReader {
@@ -1,5 +1,6 @@
1
1
  //#region src/wasi/SharedInputQueue.ts
2
2
  const controlSlots = 3;
3
+ const capacityWaitTimeoutMilliseconds = 50;
3
4
  const sharedInputQueueDefaultCapacity = 64 * 1024;
4
5
  function createSharedInputQueue(capacity = sharedInputQueueDefaultCapacity) {
5
6
  if (typeof SharedArrayBuffer === "undefined") throw new Error("SharedArrayBuffer is unavailable. Serve the app with COOP/COEP headers so browser WASI stdin can stay live.");
@@ -33,9 +34,29 @@ var SharedInputQueueWriter = class {
33
34
  Atomics.store(this.queue.control, 1, ringAdvance(writeIndex, bytes.length, length));
34
35
  Atomics.notify(this.queue.control, 1);
35
36
  }
37
+ availableCapacity() {
38
+ const length = this.queue.data.length;
39
+ return length - ringUsed(Atomics.load(this.queue.control, 0), Atomics.load(this.queue.control, 1), length);
40
+ }
41
+ async waitForCapacity(minimumBytes) {
42
+ const required = Math.max(0, Math.ceil(minimumBytes));
43
+ if (required > this.queue.data.length) return false;
44
+ while (true) {
45
+ const readIndex = Atomics.load(this.queue.control, 0);
46
+ if (Atomics.load(this.queue.control, 2) !== 0) return false;
47
+ if (this.availableCapacity() >= required) return true;
48
+ if (typeof Atomics.waitAsync === "function") {
49
+ const waiting = Atomics.waitAsync(this.queue.control, 0, readIndex, capacityWaitTimeoutMilliseconds);
50
+ if (waiting.async) await waiting.value;
51
+ } else await new Promise((resolve) => {
52
+ setTimeout(resolve, 1);
53
+ });
54
+ }
55
+ }
36
56
  close() {
37
57
  Atomics.store(this.queue.control, 2, 1);
38
58
  Atomics.notify(this.queue.control, 1);
59
+ Atomics.notify(this.queue.control, 0);
39
60
  }
40
61
  };
41
62
  var SharedInputQueueReader = class {
@@ -61,6 +82,7 @@ var SharedInputQueueReader = class {
61
82
  const byteCount = Math.min(maxBytes, availableBytes);
62
83
  const chunk = readFromRingBuffer(this.queue.data, readIndex, byteCount);
63
84
  Atomics.store(this.queue.control, 0, ringAdvance(readIndex, byteCount, length));
85
+ Atomics.notify(this.queue.control, 0);
64
86
  return chunk;
65
87
  }
66
88
  availableBytes() {