@swifttui/web 0.0.27 → 0.1.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"WebHostSceneRuntime.js","names":[],"sources":["../../src/WebHostSceneRuntime.ts"],"sourcesContent":["import {\n applyWebHostTerminalStyle,\n normalizeWebHostTerminalStyle,\n type ResolvedWebHostTerminalStyle,\n type WebHostTerminalStyle,\n webTUITerminalBackgroundColor,\n} from \"./WebHostTerminalStyle.ts\";\nimport {\n canRenderBoxDrawing,\n drawBoxDrawing,\n} from \"./BoxDrawingRenderer.ts\";\nimport { AccessibilityTreeMounter } from \"./AccessibilityTree.ts\";\nimport {\n encodeKeyInputMessage,\n encodeMouseInputMessage,\n encodePasteInputMessage,\n type WebHostFrameDiagnosticRecord,\n type WebHostOutputSink,\n type WebHostKeyInput,\n type WebHostRuntimeIssue,\n type WebHostScrollRegion,\n type WebHostSurfaceDamage,\n type WebHostSurfaceFrame,\n type WebHostSurfaceImage,\n type WebHostSurfaceImageFormat,\n type WebHostSurfaceStyle,\n} from \"./WebHostSurfaceTransport.ts\";\nimport type { WebHostSceneDescriptor } from \"./WebHostSceneManifest.ts\";\n\nexport interface WebHostSceneBridge {\n bindOutput(sink: WebHostOutputSink): void;\n resize(columns: number, rows: number, cellWidth?: number, cellHeight?: number): void;\n updateRenderStyle(style: WebHostTerminalStyle): void;\n sendInput(chunk: Uint8Array): void;\n dispose(): void;\n}\n\nexport interface WebHostSceneRuntimeOptions {\n mount: HTMLElement;\n descriptor: WebHostSceneDescriptor;\n style: WebHostTerminalStyle;\n bridge?: WebHostSceneBridge;\n onInput(chunk: Uint8Array): void;\n onFrameDiagnostic?: (diagnostic: WebHostFrameDiagnosticRecord) => void;\n synchronizeAccessibilityFocus?: boolean;\n /**\n * How the embedded view treats mouse-wheel input.\n * - `\"chain\"` (default): forward the wheel only while a scrollable region\n * under the pointer can still scroll in that direction; otherwise let it\n * fall through so the page (or parent iframe) scrolls — iframe-like nested\n * scrolling. A scene with no `ScrollView` never traps the wheel. Uses the\n * `scrollRegions` the app publishes in its frames.\n * - `\"capture\"`: always forward the wheel to the app while the pointer is over\n * the surface (and `preventDefault` page scroll). Best for full-screen apps\n * where there is no page to scroll past.\n * - `\"passive\"`: never capture; the page always scrolls.\n *\n * Takes precedence over the legacy `captureWheelInput` flag.\n */\n wheelMode?: WheelMode;\n /**\n * Legacy boolean wheel gate. `true` → `\"capture\"`, `false` → `\"passive\"`.\n * Prefer `wheelMode`. Ignored when `wheelMode` is set. When neither is set the\n * mode defaults to `\"chain\"`.\n */\n captureWheelInput?: boolean;\n}\n\nexport type WheelMode = \"capture\" | \"chain\" | \"passive\";\n\n/**\n * Resolves the legacy `captureWheelInput` flag to a {@link WheelMode}. When the\n * flag is unset the mode defaults to `\"chain\"`, so embeds never trap a visitor\n * who is merely scrolling past the view; `true` maps to `\"capture\"` and `false`\n * to `\"passive\"` to preserve the old boolean behavior.\n */\nfunction legacyWheelMode(captureWheelInput: boolean | undefined): WheelMode {\n if (captureWheelInput === undefined) {\n return \"chain\";\n }\n return captureWheelInput ? \"capture\" : \"passive\";\n}\n\ninterface CachedWebHostImage {\n image?: CanvasImageSource;\n promise?: Promise<CanvasImageSource>;\n}\n\ninterface DirtyRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface DirtyCellRange {\n start: number;\n end: number;\n}\n\ntype DirtyRowRanges = \"full\" | DirtyCellRange[];\n\ninterface DirtyRegion {\n rects: DirtyRect[];\n rows: Map<number, DirtyRowRanges>;\n}\n\nexport class WebHostSceneRuntime {\n readonly descriptor: WebHostSceneDescriptor;\n readonly element: HTMLElement;\n readonly terminalMount: HTMLElement;\n\n private readonly bridge?: WebHostSceneBridge;\n private readonly onInput: (chunk: Uint8Array) => void;\n private readonly onFrameDiagnostic?: (diagnostic: WebHostFrameDiagnosticRecord) => void;\n private readonly synchronizeAccessibilityFocus: boolean;\n private readonly wheelMode: WheelMode;\n private readonly imageCache = new Map<string, CachedWebHostImage>();\n private currentStyle: ResolvedWebHostTerminalStyle;\n private canvas?: HTMLCanvasElement;\n private accessibilityTree?: AccessibilityTreeMounter;\n private diagnosticText?: HTMLElement;\n private resizeObserver?: ResizeObserver;\n private detachInputHandlers?: () => void;\n private currentFrame?: WebHostSurfaceFrame;\n private columns = 80;\n private rows = 24;\n private cellWidth = 8;\n private cellHeight = 18;\n private activePointerButton: \"primary\" | \"middle\" | \"secondary\" = \"primary\";\n private hasCapturedPointer = false;\n private lastSentResize?: {\n columns: number;\n rows: number;\n cellWidth: number;\n cellHeight: number;\n };\n private isVisible = false;\n\n constructor(options: WebHostSceneRuntimeOptions) {\n this.descriptor = options.descriptor;\n this.currentStyle = normalizeWebHostTerminalStyle(options.style);\n this.bridge = options.bridge;\n this.onInput = options.onInput;\n this.onFrameDiagnostic = options.onFrameDiagnostic;\n this.synchronizeAccessibilityFocus = options.synchronizeAccessibilityFocus ?? true;\n this.wheelMode = options.wheelMode ?? legacyWheelMode(options.captureWheelInput);\n this.element = document.createElement(\"section\");\n this.element.className = \"webhost-scene\";\n this.element.dataset.sceneId = options.descriptor.id;\n this.element.hidden = true;\n\n const header = document.createElement(\"div\");\n header.className = \"webhost-scene__header\";\n header.textContent = options.descriptor.title ?? options.descriptor.id;\n\n this.terminalMount = document.createElement(\"div\");\n this.terminalMount.className = \"webhost-scene__terminal\";\n this.terminalMount.tabIndex = 0;\n\n this.element.append(header, this.terminalMount);\n options.mount.appendChild(this.element);\n this.applyVisibility();\n }\n\n async mount(): Promise<void> {\n if (this.canvas) {\n return;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"webhost-scene__surface\";\n canvas.setAttribute(\"aria-hidden\", \"true\");\n this.canvas = canvas;\n this.accessibilityTree = new AccessibilityTreeMounter();\n this.terminalMount.replaceChildren(\n canvas,\n this.accessibilityTree.element,\n this.accessibilityTree.announcerElement\n );\n this.installInputHandlers();\n this.installResizeObserver();\n\n this.bridge?.bindOutput({\n presentSurface: (frame) => this.presentSurface(frame),\n writeClipboard: (text) => this.writeClipboard(text),\n notifyRuntimeIssue: (issue) => this.notifyRuntimeIssue(issue),\n recordFrameDiagnostic: (diagnostic) => this.recordFrameDiagnostic(diagnostic),\n writeOutput: (text) => this.writeOutput(text),\n writeError: (text) => this.writeOutput(text),\n });\n\n this.applyStyle(this.currentStyle);\n this.measureCells();\n this.resizeToMount();\n this.draw();\n this.syncAccessibilityTree();\n }\n\n setVisible(\n visible: boolean\n ): void {\n this.isVisible = visible;\n this.applyVisibility();\n if (visible) {\n this.resizeToMount();\n if (this.synchronizeAccessibilityFocus) {\n this.terminalMount.focus?.({ preventScroll: true });\n }\n }\n }\n\n setStyle(\n style: WebHostTerminalStyle\n ): void {\n this.currentStyle = normalizeWebHostTerminalStyle(style);\n this.applyStyle(this.currentStyle);\n this.bridge?.updateRenderStyle(this.currentStyle);\n this.measureCells();\n this.resizeToMount();\n this.draw();\n this.syncAccessibilityTree();\n }\n\n resize(\n columns: number,\n rows: number\n ): void {\n this.columns = Math.max(1, Math.round(columns));\n this.rows = Math.max(1, Math.round(rows));\n this.resizeCanvas();\n this.draw();\n this.syncAccessibilityTree();\n }\n\n writeOutput(\n text: string\n ): void {\n if (!this.diagnosticText) {\n const diagnosticText = document.createElement(\"pre\");\n diagnosticText.className = \"webhost-scene__diagnostic\";\n this.diagnosticText = diagnosticText;\n this.terminalMount.appendChild(diagnosticText);\n }\n this.diagnosticText.textContent = `${this.diagnosticText.textContent ?? \"\"}${text}`;\n }\n\n notifyRuntimeIssue(\n issue: WebHostRuntimeIssue\n ): void {\n console.log(issue.description);\n }\n\n private recordFrameDiagnostic(\n diagnostic: WebHostFrameDiagnosticRecord\n ): void {\n this.onFrameDiagnostic?.(diagnostic);\n }\n\n async writeClipboard(\n text: string\n ): Promise<void> {\n const clipboard = globalThis.navigator?.clipboard;\n if (!clipboard?.writeText) {\n return;\n }\n\n try {\n await clipboard.writeText(text);\n } catch {\n // Clipboard permissions are browser/user-gesture dependent; hosts treat\n // rejection as a best-effort no-op rather than surfacing diagnostics.\n }\n }\n\n sendInput(\n chunk: Uint8Array\n ): void {\n this.onInput(chunk);\n }\n\n dispose(): void {\n this.detachInputHandlers?.();\n this.resizeObserver?.disconnect();\n this.element.remove();\n }\n\n private presentSurface(\n frame: WebHostSurfaceFrame\n ): void {\n const previousFrame = this.currentFrame;\n this.currentFrame = frame;\n this.columns = Math.max(1, Math.round(frame.width));\n this.rows = Math.max(1, Math.round(frame.height));\n const resized = this.resizeCanvas();\n this.draw(previousFrame && !resized ? frame.damage : undefined);\n this.syncAccessibilityTree();\n }\n\n private applyStyle(\n style: WebHostTerminalStyle\n ): void {\n applyWebHostTerminalStyle(this.element, style);\n this.element.style.padding = \"0.75rem\";\n this.element.style.borderRadius = \"16px\";\n this.element.style.boxShadow = \"0 20px 50px rgba(0, 0, 0, 0.28)\";\n this.element.style.overflow = \"hidden\";\n this.element.style.gap = \"0.5rem\";\n this.element.style.gridTemplateRows = \"auto 1fr\";\n\n this.terminalMount.style.position = \"relative\";\n this.terminalMount.style.overflow = \"hidden\";\n // Keep a captured wheel from rubber-banding/chaining the page; the wheel\n // capture vs. fall-through decision lives in handleWheel.\n this.terminalMount.style.overscrollBehavior = \"contain\";\n this.terminalMount.style.outline = \"none\";\n this.terminalMount.style.background = webTUITerminalBackgroundColor(this.currentStyle);\n this.terminalMount.style.minHeight = `${this.cellHeight * 8}px`;\n\n if (this.canvas) {\n this.canvas.style.display = \"block\";\n this.canvas.style.width = \"100%\";\n this.canvas.style.height = \"100%\";\n }\n }\n\n private applyVisibility(): void {\n this.element.hidden = !this.isVisible;\n this.element.style.setProperty(\n \"display\",\n this.isVisible ? \"grid\" : \"none\",\n \"important\"\n );\n }\n\n private installResizeObserver(): void {\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n\n this.resizeObserver = new ResizeObserver(() => {\n this.resizeToMount();\n });\n this.resizeObserver.observe(this.terminalMount);\n }\n\n private installInputHandlers(): void {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.metaKey || event.isComposing) {\n return;\n }\n const key = keyInputFromKeyboardEvent(event);\n if (!key) {\n return;\n }\n\n this.onInput(encodeKeyInputMessage({\n ...key,\n modifiers: modifierMask(event),\n }));\n event.preventDefault();\n };\n\n const handlePaste = (event: ClipboardEvent) => {\n const text = event.clipboardData?.getData(\"text/plain\") ?? \"\";\n if (!text) {\n return;\n }\n this.onInput(encodePasteInputMessage(text));\n event.preventDefault();\n };\n\n const handlePointerDown = (event: PointerEvent) => {\n const location = this.cellLocation(event);\n if (!location) {\n return;\n }\n\n const button = pointerButton(event.button);\n this.activePointerButton = button;\n this.hasCapturedPointer = true;\n this.terminalMount.focus?.({ preventScroll: true });\n this.terminalMount.setPointerCapture?.(event.pointerId);\n this.onInput(encodeMouseInputMessage({\n kind: \"down\",\n x: location.x,\n y: location.y,\n button,\n modifiers: modifierMask(event),\n }));\n event.preventDefault();\n };\n\n const handlePointerUp = (event: PointerEvent) => {\n const location = this.hasCapturedPointer\n ? this.rawCellLocation(event)\n : this.cellLocation(event);\n this.terminalMount.releasePointerCapture?.(event.pointerId);\n this.hasCapturedPointer = false;\n if (!location) {\n return;\n }\n\n this.onInput(encodeMouseInputMessage({\n kind: \"up\",\n x: location.x,\n y: location.y,\n button: pointerButton(event.button) ?? this.activePointerButton,\n modifiers: modifierMask(event),\n }));\n event.preventDefault();\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n const location = event.buttons && this.hasCapturedPointer\n ? this.rawCellLocation(event)\n : this.cellLocation(event);\n if (!location) {\n return;\n }\n\n this.onInput(encodeMouseInputMessage({\n kind: event.buttons ? \"dragged\" : \"moved\",\n x: location.x,\n y: location.y,\n button: this.activePointerButton,\n modifiers: modifierMask(event),\n }));\n };\n\n const handleWheel = (event: WheelEvent) => {\n if (this.wheelMode === \"passive\") {\n return;\n }\n\n const location = this.cellLocation(event);\n if (!location) {\n // Pointer is outside the cell grid (sub-cell margin / gutter). Don't\n // capture — let the wheel fall through to the page.\n return;\n }\n\n // In \"chain\" mode, capture only while a scrollable region under the\n // pointer can still move in this direction; otherwise let the wheel fall\n // through so the page (or parent iframe) scrolls — iframe-like behavior.\n // \"capture\" mode always forwards while over the surface (legacy).\n if (this.wheelMode === \"chain\"\n && !this.wheelTargetCanScroll(location, event.deltaX, event.deltaY)) {\n return;\n }\n\n this.onInput(encodeMouseInputMessage({\n kind: \"scrolled\",\n x: location.x,\n y: location.y,\n deltaX: normalizedWheelDelta(event.deltaX),\n deltaY: normalizedWheelDelta(event.deltaY),\n modifiers: modifierMask(event),\n }));\n event.preventDefault();\n };\n\n this.terminalMount.addEventListener(\"keydown\", handleKeyDown);\n this.terminalMount.addEventListener(\"paste\", handlePaste);\n this.terminalMount.addEventListener(\"pointerdown\", handlePointerDown);\n this.terminalMount.addEventListener(\"pointerup\", handlePointerUp);\n this.terminalMount.addEventListener(\"pointermove\", handlePointerMove);\n this.terminalMount.addEventListener(\"wheel\", handleWheel, { passive: false });\n\n this.detachInputHandlers = () => {\n this.terminalMount.removeEventListener(\"keydown\", handleKeyDown);\n this.terminalMount.removeEventListener(\"paste\", handlePaste);\n this.terminalMount.removeEventListener(\"pointerdown\", handlePointerDown);\n this.terminalMount.removeEventListener(\"pointerup\", handlePointerUp);\n this.terminalMount.removeEventListener(\"pointermove\", handlePointerMove);\n this.terminalMount.removeEventListener(\"wheel\", handleWheel);\n };\n }\n\n private resizeToMount(): void {\n this.measureCells();\n const rect = this.terminalMount.getBoundingClientRect?.();\n const width = rect?.width && rect.width > 0 ? rect.width : this.columns * this.cellWidth;\n const height = rect?.height && rect.height > 0 ? rect.height : this.rows * this.cellHeight;\n const nextColumns = Math.max(1, Math.floor(width / this.cellWidth));\n const nextRows = Math.max(1, Math.floor(height / this.cellHeight));\n\n this.columns = nextColumns;\n this.rows = nextRows;\n this.sendResizeIfNeeded();\n this.resizeCanvas();\n }\n\n private sendResizeIfNeeded(): void {\n const current = {\n columns: this.columns,\n rows: this.rows,\n cellWidth: this.cellWidth,\n cellHeight: this.cellHeight,\n };\n if (this.lastSentResize\n && this.lastSentResize.columns === current.columns\n && this.lastSentResize.rows === current.rows\n && this.lastSentResize.cellWidth === current.cellWidth\n && this.lastSentResize.cellHeight === current.cellHeight\n ) {\n return;\n }\n\n this.lastSentResize = current;\n this.bridge?.resize(current.columns, current.rows, current.cellWidth, current.cellHeight);\n }\n\n private resizeCanvas(): boolean {\n if (!this.canvas) {\n return false;\n }\n\n const cssWidth = Math.max(1, this.columns * this.cellWidth);\n const cssHeight = Math.max(1, this.rows * this.cellHeight);\n const scale = globalThis.window?.devicePixelRatio || 1;\n const width = Math.ceil(cssWidth * scale);\n const height = Math.ceil(cssHeight * scale);\n const styleWidth = `${cssWidth}px`;\n const styleHeight = `${cssHeight}px`;\n if (this.canvas.width === width\n && this.canvas.height === height\n && this.canvas.style.width === styleWidth\n && this.canvas.style.height === styleHeight\n ) {\n return false;\n }\n\n this.canvas.width = width;\n this.canvas.height = height;\n this.canvas.style.width = styleWidth;\n this.canvas.style.height = styleHeight;\n return true;\n }\n\n private measureCells(): void {\n const canvas = this.canvas ?? document.createElement(\"canvas\");\n const context = canvas.getContext?.(\"2d\");\n if (!context) {\n this.cellWidth = Math.max(1, Math.round(this.currentStyle.fontSize * 0.62));\n this.cellHeight = Math.max(1, Math.round(this.currentStyle.fontSize * 1.35));\n return;\n }\n\n context.font = this.fontForStyle();\n this.cellWidth = Math.max(1, Math.ceil(context.measureText(\"W\").width));\n this.cellHeight = Math.max(1, Math.ceil(this.currentStyle.fontSize * 1.35));\n }\n\n private draw(\n damage?: WebHostSurfaceDamage\n ): void {\n const canvas = this.canvas;\n const context = canvas?.getContext(\"2d\");\n if (!canvas || !context) {\n return;\n }\n\n const frame = this.currentFrame;\n const dirtyRegion = frame ? this.dirtyRegionForDamage(damage, frame) : undefined;\n if (dirtyRegion?.rects.length === 0) {\n return;\n }\n\n const scale = globalThis.window?.devicePixelRatio || 1;\n context.setTransform(scale, 0, 0, scale, 0, 0);\n context.textBaseline = \"alphabetic\";\n\n context.fillStyle = webTUITerminalBackgroundColor(this.currentStyle);\n if (dirtyRegion) {\n for (const rect of dirtyRegion.rects) {\n context.clearRect(rect.x, rect.y, rect.width, rect.height);\n context.fillRect(rect.x, rect.y, rect.width, rect.height);\n }\n } else {\n context.clearRect(0, 0, canvas.width / scale, canvas.height / scale);\n context.fillRect(0, 0, this.columns * this.cellWidth, this.rows * this.cellHeight);\n }\n\n if (!frame) {\n return;\n }\n\n this.drawRows(context, frame, dirtyRegion);\n this.drawImages(context, frame.images ?? [], dirtyRegion);\n }\n\n private drawRows(\n context: CanvasRenderingContext2D,\n frame: WebHostSurfaceFrame,\n dirtyRegion?: DirtyRegion\n ): void {\n if (dirtyRegion) {\n for (const [y, ranges] of dirtyRegion.rows) {\n const row = frame.rows[y] ?? [];\n this.drawRow(context, frame, row, y, ranges);\n }\n return;\n }\n\n for (let y = 0; y < frame.rows.length; y += 1) {\n const row = frame.rows[y] ?? [];\n this.drawRow(context, frame, row, y);\n }\n }\n\n private drawRow(\n context: CanvasRenderingContext2D,\n frame: WebHostSurfaceFrame,\n row: WebHostSurfaceFrame[\"rows\"][number],\n y: number,\n ranges?: DirtyRowRanges\n ): void {\n for (const cell of row) {\n const [x, text, span, styleIndex] = cell;\n if (ranges !== undefined && !cellIntersectsRanges(x, span, ranges)) {\n continue;\n }\n const style = frame.styles[styleIndex] ?? undefined;\n this.drawCell(context, x, y, text, span, style);\n }\n }\n\n private syncAccessibilityTree(): void {\n const tree = this.accessibilityTree;\n if (!tree || !this.currentFrame) {\n return;\n }\n\n tree.present(this.currentFrame.accessibilityTree ?? [], {\n cellWidth: this.cellWidth,\n cellHeight: this.cellHeight,\n }, this.currentFrame.accessibilityAnnouncements ?? [], {\n synchronizeFocus: this.synchronizeAccessibilityFocus,\n });\n }\n\n private drawImages(\n context: CanvasRenderingContext2D,\n images: WebHostSurfaceImage[],\n dirtyRegion?: DirtyRegion\n ): void {\n for (const image of images) {\n this.drawImage(context, image, dirtyRegion);\n }\n }\n\n private drawImage(\n context: CanvasRenderingContext2D,\n image: WebHostSurfaceImage,\n dirtyRegion?: DirtyRegion\n ): void {\n const decodedImage = this.cachedImage(image);\n if (!decodedImage) {\n return;\n }\n\n const [boundsX, boundsY, boundsWidth, boundsHeight] = image.bounds;\n const [clipX, clipY, clipWidth, clipHeight] = image.visibleBounds;\n if (boundsWidth <= 0 || boundsHeight <= 0 || clipWidth <= 0 || clipHeight <= 0) {\n return;\n }\n if (\n dirtyRegion\n && !dirtyRegionIntersectsCellRect(dirtyRegion, clipX, clipY, clipWidth, clipHeight)\n ) {\n return;\n }\n\n context.save();\n context.beginPath();\n context.rect(\n clipX * this.cellWidth,\n clipY * this.cellHeight,\n clipWidth * this.cellWidth,\n clipHeight * this.cellHeight\n );\n context.clip();\n context.drawImage(\n decodedImage,\n boundsX * this.cellWidth,\n boundsY * this.cellHeight,\n boundsWidth * this.cellWidth,\n boundsHeight * this.cellHeight\n );\n context.restore();\n }\n\n private cachedImage(\n image: WebHostSurfaceImage\n ): CanvasImageSource | undefined {\n const cached = this.imageCache.get(image.id);\n if (cached?.image) {\n return cached.image;\n }\n\n if (!cached?.promise && image.dataBase64) {\n const promise = decodeImage(image.dataBase64, image.format);\n this.imageCache.set(image.id, { promise });\n void promise.then((decodedImage) => {\n const latest = this.imageCache.get(image.id);\n if (latest?.promise !== promise) {\n return;\n }\n this.imageCache.set(image.id, { image: decodedImage });\n this.draw();\n }).catch(() => {\n this.imageCache.delete(image.id);\n });\n }\n\n return undefined;\n }\n\n private drawCell(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n text: string,\n span: number,\n style?: WebHostSurfaceStyle | null\n ): void {\n const rectX = x * this.cellWidth;\n const rectY = y * this.cellHeight;\n const width = Math.max(1, span) * this.cellWidth;\n const background = resolvedBackground(style, this.currentStyle);\n const foreground = resolvedForeground(style, this.currentStyle);\n const opacity = style?.opacity ?? 1;\n\n if (background) {\n context.globalAlpha = opacity;\n context.fillStyle = background;\n context.fillRect(rectX, rectY, width, this.cellHeight);\n }\n\n if (text !== \" \") {\n context.globalAlpha = opacity;\n context.fillStyle = foreground;\n context.strokeStyle = foreground;\n if (!canRenderBoxDrawing(text) || !drawBoxDrawing(context, text, {\n x: rectX,\n y: rectY,\n width,\n height: this.cellHeight,\n })) {\n context.font = this.fontForStyle(style);\n context.fillText(\n text,\n rectX,\n rectY + Math.floor((this.cellHeight + this.currentStyle.fontSize) / 2) - 2\n );\n }\n }\n\n this.drawTextLine(context, rectX, rectY, width, style?.underline, \"underline\", foreground);\n this.drawTextLine(context, rectX, rectY, width, style?.strikethrough, \"strike\", foreground);\n context.globalAlpha = 1;\n }\n\n private dirtyRegionForDamage(\n damage: WebHostSurfaceDamage | undefined,\n frame: WebHostSurfaceFrame\n ): DirtyRegion | undefined {\n if (!damage || damage.requiresFullTextRepaint || damage.requiresFullGraphicsReplay) {\n return undefined;\n }\n\n const rects: DirtyRect[] = [];\n const rows = new Map<number, DirtyRowRanges>();\n for (const [row, ranges] of damage.textRows) {\n if (row < 0 || row >= frame.height) {\n continue;\n }\n if (ranges.length === 0) {\n rects.push(this.cellRect(0, row, frame.width));\n rows.set(row, \"full\");\n continue;\n }\n const rowRanges: DirtyCellRange[] = rows.get(row) === \"full\"\n ? []\n : [...(rows.get(row) as DirtyCellRange[] | undefined ?? [])];\n for (const [start, end] of ranges) {\n const lowerBound = Math.max(0, Math.min(frame.width, Math.floor(start)));\n const upperBound = Math.max(lowerBound, Math.min(frame.width, Math.ceil(end)));\n if (lowerBound >= upperBound) {\n continue;\n }\n rects.push(this.cellRect(lowerBound, row, upperBound - lowerBound));\n rowRanges.push({ start: lowerBound, end: upperBound });\n }\n if (rows.get(row) !== \"full\" && rowRanges.length > 0) {\n rows.set(row, normalizeCellRanges(rowRanges));\n }\n }\n return { rects, rows };\n }\n\n private cellRect(\n x: number,\n y: number,\n span: number\n ): DirtyRect {\n return {\n x: x * this.cellWidth,\n y: y * this.cellHeight,\n width: Math.max(1, span) * this.cellWidth,\n height: this.cellHeight,\n };\n }\n\n private drawTextLine(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n width: number,\n line: WebHostSurfaceStyle[\"underline\"],\n placement: \"underline\" | \"strike\",\n fallbackColor: string\n ): void {\n if (!line) {\n return;\n }\n context.strokeStyle = line.color ?? fallbackColor;\n context.lineWidth = line.pattern === \"double\" ? 2 : 1;\n if (line.pattern === \"dot\") {\n context.setLineDash([1, 3]);\n } else if (line.pattern === \"dash\") {\n context.setLineDash([4, 3]);\n } else {\n context.setLineDash([]);\n }\n\n const lineY = placement === \"underline\"\n ? y + this.cellHeight - 2\n : y + Math.floor(this.cellHeight / 2);\n context.beginPath();\n context.moveTo(x, lineY);\n context.lineTo(x + width, lineY);\n context.stroke();\n context.setLineDash([]);\n }\n\n private fontForStyle(\n style?: WebHostSurfaceStyle | null\n ): string {\n const emphasis = style?.em ?? 0;\n const italic = (emphasis & 2) !== 0 ? \"italic \" : \"\";\n const weight = (emphasis & 1) !== 0 ? \"700 \" : \"\";\n return `${italic}${weight}${this.currentStyle.fontSize}px ${this.currentStyle.fontFamily}`;\n }\n\n /**\n * Whether any scrollable region under `location` can still scroll in the\n * wheel's direction. Mirrors the Swift host's scroll hit-test: a region\n * qualifies when its viewport contains the cell AND it has remaining headroom\n * in the delta's direction. Used by \"chain\" wheel mode to decide capture vs.\n * fall-through. With no published `scrollRegions`, nothing can scroll, so the\n * wheel chains to the page (a scene with no ScrollView stays fully passive).\n */\n private wheelTargetCanScroll(\n location: { x: number; y: number },\n deltaX: number,\n deltaY: number\n ): boolean {\n const regions = this.currentFrame?.scrollRegions;\n if (!regions || regions.length === 0) {\n return false;\n }\n\n const cellX = Math.floor(location.x);\n const cellY = Math.floor(location.y);\n // Any region under the pointer that can move in this direction qualifies —\n // this is what lets an outer ScrollView take over when an inner one is at\n // its edge (nested scroll), and chains to the page only when none can.\n for (const region of regions) {\n const [rx, ry, rw, rh] = region.rect;\n if (cellX < rx || cellY < ry || cellX >= rx + rw || cellY >= ry + rh) {\n continue;\n }\n if (regionCanScrollInDirection(region, deltaX, deltaY)) {\n return true;\n }\n }\n return false;\n }\n\n private cellLocation(\n event: MouseEvent\n ): { x: number; y: number } | undefined {\n const location = this.rawCellLocation(event);\n if (!location) {\n return undefined;\n }\n\n const cellX = Math.floor(location.x);\n const cellY = Math.floor(location.y);\n if (cellX < 0 || cellY < 0 || cellX >= this.columns || cellY >= this.rows) {\n return undefined;\n }\n return location;\n }\n\n private rawCellLocation(\n event: MouseEvent\n ): { x: number; y: number } | undefined {\n const rect = this.canvas?.getBoundingClientRect?.() ?? this.terminalMount.getBoundingClientRect?.();\n if (!rect) {\n return undefined;\n }\n\n const x = (event.clientX - rect.left) / this.cellWidth;\n const y = (event.clientY - rect.top) / this.cellHeight;\n return { x, y };\n }\n}\n\nasync function decodeImage(\n dataBase64: string,\n format: WebHostSurfaceImageFormat\n): Promise<CanvasImageSource> {\n const bytes = decodeBase64Bytes(dataBase64);\n const blob = new Blob([bytes], { type: `image/${format}` });\n\n if (typeof createImageBitmap === \"function\") {\n // Animated GIFs collapse to their first frame in createImageBitmap\n // — that matches the Kitty path's first-frame composite. Phase 7\n // will replace this with a frame ticker.\n return createImageBitmap(blob);\n }\n\n return new Promise((resolve, reject) => {\n const image = new Image();\n const url = URL.createObjectURL(blob);\n image.onload = () => {\n URL.revokeObjectURL(url);\n resolve(image);\n };\n image.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error(`Failed to decode ${format} image`));\n };\n image.src = url;\n });\n}\n\nfunction decodeBase64Bytes(\n value: string\n): Uint8Array {\n if (typeof atob === \"function\") {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n }\n\n return new Uint8Array(Buffer.from(value, \"base64\"));\n}\n\nfunction keyInputFromKeyboardEvent(\n event: KeyboardEvent\n): Pick<WebHostKeyInput, \"key\" | \"character\"> | undefined {\n switch (event.key) {\n case \"Enter\":\n return { key: \"return\" };\n case \" \":\n return { key: \"space\" };\n case \"Tab\":\n return { key: \"tab\" };\n case \"ArrowLeft\":\n return { key: \"arrowLeft\" };\n case \"ArrowRight\":\n return { key: \"arrowRight\" };\n case \"ArrowUp\":\n return { key: \"arrowUp\" };\n case \"ArrowDown\":\n return { key: \"arrowDown\" };\n case \"Backspace\":\n return { key: \"backspace\" };\n case \"Escape\":\n return { key: \"escape\" };\n case \"Home\":\n return { key: \"home\" };\n case \"End\":\n return { key: \"end\" };\n default:\n {\n const characters = Array.from(event.key);\n if (characters.length !== 1) {\n return undefined;\n }\n return {\n key: \"character\",\n character: characters[0],\n };\n }\n }\n}\n\nfunction pointerButton(\n button: number\n): \"primary\" | \"middle\" | \"secondary\" {\n switch (button) {\n case 1:\n return \"middle\";\n case 2:\n return \"secondary\";\n default:\n return \"primary\";\n }\n}\n\nfunction modifierMask(\n event: MouseEvent | KeyboardEvent\n): number {\n let mask = 0;\n if (event.shiftKey) {\n mask |= 1;\n }\n if (event.altKey) {\n mask |= 2;\n }\n if (event.ctrlKey) {\n mask |= 4;\n }\n return mask;\n}\n\nfunction normalizedWheelDelta(\n delta: number\n): number {\n if (delta > 0) {\n return 1;\n }\n if (delta < 0) {\n return -1;\n }\n return 0;\n}\n\n/**\n * Whether a published scroll region has remaining headroom in the wheel's\n * direction, recomputing the per-direction extent from offset/content/viewport.\n * Mirrors SwiftTUI's clamp (`min(max(0, offset), max(0, content - viewport))`)\n * so the host and the app agree on \"at edge\". Wheel sign convention matches the\n * app: `deltaY > 0` scrolls down (offset grows toward the content bottom).\n * Diagonal wheels qualify if either axis has headroom.\n */\nfunction regionCanScrollInDirection(\n region: WebHostScrollRegion,\n deltaX: number,\n deltaY: number\n): boolean {\n const [, , viewportWidth, viewportHeight] = region.rect;\n const [offsetX, offsetY] = region.offset;\n const [contentWidth, contentHeight] = region.content;\n const maxX = Math.max(0, contentWidth - viewportWidth);\n const maxY = Math.max(0, contentHeight - viewportHeight);\n const clampedX = Math.min(Math.max(0, offsetX), maxX);\n const clampedY = Math.min(Math.max(0, offsetY), maxY);\n\n if (deltaY > 0 && clampedY < maxY) {\n return true;\n }\n if (deltaY < 0 && clampedY > 0) {\n return true;\n }\n if (deltaX > 0 && clampedX < maxX) {\n return true;\n }\n if (deltaX < 0 && clampedX > 0) {\n return true;\n }\n return false;\n}\n\nfunction normalizeCellRanges(\n ranges: DirtyCellRange[]\n): DirtyCellRange[] {\n const sorted = ranges\n .filter((range) => range.end > range.start)\n .sort((lhs, rhs) => lhs.start - rhs.start || lhs.end - rhs.end);\n const normalized: DirtyCellRange[] = [];\n for (const range of sorted) {\n const previous = normalized[normalized.length - 1];\n if (previous && range.start <= previous.end) {\n previous.end = Math.max(previous.end, range.end);\n continue;\n }\n normalized.push({ ...range });\n }\n return normalized;\n}\n\nfunction cellIntersectsRanges(\n x: number,\n span: number,\n ranges: DirtyRowRanges\n): boolean {\n if (ranges === \"full\") {\n return true;\n }\n const start = Math.floor(x);\n const end = start + Math.max(1, Math.ceil(span));\n return ranges.some((range) => start < range.end && end > range.start);\n}\n\nfunction dirtyRegionIntersectsCellRect(\n region: DirtyRegion,\n x: number,\n y: number,\n width: number,\n height: number\n): boolean {\n const startRow = Math.max(0, Math.floor(y));\n const endRow = Math.max(startRow, Math.ceil(y + height));\n const rectRange = {\n start: Math.floor(x),\n end: Math.floor(x) + Math.max(1, Math.ceil(width)),\n };\n for (let row = startRow; row < endRow; row += 1) {\n const ranges = region.rows.get(row);\n if (!ranges) {\n continue;\n }\n if (cellIntersectsRanges(rectRange.start, rectRange.end - rectRange.start, ranges)) {\n return true;\n }\n }\n return false;\n}\n\nfunction resolvedForeground(\n style: WebHostSurfaceStyle | null | undefined,\n terminalStyle: ResolvedWebHostTerminalStyle\n): string {\n if ((style?.em ?? 0) & 16) {\n return style?.bg ?? terminalStyle.theme.background;\n }\n return style?.fg ?? terminalStyle.theme.foreground;\n}\n\nfunction resolvedBackground(\n style: WebHostSurfaceStyle | null | undefined,\n terminalStyle: ResolvedWebHostTerminalStyle\n): string | undefined {\n if ((style?.em ?? 0) & 16) {\n return style?.fg ?? terminalStyle.theme.foreground;\n }\n return style?.bg;\n}\n"],"mappings":";;;;;;;;;;;AA4EA,SAAS,gBAAgB,mBAAmD;CAC1E,IAAI,sBAAsB,KAAA,GACxB,OAAO;CAET,OAAO,oBAAoB,YAAY;AACzC;AA0BA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA,6BAA8B,IAAI,IAAgC;CAClE;CACA;CACA;CACA;CACA;CACA;CACA;CACA,UAAkB;CAClB,OAAe;CACf,YAAoB;CACpB,aAAqB;CACrB,sBAAkE;CAClE,qBAA6B;CAC7B;CAMA,YAAoB;CAEpB,YAAY,SAAqC;EAC/C,KAAK,aAAa,QAAQ;EAC1B,KAAK,eAAe,8BAA8B,QAAQ,KAAK;EAC/D,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU,QAAQ;EACvB,KAAK,oBAAoB,QAAQ;EACjC,KAAK,gCAAgC,QAAQ,iCAAiC;EAC9E,KAAK,YAAY,QAAQ,aAAa,gBAAgB,QAAQ,iBAAiB;EAC/E,KAAK,UAAU,SAAS,cAAc,SAAS;EAC/C,KAAK,QAAQ,YAAY;EACzB,KAAK,QAAQ,QAAQ,UAAU,QAAQ,WAAW;EAClD,KAAK,QAAQ,SAAS;EAEtB,MAAM,SAAS,SAAS,cAAc,KAAK;EAC3C,OAAO,YAAY;EACnB,OAAO,cAAc,QAAQ,WAAW,SAAS,QAAQ,WAAW;EAEpE,KAAK,gBAAgB,SAAS,cAAc,KAAK;EACjD,KAAK,cAAc,YAAY;EAC/B,KAAK,cAAc,WAAW;EAE9B,KAAK,QAAQ,OAAO,QAAQ,KAAK,aAAa;EAC9C,QAAQ,MAAM,YAAY,KAAK,OAAO;EACtC,KAAK,gBAAgB;CACvB;CAEA,MAAM,QAAuB;EAC3B,IAAI,KAAK,QACP;EAGF,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,YAAY;EACnB,OAAO,aAAa,eAAe,MAAM;EACzC,KAAK,SAAS;EACd,KAAK,oBAAoB,IAAI,yBAAyB;EACtD,KAAK,cAAc,gBACjB,QACA,KAAK,kBAAkB,SACvB,KAAK,kBAAkB,gBACzB;EACA,KAAK,qBAAqB;EAC1B,KAAK,sBAAsB;EAE3B,KAAK,QAAQ,WAAW;GACtB,iBAAiB,UAAU,KAAK,eAAe,KAAK;GACpD,iBAAiB,SAAS,KAAK,eAAe,IAAI;GAClD,qBAAqB,UAAU,KAAK,mBAAmB,KAAK;GAC5D,wBAAwB,eAAe,KAAK,sBAAsB,UAAU;GAC5E,cAAc,SAAS,KAAK,YAAY,IAAI;GAC5C,aAAa,SAAS,KAAK,YAAY,IAAI;EAC7C,CAAC;EAED,KAAK,WAAW,KAAK,YAAY;EACjC,KAAK,aAAa;EAClB,KAAK,cAAc;EACnB,KAAK,KAAK;EACV,KAAK,sBAAsB;CAC7B;CAEA,WACE,SACM;EACN,KAAK,YAAY;EACjB,KAAK,gBAAgB;EACrB,IAAI,SAAS;GACX,KAAK,cAAc;GACnB,IAAI,KAAK,+BACP,KAAK,cAAc,QAAQ,EAAE,eAAe,KAAK,CAAC;EAEtD;CACF;CAEA,SACE,OACM;EACN,KAAK,eAAe,8BAA8B,KAAK;EACvD,KAAK,WAAW,KAAK,YAAY;EACjC,KAAK,QAAQ,kBAAkB,KAAK,YAAY;EAChD,KAAK,aAAa;EAClB,KAAK,cAAc;EACnB,KAAK,KAAK;EACV,KAAK,sBAAsB;CAC7B;CAEA,OACE,SACA,MACM;EACN,KAAK,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;EAC9C,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC;EACxC,KAAK,aAAa;EAClB,KAAK,KAAK;EACV,KAAK,sBAAsB;CAC7B;CAEA,YACE,MACM;EACN,IAAI,CAAC,KAAK,gBAAgB;GACxB,MAAM,iBAAiB,SAAS,cAAc,KAAK;GACnD,eAAe,YAAY;GAC3B,KAAK,iBAAiB;GACtB,KAAK,cAAc,YAAY,cAAc;EAC/C;EACA,KAAK,eAAe,cAAc,GAAG,KAAK,eAAe,eAAe,KAAK;CAC/E;CAEA,mBACE,OACM;EACN,QAAQ,IAAI,MAAM,WAAW;CAC/B;CAEA,sBACE,YACM;EACN,KAAK,oBAAoB,UAAU;CACrC;CAEA,MAAM,eACJ,MACe;EACf,MAAM,YAAY,WAAW,WAAW;EACxC,IAAI,CAAC,WAAW,WACd;EAGF,IAAI;GACF,MAAM,UAAU,UAAU,IAAI;EAChC,QAAQ,CAGR;CACF;CAEA,UACE,OACM;EACN,KAAK,QAAQ,KAAK;CACpB;CAEA,UAAgB;EACd,KAAK,sBAAsB;EAC3B,KAAK,gBAAgB,WAAW;EAChC,KAAK,QAAQ,OAAO;CACtB;CAEA,eACE,OACM;EACN,MAAM,gBAAgB,KAAK;EAC3B,KAAK,eAAe;EACpB,KAAK,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,KAAK,CAAC;EAClD,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,MAAM,CAAC;EAChD,MAAM,UAAU,KAAK,aAAa;EAClC,KAAK,KAAK,iBAAiB,CAAC,UAAU,MAAM,SAAS,KAAA,CAAS;EAC9D,KAAK,sBAAsB;CAC7B;CAEA,WACE,OACM;EACN,0BAA0B,KAAK,SAAS,KAAK;EAC7C,KAAK,QAAQ,MAAM,UAAU;EAC7B,KAAK,QAAQ,MAAM,eAAe;EAClC,KAAK,QAAQ,MAAM,YAAY;EAC/B,KAAK,QAAQ,MAAM,WAAW;EAC9B,KAAK,QAAQ,MAAM,MAAM;EACzB,KAAK,QAAQ,MAAM,mBAAmB;EAEtC,KAAK,cAAc,MAAM,WAAW;EACpC,KAAK,cAAc,MAAM,WAAW;EAGpC,KAAK,cAAc,MAAM,qBAAqB;EAC9C,KAAK,cAAc,MAAM,UAAU;EACnC,KAAK,cAAc,MAAM,aAAa,8BAA8B,KAAK,YAAY;EACrF,KAAK,cAAc,MAAM,YAAY,GAAG,KAAK,aAAa,EAAE;EAE5D,IAAI,KAAK,QAAQ;GACf,KAAK,OAAO,MAAM,UAAU;GAC5B,KAAK,OAAO,MAAM,QAAQ;GAC1B,KAAK,OAAO,MAAM,SAAS;EAC7B;CACF;CAEA,kBAAgC;EAC9B,KAAK,QAAQ,SAAS,CAAC,KAAK;EAC5B,KAAK,QAAQ,MAAM,YACjB,WACA,KAAK,YAAY,SAAS,QAC1B,WACF;CACF;CAEA,wBAAsC;EACpC,IAAI,OAAO,mBAAmB,aAC5B;EAGF,KAAK,iBAAiB,IAAI,qBAAqB;GAC7C,KAAK,cAAc;EACrB,CAAC;EACD,KAAK,eAAe,QAAQ,KAAK,aAAa;CAChD;CAEA,uBAAqC;EACnC,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,WAAW,MAAM,aACzB;GAEF,MAAM,MAAM,0BAA0B,KAAK;GAC3C,IAAI,CAAC,KACH;GAGF,KAAK,QAAQ,sBAAsB;IACjC,GAAG;IACH,WAAW,aAAa,KAAK;GAC/B,CAAC,CAAC;GACF,MAAM,eAAe;EACvB;EAEA,MAAM,eAAe,UAA0B;GAC7C,MAAM,OAAO,MAAM,eAAe,QAAQ,YAAY,KAAK;GAC3D,IAAI,CAAC,MACH;GAEF,KAAK,QAAQ,wBAAwB,IAAI,CAAC;GAC1C,MAAM,eAAe;EACvB;EAEA,MAAM,qBAAqB,UAAwB;GACjD,MAAM,WAAW,KAAK,aAAa,KAAK;GACxC,IAAI,CAAC,UACH;GAGF,MAAM,SAAS,cAAc,MAAM,MAAM;GACzC,KAAK,sBAAsB;GAC3B,KAAK,qBAAqB;GAC1B,KAAK,cAAc,QAAQ,EAAE,eAAe,KAAK,CAAC;GAClD,KAAK,cAAc,oBAAoB,MAAM,SAAS;GACtD,KAAK,QAAQ,wBAAwB;IACnC,MAAM;IACN,GAAG,SAAS;IACZ,GAAG,SAAS;IACZ;IACA,WAAW,aAAa,KAAK;GAC/B,CAAC,CAAC;GACF,MAAM,eAAe;EACvB;EAEA,MAAM,mBAAmB,UAAwB;GAC/C,MAAM,WAAW,KAAK,qBAClB,KAAK,gBAAgB,KAAK,IAC1B,KAAK,aAAa,KAAK;GAC3B,KAAK,cAAc,wBAAwB,MAAM,SAAS;GAC1D,KAAK,qBAAqB;GAC1B,IAAI,CAAC,UACH;GAGF,KAAK,QAAQ,wBAAwB;IACnC,MAAM;IACN,GAAG,SAAS;IACZ,GAAG,SAAS;IACZ,QAAQ,cAAc,MAAM,MAAM,KAAK,KAAK;IAC5C,WAAW,aAAa,KAAK;GAC/B,CAAC,CAAC;GACF,MAAM,eAAe;EACvB;EAEA,MAAM,qBAAqB,UAAwB;GACjD,MAAM,WAAW,MAAM,WAAW,KAAK,qBACnC,KAAK,gBAAgB,KAAK,IAC1B,KAAK,aAAa,KAAK;GAC3B,IAAI,CAAC,UACH;GAGF,KAAK,QAAQ,wBAAwB;IACnC,MAAM,MAAM,UAAU,YAAY;IAClC,GAAG,SAAS;IACZ,GAAG,SAAS;IACZ,QAAQ,KAAK;IACb,WAAW,aAAa,KAAK;GAC/B,CAAC,CAAC;EACJ;EAEA,MAAM,eAAe,UAAsB;GACzC,IAAI,KAAK,cAAc,WACrB;GAGF,MAAM,WAAW,KAAK,aAAa,KAAK;GACxC,IAAI,CAAC,UAGH;GAOF,IAAI,KAAK,cAAc,WAClB,CAAC,KAAK,qBAAqB,UAAU,MAAM,QAAQ,MAAM,MAAM,GAClE;GAGF,KAAK,QAAQ,wBAAwB;IACnC,MAAM;IACN,GAAG,SAAS;IACZ,GAAG,SAAS;IACZ,QAAQ,qBAAqB,MAAM,MAAM;IACzC,QAAQ,qBAAqB,MAAM,MAAM;IACzC,WAAW,aAAa,KAAK;GAC/B,CAAC,CAAC;GACF,MAAM,eAAe;EACvB;EAEA,KAAK,cAAc,iBAAiB,WAAW,aAAa;EAC5D,KAAK,cAAc,iBAAiB,SAAS,WAAW;EACxD,KAAK,cAAc,iBAAiB,eAAe,iBAAiB;EACpE,KAAK,cAAc,iBAAiB,aAAa,eAAe;EAChE,KAAK,cAAc,iBAAiB,eAAe,iBAAiB;EACpE,KAAK,cAAc,iBAAiB,SAAS,aAAa,EAAE,SAAS,MAAM,CAAC;EAE5E,KAAK,4BAA4B;GAC/B,KAAK,cAAc,oBAAoB,WAAW,aAAa;GAC/D,KAAK,cAAc,oBAAoB,SAAS,WAAW;GAC3D,KAAK,cAAc,oBAAoB,eAAe,iBAAiB;GACvE,KAAK,cAAc,oBAAoB,aAAa,eAAe;GACnE,KAAK,cAAc,oBAAoB,eAAe,iBAAiB;GACvE,KAAK,cAAc,oBAAoB,SAAS,WAAW;EAC7D;CACF;CAEA,gBAA8B;EAC5B,KAAK,aAAa;EAClB,MAAM,OAAO,KAAK,cAAc,wBAAwB;EACxD,MAAM,QAAQ,MAAM,SAAS,KAAK,QAAQ,IAAI,KAAK,QAAQ,KAAK,UAAU,KAAK;EAC/E,MAAM,SAAS,MAAM,UAAU,KAAK,SAAS,IAAI,KAAK,SAAS,KAAK,OAAO,KAAK;EAChF,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK,SAAS,CAAC;EAClE,MAAM,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,KAAK,UAAU,CAAC;EAEjE,KAAK,UAAU;EACf,KAAK,OAAO;EACZ,KAAK,mBAAmB;EACxB,KAAK,aAAa;CACpB;CAEA,qBAAmC;EACjC,MAAM,UAAU;GACd,SAAS,KAAK;GACd,MAAM,KAAK;GACX,WAAW,KAAK;GAChB,YAAY,KAAK;EACnB;EACA,IAAI,KAAK,kBACJ,KAAK,eAAe,YAAY,QAAQ,WACxC,KAAK,eAAe,SAAS,QAAQ,QACrC,KAAK,eAAe,cAAc,QAAQ,aAC1C,KAAK,eAAe,eAAe,QAAQ,YAE9C;EAGF,KAAK,iBAAiB;EACtB,KAAK,QAAQ,OAAO,QAAQ,SAAS,QAAQ,MAAM,QAAQ,WAAW,QAAQ,UAAU;CAC1F;CAEA,eAAgC;EAC9B,IAAI,CAAC,KAAK,QACR,OAAO;EAGT,MAAM,WAAW,KAAK,IAAI,GAAG,KAAK,UAAU,KAAK,SAAS;EAC1D,MAAM,YAAY,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,UAAU;EACzD,MAAM,QAAQ,WAAW,QAAQ,oBAAoB;EACrD,MAAM,QAAQ,KAAK,KAAK,WAAW,KAAK;EACxC,MAAM,SAAS,KAAK,KAAK,YAAY,KAAK;EAC1C,MAAM,aAAa,GAAG,SAAS;EAC/B,MAAM,cAAc,GAAG,UAAU;EACjC,IAAI,KAAK,OAAO,UAAU,SACrB,KAAK,OAAO,WAAW,UACvB,KAAK,OAAO,MAAM,UAAU,cAC5B,KAAK,OAAO,MAAM,WAAW,aAEhC,OAAO;EAGT,KAAK,OAAO,QAAQ;EACpB,KAAK,OAAO,SAAS;EACrB,KAAK,OAAO,MAAM,QAAQ;EAC1B,KAAK,OAAO,MAAM,SAAS;EAC3B,OAAO;CACT;CAEA,eAA6B;EAE3B,MAAM,WADS,KAAK,UAAU,SAAS,cAAc,QAAQ,EAAA,CACtC,aAAa,IAAI;EACxC,IAAI,CAAC,SAAS;GACZ,KAAK,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,aAAa,WAAW,GAAI,CAAC;GAC1E,KAAK,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,aAAa,WAAW,IAAI,CAAC;GAC3E;EACF;EAEA,QAAQ,OAAO,KAAK,aAAa;EACjC,KAAK,YAAY,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC;EACtE,KAAK,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,aAAa,WAAW,IAAI,CAAC;CAC5E;CAEA,KACE,QACM;EACN,MAAM,SAAS,KAAK;EACpB,MAAM,UAAU,QAAQ,WAAW,IAAI;EACvC,IAAI,CAAC,UAAU,CAAC,SACd;EAGF,MAAM,QAAQ,KAAK;EACnB,MAAM,cAAc,QAAQ,KAAK,qBAAqB,QAAQ,KAAK,IAAI,KAAA;EACvE,IAAI,aAAa,MAAM,WAAW,GAChC;EAGF,MAAM,QAAQ,WAAW,QAAQ,oBAAoB;EACrD,QAAQ,aAAa,OAAO,GAAG,GAAG,OAAO,GAAG,CAAC;EAC7C,QAAQ,eAAe;EAEvB,QAAQ,YAAY,8BAA8B,KAAK,YAAY;EACnE,IAAI,aACF,KAAK,MAAM,QAAQ,YAAY,OAAO;GACpC,QAAQ,UAAU,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,MAAM;GACzD,QAAQ,SAAS,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,MAAM;EAC1D;OACK;GACL,QAAQ,UAAU,GAAG,GAAG,OAAO,QAAQ,OAAO,OAAO,SAAS,KAAK;GACnE,QAAQ,SAAS,GAAG,GAAG,KAAK,UAAU,KAAK,WAAW,KAAK,OAAO,KAAK,UAAU;EACnF;EAEA,IAAI,CAAC,OACH;EAGF,KAAK,SAAS,SAAS,OAAO,WAAW;EACzC,KAAK,WAAW,SAAS,MAAM,UAAU,CAAC,GAAG,WAAW;CAC1D;CAEA,SACE,SACA,OACA,aACM;EACN,IAAI,aAAa;GACf,KAAK,MAAM,CAAC,GAAG,WAAW,YAAY,MAAM;IAC1C,MAAM,MAAM,MAAM,KAAK,MAAM,CAAC;IAC9B,KAAK,QAAQ,SAAS,OAAO,KAAK,GAAG,MAAM;GAC7C;GACA;EACF;EAEA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,QAAQ,KAAK,GAAG;GAC7C,MAAM,MAAM,MAAM,KAAK,MAAM,CAAC;GAC9B,KAAK,QAAQ,SAAS,OAAO,KAAK,CAAC;EACrC;CACF;CAEA,QACE,SACA,OACA,KACA,GACA,QACM;EACN,KAAK,MAAM,QAAQ,KAAK;GACtB,MAAM,CAAC,GAAG,MAAM,MAAM,cAAc;GACpC,IAAI,WAAW,KAAA,KAAa,CAAC,qBAAqB,GAAG,MAAM,MAAM,GAC/D;GAEF,MAAM,QAAQ,MAAM,OAAO,eAAe,KAAA;GAC1C,KAAK,SAAS,SAAS,GAAG,GAAG,MAAM,MAAM,KAAK;EAChD;CACF;CAEA,wBAAsC;EACpC,MAAM,OAAO,KAAK;EAClB,IAAI,CAAC,QAAQ,CAAC,KAAK,cACjB;EAGF,KAAK,QAAQ,KAAK,aAAa,qBAAqB,CAAC,GAAG;GACtD,WAAW,KAAK;GAChB,YAAY,KAAK;EACnB,GAAG,KAAK,aAAa,8BAA8B,CAAC,GAAG,EACrD,kBAAkB,KAAK,8BACzB,CAAC;CACH;CAEA,WACE,SACA,QACA,aACM;EACN,KAAK,MAAM,SAAS,QAClB,KAAK,UAAU,SAAS,OAAO,WAAW;CAE9C;CAEA,UACE,SACA,OACA,aACM;EACN,MAAM,eAAe,KAAK,YAAY,KAAK;EAC3C,IAAI,CAAC,cACH;EAGF,MAAM,CAAC,SAAS,SAAS,aAAa,gBAAgB,MAAM;EAC5D,MAAM,CAAC,OAAO,OAAO,WAAW,cAAc,MAAM;EACpD,IAAI,eAAe,KAAK,gBAAgB,KAAK,aAAa,KAAK,cAAc,GAC3E;EAEF,IACE,eACG,CAAC,8BAA8B,aAAa,OAAO,OAAO,WAAW,UAAU,GAElF;EAGF,QAAQ,KAAK;EACb,QAAQ,UAAU;EAClB,QAAQ,KACN,QAAQ,KAAK,WACb,QAAQ,KAAK,YACb,YAAY,KAAK,WACjB,aAAa,KAAK,UACpB;EACA,QAAQ,KAAK;EACb,QAAQ,UACN,cACA,UAAU,KAAK,WACf,UAAU,KAAK,YACf,cAAc,KAAK,WACnB,eAAe,KAAK,UACtB;EACA,QAAQ,QAAQ;CAClB;CAEA,YACE,OAC+B;EAC/B,MAAM,SAAS,KAAK,WAAW,IAAI,MAAM,EAAE;EAC3C,IAAI,QAAQ,OACV,OAAO,OAAO;EAGhB,IAAI,CAAC,QAAQ,WAAW,MAAM,YAAY;GACxC,MAAM,UAAU,YAAY,MAAM,YAAY,MAAM,MAAM;GAC1D,KAAK,WAAW,IAAI,MAAM,IAAI,EAAE,QAAQ,CAAC;GACzC,QAAa,MAAM,iBAAiB;IAElC,IADe,KAAK,WAAW,IAAI,MAAM,EAChC,CAAC,EAAE,YAAY,SACtB;IAEF,KAAK,WAAW,IAAI,MAAM,IAAI,EAAE,OAAO,aAAa,CAAC;IACrD,KAAK,KAAK;GACZ,CAAC,CAAC,CAAC,YAAY;IACb,KAAK,WAAW,OAAO,MAAM,EAAE;GACjC,CAAC;EACH;CAGF;CAEA,SACE,SACA,GACA,GACA,MACA,MACA,OACM;EACN,MAAM,QAAQ,IAAI,KAAK;EACvB,MAAM,QAAQ,IAAI,KAAK;EACvB,MAAM,QAAQ,KAAK,IAAI,GAAG,IAAI,IAAI,KAAK;EACvC,MAAM,aAAa,mBAAmB,OAAO,KAAK,YAAY;EAC9D,MAAM,aAAa,mBAAmB,OAAO,KAAK,YAAY;EAC9D,MAAM,UAAU,OAAO,WAAW;EAElC,IAAI,YAAY;GACd,QAAQ,cAAc;GACtB,QAAQ,YAAY;GACpB,QAAQ,SAAS,OAAO,OAAO,OAAO,KAAK,UAAU;EACvD;EAEA,IAAI,SAAS,KAAK;GAChB,QAAQ,cAAc;GACtB,QAAQ,YAAY;GACpB,QAAQ,cAAc;GACtB,IAAI,CAAC,oBAAoB,IAAI,KAAK,CAAC,eAAe,SAAS,MAAM;IAC/D,GAAG;IACH,GAAG;IACH;IACA,QAAQ,KAAK;GACf,CAAC,GAAG;IACF,QAAQ,OAAO,KAAK,aAAa,KAAK;IACtC,QAAQ,SACN,MACA,OACA,QAAQ,KAAK,OAAO,KAAK,aAAa,KAAK,aAAa,YAAY,CAAC,IAAI,CAC3E;GACF;EACF;EAEA,KAAK,aAAa,SAAS,OAAO,OAAO,OAAO,OAAO,WAAW,aAAa,UAAU;EACzF,KAAK,aAAa,SAAS,OAAO,OAAO,OAAO,OAAO,eAAe,UAAU,UAAU;EAC1F,QAAQ,cAAc;CACxB;CAEA,qBACE,QACA,OACyB;EACzB,IAAI,CAAC,UAAU,OAAO,2BAA2B,OAAO,4BACtD;EAGF,MAAM,QAAqB,CAAC;EAC5B,MAAM,uBAAO,IAAI,IAA4B;EAC7C,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,UAAU;GAC3C,IAAI,MAAM,KAAK,OAAO,MAAM,QAC1B;GAEF,IAAI,OAAO,WAAW,GAAG;IACvB,MAAM,KAAK,KAAK,SAAS,GAAG,KAAK,MAAM,KAAK,CAAC;IAC7C,KAAK,IAAI,KAAK,MAAM;IACpB;GACF;GACA,MAAM,YAA8B,KAAK,IAAI,GAAG,MAAM,SAClD,CAAC,IACD,CAAC,GAAI,KAAK,IAAI,GAAG,KAAqC,CAAC,CAAE;GAC7D,KAAK,MAAM,CAAC,OAAO,QAAQ,QAAQ;IACjC,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC;IACvE,MAAM,aAAa,KAAK,IAAI,YAAY,KAAK,IAAI,MAAM,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC;IAC7E,IAAI,cAAc,YAChB;IAEF,MAAM,KAAK,KAAK,SAAS,YAAY,KAAK,aAAa,UAAU,CAAC;IAClE,UAAU,KAAK;KAAE,OAAO;KAAY,KAAK;IAAW,CAAC;GACvD;GACA,IAAI,KAAK,IAAI,GAAG,MAAM,UAAU,UAAU,SAAS,GACjD,KAAK,IAAI,KAAK,oBAAoB,SAAS,CAAC;EAEhD;EACA,OAAO;GAAE;GAAO;EAAK;CACvB;CAEA,SACE,GACA,GACA,MACW;EACX,OAAO;GACL,GAAG,IAAI,KAAK;GACZ,GAAG,IAAI,KAAK;GACZ,OAAO,KAAK,IAAI,GAAG,IAAI,IAAI,KAAK;GAChC,QAAQ,KAAK;EACf;CACF;CAEA,aACE,SACA,GACA,GACA,OACA,MACA,WACA,eACM;EACN,IAAI,CAAC,MACH;EAEF,QAAQ,cAAc,KAAK,SAAS;EACpC,QAAQ,YAAY,KAAK,YAAY,WAAW,IAAI;EACpD,IAAI,KAAK,YAAY,OACnB,QAAQ,YAAY,CAAC,GAAG,CAAC,CAAC;OACrB,IAAI,KAAK,YAAY,QAC1B,QAAQ,YAAY,CAAC,GAAG,CAAC,CAAC;OAE1B,QAAQ,YAAY,CAAC,CAAC;EAGxB,MAAM,QAAQ,cAAc,cACxB,IAAI,KAAK,aAAa,IACtB,IAAI,KAAK,MAAM,KAAK,aAAa,CAAC;EACtC,QAAQ,UAAU;EAClB,QAAQ,OAAO,GAAG,KAAK;EACvB,QAAQ,OAAO,IAAI,OAAO,KAAK;EAC/B,QAAQ,OAAO;EACf,QAAQ,YAAY,CAAC,CAAC;CACxB;CAEA,aACE,OACQ;EACR,MAAM,WAAW,OAAO,MAAM;EAG9B,OAAO,IAFS,WAAW,OAAO,IAAI,YAAY,MAClC,WAAW,OAAO,IAAI,SAAS,KACnB,KAAK,aAAa,SAAS,KAAK,KAAK,aAAa;CAChF;;;;;;;;;CAUA,qBACE,UACA,QACA,QACS;EACT,MAAM,UAAU,KAAK,cAAc;EACnC,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO;EAGT,MAAM,QAAQ,KAAK,MAAM,SAAS,CAAC;EACnC,MAAM,QAAQ,KAAK,MAAM,SAAS,CAAC;EAInC,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,CAAC,IAAI,IAAI,IAAI,MAAM,OAAO;GAChC,IAAI,QAAQ,MAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,SAAS,KAAK,IAChE;GAEF,IAAI,2BAA2B,QAAQ,QAAQ,MAAM,GACnD,OAAO;EAEX;EACA,OAAO;CACT;CAEA,aACE,OACsC;EACtC,MAAM,WAAW,KAAK,gBAAgB,KAAK;EAC3C,IAAI,CAAC,UACH;EAGF,MAAM,QAAQ,KAAK,MAAM,SAAS,CAAC;EACnC,MAAM,QAAQ,KAAK,MAAM,SAAS,CAAC;EACnC,IAAI,QAAQ,KAAK,QAAQ,KAAK,SAAS,KAAK,WAAW,SAAS,KAAK,MACnE;EAEF,OAAO;CACT;CAEA,gBACE,OACsC;EACtC,MAAM,OAAO,KAAK,QAAQ,wBAAwB,KAAK,KAAK,cAAc,wBAAwB;EAClG,IAAI,CAAC,MACH;EAKF,OAAO;GAAE,IAFE,MAAM,UAAU,KAAK,QAAQ,KAAK;GAEjC,IADD,MAAM,UAAU,KAAK,OAAO,KAAK;EAC9B;CAChB;AACF;AAEA,eAAe,YACb,YACA,QAC4B;CAC5B,MAAM,QAAQ,kBAAkB,UAAU;CAC1C,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,SAAS,SAAS,CAAC;CAE1D,IAAI,OAAO,sBAAsB,YAI/B,OAAO,kBAAkB,IAAI;CAG/B,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,QAAQ,IAAI,MAAM;EACxB,MAAM,MAAM,IAAI,gBAAgB,IAAI;EACpC,MAAM,eAAe;GACnB,IAAI,gBAAgB,GAAG;GACvB,QAAQ,KAAK;EACf;EACA,MAAM,gBAAgB;GACpB,IAAI,gBAAgB,GAAG;GACvB,uBAAO,IAAI,MAAM,oBAAoB,OAAO,OAAO,CAAC;EACtD;EACA,MAAM,MAAM;CACd,CAAC;AACH;AAEA,SAAS,kBACP,OACY;CACZ,IAAI,OAAO,SAAS,YAAY;EAC9B,MAAM,SAAS,KAAK,KAAK;EACzB,MAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;EAC1C,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAClD,MAAM,SAAS,OAAO,WAAW,KAAK;EAExC,OAAO;CACT;CAEA,OAAO,IAAI,WAAW,OAAO,KAAK,OAAO,QAAQ,CAAC;AACpD;AAEA,SAAS,0BACP,OACwD;CACxD,QAAQ,MAAM,KAAd;EACA,KAAK,SACH,OAAO,EAAE,KAAK,SAAS;EACzB,KAAK,KACH,OAAO,EAAE,KAAK,QAAQ;EACxB,KAAK,OACH,OAAO,EAAE,KAAK,MAAM;EACtB,KAAK,aACH,OAAO,EAAE,KAAK,YAAY;EAC5B,KAAK,cACH,OAAO,EAAE,KAAK,aAAa;EAC7B,KAAK,WACH,OAAO,EAAE,KAAK,UAAU;EAC1B,KAAK,aACH,OAAO,EAAE,KAAK,YAAY;EAC5B,KAAK,aACH,OAAO,EAAE,KAAK,YAAY;EAC5B,KAAK,UACH,OAAO,EAAE,KAAK,SAAS;EACzB,KAAK,QACH,OAAO,EAAE,KAAK,OAAO;EACvB,KAAK,OACH,OAAO,EAAE,KAAK,MAAM;EACtB,SACE;GACE,MAAM,aAAa,MAAM,KAAK,MAAM,GAAG;GACvC,IAAI,WAAW,WAAW,GACxB;GAEF,OAAO;IACL,KAAK;IACL,WAAW,WAAW;GACxB;EACF;CACF;AACF;AAEA,SAAS,cACP,QACoC;CACpC,QAAQ,QAAR;EACA,KAAK,GACH,OAAO;EACT,KAAK,GACH,OAAO;EACT,SACE,OAAO;CACT;AACF;AAEA,SAAS,aACP,OACQ;CACR,IAAI,OAAO;CACX,IAAI,MAAM,UACR,QAAQ;CAEV,IAAI,MAAM,QACR,QAAQ;CAEV,IAAI,MAAM,SACR,QAAQ;CAEV,OAAO;AACT;AAEA,SAAS,qBACP,OACQ;CACR,IAAI,QAAQ,GACV,OAAO;CAET,IAAI,QAAQ,GACV,OAAO;CAET,OAAO;AACT;;;;;;;;;AAUA,SAAS,2BACP,QACA,QACA,QACS;CACT,MAAM,KAAK,eAAe,kBAAkB,OAAO;CACnD,MAAM,CAAC,SAAS,WAAW,OAAO;CAClC,MAAM,CAAC,cAAc,iBAAiB,OAAO;CAC7C,MAAM,OAAO,KAAK,IAAI,GAAG,eAAe,aAAa;CACrD,MAAM,OAAO,KAAK,IAAI,GAAG,gBAAgB,cAAc;CACvD,MAAM,WAAW,KAAK,IAAI,KAAK,IAAI,GAAG,OAAO,GAAG,IAAI;CACpD,MAAM,WAAW,KAAK,IAAI,KAAK,IAAI,GAAG,OAAO,GAAG,IAAI;CAEpD,IAAI,SAAS,KAAK,WAAW,MAC3B,OAAO;CAET,IAAI,SAAS,KAAK,WAAW,GAC3B,OAAO;CAET,IAAI,SAAS,KAAK,WAAW,MAC3B,OAAO;CAET,IAAI,SAAS,KAAK,WAAW,GAC3B,OAAO;CAET,OAAO;AACT;AAEA,SAAS,oBACP,QACkB;CAClB,MAAM,SAAS,OACZ,QAAQ,UAAU,MAAM,MAAM,MAAM,KAAK,CAAC,CAC1C,MAAM,KAAK,QAAQ,IAAI,QAAQ,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG;CAChE,MAAM,aAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,WAAW,WAAW,SAAS;EAChD,IAAI,YAAY,MAAM,SAAS,SAAS,KAAK;GAC3C,SAAS,MAAM,KAAK,IAAI,SAAS,KAAK,MAAM,GAAG;GAC/C;EACF;EACA,WAAW,KAAK,EAAE,GAAG,MAAM,CAAC;CAC9B;CACA,OAAO;AACT;AAEA,SAAS,qBACP,GACA,MACA,QACS;CACT,IAAI,WAAW,QACb,OAAO;CAET,MAAM,QAAQ,KAAK,MAAM,CAAC;CAC1B,MAAM,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,CAAC;CAC/C,OAAO,OAAO,MAAM,UAAU,QAAQ,MAAM,OAAO,MAAM,MAAM,KAAK;AACtE;AAEA,SAAS,8BACP,QACA,GACA,GACA,OACA,QACS;CACT,MAAM,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC;CAC1C,MAAM,SAAS,KAAK,IAAI,UAAU,KAAK,KAAK,IAAI,MAAM,CAAC;CACvD,MAAM,YAAY;EAChB,OAAO,KAAK,MAAM,CAAC;EACnB,KAAK,KAAK,MAAM,CAAC,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,CAAC;CACnD;CACA,KAAK,IAAI,MAAM,UAAU,MAAM,QAAQ,OAAO,GAAG;EAC/C,MAAM,SAAS,OAAO,KAAK,IAAI,GAAG;EAClC,IAAI,CAAC,QACH;EAEF,IAAI,qBAAqB,UAAU,OAAO,UAAU,MAAM,UAAU,OAAO,MAAM,GAC/E,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAS,mBACP,OACA,eACQ;CACR,KAAK,OAAO,MAAM,KAAK,IACrB,OAAO,OAAO,MAAM,cAAc,MAAM;CAE1C,OAAO,OAAO,MAAM,cAAc,MAAM;AAC1C;AAEA,SAAS,mBACP,OACA,eACoB;CACpB,KAAK,OAAO,MAAM,KAAK,IACrB,OAAO,OAAO,MAAM,cAAc,MAAM;CAE1C,OAAO,OAAO;AAChB"}
1
+ {"version":3,"file":"WebHostSceneRuntime.js","names":[],"sources":["../../src/WebHostSceneRuntime.ts"],"sourcesContent":["import {\n applyWebHostTerminalStyle,\n normalizeWebHostTerminalStyle,\n type ResolvedWebHostTerminalStyle,\n type WebHostTerminalStyle,\n webTUITerminalBackgroundColor,\n} from \"./WebHostTerminalStyle.ts\";\nimport {\n CanvasSurfacePainter,\n fontForStyle,\n type CanvasSurfaceMetrics,\n} from \"./CanvasSurfacePainter.ts\";\nimport {\n InputEventEncoder,\n type CellLocation,\n type PointerButton,\n} from \"./InputEventEncoder.ts\";\nimport {\n cellLocationForEvent,\n rawCellLocationForEvent,\n wheelTargetCanScroll,\n type PointerGeometryMetrics,\n} from \"./PointerGeometry.ts\";\nimport { AccessibilityTreeMounter } from \"./AccessibilityTree.ts\";\nimport {\n type WebHostFrameDiagnosticRecord,\n type WebHostOutputSink,\n type WebHostRuntimeIssue,\n type WebHostSurfaceDamage,\n type WebHostSurfaceFrame,\n} from \"./WebHostSurfaceTransport.ts\";\nimport type { WebHostSceneDescriptor } from \"./WebHostSceneManifest.ts\";\n\nexport interface WebHostSceneBridge {\n bindOutput(sink: WebHostOutputSink): void;\n resize(columns: number, rows: number, cellWidth?: number, cellHeight?: number): void;\n updateRenderStyle(style: WebHostTerminalStyle): void;\n sendInput(chunk: Uint8Array): void;\n dispose(): void;\n}\n\nexport interface WebHostSceneRuntimeOptions {\n mount: HTMLElement;\n descriptor: WebHostSceneDescriptor;\n style: WebHostTerminalStyle;\n bridge?: WebHostSceneBridge;\n onInput(chunk: Uint8Array): void;\n onFrameDiagnostic?: (diagnostic: WebHostFrameDiagnosticRecord) => void;\n synchronizeAccessibilityFocus?: boolean;\n /**\n * How the embedded view treats mouse-wheel input.\n * - `\"chain\"` (default): forward the wheel only while a scrollable region\n * under the pointer can still scroll in that direction; otherwise let it\n * fall through so the page (or parent iframe) scrolls — iframe-like nested\n * scrolling. A scene with no `ScrollView` never traps the wheel. Uses the\n * `scrollRegions` the app publishes in its frames.\n * - `\"capture\"`: always forward the wheel to the app while the pointer is over\n * the surface (and `preventDefault` page scroll). Best for full-screen apps\n * where there is no page to scroll past.\n * - `\"passive\"`: never capture; the page always scrolls.\n *\n * Takes precedence over the legacy `captureWheelInput` flag.\n */\n wheelMode?: WheelMode;\n /**\n * Legacy boolean wheel gate. `true` → `\"capture\"`, `false` → `\"passive\"`.\n * Prefer `wheelMode`. Ignored when `wheelMode` is set. When neither is set the\n * mode defaults to `\"chain\"`.\n */\n captureWheelInput?: boolean;\n}\n\nexport type WheelMode = \"capture\" | \"chain\" | \"passive\";\n\n/**\n * Resolves the legacy `captureWheelInput` flag to a {@link WheelMode}. When the\n * flag is unset the mode defaults to `\"chain\"`, so embeds never trap a visitor\n * who is merely scrolling past the view; `true` maps to `\"capture\"` and `false`\n * to `\"passive\"` to preserve the old boolean behavior.\n */\nfunction legacyWheelMode(captureWheelInput: boolean | undefined): WheelMode {\n if (captureWheelInput === undefined) {\n return \"chain\";\n }\n return captureWheelInput ? \"capture\" : \"passive\";\n}\n\n/**\n * Coordinates a single SwiftTUI scene's browser presentation: it owns the DOM\n * mount, canvas, accessibility tree, and bridge wiring, and delegates the heavy\n * responsibilities to focused collaborators — {@link CanvasSurfacePainter} for\n * canvas drawing, {@link InputEventEncoder} for wire-message encoding, and the\n * {@link PointerGeometry} helpers for pixel→cell hit-testing and wheel chaining.\n */\nexport class WebHostSceneRuntime {\n readonly descriptor: WebHostSceneDescriptor;\n readonly element: HTMLElement;\n readonly terminalMount: HTMLElement;\n\n private readonly bridge?: WebHostSceneBridge;\n private readonly onInput: (chunk: Uint8Array) => void;\n private readonly onFrameDiagnostic?: (diagnostic: WebHostFrameDiagnosticRecord) => void;\n private readonly synchronizeAccessibilityFocus: boolean;\n private readonly wheelMode: WheelMode;\n private readonly painter = new CanvasSurfacePainter();\n private readonly inputEncoder = new InputEventEncoder();\n private currentStyle: ResolvedWebHostTerminalStyle;\n private canvas?: HTMLCanvasElement;\n private accessibilityTree?: AccessibilityTreeMounter;\n private diagnosticText?: HTMLElement;\n private resizeObserver?: ResizeObserver;\n private detachInputHandlers?: () => void;\n private currentFrame?: WebHostSurfaceFrame;\n private columns = 80;\n private rows = 24;\n private cellWidth = 8;\n private cellHeight = 18;\n private activePointerButton: PointerButton = \"primary\";\n private hasCapturedPointer = false;\n private lastSentResize?: {\n columns: number;\n rows: number;\n cellWidth: number;\n cellHeight: number;\n };\n private isVisible = false;\n\n constructor(options: WebHostSceneRuntimeOptions) {\n this.descriptor = options.descriptor;\n this.currentStyle = normalizeWebHostTerminalStyle(options.style);\n this.bridge = options.bridge;\n this.onInput = options.onInput;\n this.onFrameDiagnostic = options.onFrameDiagnostic;\n this.synchronizeAccessibilityFocus = options.synchronizeAccessibilityFocus ?? true;\n this.wheelMode = options.wheelMode ?? legacyWheelMode(options.captureWheelInput);\n this.element = document.createElement(\"section\");\n this.element.className = \"webhost-scene\";\n this.element.dataset.sceneId = options.descriptor.id;\n this.element.hidden = true;\n\n const header = document.createElement(\"div\");\n header.className = \"webhost-scene__header\";\n header.textContent = options.descriptor.title ?? options.descriptor.id;\n\n this.terminalMount = document.createElement(\"div\");\n this.terminalMount.className = \"webhost-scene__terminal\";\n this.terminalMount.tabIndex = 0;\n\n this.element.append(header, this.terminalMount);\n options.mount.appendChild(this.element);\n this.applyVisibility();\n }\n\n async mount(): Promise<void> {\n if (this.canvas) {\n return;\n }\n\n const canvas = document.createElement(\"canvas\");\n canvas.className = \"webhost-scene__surface\";\n canvas.setAttribute(\"aria-hidden\", \"true\");\n this.canvas = canvas;\n this.painter.attach(canvas, () => this.draw());\n this.accessibilityTree = new AccessibilityTreeMounter();\n this.terminalMount.replaceChildren(\n canvas,\n this.accessibilityTree.element,\n this.accessibilityTree.announcerElement\n );\n this.installInputHandlers();\n this.installResizeObserver();\n\n this.bridge?.bindOutput({\n presentSurface: (frame) => this.presentSurface(frame),\n writeClipboard: (text) => this.writeClipboard(text),\n notifyRuntimeIssue: (issue) => this.notifyRuntimeIssue(issue),\n recordFrameDiagnostic: (diagnostic) => this.recordFrameDiagnostic(diagnostic),\n writeOutput: (text) => this.writeOutput(text),\n writeError: (text) => this.writeOutput(text),\n });\n\n this.applyStyle(this.currentStyle);\n this.measureCells();\n this.resizeToMount();\n this.draw();\n this.syncAccessibilityTree();\n }\n\n setVisible(\n visible: boolean\n ): void {\n this.isVisible = visible;\n this.applyVisibility();\n if (visible) {\n this.resizeToMount();\n if (this.synchronizeAccessibilityFocus) {\n this.terminalMount.focus?.({ preventScroll: true });\n }\n }\n }\n\n setStyle(\n style: WebHostTerminalStyle\n ): void {\n this.currentStyle = normalizeWebHostTerminalStyle(style);\n this.applyStyle(this.currentStyle);\n this.bridge?.updateRenderStyle(this.currentStyle);\n this.measureCells();\n this.resizeToMount();\n this.draw();\n this.syncAccessibilityTree();\n }\n\n resize(\n columns: number,\n rows: number\n ): void {\n this.columns = Math.max(1, Math.round(columns));\n this.rows = Math.max(1, Math.round(rows));\n this.resizeCanvas();\n this.draw();\n this.syncAccessibilityTree();\n }\n\n writeOutput(\n text: string\n ): void {\n if (!this.diagnosticText) {\n const diagnosticText = document.createElement(\"pre\");\n diagnosticText.className = \"webhost-scene__diagnostic\";\n this.diagnosticText = diagnosticText;\n this.terminalMount.appendChild(diagnosticText);\n }\n this.diagnosticText.textContent = `${this.diagnosticText.textContent ?? \"\"}${text}`;\n }\n\n notifyRuntimeIssue(\n issue: WebHostRuntimeIssue\n ): void {\n console.log(issue.description);\n }\n\n private recordFrameDiagnostic(\n diagnostic: WebHostFrameDiagnosticRecord\n ): void {\n this.onFrameDiagnostic?.(diagnostic);\n }\n\n async writeClipboard(\n text: string\n ): Promise<void> {\n const clipboard = globalThis.navigator?.clipboard;\n if (!clipboard?.writeText) {\n return;\n }\n\n try {\n await clipboard.writeText(text);\n } catch {\n // Clipboard permissions are browser/user-gesture dependent; hosts treat\n // rejection as a best-effort no-op rather than surfacing diagnostics.\n }\n }\n\n sendInput(\n chunk: Uint8Array\n ): void {\n this.onInput(chunk);\n }\n\n dispose(): void {\n this.detachInputHandlers?.();\n this.resizeObserver?.disconnect();\n this.element.remove();\n }\n\n private presentSurface(\n frame: WebHostSurfaceFrame\n ): void {\n const previousFrame = this.currentFrame;\n this.currentFrame = frame;\n this.columns = Math.max(1, Math.round(frame.width));\n this.rows = Math.max(1, Math.round(frame.height));\n const resized = this.resizeCanvas();\n this.draw(previousFrame && !resized ? frame.damage : undefined);\n this.syncAccessibilityTree();\n }\n\n private applyStyle(\n style: WebHostTerminalStyle\n ): void {\n applyWebHostTerminalStyle(this.element, style);\n this.element.style.padding = \"0.75rem\";\n this.element.style.borderRadius = \"16px\";\n this.element.style.boxShadow = \"0 20px 50px rgba(0, 0, 0, 0.28)\";\n this.element.style.overflow = \"hidden\";\n this.element.style.gap = \"0.5rem\";\n this.element.style.gridTemplateRows = \"auto 1fr\";\n\n this.terminalMount.style.position = \"relative\";\n this.terminalMount.style.overflow = \"hidden\";\n // Keep a captured wheel from rubber-banding/chaining the page; the wheel\n // capture vs. fall-through decision lives in handleWheel.\n this.terminalMount.style.overscrollBehavior = \"contain\";\n this.terminalMount.style.outline = \"none\";\n this.terminalMount.style.background = webTUITerminalBackgroundColor(this.currentStyle);\n this.terminalMount.style.minHeight = `${this.cellHeight * 8}px`;\n\n if (this.canvas) {\n this.canvas.style.display = \"block\";\n this.canvas.style.width = \"100%\";\n this.canvas.style.height = \"100%\";\n }\n }\n\n private applyVisibility(): void {\n this.element.hidden = !this.isVisible;\n this.element.style.setProperty(\n \"display\",\n this.isVisible ? \"grid\" : \"none\",\n \"important\"\n );\n }\n\n private installResizeObserver(): void {\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n\n this.resizeObserver = new ResizeObserver(() => {\n this.resizeToMount();\n });\n this.resizeObserver.observe(this.terminalMount);\n }\n\n private installInputHandlers(): void {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.metaKey || event.isComposing) {\n return;\n }\n const message = this.inputEncoder.encodeKey(event);\n if (!message) {\n return;\n }\n\n this.onInput(message);\n event.preventDefault();\n };\n\n const handlePaste = (event: ClipboardEvent) => {\n const text = event.clipboardData?.getData(\"text/plain\") ?? \"\";\n if (!text) {\n return;\n }\n this.onInput(this.inputEncoder.encodePaste(text));\n event.preventDefault();\n };\n\n const handlePointerDown = (event: PointerEvent) => {\n const location = this.cellLocation(event);\n if (!location) {\n return;\n }\n\n const button = this.inputEncoder.pointerButton(event.button);\n this.activePointerButton = button;\n this.hasCapturedPointer = true;\n this.terminalMount.focus?.({ preventScroll: true });\n this.terminalMount.setPointerCapture?.(event.pointerId);\n this.onInput(this.inputEncoder.encodePointerDown(location, button, event));\n event.preventDefault();\n };\n\n const handlePointerUp = (event: PointerEvent) => {\n const location = this.hasCapturedPointer\n ? this.rawCellLocation(event)\n : this.cellLocation(event);\n this.terminalMount.releasePointerCapture?.(event.pointerId);\n this.hasCapturedPointer = false;\n if (!location) {\n return;\n }\n\n const button = this.inputEncoder.pointerButton(event.button) ?? this.activePointerButton;\n this.onInput(this.inputEncoder.encodePointerUp(location, button, event));\n event.preventDefault();\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n const location = event.buttons && this.hasCapturedPointer\n ? this.rawCellLocation(event)\n : this.cellLocation(event);\n if (!location) {\n return;\n }\n\n this.onInput(this.inputEncoder.encodePointerMove(location, this.activePointerButton, event));\n };\n\n const handleWheel = (event: WheelEvent) => {\n if (this.wheelMode === \"passive\") {\n return;\n }\n\n const location = this.cellLocation(event);\n if (!location) {\n // Pointer is outside the cell grid (sub-cell margin / gutter). Don't\n // capture — let the wheel fall through to the page.\n return;\n }\n\n // In \"chain\" mode, capture only while a scrollable region under the\n // pointer can still move in this direction; otherwise let the wheel fall\n // through so the page (or parent iframe) scrolls — iframe-like behavior.\n // \"capture\" mode always forwards while over the surface (legacy).\n if (this.wheelMode === \"chain\"\n && !wheelTargetCanScroll(this.currentFrame?.scrollRegions, location, event.deltaX, event.deltaY)) {\n return;\n }\n\n this.onInput(this.inputEncoder.encodeWheel(location, event));\n event.preventDefault();\n };\n\n this.terminalMount.addEventListener(\"keydown\", handleKeyDown);\n this.terminalMount.addEventListener(\"paste\", handlePaste);\n this.terminalMount.addEventListener(\"pointerdown\", handlePointerDown);\n this.terminalMount.addEventListener(\"pointerup\", handlePointerUp);\n this.terminalMount.addEventListener(\"pointermove\", handlePointerMove);\n this.terminalMount.addEventListener(\"wheel\", handleWheel, { passive: false });\n\n this.detachInputHandlers = () => {\n this.terminalMount.removeEventListener(\"keydown\", handleKeyDown);\n this.terminalMount.removeEventListener(\"paste\", handlePaste);\n this.terminalMount.removeEventListener(\"pointerdown\", handlePointerDown);\n this.terminalMount.removeEventListener(\"pointerup\", handlePointerUp);\n this.terminalMount.removeEventListener(\"pointermove\", handlePointerMove);\n this.terminalMount.removeEventListener(\"wheel\", handleWheel);\n };\n }\n\n private resizeToMount(): void {\n this.measureCells();\n const rect = this.terminalMount.getBoundingClientRect?.();\n const width = rect?.width && rect.width > 0 ? rect.width : this.columns * this.cellWidth;\n const height = rect?.height && rect.height > 0 ? rect.height : this.rows * this.cellHeight;\n const nextColumns = Math.max(1, Math.floor(width / this.cellWidth));\n const nextRows = Math.max(1, Math.floor(height / this.cellHeight));\n\n this.columns = nextColumns;\n this.rows = nextRows;\n this.sendResizeIfNeeded();\n this.resizeCanvas();\n }\n\n private sendResizeIfNeeded(): void {\n const current = {\n columns: this.columns,\n rows: this.rows,\n cellWidth: this.cellWidth,\n cellHeight: this.cellHeight,\n };\n if (this.lastSentResize\n && this.lastSentResize.columns === current.columns\n && this.lastSentResize.rows === current.rows\n && this.lastSentResize.cellWidth === current.cellWidth\n && this.lastSentResize.cellHeight === current.cellHeight\n ) {\n return;\n }\n\n this.lastSentResize = current;\n this.bridge?.resize(current.columns, current.rows, current.cellWidth, current.cellHeight);\n }\n\n private resizeCanvas(): boolean {\n if (!this.canvas) {\n return false;\n }\n\n const cssWidth = Math.max(1, this.columns * this.cellWidth);\n const cssHeight = Math.max(1, this.rows * this.cellHeight);\n const scale = globalThis.window?.devicePixelRatio || 1;\n const width = Math.ceil(cssWidth * scale);\n const height = Math.ceil(cssHeight * scale);\n const styleWidth = `${cssWidth}px`;\n const styleHeight = `${cssHeight}px`;\n if (this.canvas.width === width\n && this.canvas.height === height\n && this.canvas.style.width === styleWidth\n && this.canvas.style.height === styleHeight\n ) {\n return false;\n }\n\n this.canvas.width = width;\n this.canvas.height = height;\n this.canvas.style.width = styleWidth;\n this.canvas.style.height = styleHeight;\n return true;\n }\n\n private measureCells(): void {\n const canvas = this.canvas ?? document.createElement(\"canvas\");\n const context = canvas.getContext?.(\"2d\");\n if (!context) {\n this.cellWidth = Math.max(1, Math.round(this.currentStyle.fontSize * 0.62));\n this.cellHeight = Math.max(1, Math.round(this.currentStyle.fontSize * 1.35));\n return;\n }\n\n context.font = fontForStyle(this.currentStyle);\n this.cellWidth = Math.max(1, Math.ceil(context.measureText(\"W\").width));\n this.cellHeight = Math.max(1, Math.ceil(this.currentStyle.fontSize * 1.35));\n }\n\n private draw(\n damage?: WebHostSurfaceDamage\n ): void {\n this.painter.paint(this.surfaceMetrics(), this.currentFrame, damage);\n }\n\n private syncAccessibilityTree(): void {\n const tree = this.accessibilityTree;\n if (!tree || !this.currentFrame) {\n return;\n }\n\n tree.present(this.currentFrame.accessibilityTree ?? [], {\n cellWidth: this.cellWidth,\n cellHeight: this.cellHeight,\n }, this.currentFrame.accessibilityAnnouncements ?? [], {\n synchronizeFocus: this.synchronizeAccessibilityFocus,\n });\n }\n\n private surfaceMetrics(): CanvasSurfaceMetrics {\n return {\n columns: this.columns,\n rows: this.rows,\n cellWidth: this.cellWidth,\n cellHeight: this.cellHeight,\n style: this.currentStyle,\n };\n }\n\n private pointerMetrics(): PointerGeometryMetrics {\n return {\n rect: this.canvas?.getBoundingClientRect?.() ?? this.terminalMount.getBoundingClientRect?.(),\n cellWidth: this.cellWidth,\n cellHeight: this.cellHeight,\n columns: this.columns,\n rows: this.rows,\n };\n }\n\n private cellLocation(\n event: MouseEvent\n ): CellLocation | undefined {\n return cellLocationForEvent(event, this.pointerMetrics());\n }\n\n private rawCellLocation(\n event: MouseEvent\n ): CellLocation | undefined {\n return rawCellLocationForEvent(event, this.pointerMetrics());\n }\n}\n"],"mappings":"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"}
@@ -24,13 +24,13 @@ var SharedInputQueueWriter = class {
24
24
  if (Atomics.load(this.queue.control, 2) !== 0) return;
25
25
  const bytes = normalizeChunk(chunk);
26
26
  if (bytes.length == 0) return;
27
+ const length = this.queue.data.length;
27
28
  const readIndex = Atomics.load(this.queue.control, 0);
28
29
  const writeIndex = Atomics.load(this.queue.control, 1);
29
- const usedCapacity = writeIndex - readIndex;
30
- const availableCapacity = this.queue.data.length - usedCapacity;
30
+ const availableCapacity = length - ringUsed(readIndex, writeIndex, length);
31
31
  if (bytes.length > availableCapacity) throw new Error(`Shared input queue overflow: cannot enqueue ${bytes.length} byte(s) into ${availableCapacity} byte(s) of free space.`);
32
32
  writeToRingBuffer(this.queue.data, bytes, writeIndex);
33
- Atomics.store(this.queue.control, 1, writeIndex + bytes.length);
33
+ Atomics.store(this.queue.control, 1, ringAdvance(writeIndex, bytes.length, length));
34
34
  Atomics.notify(this.queue.control, 1);
35
35
  }
