@swifttui/web 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -10
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/manifest.d.ts +2 -0
- package/dist/manifest.js +2 -0
- package/dist/src/AccessibilityTree.js +156 -0
- package/dist/src/AccessibilityTree.js.map +1 -0
- package/dist/src/BoxDrawingRenderer.js +1106 -0
- package/dist/src/BoxDrawingRenderer.js.map +1 -0
- package/dist/src/WebHostApp.d.ts +41 -0
- package/dist/src/WebHostApp.js +135 -0
- package/dist/src/WebHostApp.js.map +1 -0
- package/dist/src/WebHostSceneManifest.d.ts +18 -0
- package/dist/src/WebHostSceneManifest.js +70 -0
- package/dist/src/WebHostSceneManifest.js.map +1 -0
- package/dist/src/WebHostSceneRuntime.d.ts +112 -0
- package/dist/src/WebHostSceneRuntime.js +651 -0
- package/dist/src/WebHostSceneRuntime.js.map +1 -0
- package/dist/src/WebHostSurfaceTransport.d.ts +166 -0
- package/dist/src/WebHostSurfaceTransport.js +252 -0
- package/dist/src/WebHostSurfaceTransport.js.map +1 -0
- package/dist/src/WebHostTerminalStyle.d.ts +92 -0
- package/dist/src/WebHostTerminalStyle.js +277 -0
- package/dist/src/WebHostTerminalStyle.js.map +1 -0
- package/dist/src/WebHostTestFixtures.d.ts +5 -0
- package/dist/src/WebHostTestFixtures.js +9 -0
- package/dist/src/WebHostTestFixtures.js.map +1 -0
- package/dist/src/WebSocketSceneBridge.d.ts +53 -0
- package/dist/src/WebSocketSceneBridge.js +124 -0
- package/dist/src/WebSocketSceneBridge.js.map +1 -0
- package/dist/src/wasi/BrowserWASIBridge.d.ts +33 -0
- package/dist/src/wasi/BrowserWASIBridge.js +97 -0
- package/dist/src/wasi/BrowserWASIBridge.js.map +1 -0
- package/dist/src/wasi/SharedInputQueue.d.ts +31 -0
- package/dist/src/wasi/SharedInputQueue.js +102 -0
- package/dist/src/wasi/SharedInputQueue.js.map +1 -0
- package/dist/src/wasi/StdIOPipe.d.ts +15 -0
- package/dist/src/wasi/StdIOPipe.js +56 -0
- package/dist/src/wasi/StdIOPipe.js.map +1 -0
- package/dist/src/wasi/WasiPollScheduler.js +114 -0
- package/dist/src/wasi/WasiPollScheduler.js.map +1 -0
- package/dist/src/wasi/WasmSceneRuntime.d.ts +23 -0
- package/dist/src/wasi/WasmSceneRuntime.js +119 -0
- package/dist/src/wasi/WasmSceneRuntime.js.map +1 -0
- package/dist/src/wasi/WasmSceneWorker.d.ts +27 -0
- package/dist/src/wasi/WasmSceneWorker.js +109 -0
- package/dist/src/wasi/WasmSceneWorker.js.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +2 -0
- package/dist/wasi-worker.d.ts +2 -0
- package/dist/wasi-worker.js +2 -0
- package/dist/wasi.d.ts +6 -0
- package/dist/wasi.js +6 -0
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.js +2 -0
- package/package.json +49 -18
- package/AGENTS.md +0 -52
- package/cli.ts +0 -168
- package/index.html +0 -50
- package/index.ts +0 -8
- package/manifest.ts +0 -1
- package/src/AccessibilityTree.ts +0 -262
- package/src/BoxDrawingRenderer.ts +0 -585
- package/src/PublicEntrypointBoundary.test.ts +0 -20
- package/src/WebHostApp.test.ts +0 -222
- package/src/WebHostApp.ts +0 -269
- package/src/WebHostSceneManifest.test.ts +0 -38
- package/src/WebHostSceneManifest.ts +0 -156
- package/src/WebHostSceneRuntime.test.ts +0 -1982
- package/src/WebHostSceneRuntime.ts +0 -1142
- package/src/WebHostSurfaceTransport.test.ts +0 -362
- package/src/WebHostSurfaceTransport.ts +0 -691
- package/src/WebHostTerminalStyle.test.ts +0 -123
- package/src/WebHostTerminalStyle.ts +0 -471
- package/src/WebHostTestFixtures.ts +0 -10
- package/src/WebSocketSceneBridge.test.ts +0 -198
- package/src/WebSocketSceneBridge.ts +0 -233
- package/src/browser.ts +0 -59
- package/src/wasi/BrowserWASIBridge.test.ts +0 -168
- package/src/wasi/BrowserWASIBridge.ts +0 -167
- package/src/wasi/SharedInputQueue.test.ts +0 -146
- package/src/wasi/SharedInputQueue.ts +0 -199
- package/src/wasi/StdIOPipe.ts +0 -72
- package/src/wasi/WasiPollScheduler.test.ts +0 -176
- package/src/wasi/WasiPollScheduler.ts +0 -305
- package/src/wasi/WasmSceneRuntime.ts +0 -205
- package/src/wasi/WasmSceneWorker.ts +0 -182
- package/testing.ts +0 -1
- package/tsconfig.json +0 -29
- package/wasi-worker.ts +0 -1
- package/wasi.ts +0 -4
- package/websocket.ts +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoxDrawingRenderer.js","names":[],"sources":["../../src/BoxDrawingRenderer.ts"],"sourcesContent":["type LineWeight = 0 | 1 | 2 | 3;\ntype Direction = \"north\" | \"east\" | \"south\" | \"west\";\ntype Corner = \"topLeft\" | \"topRight\" | \"bottomLeft\" | \"bottomRight\";\ntype Spec = [north: LineWeight, east: LineWeight, south: LineWeight, west: LineWeight];\n\ninterface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface StrokeMetrics {\n light: number;\n heavy: number;\n doubleGap: number;\n}\n\ninterface BoxDrawingCanvasContext {\n fillRect(x: number, y: number, width: number, height: number): void;\n beginPath(): void;\n moveTo(x: number, y: number): void;\n lineTo(x: number, y: number): void;\n bezierCurveTo(\n control1X: number,\n control1Y: number,\n control2X: number,\n control2Y: number,\n x: number,\n y: number\n ): void;\n stroke(): void;\n setLineDash(lineDash: number[]): void;\n lineWidth: number;\n lineCap: string;\n}\n\nconst none = 0;\nconst light = 1;\nconst heavy = 2;\nconst double = 3;\n\nconst lineSpecs: Record<number, Spec> = {\n 0x2500: [none, light, none, light],\n 0x2501: [none, heavy, none, heavy],\n 0x2502: [light, none, light, none],\n 0x2503: [heavy, none, heavy, none],\n 0x250c: [none, light, light, none],\n 0x250d: [none, heavy, light, none],\n 0x250e: [none, light, heavy, none],\n 0x250f: [none, heavy, heavy, none],\n 0x2510: [none, none, light, light],\n 0x2511: [none, none, light, heavy],\n 0x2512: [none, none, heavy, light],\n 0x2513: [none, none, heavy, heavy],\n 0x2514: [light, light, none, none],\n 0x2515: [light, heavy, none, none],\n 0x2516: [heavy, light, none, none],\n 0x2517: [heavy, heavy, none, none],\n 0x2518: [light, none, none, light],\n 0x2519: [light, none, none, heavy],\n 0x251a: [heavy, none, none, light],\n 0x251b: [heavy, none, none, heavy],\n 0x251c: [light, light, light, none],\n 0x251d: [light, heavy, light, none],\n 0x251e: [heavy, light, light, none],\n 0x251f: [light, light, heavy, none],\n 0x2520: [heavy, light, heavy, none],\n 0x2521: [heavy, heavy, light, none],\n 0x2522: [light, heavy, heavy, none],\n 0x2523: [heavy, heavy, heavy, none],\n 0x2524: [light, none, light, light],\n 0x2525: [light, none, light, heavy],\n 0x2526: [heavy, none, light, light],\n 0x2527: [light, none, heavy, light],\n 0x2528: [heavy, none, heavy, light],\n 0x2529: [heavy, none, light, heavy],\n 0x252a: [light, none, heavy, heavy],\n 0x252b: [heavy, none, heavy, heavy],\n 0x252c: [none, light, light, light],\n 0x252d: [none, light, light, heavy],\n 0x252e: [none, heavy, light, light],\n 0x252f: [none, heavy, light, heavy],\n 0x2530: [none, light, heavy, light],\n 0x2531: [none, light, heavy, heavy],\n 0x2532: [none, heavy, heavy, light],\n 0x2533: [none, heavy, heavy, heavy],\n 0x2534: [light, light, none, light],\n 0x2535: [light, light, none, heavy],\n 0x2536: [light, heavy, none, light],\n 0x2537: [light, heavy, none, heavy],\n 0x2538: [heavy, light, none, light],\n 0x2539: [heavy, light, none, heavy],\n 0x253a: [heavy, heavy, none, light],\n 0x253b: [heavy, heavy, none, heavy],\n 0x253c: [light, light, light, light],\n 0x253d: [light, light, light, heavy],\n 0x253e: [light, heavy, light, light],\n 0x253f: [light, heavy, light, heavy],\n 0x2540: [heavy, light, light, light],\n 0x2541: [light, light, heavy, light],\n 0x2542: [heavy, light, heavy, light],\n 0x2543: [heavy, light, light, heavy],\n 0x2544: [heavy, heavy, light, light],\n 0x2545: [light, light, heavy, heavy],\n 0x2546: [light, heavy, heavy, light],\n 0x2547: [heavy, heavy, light, heavy],\n 0x2548: [light, heavy, heavy, heavy],\n 0x2549: [heavy, light, heavy, heavy],\n 0x254a: [heavy, heavy, heavy, light],\n 0x254b: [heavy, heavy, heavy, heavy],\n 0x2550: [none, double, none, double],\n 0x2551: [double, none, double, none],\n 0x2552: [none, double, light, none],\n 0x2553: [none, light, double, none],\n 0x2554: [none, double, double, none],\n 0x2555: [none, none, light, double],\n 0x2556: [none, none, double, light],\n 0x2557: [none, none, double, double],\n 0x2558: [light, double, none, none],\n 0x2559: [double, light, none, none],\n 0x255a: [double, double, none, none],\n 0x255b: [light, none, none, double],\n 0x255c: [double, none, none, light],\n 0x255d: [double, none, none, double],\n 0x255e: [light, double, light, none],\n 0x255f: [double, light, double, none],\n 0x2560: [double, double, double, none],\n 0x2561: [light, none, light, double],\n 0x2562: [double, none, double, light],\n 0x2563: [double, none, double, double],\n 0x2564: [none, double, light, double],\n 0x2565: [none, light, double, light],\n 0x2566: [none, double, double, double],\n 0x2567: [light, double, none, double],\n 0x2568: [double, light, none, light],\n 0x2569: [double, double, none, double],\n 0x256a: [light, double, light, double],\n 0x256b: [double, light, double, light],\n 0x256c: [double, double, double, double],\n 0x2574: [none, none, none, light],\n 0x2575: [light, none, none, none],\n 0x2576: [none, light, none, none],\n 0x2577: [none, none, light, none],\n 0x2578: [none, none, none, heavy],\n 0x2579: [heavy, none, none, none],\n 0x257a: [none, heavy, none, none],\n 0x257b: [none, none, heavy, none],\n 0x257c: [none, heavy, none, light],\n 0x257d: [light, none, heavy, none],\n 0x257e: [none, light, none, heavy],\n 0x257f: [heavy, none, light, none],\n};\n\nexport function canRenderBoxDrawing(\n text: string\n): boolean {\n const codePoint = singleCodePoint(text);\n if (codePoint === undefined) {\n return false;\n }\n return (\n (codePoint >= 0x2500 && codePoint <= 0x259f) ||\n (codePoint >= 0x2800 && codePoint <= 0x28ff)\n );\n}\n\nexport function drawBoxDrawing(\n context: BoxDrawingCanvasContext,\n text: string,\n rect: Rect\n): boolean {\n const codePoint = singleCodePoint(text);\n if (codePoint === undefined) {\n return false;\n }\n\n if (codePoint >= 0x2500 && codePoint <= 0x257f) {\n return drawBoxDrawingCodePoint(context, codePoint, rect);\n }\n if (codePoint >= 0x2580 && codePoint <= 0x259f) {\n return drawBlockElement(context, codePoint, rect);\n }\n if (codePoint >= 0x2800 && codePoint <= 0x28ff) {\n return drawBraille(context, codePoint, rect);\n }\n return false;\n}\n\nfunction singleCodePoint(\n text: string\n): number | undefined {\n const characters = Array.from(text);\n if (characters.length !== 1) {\n return undefined;\n }\n return characters[0]?.codePointAt(0);\n}\n\nfunction drawBoxDrawingCodePoint(\n context: BoxDrawingCanvasContext,\n codePoint: number,\n rect: Rect\n): boolean {\n const spec = lineSpecs[codePoint];\n if (spec) {\n drawCellLines(context, spec, rect);\n return true;\n }\n\n switch (codePoint) {\n case 0x2504: drawDashedHorizontal(context, rect, light, 3); return true;\n case 0x2505: drawDashedHorizontal(context, rect, heavy, 3); return true;\n case 0x2506: drawDashedVertical(context, rect, light, 3); return true;\n case 0x2507: drawDashedVertical(context, rect, heavy, 3); return true;\n case 0x2508: drawDashedHorizontal(context, rect, light, 4); return true;\n case 0x2509: drawDashedHorizontal(context, rect, heavy, 4); return true;\n case 0x250a: drawDashedVertical(context, rect, light, 4); return true;\n case 0x250b: drawDashedVertical(context, rect, heavy, 4); return true;\n case 0x254c: drawDashedHorizontal(context, rect, light, 2); return true;\n case 0x254d: drawDashedHorizontal(context, rect, heavy, 2); return true;\n case 0x254e: drawDashedVertical(context, rect, light, 2); return true;\n case 0x254f: drawDashedVertical(context, rect, heavy, 2); return true;\n case 0x2571: drawDiagonal(context, rect, false); return true;\n case 0x2572: drawDiagonal(context, rect, true); return true;\n case 0x2573:\n drawDiagonal(context, rect, false);\n drawDiagonal(context, rect, true);\n return true;\n case 0x256d: drawArc(context, rect, \"topLeft\"); return true;\n case 0x256e: drawArc(context, rect, \"topRight\"); return true;\n case 0x256f: drawArc(context, rect, \"bottomRight\"); return true;\n case 0x2570: drawArc(context, rect, \"bottomLeft\"); return true;\n default:\n return false;\n }\n}\n\nfunction strokeMetrics(\n rect: Rect\n): StrokeMetrics {\n const unit = Math.max(1, Math.round(Math.min(rect.width, rect.height) / 16));\n return {\n light: unit,\n heavy: unit * 2,\n doubleGap: unit,\n };\n}\n\nfunction drawCellLines(\n context: BoxDrawingCanvasContext,\n spec: Spec,\n rect: Rect\n): void {\n const metrics = strokeMetrics(rect);\n const edges: Array<[LineWeight, Direction]> = [\n [spec[0], \"north\"],\n [spec[1], \"east\"],\n [spec[2], \"south\"],\n [spec[3], \"west\"],\n ];\n for (const [weight, direction] of edges.sort((lhs, rhs) => lhs[0] - rhs[0])) {\n drawHalfStroke(context, weight, direction, rect, metrics);\n }\n}\n\nfunction drawHalfStroke(\n context: BoxDrawingCanvasContext,\n weight: LineWeight,\n direction: Direction,\n rect: Rect,\n metrics: StrokeMetrics\n): void {\n if (weight === none) {\n return;\n }\n\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n const maxX = rect.x + rect.width;\n const maxY = rect.y + rect.height;\n\n const segment = (thickness: number, offset: number) => {\n switch (direction) {\n case \"north\":\n context.fillRect(cx - thickness / 2 + offset, rect.y, thickness, cy - rect.y + thickness / 2);\n break;\n case \"south\":\n context.fillRect(\n cx - thickness / 2 + offset,\n cy - thickness / 2,\n thickness,\n maxY - cy + thickness / 2\n );\n break;\n case \"west\":\n context.fillRect(rect.x, cy - thickness / 2 + offset, cx - rect.x + thickness / 2, thickness);\n break;\n case \"east\":\n context.fillRect(\n cx - thickness / 2,\n cy - thickness / 2 + offset,\n maxX - cx + thickness / 2,\n thickness\n );\n break;\n }\n };\n\n switch (weight) {\n case light:\n segment(metrics.light, 0);\n break;\n case heavy:\n segment(metrics.heavy, 0);\n break;\n case double: {\n const thickness = metrics.light;\n const offset = (thickness + metrics.doubleGap) / 2;\n segment(thickness, -offset);\n segment(thickness, offset);\n break;\n }\n }\n}\n\nfunction drawDashedHorizontal(\n context: BoxDrawingCanvasContext,\n rect: Rect,\n weight: LineWeight,\n segments: number\n): void {\n const metrics = strokeMetrics(rect);\n const thickness = weight === heavy ? metrics.heavy : metrics.light;\n const segmentWidth = rect.width / segments;\n const dashWidth = segmentWidth * 0.55;\n const gapWidth = segmentWidth - dashWidth;\n const cy = rect.y + rect.height / 2;\n for (let index = 0; index < segments; index += 1) {\n const x = rect.x + index * segmentWidth + gapWidth / 2;\n context.fillRect(x, cy - thickness / 2, dashWidth, thickness);\n }\n}\n\nfunction drawDashedVertical(\n context: BoxDrawingCanvasContext,\n rect: Rect,\n weight: LineWeight,\n segments: number\n): void {\n const metrics = strokeMetrics(rect);\n const thickness = weight === heavy ? metrics.heavy : metrics.light;\n const segmentHeight = rect.height / segments;\n const dashHeight = segmentHeight * 0.55;\n const gapHeight = segmentHeight - dashHeight;\n const cx = rect.x + rect.width / 2;\n for (let index = 0; index < segments; index += 1) {\n const y = rect.y + index * segmentHeight + gapHeight / 2;\n context.fillRect(cx - thickness / 2, y, thickness, dashHeight);\n }\n}\n\nfunction drawDiagonal(\n context: BoxDrawingCanvasContext,\n rect: Rect,\n descending: boolean\n): void {\n const metrics = strokeMetrics(rect);\n context.lineWidth = metrics.light;\n context.lineCap = \"square\";\n context.setLineDash([]);\n context.beginPath();\n if (descending) {\n context.moveTo(rect.x, rect.y);\n context.lineTo(rect.x + rect.width, rect.y + rect.height);\n } else {\n context.moveTo(rect.x + rect.width, rect.y);\n context.lineTo(rect.x, rect.y + rect.height);\n }\n context.stroke();\n context.lineCap = \"butt\";\n}\n\nfunction drawArc(\n context: BoxDrawingCanvasContext,\n rect: Rect,\n corner: Corner\n): void {\n const metrics = strokeMetrics(rect);\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n const maxX = rect.x + rect.width;\n const maxY = rect.y + rect.height;\n const radius = Math.min(rect.width, rect.height) * 0.4;\n const kappa = radius * 0.5523;\n\n context.lineWidth = metrics.light;\n context.lineCap = \"butt\";\n context.setLineDash([]);\n context.beginPath();\n\n switch (corner) {\n case \"topLeft\":\n context.moveTo(cx, cy + radius);\n context.lineTo(cx, maxY);\n context.moveTo(cx + radius, cy);\n context.lineTo(maxX, cy);\n context.moveTo(cx, cy + radius);\n context.bezierCurveTo(cx, cy + radius - kappa, cx + radius - kappa, cy, cx + radius, cy);\n break;\n case \"topRight\":\n context.moveTo(cx, cy + radius);\n context.lineTo(cx, maxY);\n context.moveTo(cx - radius, cy);\n context.lineTo(rect.x, cy);\n context.moveTo(cx - radius, cy);\n context.bezierCurveTo(cx - radius + kappa, cy, cx, cy + radius - kappa, cx, cy + radius);\n break;\n case \"bottomRight\":\n context.moveTo(cx, cy - radius);\n context.lineTo(cx, rect.y);\n context.moveTo(cx - radius, cy);\n context.lineTo(rect.x, cy);\n context.moveTo(cx, cy - radius);\n context.bezierCurveTo(cx, cy - radius + kappa, cx - radius + kappa, cy, cx - radius, cy);\n break;\n case \"bottomLeft\":\n context.moveTo(cx, cy - radius);\n context.lineTo(cx, rect.y);\n context.moveTo(cx + radius, cy);\n context.lineTo(maxX, cy);\n context.moveTo(cx + radius, cy);\n context.bezierCurveTo(cx + radius - kappa, cy, cx, cy - radius + kappa, cx, cy - radius);\n break;\n }\n\n context.stroke();\n}\n\nfunction drawBlockElement(\n context: BoxDrawingCanvasContext,\n codePoint: number,\n rect: Rect\n): boolean {\n const maxX = rect.x + rect.width;\n const maxY = rect.y + rect.height;\n\n const lowerEighths = (count: number) => {\n const height = rect.height * count / 8;\n context.fillRect(rect.x, maxY - height, rect.width, height);\n };\n const leftEighths = (count: number) => {\n const width = rect.width * count / 8;\n context.fillRect(rect.x, rect.y, width, rect.height);\n };\n\n switch (codePoint) {\n case 0x2580: context.fillRect(rect.x, rect.y, rect.width, rect.height / 2); return true;\n case 0x2581: lowerEighths(1); return true;\n case 0x2582: lowerEighths(2); return true;\n case 0x2583: lowerEighths(3); return true;\n case 0x2584: lowerEighths(4); return true;\n case 0x2585: lowerEighths(5); return true;\n case 0x2586: lowerEighths(6); return true;\n case 0x2587: lowerEighths(7); return true;\n case 0x2588: context.fillRect(rect.x, rect.y, rect.width, rect.height); return true;\n case 0x2589: leftEighths(7); return true;\n case 0x258a: leftEighths(6); return true;\n case 0x258b: leftEighths(5); return true;\n case 0x258c: leftEighths(4); return true;\n case 0x258d: leftEighths(3); return true;\n case 0x258e: leftEighths(2); return true;\n case 0x258f: leftEighths(1); return true;\n case 0x2590:\n context.fillRect(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);\n return true;\n case 0x2591: drawShade(context, rect, \"light\"); return true;\n case 0x2592: drawShade(context, rect, \"medium\"); return true;\n case 0x2593: drawShade(context, rect, \"dark\"); return true;\n case 0x2594: context.fillRect(rect.x, rect.y, rect.width, rect.height / 8); return true;\n case 0x2595:\n context.fillRect(maxX - rect.width / 8, rect.y, rect.width / 8, rect.height);\n return true;\n case 0x2596: fillQuadrants(context, rect, [\"bottomLeft\"]); return true;\n case 0x2597: fillQuadrants(context, rect, [\"bottomRight\"]); return true;\n case 0x2598: fillQuadrants(context, rect, [\"topLeft\"]); return true;\n case 0x2599: fillQuadrants(context, rect, [\"topLeft\", \"bottomLeft\", \"bottomRight\"]); return true;\n case 0x259a: fillQuadrants(context, rect, [\"topLeft\", \"bottomRight\"]); return true;\n case 0x259b: fillQuadrants(context, rect, [\"topLeft\", \"topRight\", \"bottomLeft\"]); return true;\n case 0x259c: fillQuadrants(context, rect, [\"topLeft\", \"topRight\", \"bottomRight\"]); return true;\n case 0x259d: fillQuadrants(context, rect, [\"topRight\"]); return true;\n case 0x259e: fillQuadrants(context, rect, [\"topRight\", \"bottomLeft\"]); return true;\n case 0x259f: fillQuadrants(context, rect, [\"topRight\", \"bottomLeft\", \"bottomRight\"]); return true;\n default:\n return false;\n }\n}\n\nfunction fillQuadrants(\n context: BoxDrawingCanvasContext,\n rect: Rect,\n quadrants: Corner[]\n): void {\n const halfWidth = rect.width / 2;\n const halfHeight = rect.height / 2;\n for (const quadrant of quadrants) {\n switch (quadrant) {\n case \"topLeft\":\n context.fillRect(rect.x, rect.y, halfWidth, halfHeight);\n break;\n case \"topRight\":\n context.fillRect(rect.x + halfWidth, rect.y, halfWidth, halfHeight);\n break;\n case \"bottomLeft\":\n context.fillRect(rect.x, rect.y + halfHeight, halfWidth, halfHeight);\n break;\n case \"bottomRight\":\n context.fillRect(rect.x + halfWidth, rect.y + halfHeight, halfWidth, halfHeight);\n break;\n }\n }\n}\n\nfunction drawShade(\n context: BoxDrawingCanvasContext,\n rect: Rect,\n density: \"light\" | \"medium\" | \"dark\"\n): void {\n const pixels = density === \"light\"\n ? [[0, 0]]\n : density === \"medium\"\n ? [[0, 0], [1, 1]]\n : [[0, 0], [1, 0], [0, 1]];\n\n for (let y = rect.y; y < rect.y + rect.height; y += 2) {\n for (let x = rect.x; x < rect.x + rect.width; x += 2) {\n for (const [px, py] of pixels) {\n const dotX = x + (px ?? 0);\n const dotY = y + (py ?? 0);\n if (dotX < rect.x + rect.width && dotY < rect.y + rect.height) {\n context.fillRect(dotX, dotY, 1, 1);\n }\n }\n }\n }\n}\n\n// Bit -> (column, row) layout for the 2x4 braille mosaic. Mirrors\n// `BrailleCell.bit(x:y:)` in `Sources/Core/BrailleCanvas.swift` so that\n// every glyph emitted by `BrailleCanvas` round-trips visually. Each set\n// bit fills its sub-cell rectangle solid (rather than drawing a font-style\n// dot) so that adjacent set bits — both within and across cells — connect\n// without visible mid-fill spacing, and a fully-set mask renders identical\n// to U+2588 FULL BLOCK.\nconst brailleSubpixels: ReadonlyArray<readonly [bit: number, col: number, row: number]> = [\n [0x01, 0, 0], [0x08, 1, 0],\n [0x02, 0, 1], [0x10, 1, 1],\n [0x04, 0, 2], [0x20, 1, 2],\n [0x40, 0, 3], [0x80, 1, 3],\n];\n\nfunction drawBraille(\n context: BoxDrawingCanvasContext,\n codePoint: number,\n rect: Rect\n): boolean {\n const mask = codePoint - 0x2800;\n if (mask === 0) {\n // U+2800 (BRAILLE PATTERN BLANK) renders as whitespace.\n return true;\n }\n\n const cellWidth = rect.width / 2;\n const rowHeight = rect.height / 4;\n\n for (const [bit, col, row] of brailleSubpixels) {\n if ((mask & bit) === 0) {\n continue;\n }\n const x = rect.x + col * cellWidth;\n const y = rect.y + row * rowHeight;\n context.fillRect(x, y, cellWidth, rowHeight);\n }\n return true;\n}\n"],"mappings":";AAqCA,MAAM,OAAO;AACb,MAAM,QAAQ;AACd,MAAM,QAAQ;AACd,MAAM,SAAS;AAEf,MAAM,YAAkC;CACtC,MAAQ;EAAC;EAAM;EAAO;EAAM;CAAK;CACjC,MAAQ;EAAC;EAAM;EAAO;EAAM;CAAK;CACjC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAI;CACjC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAI;CACjC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAI;CACjC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAI;CACjC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAI;CACjC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAI;CACjC,MAAQ;EAAC;EAAM;EAAM;EAAO;CAAK;CACjC,MAAQ;EAAC;EAAM;EAAM;EAAO;CAAK;CACjC,MAAQ;EAAC;EAAM;EAAM;EAAO;CAAK;CACjC,MAAQ;EAAC;EAAM;EAAM;EAAO;CAAK;CACjC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAI;CACjC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAI;CACjC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAI;CACjC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAI;CACjC,MAAQ;EAAC;EAAO;EAAM;EAAM;CAAK;CACjC,MAAQ;EAAC;EAAO;EAAM;EAAM;CAAK;CACjC,MAAQ;EAAC;EAAO;EAAM;EAAM;CAAK;CACjC,MAAQ;EAAC;EAAO;EAAM;EAAM;CAAK;CACjC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAI;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAM;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAM;EAAO;EAAO;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAM;CAAK;CAClC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EAAO;EAAO;EAAO;CAAK;CACnC,MAAQ;EAAC;EA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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { WebHostTerminalStyle } from "./WebHostTerminalStyle.js";
|
|
2
|
+
import { WebHostSceneDescriptor, WebHostSceneManifestSource } from "./WebHostSceneManifest.js";
|
|
3
|
+
import { WebHostSceneBridge, WebHostSceneRuntime, WebHostSceneRuntimeOptions } from "./WebHostSceneRuntime.js";
|
|
4
|
+
import { WebSocketSceneBridgeOptions } from "./WebSocketSceneBridge.js";
|
|
5
|
+
|
|
6
|
+
//#region src/WebHostApp.d.ts
|
|
7
|
+
interface WebHostEmbeddedHostConfig {
|
|
8
|
+
token: string;
|
|
9
|
+
webSocketBaseURL?: string | URL;
|
|
10
|
+
webSocketFactory?: WebSocketSceneBridgeOptions["webSocketFactory"];
|
|
11
|
+
}
|
|
12
|
+
interface WebHostBridgeFactoryOptions {
|
|
13
|
+
sceneId: string;
|
|
14
|
+
descriptor: WebHostSceneDescriptor;
|
|
15
|
+
style: WebHostTerminalStyle;
|
|
16
|
+
environment?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
type WebHostBridgeFactory = (options: WebHostBridgeFactoryOptions) => WebHostSceneBridge;
|
|
19
|
+
interface WebHostAppOptions {
|
|
20
|
+
mount: HTMLElement;
|
|
21
|
+
manifest?: WebHostSceneManifestSource;
|
|
22
|
+
manifestUrl?: string | URL;
|
|
23
|
+
initialSceneId?: string;
|
|
24
|
+
style?: WebHostTerminalStyle;
|
|
25
|
+
environment?: Record<string, string>;
|
|
26
|
+
embeddedHost?: WebHostEmbeddedHostConfig;
|
|
27
|
+
bridgeFactory?: WebHostBridgeFactory;
|
|
28
|
+
createElement?: (tagName: string) => HTMLElement;
|
|
29
|
+
sceneRuntimeFactory?: (options: WebHostSceneRuntimeOptions) => WebHostSceneRuntime;
|
|
30
|
+
}
|
|
31
|
+
interface WebHostAppController {
|
|
32
|
+
scenes: WebHostSceneDescriptor[];
|
|
33
|
+
selectedSceneId: string;
|
|
34
|
+
switchScene(id: string): Promise<void>;
|
|
35
|
+
setStyle(style: WebHostTerminalStyle): void;
|
|
36
|
+
dispose(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
declare function createWebHostApp(options: WebHostAppOptions): Promise<WebHostAppController>;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { WebHostAppController, WebHostAppOptions, WebHostBridgeFactory, WebHostBridgeFactoryOptions, WebHostEmbeddedHostConfig, createWebHostApp };
|
|
41
|
+
//# sourceMappingURL=WebHostApp.d.ts.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { mergeWebHostTerminalStyle, normalizeWebHostTerminalStyle } from "./WebHostTerminalStyle.js";
|
|
2
|
+
import { BrowserWASIBridge } from "./wasi/BrowserWASIBridge.js";
|
|
3
|
+
import { WebSocketSceneBridge } from "./WebSocketSceneBridge.js";
|
|
4
|
+
import { loadWebHostSceneManifest, normalizeWebHostSceneManifest } from "./WebHostSceneManifest.js";
|
|
5
|
+
import { WebHostSceneRuntime } from "./WebHostSceneRuntime.js";
|
|
6
|
+
//#region src/WebHostApp.ts
|
|
7
|
+
async function createWebHostApp(options) {
|
|
8
|
+
const manifest = await resolveManifest(options);
|
|
9
|
+
const controller = new InternalWebHostAppController({
|
|
10
|
+
mount: options.mount,
|
|
11
|
+
manifest,
|
|
12
|
+
style: options.style,
|
|
13
|
+
environment: options.environment,
|
|
14
|
+
embeddedHost: options.embeddedHost,
|
|
15
|
+
bridgeFactory: options.bridgeFactory,
|
|
16
|
+
initialSceneId: options.initialSceneId,
|
|
17
|
+
createElement: options.createElement,
|
|
18
|
+
sceneRuntimeFactory: options.sceneRuntimeFactory ?? ((runtimeOptions) => new WebHostSceneRuntime(runtimeOptions))
|
|
19
|
+
});
|
|
20
|
+
await controller.initialize();
|
|
21
|
+
return controller;
|
|
22
|
+
}
|
|
23
|
+
var InternalWebHostAppController = class {
|
|
24
|
+
scenes;
|
|
25
|
+
selectedSceneId;
|
|
26
|
+
mount;
|
|
27
|
+
sceneRoot;
|
|
28
|
+
style;
|
|
29
|
+
environment;
|
|
30
|
+
embeddedHost;
|
|
31
|
+
bridgeFactory;
|
|
32
|
+
sceneRuntimeFactory;
|
|
33
|
+
runtimes = /* @__PURE__ */ new Map();
|
|
34
|
+
bridges = /* @__PURE__ */ new Map();
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.mount = options.mount;
|
|
37
|
+
this.style = normalizeWebHostTerminalStyle(options.style ?? {});
|
|
38
|
+
this.environment = options.environment;
|
|
39
|
+
this.embeddedHost = options.embeddedHost;
|
|
40
|
+
this.bridgeFactory = options.bridgeFactory;
|
|
41
|
+
this.sceneRuntimeFactory = options.sceneRuntimeFactory;
|
|
42
|
+
this.scenes = options.manifest.scenes;
|
|
43
|
+
this.selectedSceneId = options.initialSceneId && options.manifest.scenes.some((scene) => scene.id === options.initialSceneId) ? options.initialSceneId : options.manifest.scenes.find((scene) => scene.id === options.manifest.defaultSceneId)?.id ?? options.manifest.defaultSceneId;
|
|
44
|
+
this.sceneRoot = (options.createElement ?? defaultCreateElement)("div");
|
|
45
|
+
this.sceneRoot.className = "webhost-scene-root";
|
|
46
|
+
this.mount.replaceChildren(this.sceneRoot);
|
|
47
|
+
this.applyHostFrameStyle();
|
|
48
|
+
}
|
|
49
|
+
async initialize() {
|
|
50
|
+
await this.ensureRuntime(this.selectedSceneId);
|
|
51
|
+
await this.switchScene(this.selectedSceneId);
|
|
52
|
+
}
|
|
53
|
+
async switchScene(id) {
|
|
54
|
+
if (!this.scenes.find((scene) => scene.id === id)) throw new Error(`Unknown scene: ${id}`);
|
|
55
|
+
for (const [sceneId, runtime] of this.runtimes) runtime.setVisible(sceneId === id);
|
|
56
|
+
(await this.ensureRuntime(id)).setVisible(true);
|
|
57
|
+
this.selectedSceneId = id;
|
|
58
|
+
}
|
|
59
|
+
setStyle(style) {
|
|
60
|
+
const merged = mergeWebHostTerminalStyle(this.style, style);
|
|
61
|
+
this.style = merged;
|
|
62
|
+
for (const runtime of this.runtimes.values()) runtime.setStyle(this.style);
|
|
63
|
+
this.applyHostFrameStyle();
|
|
64
|
+
}
|
|
65
|
+
async dispose() {
|
|
66
|
+
for (const runtime of this.runtimes.values()) runtime.dispose();
|
|
67
|
+
for (const bridge of this.bridges.values()) bridge.dispose();
|
|
68
|
+
this.runtimes.clear();
|
|
69
|
+
this.bridges.clear();
|
|
70
|
+
this.mount.replaceChildren();
|
|
71
|
+
}
|
|
72
|
+
async ensureRuntime(id) {
|
|
73
|
+
const existing = this.runtimes.get(id);
|
|
74
|
+
if (existing) return existing;
|
|
75
|
+
const descriptor = this.scenes.find((scene) => scene.id === id);
|
|
76
|
+
if (!descriptor) throw new Error(`Unknown scene: ${id}`);
|
|
77
|
+
const bridge = this.makeBridge(id, descriptor);
|
|
78
|
+
const runtime = this.sceneRuntimeFactory({
|
|
79
|
+
mount: this.sceneRoot,
|
|
80
|
+
descriptor,
|
|
81
|
+
style: this.style,
|
|
82
|
+
bridge,
|
|
83
|
+
onInput: (chunk) => bridge.sendInput(chunk)
|
|
84
|
+
});
|
|
85
|
+
this.bridges.set(id, bridge);
|
|
86
|
+
this.runtimes.set(id, runtime);
|
|
87
|
+
await runtime.mount();
|
|
88
|
+
runtime.setVisible(id === this.selectedSceneId);
|
|
89
|
+
return runtime;
|
|
90
|
+
}
|
|
91
|
+
makeBridge(sceneId, descriptor) {
|
|
92
|
+
if (this.bridgeFactory) return this.bridgeFactory({
|
|
93
|
+
sceneId,
|
|
94
|
+
descriptor,
|
|
95
|
+
style: this.style,
|
|
96
|
+
environment: this.environment
|
|
97
|
+
});
|
|
98
|
+
if (this.embeddedHost) return new WebSocketSceneBridge({
|
|
99
|
+
sceneId,
|
|
100
|
+
token: this.embeddedHost.token,
|
|
101
|
+
baseURL: this.embeddedHost.webSocketBaseURL,
|
|
102
|
+
webSocketFactory: this.embeddedHost.webSocketFactory
|
|
103
|
+
});
|
|
104
|
+
return new BrowserWASIBridge({
|
|
105
|
+
sceneId,
|
|
106
|
+
columns: 80,
|
|
107
|
+
rows: 24,
|
|
108
|
+
environment: this.environment,
|
|
109
|
+
renderStyle: this.style
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
applyHostFrameStyle() {
|
|
113
|
+
this.mount.style.background = "linear-gradient(180deg, #0f172a 0%, #111827 100%)";
|
|
114
|
+
this.mount.style.minHeight = "100%";
|
|
115
|
+
this.mount.style.display = "block";
|
|
116
|
+
this.mount.style.padding = "1rem";
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
function defaultCreateElement(tagName) {
|
|
120
|
+
if (typeof document === "undefined") throw new Error("document is not available");
|
|
121
|
+
return document.createElement(tagName);
|
|
122
|
+
}
|
|
123
|
+
async function resolveManifest(options) {
|
|
124
|
+
if (options.manifest) return loadWebHostSceneManifest(options.manifest);
|
|
125
|
+
if (options.manifestUrl) return loadWebHostSceneManifest(options.manifestUrl);
|
|
126
|
+
return normalizeWebHostSceneManifest([{
|
|
127
|
+
id: "main",
|
|
128
|
+
title: "Main",
|
|
129
|
+
isDefault: true
|
|
130
|
+
}]);
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { createWebHostApp };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=WebHostApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebHostApp.js","names":[],"sources":["../../src/WebHostApp.ts"],"sourcesContent":["import { BrowserWASIBridge } from \"./wasi/BrowserWASIBridge.ts\";\nimport {\n WebSocketSceneBridge,\n type WebSocketSceneBridgeOptions,\n} from \"./WebSocketSceneBridge.ts\";\nimport {\n loadWebHostSceneManifest,\n normalizeWebHostSceneManifest,\n type WebHostSceneDescriptor,\n type WebHostSceneManifest,\n type WebHostSceneManifestSource,\n} from \"./WebHostSceneManifest.ts\";\nimport {\n mergeWebHostTerminalStyle,\n normalizeWebHostTerminalStyle,\n type ResolvedWebHostTerminalStyle,\n type WebHostTerminalStyle,\n} from \"./WebHostTerminalStyle.ts\";\nimport {\n WebHostSceneRuntime,\n type WebHostSceneBridge,\n type WebHostSceneRuntimeOptions,\n} from \"./WebHostSceneRuntime.ts\";\n\nexport interface WebHostEmbeddedHostConfig {\n token: string;\n webSocketBaseURL?: string | URL;\n webSocketFactory?: WebSocketSceneBridgeOptions[\"webSocketFactory\"];\n}\n\nexport interface WebHostBridgeFactoryOptions {\n sceneId: string;\n descriptor: WebHostSceneDescriptor;\n style: WebHostTerminalStyle;\n environment?: Record<string, string>;\n}\n\nexport type WebHostBridgeFactory = (options: WebHostBridgeFactoryOptions) => WebHostSceneBridge;\n\nexport interface WebHostAppOptions {\n mount: HTMLElement;\n manifest?: WebHostSceneManifestSource;\n manifestUrl?: string | URL;\n initialSceneId?: string;\n style?: WebHostTerminalStyle;\n environment?: Record<string, string>;\n embeddedHost?: WebHostEmbeddedHostConfig;\n bridgeFactory?: WebHostBridgeFactory;\n createElement?: (tagName: string) => HTMLElement;\n sceneRuntimeFactory?: (options: WebHostSceneRuntimeOptions) => WebHostSceneRuntime;\n}\n\nexport interface WebHostAppController {\n scenes: WebHostSceneDescriptor[];\n selectedSceneId: string;\n switchScene(id: string): Promise<void>;\n setStyle(style: WebHostTerminalStyle): void;\n dispose(): Promise<void>;\n}\n\ntype RuntimeFactory = (options: WebHostSceneRuntimeOptions) => WebHostSceneRuntime;\n\nexport async function createWebHostApp(\n options: WebHostAppOptions\n): Promise<WebHostAppController> {\n const manifest = await resolveManifest(options);\n const controller = new InternalWebHostAppController({\n mount: options.mount,\n manifest,\n style: options.style,\n environment: options.environment,\n embeddedHost: options.embeddedHost,\n bridgeFactory: options.bridgeFactory,\n initialSceneId: options.initialSceneId,\n createElement: options.createElement,\n sceneRuntimeFactory: options.sceneRuntimeFactory ?? ((runtimeOptions) => new WebHostSceneRuntime(runtimeOptions)),\n });\n await controller.initialize();\n return controller;\n}\n\nclass InternalWebHostAppController implements WebHostAppController {\n readonly scenes: WebHostSceneDescriptor[];\n selectedSceneId: string;\n\n private readonly mount: HTMLElement;\n private readonly sceneRoot: HTMLElement;\n private style: ResolvedWebHostTerminalStyle;\n private readonly environment?: Record<string, string>;\n private readonly embeddedHost?: WebHostEmbeddedHostConfig;\n private readonly bridgeFactory?: WebHostBridgeFactory;\n private readonly sceneRuntimeFactory: RuntimeFactory;\n private readonly runtimes = new Map<string, WebHostSceneRuntime>();\n private readonly bridges = new Map<string, WebHostSceneBridge>();\n\n constructor(options: {\n mount: HTMLElement;\n manifest: WebHostSceneManifest;\n style?: WebHostTerminalStyle;\n environment?: Record<string, string>;\n embeddedHost?: WebHostEmbeddedHostConfig;\n bridgeFactory?: WebHostBridgeFactory;\n initialSceneId?: string;\n createElement?: (tagName: string) => HTMLElement;\n sceneRuntimeFactory: RuntimeFactory;\n }) {\n this.mount = options.mount;\n this.style = normalizeWebHostTerminalStyle(options.style ?? {});\n this.environment = options.environment;\n this.embeddedHost = options.embeddedHost;\n this.bridgeFactory = options.bridgeFactory;\n this.sceneRuntimeFactory = options.sceneRuntimeFactory;\n this.scenes = options.manifest.scenes;\n this.selectedSceneId =\n options.initialSceneId &&\n options.manifest.scenes.some((scene) => scene.id === options.initialSceneId)\n ? options.initialSceneId\n : options.manifest.scenes.find((scene) => scene.id === options.manifest.defaultSceneId)?.id ??\n options.manifest.defaultSceneId;\n\n this.sceneRoot = (options.createElement ?? defaultCreateElement)(\"div\");\n this.sceneRoot.className = \"webhost-scene-root\";\n this.mount.replaceChildren(this.sceneRoot);\n this.applyHostFrameStyle();\n }\n\n async initialize(): Promise<void> {\n await this.ensureRuntime(this.selectedSceneId);\n await this.switchScene(this.selectedSceneId);\n }\n\n async switchScene(\n id: string\n ): Promise<void> {\n const descriptor = this.scenes.find((scene) => scene.id === id);\n if (!descriptor) {\n throw new Error(`Unknown scene: ${id}`);\n }\n\n for (const [sceneId, runtime] of this.runtimes) {\n runtime.setVisible(sceneId === id);\n }\n\n const runtime = await this.ensureRuntime(id);\n runtime.setVisible(true);\n this.selectedSceneId = id;\n }\n\n setStyle(\n style: WebHostTerminalStyle\n ): void {\n const merged = mergeWebHostTerminalStyle(this.style, style);\n this.style = merged;\n\n for (const runtime of this.runtimes.values()) {\n runtime.setStyle(this.style);\n }\n this.applyHostFrameStyle();\n }\n\n async dispose(): Promise<void> {\n for (const runtime of this.runtimes.values()) {\n runtime.dispose();\n }\n for (const bridge of this.bridges.values()) {\n bridge.dispose();\n }\n this.runtimes.clear();\n this.bridges.clear();\n this.mount.replaceChildren();\n }\n\n private async ensureRuntime(\n id: string\n ): Promise<WebHostSceneRuntime> {\n const existing = this.runtimes.get(id);\n if (existing) {\n return existing;\n }\n\n const descriptor = this.scenes.find((scene) => scene.id === id);\n if (!descriptor) {\n throw new Error(`Unknown scene: ${id}`);\n }\n\n const bridge = this.makeBridge(id, descriptor);\n const runtime = this.sceneRuntimeFactory({\n mount: this.sceneRoot,\n descriptor,\n style: this.style,\n bridge,\n onInput: (chunk) => bridge.sendInput(chunk),\n });\n\n this.bridges.set(id, bridge);\n this.runtimes.set(id, runtime);\n await runtime.mount();\n runtime.setVisible(id === this.selectedSceneId);\n return runtime;\n }\n\n private makeBridge(\n sceneId: string,\n descriptor: WebHostSceneDescriptor\n ): WebHostSceneBridge {\n if (this.bridgeFactory) {\n return this.bridgeFactory({\n sceneId,\n descriptor,\n style: this.style,\n environment: this.environment,\n });\n }\n\n if (this.embeddedHost) {\n return new WebSocketSceneBridge({\n sceneId,\n token: this.embeddedHost.token,\n baseURL: this.embeddedHost.webSocketBaseURL,\n webSocketFactory: this.embeddedHost.webSocketFactory,\n });\n }\n\n return new BrowserWASIBridge({\n sceneId,\n columns: 80,\n rows: 24,\n environment: this.environment,\n renderStyle: this.style,\n });\n }\n\n private applyHostFrameStyle(): void {\n this.mount.style.background = \"linear-gradient(180deg, #0f172a 0%, #111827 100%)\";\n this.mount.style.minHeight = \"100%\";\n this.mount.style.display = \"block\";\n this.mount.style.padding = \"1rem\";\n }\n}\n\nfunction defaultCreateElement(\n tagName: string\n): HTMLElement {\n if (typeof document === \"undefined\") {\n throw new Error(\"document is not available\");\n }\n\n return document.createElement(tagName);\n}\n\nasync function resolveManifest(\n options: WebHostAppOptions\n): Promise<WebHostSceneManifest> {\n if (options.manifest) {\n return loadWebHostSceneManifest(options.manifest);\n }\n\n if (options.manifestUrl) {\n return loadWebHostSceneManifest(options.manifestUrl);\n }\n\n return normalizeWebHostSceneManifest([\n {\n id: \"main\",\n title: \"Main\",\n isDefault: true,\n },\n ]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/WebHostSceneManifest.d.ts
|
|
2
|
+
interface WebHostSceneDescriptor {
|
|
3
|
+
id: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
isDefault: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface WebHostSceneManifest {
|
|
8
|
+
defaultSceneId: string;
|
|
9
|
+
scenes: WebHostSceneDescriptor[];
|
|
10
|
+
}
|
|
11
|
+
type WebHostSceneManifestSource = WebHostSceneManifest | WebHostSceneDescriptor[] | string | URL | Request | Response;
|
|
12
|
+
declare function normalizeWebHostSceneManifest(source: unknown): WebHostSceneManifest;
|
|
13
|
+
declare function webTUISceneManifestToJSON(manifest: WebHostSceneManifest): string;
|
|
14
|
+
declare function webTUISceneManifestFromDescriptors(descriptors: WebHostSceneDescriptor[]): WebHostSceneManifest;
|
|
15
|
+
declare function loadWebHostSceneManifest(source: WebHostSceneManifestSource): Promise<WebHostSceneManifest>;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { WebHostSceneDescriptor, WebHostSceneManifest, WebHostSceneManifestSource, loadWebHostSceneManifest, normalizeWebHostSceneManifest, webTUISceneManifestFromDescriptors, webTUISceneManifestToJSON };
|
|
18
|
+
//# sourceMappingURL=WebHostSceneManifest.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//#region src/WebHostSceneManifest.ts
|
|
2
|
+
function normalizeWebHostSceneManifest(source) {
|
|
3
|
+
const scenes = normalizeSceneDescriptors(source);
|
|
4
|
+
if (scenes.length === 0) throw new Error("scene manifest must contain at least one scene");
|
|
5
|
+
const defaultScene = scenes.find((scene) => scene.isDefault) ?? scenes[0];
|
|
6
|
+
return {
|
|
7
|
+
defaultSceneId: defaultScene.id,
|
|
8
|
+
scenes: scenes.map((scene, index) => ({
|
|
9
|
+
...scene,
|
|
10
|
+
isDefault: scene.id === defaultScene.id || index === 0 && !scenes.some((entry) => entry.isDefault)
|
|
11
|
+
}))
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function webTUISceneManifestToJSON(manifest) {
|
|
15
|
+
return JSON.stringify({
|
|
16
|
+
defaultSceneId: manifest.defaultSceneId,
|
|
17
|
+
scenes: manifest.scenes.map((scene) => ({
|
|
18
|
+
id: scene.id,
|
|
19
|
+
...scene.title ? { title: scene.title } : {},
|
|
20
|
+
isDefault: scene.isDefault
|
|
21
|
+
}))
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function webTUISceneManifestFromDescriptors(descriptors) {
|
|
25
|
+
return normalizeWebHostSceneManifest(descriptors);
|
|
26
|
+
}
|
|
27
|
+
async function loadWebHostSceneManifest(source) {
|
|
28
|
+
if (Array.isArray(source) || isSceneManifest(source)) return normalizeWebHostSceneManifest(source);
|
|
29
|
+
if (source instanceof URL) return loadWebHostSceneManifestFromResponse(await fetch(source));
|
|
30
|
+
if (source instanceof Request) return loadWebHostSceneManifestFromResponse(await fetch(source));
|
|
31
|
+
if (source instanceof Response) return loadWebHostSceneManifestFromResponse(source);
|
|
32
|
+
if (typeof source === "string") {
|
|
33
|
+
const trimmed = source.trim();
|
|
34
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return normalizeWebHostSceneManifest(JSON.parse(trimmed));
|
|
35
|
+
return loadWebHostSceneManifest(new URL(source, import.meta.url));
|
|
36
|
+
}
|
|
37
|
+
return normalizeWebHostSceneManifest(source);
|
|
38
|
+
}
|
|
39
|
+
function normalizeSceneDescriptors(source) {
|
|
40
|
+
if (Array.isArray(source)) return source.map(normalizeDescriptor);
|
|
41
|
+
if (isSceneManifest(source)) return source.scenes.map(normalizeDescriptor);
|
|
42
|
+
if (isObject(source) && Array.isArray(source.scenes)) return (source.scenes ?? []).map(normalizeDescriptor);
|
|
43
|
+
throw new Error("scene manifest must be an array or an object with scenes");
|
|
44
|
+
}
|
|
45
|
+
function normalizeDescriptor(value, index) {
|
|
46
|
+
if (!isObject(value)) throw new Error(`scene descriptor at index ${index ?? 0} must be an object`);
|
|
47
|
+
const id = String(value.id ?? "").trim();
|
|
48
|
+
if (!id) throw new Error(`scene descriptor at index ${index ?? 0} is missing an id`);
|
|
49
|
+
const titleValue = value.title;
|
|
50
|
+
const isDefaultValue = Boolean(value.isDefault);
|
|
51
|
+
return {
|
|
52
|
+
id,
|
|
53
|
+
title: typeof titleValue === "string" && titleValue.trim().length > 0 ? titleValue.trim() : void 0,
|
|
54
|
+
isDefault: isDefaultValue
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function isSceneManifest(value) {
|
|
58
|
+
return isObject(value) && typeof value.defaultSceneId === "string" && Array.isArray(value.scenes);
|
|
59
|
+
}
|
|
60
|
+
function isObject(value) {
|
|
61
|
+
return typeof value === "object" && value !== null;
|
|
62
|
+
}
|
|
63
|
+
async function loadWebHostSceneManifestFromResponse(response) {
|
|
64
|
+
if (!response.ok) throw new Error(`failed to load scene manifest: ${response.status} ${response.statusText}`);
|
|
65
|
+
return normalizeWebHostSceneManifest(await response.json());
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { loadWebHostSceneManifest, normalizeWebHostSceneManifest, webTUISceneManifestFromDescriptors, webTUISceneManifestToJSON };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=WebHostSceneManifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebHostSceneManifest.js","names":[],"sources":["../../src/WebHostSceneManifest.ts"],"sourcesContent":["export interface WebHostSceneDescriptor {\n id: string;\n title?: string;\n isDefault: boolean;\n}\n\nexport interface WebHostSceneManifest {\n defaultSceneId: string;\n scenes: WebHostSceneDescriptor[];\n}\n\nexport type WebHostSceneManifestSource =\n | WebHostSceneManifest\n | WebHostSceneDescriptor[]\n | string\n | URL\n | Request\n | Response;\n\nexport function normalizeWebHostSceneManifest(\n source: unknown\n): WebHostSceneManifest {\n const scenes = normalizeSceneDescriptors(source);\n if (scenes.length === 0) {\n throw new Error(\"scene manifest must contain at least one scene\");\n }\n\n const defaultScene = scenes.find((scene) => scene.isDefault) ?? scenes[0];\n return {\n defaultSceneId: defaultScene.id,\n scenes: scenes.map((scene, index) => ({\n ...scene,\n isDefault: scene.id === defaultScene.id || (index === 0 && !scenes.some((entry) => entry.isDefault)),\n })),\n };\n}\n\nexport function webTUISceneManifestToJSON(\n manifest: WebHostSceneManifest\n): string {\n return JSON.stringify({\n defaultSceneId: manifest.defaultSceneId,\n scenes: manifest.scenes.map((scene) => ({\n id: scene.id,\n ...(scene.title ? { title: scene.title } : {}),\n isDefault: scene.isDefault,\n })),\n });\n}\n\nexport function webTUISceneManifestFromDescriptors(\n descriptors: WebHostSceneDescriptor[]\n): WebHostSceneManifest {\n return normalizeWebHostSceneManifest(descriptors);\n}\n\nexport async function loadWebHostSceneManifest(\n source: WebHostSceneManifestSource\n): Promise<WebHostSceneManifest> {\n if (Array.isArray(source) || isSceneManifest(source)) {\n return normalizeWebHostSceneManifest(source);\n }\n\n if (source instanceof URL) {\n return loadWebHostSceneManifestFromResponse(await fetch(source));\n }\n\n if (source instanceof Request) {\n return loadWebHostSceneManifestFromResponse(await fetch(source));\n }\n\n if (source instanceof Response) {\n return loadWebHostSceneManifestFromResponse(source);\n }\n\n if (typeof source === \"string\") {\n const trimmed = source.trim();\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n return normalizeWebHostSceneManifest(JSON.parse(trimmed));\n }\n\n return loadWebHostSceneManifest(new URL(source, import.meta.url));\n }\n\n return normalizeWebHostSceneManifest(source);\n}\n\nfunction normalizeSceneDescriptors(\n source: unknown\n): WebHostSceneDescriptor[] {\n if (Array.isArray(source)) {\n return source.map(normalizeDescriptor);\n }\n\n if (isSceneManifest(source)) {\n return source.scenes.map(normalizeDescriptor);\n }\n\n if (isObject(source) && Array.isArray((source as { scenes?: unknown }).scenes)) {\n return ((source as { scenes: unknown[] }).scenes ?? []).map(normalizeDescriptor);\n }\n\n throw new Error(\"scene manifest must be an array or an object with scenes\");\n}\n\nfunction normalizeDescriptor(\n value: unknown,\n index?: number\n): WebHostSceneDescriptor {\n if (!isObject(value)) {\n throw new Error(`scene descriptor at index ${index ?? 0} must be an object`);\n }\n\n const id = String((value as { id?: unknown }).id ?? \"\").trim();\n if (!id) {\n throw new Error(`scene descriptor at index ${index ?? 0} is missing an id`);\n }\n\n const titleValue = (value as { title?: unknown }).title;\n const isDefaultValue = Boolean((value as { isDefault?: unknown }).isDefault);\n\n return {\n id,\n title:\n typeof titleValue === \"string\" && titleValue.trim().length > 0\n ? titleValue.trim()\n : undefined,\n isDefault: isDefaultValue,\n };\n}\n\nfunction isSceneManifest(\n value: unknown\n): value is WebHostSceneManifest {\n return (\n isObject(value) &&\n typeof (value as { defaultSceneId?: unknown }).defaultSceneId === \"string\" &&\n Array.isArray((value as { scenes?: unknown }).scenes)\n );\n}\n\nfunction isObject(\n value: unknown\n): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nasync function loadWebHostSceneManifestFromResponse(\n response: Response\n): Promise<WebHostSceneManifest> {\n if (!response.ok) {\n throw new Error(`failed to load scene manifest: ${response.status} ${response.statusText}`);\n }\n\n return normalizeWebHostSceneManifest(await response.json());\n}\n"],"mappings":";AAmBA,SAAgB,8BACd,QACsB;CACtB,MAAM,SAAS,0BAA0B,MAAM;CAC/C,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,eAAe,OAAO,MAAM,UAAU,MAAM,SAAS,KAAK,OAAO;CACvE,OAAO;EACL,gBAAgB,aAAa;EAC7B,QAAQ,OAAO,KAAK,OAAO,WAAW;GACpC,GAAG;GACH,WAAW,MAAM,OAAO,aAAa,MAAO,UAAU,KAAK,CAAC,OAAO,MAAM,UAAU,MAAM,SAAS;EACpG,EAAE;CACJ;AACF;AAEA,SAAgB,0BACd,UACQ;CACR,OAAO,KAAK,UAAU;EACpB,gBAAgB,SAAS;EACzB,QAAQ,SAAS,OAAO,KAAK,WAAW;GACtC,IAAI,MAAM;GACV,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;GAC5C,WAAW,MAAM;EACnB,EAAE;CACJ,CAAC;AACH;AAEA,SAAgB,mCACd,aACsB;CACtB,OAAO,8BAA8B,WAAW;AAClD;AAEA,eAAsB,yBACpB,QAC+B;CAC/B,IAAI,MAAM,QAAQ,MAAM,KAAK,gBAAgB,MAAM,GACjD,OAAO,8BAA8B,MAAM;CAG7C,IAAI,kBAAkB,KACpB,OAAO,qCAAqC,MAAM,MAAM,MAAM,CAAC;CAGjE,IAAI,kBAAkB,SACpB,OAAO,qCAAqC,MAAM,MAAM,MAAM,CAAC;CAGjE,IAAI,kBAAkB,UACpB,OAAO,qCAAqC,MAAM;CAGpD,IAAI,OAAO,WAAW,UAAU;EAC9B,MAAM,UAAU,OAAO,KAAK;EAC5B,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,GACnD,OAAO,8BAA8B,KAAK,MAAM,OAAO,CAAC;EAG1D,OAAO,yBAAyB,IAAI,IAAI,QAAQ,OAAO,KAAK,GAAG,CAAC;CAClE;CAEA,OAAO,8BAA8B,MAAM;AAC7C;AAEA,SAAS,0BACP,QAC0B;CAC1B,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,OAAO,IAAI,mBAAmB;CAGvC,IAAI,gBAAgB,MAAM,GACxB,OAAO,OAAO,OAAO,IAAI,mBAAmB;CAG9C,IAAI,SAAS,MAAM,KAAK,MAAM,QAAS,OAAgC,MAAM,GAC3E,QAAS,OAAiC,UAAU,CAAC,EAAA,CAAG,IAAI,mBAAmB;CAGjF,MAAM,IAAI,MAAM,0DAA0D;AAC5E;AAEA,SAAS,oBACP,OACA,OACwB;CACxB,IAAI,CAAC,SAAS,KAAK,GACjB,MAAM,IAAI,MAAM,6BAA6B,SAAS,EAAE,mBAAmB;CAG7E,MAAM,KAAK,OAAQ,MAA2B,MAAM,EAAE,CAAC,CAAC,KAAK;CAC7D,IAAI,CAAC,IACH,MAAM,IAAI,MAAM,6BAA6B,SAAS,EAAE,kBAAkB;CAG5E,MAAM,aAAc,MAA8B;CAClD,MAAM,iBAAiB,QAAS,MAAkC,SAAS;CAE3E,OAAO;EACL;EACA,OACE,OAAO,eAAe,YAAY,WAAW,KAAK,CAAC,CAAC,SAAS,IACzD,WAAW,KAAK,IAChB,KAAA;EACN,WAAW;CACb;AACF;AAEA,SAAS,gBACP,OAC+B;CAC/B,OACE,SAAS,KAAK,KACd,OAAQ,MAAuC,mBAAmB,YAClE,MAAM,QAAS,MAA+B,MAAM;AAExD;AAEA,SAAS,SACP,OACkC;CAClC,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,eAAe,qCACb,UAC+B;CAC/B,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,kCAAkC,SAAS,OAAO,GAAG,SAAS,YAAY;CAG5F,OAAO,8BAA8B,MAAM,SAAS,KAAK,CAAC;AAC5D"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { WebHostTerminalStyle } from "./WebHostTerminalStyle.js";
|
|
2
|
+
import { WebHostFrameDiagnosticRecord, WebHostOutputSink, WebHostRuntimeIssue } from "./WebHostSurfaceTransport.js";
|
|
3
|
+
import { WebHostSceneDescriptor } from "./WebHostSceneManifest.js";
|
|
4
|
+
|
|
5
|
+
//#region src/WebHostSceneRuntime.d.ts
|
|
6
|
+
interface WebHostSceneBridge {
|
|
7
|
+
bindOutput(sink: WebHostOutputSink): void;
|
|
8
|
+
resize(columns: number, rows: number, cellWidth?: number, cellHeight?: number): void;
|
|
9
|
+
updateRenderStyle(style: WebHostTerminalStyle): void;
|
|
10
|
+
sendInput(chunk: Uint8Array): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
interface WebHostSceneRuntimeOptions {
|
|
14
|
+
mount: HTMLElement;
|
|
15
|
+
descriptor: WebHostSceneDescriptor;
|
|
16
|
+
style: WebHostTerminalStyle;
|
|
17
|
+
bridge?: WebHostSceneBridge;
|
|
18
|
+
onInput(chunk: Uint8Array): void;
|
|
19
|
+
onFrameDiagnostic?: (diagnostic: WebHostFrameDiagnosticRecord) => void;
|
|
20
|
+
synchronizeAccessibilityFocus?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* How the embedded view treats mouse-wheel input.
|
|
23
|
+
* - `"capture"`: always forward the wheel to the app while the pointer is over
|
|
24
|
+
* the surface (and `preventDefault` page scroll). Legacy default.
|
|
25
|
+
* - `"chain"`: forward the wheel only while a scrollable region under the
|
|
26
|
+
* pointer can still scroll in that direction; otherwise let it fall through
|
|
27
|
+
* so the page (or parent iframe) scrolls. Requires the app to publish
|
|
28
|
+
* `scrollRegions` in its frames.
|
|
29
|
+
* - `"passive"`: never capture; the page always scrolls.
|
|
30
|
+
*
|
|
31
|
+
* Takes precedence over the legacy `captureWheelInput` flag.
|
|
32
|
+
*/
|
|
33
|
+
wheelMode?: WheelMode;
|
|
34
|
+
/**
|
|
35
|
+
* Legacy boolean wheel gate. `true` → `"capture"`, `false` → `"passive"`.
|
|
36
|
+
* Prefer `wheelMode`. Ignored when `wheelMode` is set.
|
|
37
|
+
*/
|
|
38
|
+
captureWheelInput?: boolean;
|
|
39
|
+
}
|
|
40
|
+
type WheelMode = "capture" | "chain" | "passive";
|
|
41
|
+
declare class WebHostSceneRuntime {
|
|
42
|
+
readonly descriptor: WebHostSceneDescriptor;
|
|
43
|
+
readonly element: HTMLElement;
|
|
44
|
+
readonly terminalMount: HTMLElement;
|
|
45
|
+
private readonly bridge?;
|
|
46
|
+
private readonly onInput;
|
|
47
|
+
private readonly onFrameDiagnostic?;
|
|
48
|
+
private readonly synchronizeAccessibilityFocus;
|
|
49
|
+
private readonly wheelMode;
|
|
50
|
+
private readonly imageCache;
|
|
51
|
+
private currentStyle;
|
|
52
|
+
private canvas?;
|
|
53
|
+
private accessibilityTree?;
|
|
54
|
+
private diagnosticText?;
|
|
55
|
+
private resizeObserver?;
|
|
56
|
+
private detachInputHandlers?;
|
|
57
|
+
private currentFrame?;
|
|
58
|
+
private columns;
|
|
59
|
+
private rows;
|
|
60
|
+
private cellWidth;
|
|
61
|
+
private cellHeight;
|
|
62
|
+
private activePointerButton;
|
|
63
|
+
private hasCapturedPointer;
|
|
64
|
+
private lastSentResize?;
|
|
65
|
+
private isVisible;
|
|
66
|
+
constructor(options: WebHostSceneRuntimeOptions);
|
|
67
|
+
mount(): Promise<void>;
|
|
68
|
+
setVisible(visible: boolean): void;
|
|
69
|
+
setStyle(style: WebHostTerminalStyle): void;
|
|
70
|
+
resize(columns: number, rows: number): void;
|
|
71
|
+
writeOutput(text: string): void;
|
|
72
|
+
notifyRuntimeIssue(issue: WebHostRuntimeIssue): void;
|
|
73
|
+
private recordFrameDiagnostic;
|
|
74
|
+
writeClipboard(text: string): Promise<void>;
|
|
75
|
+
sendInput(chunk: Uint8Array): void;
|
|
76
|
+
dispose(): void;
|
|
77
|
+
private presentSurface;
|
|
78
|
+
private applyStyle;
|
|
79
|
+
private applyVisibility;
|
|
80
|
+
private installResizeObserver;
|
|
81
|
+
private installInputHandlers;
|
|
82
|
+
private resizeToMount;
|
|
83
|
+
private sendResizeIfNeeded;
|
|
84
|
+
private resizeCanvas;
|
|
85
|
+
private measureCells;
|
|
86
|
+
private draw;
|
|
87
|
+
private drawRows;
|
|
88
|
+
private drawRow;
|
|
89
|
+
private syncAccessibilityTree;
|
|
90
|
+
private drawImages;
|
|
91
|
+
private drawImage;
|
|
92
|
+
private cachedImage;
|
|
93
|
+
private drawCell;
|
|
94
|
+
private dirtyRegionForDamage;
|
|
95
|
+
private cellRect;
|
|
96
|
+
private drawTextLine;
|
|
97
|
+
private fontForStyle;
|
|
98
|
+
/**
|
|
99
|
+
* Whether any scrollable region under `location` can still scroll in the
|
|
100
|
+
* wheel's direction. Mirrors the Swift host's scroll hit-test: a region
|
|
101
|
+
* qualifies when its viewport contains the cell AND it has remaining headroom
|
|
102
|
+
* in the delta's direction. Used by "chain" wheel mode to decide capture vs.
|
|
103
|
+
* fall-through. With no published `scrollRegions`, nothing can scroll, so the
|
|
104
|
+
* wheel chains to the page (a scene with no ScrollView stays fully passive).
|
|
105
|
+
*/
|
|
106
|
+
private wheelTargetCanScroll;
|
|
107
|
+
private cellLocation;
|
|
108
|
+
private rawCellLocation;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { WebHostSceneBridge, WebHostSceneRuntime, WebHostSceneRuntimeOptions, WheelMode };
|
|
112
|
+
//# sourceMappingURL=WebHostSceneRuntime.d.ts.map
|