36
36
  close() {
@@ -54,18 +54,17 @@ var SharedInputQueueReader = class {
54
54
  }
55
55
  readAvailable(maxBytes) {
56
56
  if (!Number.isInteger(maxBytes) || maxBytes <= 0) return new Uint8Array();
57
+ const length = this.queue.data.length;
57
58
  const readIndex = Atomics.load(this.queue.control, 0);
58
- const availableBytes = Atomics.load(this.queue.control, 1) - readIndex;
59
+ const availableBytes = ringUsed(readIndex, Atomics.load(this.queue.control, 1), length);
59
60
  if (availableBytes <= 0) return;
60
61
  const byteCount = Math.min(maxBytes, availableBytes);
61
62
  const chunk = readFromRingBuffer(this.queue.data, readIndex, byteCount);
62
- Atomics.store(this.queue.control, 0, readIndex + byteCount);
63
+ Atomics.store(this.queue.control, 0, ringAdvance(readIndex, byteCount, length));
63
64
  return chunk;
64
65
  }
65
66
  availableBytes() {
66
- const readIndex = Atomics.load(this.queue.control, 0);
67
- const writeIndex = Atomics.load(this.queue.control, 1);
68
- return Math.max(0, writeIndex - readIndex);
67
+ return ringUsed(Atomics.load(this.queue.control, 0), Atomics.load(this.queue.control, 1), this.queue.data.length);
69
68
  }
70
69
  waitForReadable(timeoutMilliseconds) {
71
70
  while (true) {
@@ -82,6 +81,13 @@ var SharedInputQueueReader = class {
82
81
  function normalizeChunk(chunk) {
83
82
  return typeof chunk == "string" ? new TextEncoder().encode(chunk) : new Uint8Array(chunk);
84
83
  }
84
+ function ringUsed(readIndex, writeIndex, length) {
85
+ const span = 2 * length;
86
+ return ((writeIndex - readIndex) % span + span) % span;
87
+ }
88
+ function ringAdvance(index, delta, length) {
89
+ return (index + delta) % (2 * length);
90
+ }
85
91
  function writeToRingBuffer(buffer, chunk, startIndex) {
86
92
  const offset = startIndex % buffer.length;
87
93
  const firstSegmentLength = Math.min(chunk.length, buffer.length - offset);
@@ -1 +1 @@
1
- {"version":3,"file":"SharedInputQueue.js","names":[],"sources":["../../../src/wasi/SharedInputQueue.ts"],"sourcesContent":["const controlSlots = 3;\n\nconst enum ControlSlot {\n readIndex = 0,\n writeIndex = 1,\n closed = 2,\n}\n\nexport const sharedInputQueueDefaultCapacity = 64 * 1024;\n\nexport interface SharedInputQueueBuffers {\n readonly controlBuffer: SharedArrayBuffer;\n readonly dataBuffer: SharedArrayBuffer;\n}\n\nexport type SharedInputReadiness = \"readable\" | \"closed\" | \"timedOut\";\n\ninterface SharedInputQueueState {\n readonly control: Int32Array;\n readonly data: Uint8Array;\n}\n\nexport function createSharedInputQueue(\n capacity: number = sharedInputQueueDefaultCapacity\n): SharedInputQueueBuffers {\n if (typeof SharedArrayBuffer === \"undefined\") {\n throw new Error(\n \"SharedArrayBuffer is unavailable. Serve the app with COOP/COEP headers so browser WASI stdin can stay live.\"\n );\n }\n\n if (!Number.isInteger(capacity) || capacity <= 0) {\n throw new Error(`Shared input queue capacity must be a positive integer, received ${capacity}.`);\n }\n\n return {\n controlBuffer: new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * controlSlots),\n dataBuffer: new SharedArrayBuffer(capacity),\n };\n}\n\nexport function hydrateSharedInputQueue(\n buffers: SharedInputQueueBuffers\n): SharedInputQueueState {\n return {\n control: new Int32Array(buffers.controlBuffer),\n data: new Uint8Array(buffers.dataBuffer),\n };\n}\n\nexport class SharedInputQueueWriter {\n private readonly queue: SharedInputQueueState;\n\n constructor(buffers: SharedInputQueueBuffers) {\n this.queue = hydrateSharedInputQueue(buffers);\n }\n\n write(chunk: Uint8Array | string): void {\n if (Atomics.load(this.queue.control, ControlSlot.closed) !== 0) {\n return;\n }\n\n const bytes = normalizeChunk(chunk);\n if (bytes.length == 0) {\n return;\n }\n\n const readIndex = Atomics.load(this.queue.control, ControlSlot.readIndex);\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n const usedCapacity = writeIndex - readIndex;\n const availableCapacity = this.queue.data.length - usedCapacity;\n\n if (bytes.length > availableCapacity) {\n throw new Error(\n `Shared input queue overflow: cannot enqueue ${bytes.length} byte(s) into ${availableCapacity} byte(s) of free space.`\n );\n }\n\n writeToRingBuffer(this.queue.data, bytes, writeIndex);\n Atomics.store(this.queue.control, ControlSlot.writeIndex, writeIndex + bytes.length);\n Atomics.notify(this.queue.control, ControlSlot.writeIndex);\n }\n\n close(): void {\n Atomics.store(this.queue.control, ControlSlot.closed, 1);\n Atomics.notify(this.queue.control, ControlSlot.writeIndex);\n }\n}\n\nexport class SharedInputQueueReader {\n private readonly queue: SharedInputQueueState;\n\n constructor(buffers: SharedInputQueueBuffers) {\n this.queue = hydrateSharedInputQueue(buffers);\n }\n\n read(maxBytes: number): Uint8Array | undefined {\n while (true) {\n const next = this.readAvailable(maxBytes);\n if (next) {\n return next;\n }\n\n if (this.isClosed()) {\n return undefined;\n }\n\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n Atomics.wait(this.queue.control, ControlSlot.writeIndex, writeIndex);\n }\n }\n\n readAvailable(maxBytes: number): Uint8Array | undefined {\n if (!Number.isInteger(maxBytes) || maxBytes <= 0) {\n return new Uint8Array();\n }\n\n const readIndex = Atomics.load(this.queue.control, ControlSlot.readIndex);\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n const availableBytes = writeIndex - readIndex;\n\n if (availableBytes <= 0) {\n return undefined;\n }\n\n const byteCount = Math.min(maxBytes, availableBytes);\n const chunk = readFromRingBuffer(this.queue.data, readIndex, byteCount);\n Atomics.store(this.queue.control, ControlSlot.readIndex, readIndex + byteCount);\n return chunk;\n }\n\n availableBytes(): number {\n const readIndex = Atomics.load(this.queue.control, ControlSlot.readIndex);\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n return Math.max(0, writeIndex - readIndex);\n }\n\n waitForReadable(\n timeoutMilliseconds?: number\n ): SharedInputReadiness {\n while (true) {\n if (this.availableBytes() > 0) {\n return \"readable\";\n }\n if (this.isClosed()) {\n return \"closed\";\n }\n\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n const result = Atomics.wait(\n this.queue.control,\n ControlSlot.writeIndex,\n writeIndex,\n timeoutMilliseconds\n );\n if (result === \"timed-out\") {\n return \"timedOut\";\n }\n }\n }\n\n isClosed(): boolean {\n return Atomics.load(this.queue.control, ControlSlot.closed) !== 0;\n }\n}\n\nfunction normalizeChunk(\n chunk: Uint8Array | string\n): Uint8Array {\n return typeof chunk == \"string\" ? new TextEncoder().encode(chunk) : new Uint8Array(chunk);\n}\n\nfunction writeToRingBuffer(\n buffer: Uint8Array,\n chunk: Uint8Array,\n startIndex: number\n): void {\n const offset = startIndex % buffer.length;\n const firstSegmentLength = Math.min(chunk.length, buffer.length - offset);\n buffer.set(chunk.subarray(0, firstSegmentLength), offset);\n if (firstSegmentLength < chunk.length) {\n buffer.set(chunk.subarray(firstSegmentLength), 0);\n }\n}\n\nfunction readFromRingBuffer(\n buffer: Uint8Array,\n startIndex: number,\n byteCount: number\n): Uint8Array {\n const chunk = new Uint8Array(byteCount);\n const offset = startIndex % buffer.length;\n const firstSegmentLength = Math.min(byteCount, buffer.length - offset);\n chunk.set(buffer.subarray(offset, offset + firstSegmentLength), 0);\n if (firstSegmentLength < byteCount) {\n chunk.set(buffer.subarray(0, byteCount - firstSegmentLength), firstSegmentLength);\n }\n return chunk;\n}\n"],"mappings":";AAAA,MAAM,eAAe;AAQrB,MAAa,kCAAkC,KAAK;AAcpD,SAAgB,uBACd,WAAmB,iCACM;CACzB,IAAI,OAAO,sBAAsB,aAC/B,MAAM,IAAI,MACR,6GACF;CAGF,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,YAAY,GAC7C,MAAM,IAAI,MAAM,oEAAoE,SAAS,EAAE;CAGjG,OAAO;EACL,eAAe,IAAI,kBAAkB,WAAW,oBAAoB,YAAY;EAChF,YAAY,IAAI,kBAAkB,QAAQ;CAC5C;AACF;AAEA,SAAgB,wBACd,SACuB;CACvB,OAAO;EACL,SAAS,IAAI,WAAW,QAAQ,aAAa;EAC7C,MAAM,IAAI,WAAW,QAAQ,UAAU;CACzC;AACF;AAEA,IAAa,yBAAb,MAAoC;CAClC;CAEA,YAAY,SAAkC;EAC5C,KAAK,QAAQ,wBAAwB,OAAO;CAC9C;CAEA,MAAM,OAAkC;EACtC,IAAI,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA2B,MAAM,GAC3D;EAGF,MAAM,QAAQ,eAAe,KAAK;EAClC,IAAI,MAAM,UAAU,GAClB;EAGF,MAAM,YAAY,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA8B;EACxE,MAAM,aAAa,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA+B;EAC1E,MAAM,eAAe,aAAa;EAClC,MAAM,oBAAoB,KAAK,MAAM,KAAK,SAAS;EAEnD,IAAI,MAAM,SAAS,mBACjB,MAAM,IAAI,MACR,+CAA+C,MAAM,OAAO,gBAAgB,kBAAkB,wBAChG;EAGF,kBAAkB,KAAK,MAAM,MAAM,OAAO,UAAU;EACpD,QAAQ,MAAM,KAAK,MAAM,SAAA,GAAiC,aAAa,MAAM,MAAM;EACnF,QAAQ,OAAO,KAAK,MAAM,SAAA,CAA+B;CAC3D;CAEA,QAAc;EACZ,QAAQ,MAAM,KAAK,MAAM,SAAA,GAA6B,CAAC;EACvD,QAAQ,OAAO,KAAK,MAAM,SAAA,CAA+B;CAC3D;AACF;AAEA,IAAa,yBAAb,MAAoC;CAClC;CAEA,YAAY,SAAkC;EAC5C,KAAK,QAAQ,wBAAwB,OAAO;CAC9C;CAEA,KAAK,UAA0C;EAC7C,OAAO,MAAM;GACX,MAAM,OAAO,KAAK,cAAc,QAAQ;GACxC,IAAI,MACF,OAAO;GAGT,IAAI,KAAK,SAAS,GAChB;GAGF,MAAM,aAAa,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA+B;GAC1E,QAAQ,KAAK,KAAK,MAAM,SAAA,GAAiC,UAAU;EACrE;CACF;CAEA,cAAc,UAA0C;EACtD,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,YAAY,GAC7C,OAAO,IAAI,WAAW;EAGxB,MAAM,YAAY,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA8B;EAExE,MAAM,iBADa,QAAQ,KAAK,KAAK,MAAM,SAAA,CACX,IAAI;EAEpC,IAAI,kBAAkB,GACpB;EAGF,MAAM,YAAY,KAAK,IAAI,UAAU,cAAc;EACnD,MAAM,QAAQ,mBAAmB,KAAK,MAAM,MAAM,WAAW,SAAS;EACtE,QAAQ,MAAM,KAAK,MAAM,SAAA,GAAgC,YAAY,SAAS;EAC9E,OAAO;CACT;CAEA,iBAAyB;EACvB,MAAM,YAAY,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA8B;EACxE,MAAM,aAAa,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA+B;EAC1E,OAAO,KAAK,IAAI,GAAG,aAAa,SAAS;CAC3C;CAEA,gBACE,qBACsB;EACtB,OAAO,MAAM;GACX,IAAI,KAAK,eAAe,IAAI,GAC1B,OAAO;GAET,IAAI,KAAK,SAAS,GAChB,OAAO;GAGT,MAAM,aAAa,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA+B;GAO1E,IANe,QAAQ,KACrB,KAAK,MAAM,SAAA,GAEX,YACA,mBAEO,MAAM,aACb,OAAO;EAEX;CACF;CAEA,WAAoB;EAClB,OAAO,QAAQ,KAAK,KAAK,MAAM,SAAA,CAA2B,MAAM;CAClE;AACF;AAEA,SAAS,eACP,OACY;CACZ,OAAO,OAAO,SAAS,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,IAAI,IAAI,WAAW,KAAK;AAC1F;AAEA,SAAS,kBACP,QACA,OACA,YACM;CACN,MAAM,SAAS,aAAa,OAAO;CACnC,MAAM,qBAAqB,KAAK,IAAI,MAAM,QAAQ,OAAO,SAAS,MAAM;CACxE,OAAO,IAAI,MAAM,SAAS,GAAG,kBAAkB,GAAG,MAAM;CACxD,IAAI,qBAAqB,MAAM,QAC7B,OAAO,IAAI,MAAM,SAAS,kBAAkB,GAAG,CAAC;AAEpD;AAEA,SAAS,mBACP,QACA,YACA,WACY;CACZ,MAAM,QAAQ,IAAI,WAAW,SAAS;CACtC,MAAM,SAAS,aAAa,OAAO;CACnC,MAAM,qBAAqB,KAAK,IAAI,WAAW,OAAO,SAAS,MAAM;CACrE,MAAM,IAAI,OAAO,SAAS,QAAQ,SAAS,kBAAkB,GAAG,CAAC;CACjE,IAAI,qBAAqB,WACvB,MAAM,IAAI,OAAO,SAAS,GAAG,YAAY,kBAAkB,GAAG,kBAAkB;CAElF,OAAO;AACT"}
1
+ {"version":3,"file":"SharedInputQueue.js","names":[],"sources":["../../../src/wasi/SharedInputQueue.ts"],"sourcesContent":["const controlSlots = 3;\n\nconst enum ControlSlot {\n readIndex = 0,\n writeIndex = 1,\n closed = 2,\n}\n\nexport const sharedInputQueueDefaultCapacity = 64 * 1024;\n\nexport interface SharedInputQueueBuffers {\n readonly controlBuffer: SharedArrayBuffer;\n readonly dataBuffer: SharedArrayBuffer;\n}\n\nexport type SharedInputReadiness = \"readable\" | \"closed\" | \"timedOut\";\n\ninterface SharedInputQueueState {\n readonly control: Int32Array;\n readonly data: Uint8Array;\n}\n\nexport function createSharedInputQueue(\n capacity: number = sharedInputQueueDefaultCapacity\n): SharedInputQueueBuffers {\n if (typeof SharedArrayBuffer === \"undefined\") {\n throw new Error(\n \"SharedArrayBuffer is unavailable. Serve the app with COOP/COEP headers so browser WASI stdin can stay live.\"\n );\n }\n\n if (!Number.isInteger(capacity) || capacity <= 0) {\n throw new Error(`Shared input queue capacity must be a positive integer, received ${capacity}.`);\n }\n\n return {\n controlBuffer: new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * controlSlots),\n dataBuffer: new SharedArrayBuffer(capacity),\n };\n}\n\nexport function hydrateSharedInputQueue(\n buffers: SharedInputQueueBuffers\n): SharedInputQueueState {\n return {\n control: new Int32Array(buffers.controlBuffer),\n data: new Uint8Array(buffers.dataBuffer),\n };\n}\n\nexport class SharedInputQueueWriter {\n private readonly queue: SharedInputQueueState;\n\n constructor(buffers: SharedInputQueueBuffers) {\n this.queue = hydrateSharedInputQueue(buffers);\n }\n\n write(chunk: Uint8Array | string): void {\n if (Atomics.load(this.queue.control, ControlSlot.closed) !== 0) {\n return;\n }\n\n const bytes = normalizeChunk(chunk);\n if (bytes.length == 0) {\n return;\n }\n\n const length = this.queue.data.length;\n const readIndex = Atomics.load(this.queue.control, ControlSlot.readIndex);\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n const usedCapacity = ringUsed(readIndex, writeIndex, length);\n const availableCapacity = length - usedCapacity;\n\n if (bytes.length > availableCapacity) {\n throw new Error(\n `Shared input queue overflow: cannot enqueue ${bytes.length} byte(s) into ${availableCapacity} byte(s) of free space.`\n );\n }\n\n writeToRingBuffer(this.queue.data, bytes, writeIndex);\n Atomics.store(\n this.queue.control,\n ControlSlot.writeIndex,\n ringAdvance(writeIndex, bytes.length, length)\n );\n Atomics.notify(this.queue.control, ControlSlot.writeIndex);\n }\n\n close(): void {\n Atomics.store(this.queue.control, ControlSlot.closed, 1);\n Atomics.notify(this.queue.control, ControlSlot.writeIndex);\n }\n}\n\nexport class SharedInputQueueReader {\n private readonly queue: SharedInputQueueState;\n\n constructor(buffers: SharedInputQueueBuffers) {\n this.queue = hydrateSharedInputQueue(buffers);\n }\n\n read(maxBytes: number): Uint8Array | undefined {\n while (true) {\n const next = this.readAvailable(maxBytes);\n if (next) {\n return next;\n }\n\n if (this.isClosed()) {\n return undefined;\n }\n\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n Atomics.wait(this.queue.control, ControlSlot.writeIndex, writeIndex);\n }\n }\n\n readAvailable(maxBytes: number): Uint8Array | undefined {\n if (!Number.isInteger(maxBytes) || maxBytes <= 0) {\n return new Uint8Array();\n }\n\n const length = this.queue.data.length;\n const readIndex = Atomics.load(this.queue.control, ControlSlot.readIndex);\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n const availableBytes = ringUsed(readIndex, writeIndex, length);\n\n if (availableBytes <= 0) {\n return undefined;\n }\n\n const byteCount = Math.min(maxBytes, availableBytes);\n const chunk = readFromRingBuffer(this.queue.data, readIndex, byteCount);\n Atomics.store(\n this.queue.control,\n ControlSlot.readIndex,\n ringAdvance(readIndex, byteCount, length)\n );\n return chunk;\n }\n\n availableBytes(): number {\n const readIndex = Atomics.load(this.queue.control, ControlSlot.readIndex);\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n return ringUsed(readIndex, writeIndex, this.queue.data.length);\n }\n\n waitForReadable(\n timeoutMilliseconds?: number\n ): SharedInputReadiness {\n while (true) {\n if (this.availableBytes() > 0) {\n return \"readable\";\n }\n if (this.isClosed()) {\n return \"closed\";\n }\n\n const writeIndex = Atomics.load(this.queue.control, ControlSlot.writeIndex);\n const result = Atomics.wait(\n this.queue.control,\n ControlSlot.writeIndex,\n writeIndex,\n timeoutMilliseconds\n );\n if (result === \"timed-out\") {\n return \"timedOut\";\n }\n }\n }\n\n isClosed(): boolean {\n return Atomics.load(this.queue.control, ControlSlot.closed) !== 0;\n }\n}\n\nfunction normalizeChunk(\n chunk: Uint8Array | string\n): Uint8Array {\n return typeof chunk == \"string\" ? new TextEncoder().encode(chunk) : new Uint8Array(chunk);\n}\n\n// The read/write cursors are kept in the half-open range [0, 2 * length) — the\n// classic \"two indices mod 2N\" ring buffer. Bounding both cursors keeps them\n// from growing without limit and overflowing Int32 across long sessions, while\n// still distinguishing a full queue (used == length) from an empty one\n// (used == 0). The data-buffer offset for either cursor is cursor % length.\nfunction ringUsed(\n readIndex: number,\n writeIndex: number,\n length: number\n): number {\n const span = 2 * length;\n return ((writeIndex - readIndex) % span + span) % span;\n}\n\nfunction ringAdvance(\n index: number,\n delta: number,\n length: number\n): number {\n return (index + delta) % (2 * length);\n}\n\nfunction writeToRingBuffer(\n buffer: Uint8Array,\n chunk: Uint8Array,\n startIndex: number\n): void {\n const offset = startIndex % buffer.length;\n const firstSegmentLength = Math.min(chunk.length, buffer.length - offset);\n buffer.set(chunk.subarray(0, firstSegmentLength), offset);\n if (firstSegmentLength < chunk.length) {\n buffer.set(chunk.subarray(firstSegmentLength), 0);\n }\n}\n\nfunction readFromRingBuffer(\n buffer: Uint8Array,\n startIndex: number,\n byteCount: number\n): Uint8Array {\n const chunk = new Uint8Array(byteCount);\n const offset = startIndex % buffer.length;\n const firstSegmentLength = Math.min(byteCount, buffer.length - offset);\n chunk.set(buffer.subarray(offset, offset + firstSegmentLength), 0);\n if (firstSegmentLength < byteCount) {\n chunk.set(buffer.subarray(0, byteCount - firstSegmentLength), firstSegmentLength);\n }\n return chunk;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swifttui/web",
3
- "version": "0.0.27",
3
+ "version": "0.1.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,