@swifttui/web 0.8.9 → 0.8.11

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.
@@ -127,6 +127,7 @@ var CanvasSurfacePainter = class {
127
127
  context.beginPath();
128
128
  context.rect(clipX * metrics.cellWidth, clipY * metrics.cellHeight, clipWidth * metrics.cellWidth, clipHeight * metrics.cellHeight);
129
129
  context.clip();
130
+ context.globalAlpha = normalizedImageOpacity(image.opacity);
130
131
  context.drawImage(decodedImage, boundsX * metrics.cellWidth, boundsY * metrics.cellHeight, boundsWidth * metrics.cellWidth, boundsHeight * metrics.cellHeight);
131
132
  context.restore();
132
133
  }
@@ -314,6 +315,10 @@ var CanvasSurfacePainter = class {
314
315
  context.setLineDash([]);
315
316
  }
316
317
  };
318
+ function normalizedImageOpacity(opacity) {
319
+ if (opacity === void 0 || !Number.isFinite(opacity)) return 1;
320
+ return Math.max(0, Math.min(1, opacity));
321
+ }
317
322
  function isPaintableSurfaceImage(image) {
318
323
  const [, , boundsWidth, boundsHeight] = image.bounds;
319
324
  const [, , clipWidth, clipHeight] = image.visibleBounds;
@@ -1 +1 @@
1
- {"version":3,"file":"CanvasSurfacePainter.js","names":[],"sources":["../../src/CanvasSurfacePainter.ts"],"sourcesContent":["import {\n canRenderBoxDrawing,\n drawBoxDrawing,\n} from \"./BoxDrawingRenderer.ts\";\nimport {\n resolvedSurfaceBackground,\n resolvedSurfaceForeground,\n type SurfaceMetrics,\n type WebHostSurfacePainter,\n} from \"./SurfaceRenderer.ts\";\nimport {\n isSupportedImageFormat,\n type NormalizedSurfaceImageFormat,\n} from \"./normalizeWireTokens.ts\";\nimport {\n type ResolvedWebHostTerminalStyle,\n webTUITerminalBackgroundColor,\n} from \"./WebHostTerminalStyle.ts\";\nimport {\n isWebHostImageRecoveryId,\n type WebHostImagePayloadRequestHandler,\n type WebHostSurfaceDamage,\n type WebHostSurfaceFrame,\n type WebHostSurfaceImage,\n type WebHostSurfaceStyle,\n} from \"./WebHostSurfaceTransport.ts\";\nimport {\n registerCanvasSurfacePainterConformanceControl,\n} from \"./SurfacePainterConformanceControl.ts\";\n\n/**\n * A read-only snapshot of the cell grid geometry and active style the painter\n * needs for a single paint pass — see {@link SurfaceMetrics}, shared with the\n * DOM painter. The alias keeps this painter's original public name stable.\n */\nexport type CanvasSurfaceMetrics = SurfaceMetrics;\n\ninterface CachedWebHostImage {\n image?: CanvasImageSource;\n promise?: Promise<CanvasImageSource>;\n payload?: string;\n /** Total decode attempts already started; bounded by MAX_IMAGE_DECODE_ATTEMPTS. */\n retries?: number;\n missReported?: boolean;\n}\n\nexport const MAX_IMAGE_DECODE_ATTEMPTS = 3;\nexport const MAX_UNRESOLVED_IMAGE_CACHE_ENTRIES = 256;\nexport const MAX_UNRESOLVED_IMAGE_PAYLOAD_CHARACTERS = 64 * 1024 * 1024;\n\nexport interface CanvasSurfacePainterOptions {\n /** Injectable decode seam for browser hosts and deterministic failure tests. */\n decodeImage?: (\n dataBase64: string,\n format: NormalizedSurfaceImageFormat,\n imageID: string\n ) => Promise<CanvasImageSource>;\n /**\n * Reports supported, positive-area images whose payload cannot be resolved\n * locally. Returning the admitted subset keeps rejected IDs eligible for a\n * later paint; `void` preserves legacy all-accepted behavior.\n */\n onImagePayloadMiss?: WebHostImagePayloadRequestHandler;\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\n/**\n * Draws SwiftTUI surface frames onto a 2D canvas: background fills, per-cell\n * text/box-drawing/decorations, surface images, and damage-scoped dirty-region\n * painting. The painter caches decoded images and asks the host to repaint once\n * an image finishes decoding (via the `requestRedraw` callback).\n *\n * Geometry and style are supplied per paint via {@link CanvasSurfaceMetrics};\n * the painter holds only the canvas handle, the image cache, and the redraw\n * callback as durable state.\n */\nexport class CanvasSurfacePainter implements WebHostSurfacePainter {\n private readonly imageCache = new Map<string, CachedWebHostImage>();\n private readonly unresolvedImageIds = new Set<string>();\n private unresolvedImagePayloadCharacters = 0;\n private readonly imageDecoder: NonNullable<CanvasSurfacePainterOptions[\"decodeImage\"]>;\n private readonly onImagePayloadMiss: WebHostImagePayloadRequestHandler;\n private canvas?: HTMLCanvasElement;\n private requestRedraw: () => void = () => {};\n private lastEpoch?: number;\n private readonly pendingImagePayloadMissIds = new Set<string>();\n private imagePayloadMissScheduled = false;\n\n constructor(options: CanvasSurfacePainterOptions = {}) {\n this.imageDecoder = options.decodeImage ?? decodeImage;\n this.onImagePayloadMiss = options.onImagePayloadMiss ?? (() => {});\n registerCanvasSurfacePainterConformanceControl(this, {\n evictImages: (ids) => {\n for (const id of ids) {\n this.removeUnresolvedImage(id);\n this.imageCache.delete(id);\n this.pendingImagePayloadMissIds.delete(id);\n }\n },\n visibleImageIDs: (images) => [...new Set(\n images\n .filter(isPaintableSurfaceImage)\n .filter((image) => this.imageCache.get(image.id)?.image !== undefined)\n .map((image) => image.id)\n )].sort(),\n });\n }\n\n /**\n * Binds the canvas the painter draws into and the callback used to request a\n * full repaint after an asynchronous image decode completes.\n */\n attach(\n canvas: HTMLCanvasElement,\n requestRedraw: () => void\n ): void {\n this.canvas = canvas;\n this.requestRedraw = requestRedraw;\n }\n\n paint(\n metrics: CanvasSurfaceMetrics,\n frame: WebHostSurfaceFrame | undefined,\n damage?: WebHostSurfaceDamage,\n recoveredImagePayloadIds: readonly string[] = []\n ): void {\n const canvas = this.canvas;\n const context = canvas?.getContext(\"2d\");\n if (!canvas || !context) {\n return;\n }\n\n if (frame?.epoch !== undefined && frame.epoch !== this.lastEpoch) {\n this.lastEpoch = frame.epoch;\n for (const cached of this.imageCache.values()) {\n if (!cached.image) {\n cached.missReported = false;\n }\n }\n }\n this.sweepUnresolvedImages(frame?.images);\n\n const dirtyRegion = frame\n ? this.dirtyRegionForDamage(damage, frame, metrics)\n : undefined;\n const recoveredPayloadIds = new Set(recoveredImagePayloadIds);\n if (dirtyRegion?.rects.length === 0) {\n this.prepareImages(frame?.images ?? [], recoveredPayloadIds);\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(metrics.style);\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, canvas.width / scale, canvas.height / scale);\n }\n\n if (!frame) {\n return;\n }\n\n this.drawRows(context, frame, metrics, dirtyRegion);\n this.drawImages(\n context,\n frame.images ?? [],\n metrics,\n dirtyRegion,\n recoveredPayloadIds\n );\n }\n\n private drawRows(\n context: CanvasRenderingContext2D,\n frame: WebHostSurfaceFrame,\n metrics: CanvasSurfaceMetrics,\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, metrics, 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, metrics, row, y);\n }\n }\n\n private drawRow(\n context: CanvasRenderingContext2D,\n frame: WebHostSurfaceFrame,\n metrics: CanvasSurfaceMetrics,\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, metrics, x, y, text, span, style);\n }\n }\n\n private drawImages(\n context: CanvasRenderingContext2D,\n images: WebHostSurfaceImage[],\n metrics: CanvasSurfaceMetrics,\n dirtyRegion: DirtyRegion | undefined,\n recoveredPayloadIds: Set<string>\n ): void {\n const missingPayloadIds = new Set<string>();\n for (const image of images) {\n if (!isPaintableSurfaceImage(image)) {\n continue;\n }\n this.drawImage(\n context,\n image,\n metrics,\n dirtyRegion,\n missingPayloadIds,\n recoveredPayloadIds\n );\n }\n this.reportImagePayloadMisses(missingPayloadIds);\n }\n\n private prepareImages(\n images: WebHostSurfaceImage[],\n recoveredPayloadIds: Set<string>\n ): void {\n const missingPayloadIds = new Set<string>();\n for (const image of images) {\n if (!isPaintableSurfaceImage(image)) {\n continue;\n }\n this.cachedImage(image, missingPayloadIds, recoveredPayloadIds);\n }\n this.reportImagePayloadMisses(missingPayloadIds);\n }\n\n private drawImage(\n context: CanvasRenderingContext2D,\n image: WebHostSurfaceImage,\n metrics: CanvasSurfaceMetrics,\n dirtyRegion: DirtyRegion | undefined,\n missingPayloadIds: Set<string>,\n recoveredPayloadIds: Set<string>\n ): void {\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\n const decodedImage = this.cachedImage(\n image,\n missingPayloadIds,\n recoveredPayloadIds\n );\n if (!decodedImage) {\n return;\n }\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 * metrics.cellWidth,\n clipY * metrics.cellHeight,\n clipWidth * metrics.cellWidth,\n clipHeight * metrics.cellHeight\n );\n context.clip();\n context.drawImage(\n decodedImage,\n boundsX * metrics.cellWidth,\n boundsY * metrics.cellHeight,\n boundsWidth * metrics.cellWidth,\n boundsHeight * metrics.cellHeight\n );\n context.restore();\n }\n\n private cachedImage(\n image: WebHostSurfaceImage,\n missingPayloadIds: Set<string>,\n recoveredPayloadIds: Set<string>\n ): CanvasImageSource | undefined {\n let cached = this.imageCache.get(image.id);\n if (cached?.image) {\n return cached.image;\n }\n\n const beginsRecoveredGeneration = image.dataBase64 !== undefined\n && recoveredPayloadIds.delete(image.id);\n if (\n image.dataBase64 !== undefined\n && (cached?.payload === undefined || beginsRecoveredGeneration)\n ) {\n if (!this.canTrackUnresolvedImage(image.id, image.dataBase64)) {\n return undefined;\n }\n cached = {\n payload: image.dataBase64,\n retries: 0,\n };\n this.setUnresolvedImage(image.id, cached);\n } else if (!cached) {\n if (!isWebHostImageRecoveryId(image.id)) {\n return undefined;\n }\n if (!this.canTrackUnresolvedImage(image.id)) {\n return undefined;\n }\n cached = { missReported: false };\n this.setUnresolvedImage(image.id, cached);\n missingPayloadIds.add(image.id);\n return undefined;\n }\n\n const attempts = cached.retries ?? 0;\n if (cached.payload === undefined) {\n if (!cached.missReported) {\n cached.missReported = true;\n missingPayloadIds.add(image.id);\n }\n return undefined;\n }\n if (attempts >= MAX_IMAGE_DECODE_ATTEMPTS) {\n if (!cached.missReported) {\n cached.missReported = true;\n missingPayloadIds.add(image.id);\n }\n return undefined;\n }\n\n if (!cached.promise) {\n const nextAttempts = attempts + 1;\n const promise = this.imageDecoder(cached.payload, image.format, image.id);\n cached.promise = promise;\n cached.retries = nextAttempts;\n void promise.then((decodedImage) => {\n const latest = this.imageCache.get(image.id);\n if (latest?.promise !== promise) {\n return;\n }\n this.removeUnresolvedImage(image.id);\n this.imageCache.set(image.id, { image: decodedImage });\n this.requestRedraw();\n }).catch(() => {\n const latest = this.imageCache.get(image.id);\n if (latest?.promise !== promise) {\n return;\n }\n latest.promise = undefined;\n if (\n (latest.retries ?? 0) >= MAX_IMAGE_DECODE_ATTEMPTS\n && !latest.missReported\n ) {\n latest.missReported = true;\n if (isWebHostImageRecoveryId(image.id)) {\n this.scheduleImagePayloadMiss(image.id);\n }\n }\n this.requestRedraw();\n });\n }\n\n return undefined;\n }\n\n private canTrackUnresolvedImage(\n id: string,\n payload?: string\n ): boolean {\n const existing = this.imageCache.get(id);\n const existingPayloadCharacters = existing?.image\n ? 0\n : existing?.payload?.length ?? 0;\n const nextPayloadCharacters = this.unresolvedImagePayloadCharacters\n - existingPayloadCharacters\n + (payload?.length ?? 0);\n return (\n (this.unresolvedImageIds.has(id)\n || this.unresolvedImageIds.size < MAX_UNRESOLVED_IMAGE_CACHE_ENTRIES)\n && nextPayloadCharacters <= MAX_UNRESOLVED_IMAGE_PAYLOAD_CHARACTERS\n );\n }\n\n private setUnresolvedImage(\n id: string,\n cached: CachedWebHostImage\n ): void {\n const existing = this.imageCache.get(id);\n if (this.unresolvedImageIds.has(id)) {\n this.unresolvedImagePayloadCharacters -= existing?.payload?.length ?? 0;\n }\n this.imageCache.set(id, cached);\n this.unresolvedImageIds.add(id);\n this.unresolvedImagePayloadCharacters += cached.payload?.length ?? 0;\n this.pendingImagePayloadMissIds.delete(id);\n }\n\n private removeUnresolvedImage(\n id: string\n ): void {\n if (!this.unresolvedImageIds.delete(id)) {\n return;\n }\n this.unresolvedImagePayloadCharacters -=\n this.imageCache.get(id)?.payload?.length ?? 0;\n this.pendingImagePayloadMissIds.delete(id);\n }\n\n private sweepUnresolvedImages(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n const presentedIds = new Set<string>();\n for (const image of images ?? []) {\n if (!this.unresolvedImageIds.has(image.id)) {\n continue;\n }\n if (isPaintableSurfaceImage(image)) {\n presentedIds.add(image.id);\n }\n }\n for (const id of this.unresolvedImageIds) {\n if (presentedIds.has(id)) {\n continue;\n }\n this.removeUnresolvedImage(id);\n this.imageCache.delete(id);\n }\n }\n\n private reportImagePayloadMisses(\n ids: Set<string>\n ): void {\n if (ids.size === 0) {\n return;\n }\n const candidateIds = [...ids].sort();\n this.applyImagePayloadMissAdmission(\n candidateIds,\n this.onImagePayloadMiss(candidateIds)\n );\n }\n\n private scheduleImagePayloadMiss(\n id: string\n ): void {\n this.pendingImagePayloadMissIds.add(id);\n if (this.imagePayloadMissScheduled) {\n return;\n }\n this.imagePayloadMissScheduled = true;\n queueMicrotask(() => {\n this.imagePayloadMissScheduled = false;\n const ids = [...this.pendingImagePayloadMissIds].sort();\n this.pendingImagePayloadMissIds.clear();\n if (ids.length > 0) {\n this.applyImagePayloadMissAdmission(\n ids,\n this.onImagePayloadMiss(ids)\n );\n }\n });\n }\n\n private applyImagePayloadMissAdmission(\n candidateIds: readonly string[],\n admittedIds: readonly string[] | void\n ): void {\n // Callbacks authored against the former void contract commonly use a\n // concise `array.push(...)` body, whose runtime result is a number.\n const acceptedIds = new Set(\n Array.isArray(admittedIds) ? admittedIds : candidateIds\n );\n for (const id of candidateIds) {\n const cached = this.imageCache.get(id);\n if (cached && !cached.image) {\n cached.missReported = acceptedIds.has(id);\n }\n }\n }\n\n private drawCell(\n context: CanvasRenderingContext2D,\n metrics: CanvasSurfaceMetrics,\n x: number,\n y: number,\n text: string,\n span: number,\n style?: WebHostSurfaceStyle | null\n ): void {\n const rectX = x * metrics.cellWidth;\n const rectY = y * metrics.cellHeight;\n const width = Math.max(1, span) * metrics.cellWidth;\n const background = resolvedSurfaceBackground(style, metrics.style);\n const foreground = resolvedSurfaceForeground(style, metrics.style);\n const opacity = style?.opacity ?? 1;\n\n if (background) {\n context.globalAlpha = opacity;\n context.fillStyle = background;\n context.fillRect(rectX, rectY, width, metrics.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: metrics.cellHeight,\n })) {\n context.font = fontForStyle(metrics.style, style);\n context.fillText(\n text,\n rectX,\n rectY + Math.floor((metrics.cellHeight + metrics.style.fontSize) / 2) - 2\n );\n }\n }\n\n this.drawTextLine(context, metrics, rectX, rectY, width, style?.underline, \"underline\", foreground);\n this.drawTextLine(context, metrics, rectX, rectY, width, style?.strikethrough, \"strike\", foreground);\n context.globalAlpha = 1;\n }\n\n private dirtyRegionForDamage(\n damage: WebHostSurfaceDamage | undefined,\n frame: WebHostSurfaceFrame,\n metrics: CanvasSurfaceMetrics\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(cellRect(metrics, 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(cellRect(metrics, 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 drawTextLine(\n context: CanvasRenderingContext2D,\n metrics: CanvasSurfaceMetrics,\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 + metrics.cellHeight - 2\n : y + Math.floor(metrics.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\nfunction isPaintableSurfaceImage(\n image: WebHostSurfaceImage\n): boolean {\n const [, , boundsWidth, boundsHeight] = image.bounds;\n const [, , clipWidth, clipHeight] = image.visibleBounds;\n return isSupportedImageFormat(image.format)\n && boundsWidth > 0\n && boundsHeight > 0\n && clipWidth > 0\n && clipHeight > 0;\n}\n\n/**\n * The CSS font string for a cell, folding the surface emphasis bits (bold,\n * italic) over the host terminal's font size/family. Exposed so the host can\n * reuse the exact same metric when measuring cell dimensions.\n */\nexport function fontForStyle(\n terminalStyle: ResolvedWebHostTerminalStyle,\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}${terminalStyle.fontSize}px ${terminalStyle.fontFamily}`;\n}\n\nfunction cellRect(\n metrics: CanvasSurfaceMetrics,\n x: number,\n y: number,\n span: number\n): DirtyRect {\n return {\n x: x * metrics.cellWidth,\n y: y * metrics.cellHeight,\n width: Math.max(1, span) * metrics.cellWidth,\n height: metrics.cellHeight,\n };\n}\n\nasync function decodeImage(\n dataBase64: string,\n format: NormalizedSurfaceImageFormat\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 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"],"mappings":"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"}
1
+ {"version":3,"file":"CanvasSurfacePainter.js","names":[],"sources":["../../src/CanvasSurfacePainter.ts"],"sourcesContent":["import {\n canRenderBoxDrawing,\n drawBoxDrawing,\n} from \"./BoxDrawingRenderer.ts\";\nimport {\n resolvedSurfaceBackground,\n resolvedSurfaceForeground,\n type SurfaceMetrics,\n type WebHostSurfacePainter,\n} from \"./SurfaceRenderer.ts\";\nimport {\n isSupportedImageFormat,\n type NormalizedSurfaceImageFormat,\n} from \"./normalizeWireTokens.ts\";\nimport {\n type ResolvedWebHostTerminalStyle,\n webTUITerminalBackgroundColor,\n} from \"./WebHostTerminalStyle.ts\";\nimport {\n isWebHostImageRecoveryId,\n type WebHostImagePayloadRequestHandler,\n type WebHostSurfaceDamage,\n type WebHostSurfaceFrame,\n type WebHostSurfaceImage,\n type WebHostSurfaceStyle,\n} from \"./WebHostSurfaceTransport.ts\";\nimport {\n registerCanvasSurfacePainterConformanceControl,\n} from \"./SurfacePainterConformanceControl.ts\";\n\n/**\n * A read-only snapshot of the cell grid geometry and active style the painter\n * needs for a single paint pass — see {@link SurfaceMetrics}, shared with the\n * DOM painter. The alias keeps this painter's original public name stable.\n */\nexport type CanvasSurfaceMetrics = SurfaceMetrics;\n\ninterface CachedWebHostImage {\n image?: CanvasImageSource;\n promise?: Promise<CanvasImageSource>;\n payload?: string;\n /** Total decode attempts already started; bounded by MAX_IMAGE_DECODE_ATTEMPTS. */\n retries?: number;\n missReported?: boolean;\n}\n\nexport const MAX_IMAGE_DECODE_ATTEMPTS = 3;\nexport const MAX_UNRESOLVED_IMAGE_CACHE_ENTRIES = 256;\nexport const MAX_UNRESOLVED_IMAGE_PAYLOAD_CHARACTERS = 64 * 1024 * 1024;\n\nexport interface CanvasSurfacePainterOptions {\n /** Injectable decode seam for browser hosts and deterministic failure tests. */\n decodeImage?: (\n dataBase64: string,\n format: NormalizedSurfaceImageFormat,\n imageID: string\n ) => Promise<CanvasImageSource>;\n /**\n * Reports supported, positive-area images whose payload cannot be resolved\n * locally. Returning the admitted subset keeps rejected IDs eligible for a\n * later paint; `void` preserves legacy all-accepted behavior.\n */\n onImagePayloadMiss?: WebHostImagePayloadRequestHandler;\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\n/**\n * Draws SwiftTUI surface frames onto a 2D canvas: background fills, per-cell\n * text/box-drawing/decorations, surface images, and damage-scoped dirty-region\n * painting. The painter caches decoded images and asks the host to repaint once\n * an image finishes decoding (via the `requestRedraw` callback).\n *\n * Geometry and style are supplied per paint via {@link CanvasSurfaceMetrics};\n * the painter holds only the canvas handle, the image cache, and the redraw\n * callback as durable state.\n */\nexport class CanvasSurfacePainter implements WebHostSurfacePainter {\n private readonly imageCache = new Map<string, CachedWebHostImage>();\n private readonly unresolvedImageIds = new Set<string>();\n private unresolvedImagePayloadCharacters = 0;\n private readonly imageDecoder: NonNullable<CanvasSurfacePainterOptions[\"decodeImage\"]>;\n private readonly onImagePayloadMiss: WebHostImagePayloadRequestHandler;\n private canvas?: HTMLCanvasElement;\n private requestRedraw: () => void = () => {};\n private lastEpoch?: number;\n private readonly pendingImagePayloadMissIds = new Set<string>();\n private imagePayloadMissScheduled = false;\n\n constructor(options: CanvasSurfacePainterOptions = {}) {\n this.imageDecoder = options.decodeImage ?? decodeImage;\n this.onImagePayloadMiss = options.onImagePayloadMiss ?? (() => {});\n registerCanvasSurfacePainterConformanceControl(this, {\n evictImages: (ids) => {\n for (const id of ids) {\n this.removeUnresolvedImage(id);\n this.imageCache.delete(id);\n this.pendingImagePayloadMissIds.delete(id);\n }\n },\n visibleImageIDs: (images) => [...new Set(\n images\n .filter(isPaintableSurfaceImage)\n .filter((image) => this.imageCache.get(image.id)?.image !== undefined)\n .map((image) => image.id)\n )].sort(),\n });\n }\n\n /**\n * Binds the canvas the painter draws into and the callback used to request a\n * full repaint after an asynchronous image decode completes.\n */\n attach(\n canvas: HTMLCanvasElement,\n requestRedraw: () => void\n ): void {\n this.canvas = canvas;\n this.requestRedraw = requestRedraw;\n }\n\n paint(\n metrics: CanvasSurfaceMetrics,\n frame: WebHostSurfaceFrame | undefined,\n damage?: WebHostSurfaceDamage,\n recoveredImagePayloadIds: readonly string[] = []\n ): void {\n const canvas = this.canvas;\n const context = canvas?.getContext(\"2d\");\n if (!canvas || !context) {\n return;\n }\n\n if (frame?.epoch !== undefined && frame.epoch !== this.lastEpoch) {\n this.lastEpoch = frame.epoch;\n for (const cached of this.imageCache.values()) {\n if (!cached.image) {\n cached.missReported = false;\n }\n }\n }\n this.sweepUnresolvedImages(frame?.images);\n\n const dirtyRegion = frame\n ? this.dirtyRegionForDamage(damage, frame, metrics)\n : undefined;\n const recoveredPayloadIds = new Set(recoveredImagePayloadIds);\n if (dirtyRegion?.rects.length === 0) {\n this.prepareImages(frame?.images ?? [], recoveredPayloadIds);\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(metrics.style);\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, canvas.width / scale, canvas.height / scale);\n }\n\n if (!frame) {\n return;\n }\n\n this.drawRows(context, frame, metrics, dirtyRegion);\n this.drawImages(\n context,\n frame.images ?? [],\n metrics,\n dirtyRegion,\n recoveredPayloadIds\n );\n }\n\n private drawRows(\n context: CanvasRenderingContext2D,\n frame: WebHostSurfaceFrame,\n metrics: CanvasSurfaceMetrics,\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, metrics, 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, metrics, row, y);\n }\n }\n\n private drawRow(\n context: CanvasRenderingContext2D,\n frame: WebHostSurfaceFrame,\n metrics: CanvasSurfaceMetrics,\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, metrics, x, y, text, span, style);\n }\n }\n\n private drawImages(\n context: CanvasRenderingContext2D,\n images: WebHostSurfaceImage[],\n metrics: CanvasSurfaceMetrics,\n dirtyRegion: DirtyRegion | undefined,\n recoveredPayloadIds: Set<string>\n ): void {\n const missingPayloadIds = new Set<string>();\n for (const image of images) {\n if (!isPaintableSurfaceImage(image)) {\n continue;\n }\n this.drawImage(\n context,\n image,\n metrics,\n dirtyRegion,\n missingPayloadIds,\n recoveredPayloadIds\n );\n }\n this.reportImagePayloadMisses(missingPayloadIds);\n }\n\n private prepareImages(\n images: WebHostSurfaceImage[],\n recoveredPayloadIds: Set<string>\n ): void {\n const missingPayloadIds = new Set<string>();\n for (const image of images) {\n if (!isPaintableSurfaceImage(image)) {\n continue;\n }\n this.cachedImage(image, missingPayloadIds, recoveredPayloadIds);\n }\n this.reportImagePayloadMisses(missingPayloadIds);\n }\n\n private drawImage(\n context: CanvasRenderingContext2D,\n image: WebHostSurfaceImage,\n metrics: CanvasSurfaceMetrics,\n dirtyRegion: DirtyRegion | undefined,\n missingPayloadIds: Set<string>,\n recoveredPayloadIds: Set<string>\n ): void {\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\n const decodedImage = this.cachedImage(\n image,\n missingPayloadIds,\n recoveredPayloadIds\n );\n if (!decodedImage) {\n return;\n }\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 * metrics.cellWidth,\n clipY * metrics.cellHeight,\n clipWidth * metrics.cellWidth,\n clipHeight * metrics.cellHeight\n );\n context.clip();\n // Opacity is placement state, not image-content identity. Keep the decoded\n // payload cached by id and apply alpha for every replay so alpha-only frame\n // updates neither decode nor retransmit the image bytes.\n context.globalAlpha = normalizedImageOpacity(image.opacity);\n context.drawImage(\n decodedImage,\n boundsX * metrics.cellWidth,\n boundsY * metrics.cellHeight,\n boundsWidth * metrics.cellWidth,\n boundsHeight * metrics.cellHeight\n );\n context.restore();\n }\n\n private cachedImage(\n image: WebHostSurfaceImage,\n missingPayloadIds: Set<string>,\n recoveredPayloadIds: Set<string>\n ): CanvasImageSource | undefined {\n let cached = this.imageCache.get(image.id);\n if (cached?.image) {\n return cached.image;\n }\n\n const beginsRecoveredGeneration = image.dataBase64 !== undefined\n && recoveredPayloadIds.delete(image.id);\n if (\n image.dataBase64 !== undefined\n && (cached?.payload === undefined || beginsRecoveredGeneration)\n ) {\n if (!this.canTrackUnresolvedImage(image.id, image.dataBase64)) {\n return undefined;\n }\n cached = {\n payload: image.dataBase64,\n retries: 0,\n };\n this.setUnresolvedImage(image.id, cached);\n } else if (!cached) {\n if (!isWebHostImageRecoveryId(image.id)) {\n return undefined;\n }\n if (!this.canTrackUnresolvedImage(image.id)) {\n return undefined;\n }\n cached = { missReported: false };\n this.setUnresolvedImage(image.id, cached);\n missingPayloadIds.add(image.id);\n return undefined;\n }\n\n const attempts = cached.retries ?? 0;\n if (cached.payload === undefined) {\n if (!cached.missReported) {\n cached.missReported = true;\n missingPayloadIds.add(image.id);\n }\n return undefined;\n }\n if (attempts >= MAX_IMAGE_DECODE_ATTEMPTS) {\n if (!cached.missReported) {\n cached.missReported = true;\n missingPayloadIds.add(image.id);\n }\n return undefined;\n }\n\n if (!cached.promise) {\n const nextAttempts = attempts + 1;\n const promise = this.imageDecoder(cached.payload, image.format, image.id);\n cached.promise = promise;\n cached.retries = nextAttempts;\n void promise.then((decodedImage) => {\n const latest = this.imageCache.get(image.id);\n if (latest?.promise !== promise) {\n return;\n }\n this.removeUnresolvedImage(image.id);\n this.imageCache.set(image.id, { image: decodedImage });\n this.requestRedraw();\n }).catch(() => {\n const latest = this.imageCache.get(image.id);\n if (latest?.promise !== promise) {\n return;\n }\n latest.promise = undefined;\n if (\n (latest.retries ?? 0) >= MAX_IMAGE_DECODE_ATTEMPTS\n && !latest.missReported\n ) {\n latest.missReported = true;\n if (isWebHostImageRecoveryId(image.id)) {\n this.scheduleImagePayloadMiss(image.id);\n }\n }\n this.requestRedraw();\n });\n }\n\n return undefined;\n }\n\n private canTrackUnresolvedImage(\n id: string,\n payload?: string\n ): boolean {\n const existing = this.imageCache.get(id);\n const existingPayloadCharacters = existing?.image\n ? 0\n : existing?.payload?.length ?? 0;\n const nextPayloadCharacters = this.unresolvedImagePayloadCharacters\n - existingPayloadCharacters\n + (payload?.length ?? 0);\n return (\n (this.unresolvedImageIds.has(id)\n || this.unresolvedImageIds.size < MAX_UNRESOLVED_IMAGE_CACHE_ENTRIES)\n && nextPayloadCharacters <= MAX_UNRESOLVED_IMAGE_PAYLOAD_CHARACTERS\n );\n }\n\n private setUnresolvedImage(\n id: string,\n cached: CachedWebHostImage\n ): void {\n const existing = this.imageCache.get(id);\n if (this.unresolvedImageIds.has(id)) {\n this.unresolvedImagePayloadCharacters -= existing?.payload?.length ?? 0;\n }\n this.imageCache.set(id, cached);\n this.unresolvedImageIds.add(id);\n this.unresolvedImagePayloadCharacters += cached.payload?.length ?? 0;\n this.pendingImagePayloadMissIds.delete(id);\n }\n\n private removeUnresolvedImage(\n id: string\n ): void {\n if (!this.unresolvedImageIds.delete(id)) {\n return;\n }\n this.unresolvedImagePayloadCharacters -=\n this.imageCache.get(id)?.payload?.length ?? 0;\n this.pendingImagePayloadMissIds.delete(id);\n }\n\n private sweepUnresolvedImages(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n const presentedIds = new Set<string>();\n for (const image of images ?? []) {\n if (!this.unresolvedImageIds.has(image.id)) {\n continue;\n }\n if (isPaintableSurfaceImage(image)) {\n presentedIds.add(image.id);\n }\n }\n for (const id of this.unresolvedImageIds) {\n if (presentedIds.has(id)) {\n continue;\n }\n this.removeUnresolvedImage(id);\n this.imageCache.delete(id);\n }\n }\n\n private reportImagePayloadMisses(\n ids: Set<string>\n ): void {\n if (ids.size === 0) {\n return;\n }\n const candidateIds = [...ids].sort();\n this.applyImagePayloadMissAdmission(\n candidateIds,\n this.onImagePayloadMiss(candidateIds)\n );\n }\n\n private scheduleImagePayloadMiss(\n id: string\n ): void {\n this.pendingImagePayloadMissIds.add(id);\n if (this.imagePayloadMissScheduled) {\n return;\n }\n this.imagePayloadMissScheduled = true;\n queueMicrotask(() => {\n this.imagePayloadMissScheduled = false;\n const ids = [...this.pendingImagePayloadMissIds].sort();\n this.pendingImagePayloadMissIds.clear();\n if (ids.length > 0) {\n this.applyImagePayloadMissAdmission(\n ids,\n this.onImagePayloadMiss(ids)\n );\n }\n });\n }\n\n private applyImagePayloadMissAdmission(\n candidateIds: readonly string[],\n admittedIds: readonly string[] | void\n ): void {\n // Callbacks authored against the former void contract commonly use a\n // concise `array.push(...)` body, whose runtime result is a number.\n const acceptedIds = new Set(\n Array.isArray(admittedIds) ? admittedIds : candidateIds\n );\n for (const id of candidateIds) {\n const cached = this.imageCache.get(id);\n if (cached && !cached.image) {\n cached.missReported = acceptedIds.has(id);\n }\n }\n }\n\n private drawCell(\n context: CanvasRenderingContext2D,\n metrics: CanvasSurfaceMetrics,\n x: number,\n y: number,\n text: string,\n span: number,\n style?: WebHostSurfaceStyle | null\n ): void {\n const rectX = x * metrics.cellWidth;\n const rectY = y * metrics.cellHeight;\n const width = Math.max(1, span) * metrics.cellWidth;\n const background = resolvedSurfaceBackground(style, metrics.style);\n const foreground = resolvedSurfaceForeground(style, metrics.style);\n const opacity = style?.opacity ?? 1;\n\n if (background) {\n context.globalAlpha = opacity;\n context.fillStyle = background;\n context.fillRect(rectX, rectY, width, metrics.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: metrics.cellHeight,\n })) {\n context.font = fontForStyle(metrics.style, style);\n context.fillText(\n text,\n rectX,\n rectY + Math.floor((metrics.cellHeight + metrics.style.fontSize) / 2) - 2\n );\n }\n }\n\n this.drawTextLine(context, metrics, rectX, rectY, width, style?.underline, \"underline\", foreground);\n this.drawTextLine(context, metrics, rectX, rectY, width, style?.strikethrough, \"strike\", foreground);\n context.globalAlpha = 1;\n }\n\n private dirtyRegionForDamage(\n damage: WebHostSurfaceDamage | undefined,\n frame: WebHostSurfaceFrame,\n metrics: CanvasSurfaceMetrics\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(cellRect(metrics, 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(cellRect(metrics, 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 drawTextLine(\n context: CanvasRenderingContext2D,\n metrics: CanvasSurfaceMetrics,\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 + metrics.cellHeight - 2\n : y + Math.floor(metrics.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\nfunction normalizedImageOpacity(\n opacity: number | undefined\n): number {\n if (opacity === undefined || !Number.isFinite(opacity)) {\n return 1;\n }\n return Math.max(0, Math.min(1, opacity));\n}\n\nfunction isPaintableSurfaceImage(\n image: WebHostSurfaceImage\n): boolean {\n const [, , boundsWidth, boundsHeight] = image.bounds;\n const [, , clipWidth, clipHeight] = image.visibleBounds;\n return isSupportedImageFormat(image.format)\n && boundsWidth > 0\n && boundsHeight > 0\n && clipWidth > 0\n && clipHeight > 0;\n}\n\n/**\n * The CSS font string for a cell, folding the surface emphasis bits (bold,\n * italic) over the host terminal's font size/family. Exposed so the host can\n * reuse the exact same metric when measuring cell dimensions.\n */\nexport function fontForStyle(\n terminalStyle: ResolvedWebHostTerminalStyle,\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}${terminalStyle.fontSize}px ${terminalStyle.fontFamily}`;\n}\n\nfunction cellRect(\n metrics: CanvasSurfaceMetrics,\n x: number,\n y: number,\n span: number\n): DirtyRect {\n return {\n x: x * metrics.cellWidth,\n y: y * metrics.cellHeight,\n width: Math.max(1, span) * metrics.cellWidth,\n height: metrics.cellHeight,\n };\n}\n\nasync function decodeImage(\n dataBase64: string,\n format: NormalizedSurfaceImageFormat\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 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"],"mappings":";;;;;;AAgDA,MAAa,0CAA0C,KAAK,OAAO;;;;;;;;;;;AA8CnE,IAAa,uBAAb,MAAmE;CACjE,6BAA8B,IAAI,IAAgC;CAClE,qCAAsC,IAAI,IAAY;CACtD,mCAA2C;CAC3C;CACA;CACA;CACA,sBAA0C,CAAC;CAC3C;CACA,6CAA8C,IAAI,IAAY;CAC9D,4BAAoC;CAEpC,YAAY,UAAuC,CAAC,GAAG;EACrD,KAAK,eAAe,QAAQ,eAAe;EAC3C,KAAK,qBAAqB,QAAQ,6BAA6B,CAAC;EAChE,+CAA+C,MAAM;GACnD,cAAc,QAAQ;IACpB,KAAK,MAAM,MAAM,KAAK;KACpB,KAAK,sBAAsB,EAAE;KAC7B,KAAK,WAAW,OAAO,EAAE;KACzB,KAAK,2BAA2B,OAAO,EAAE;IAC3C;GACF;GACA,kBAAkB,WAAW,CAAC,GAAG,IAAI,IACnC,OACG,OAAO,uBAAuB,CAAC,CAC/B,QAAQ,UAAU,KAAK,WAAW,IAAI,MAAM,EAAE,CAAC,EAAE,UAAU,KAAA,CAAS,CAAC,CACrE,KAAK,UAAU,MAAM,EAAE,CAC5B,CAAC,CAAC,CAAC,KAAK;EACV,CAAC;CACH;;;;;CAMA,OACE,QACA,eACM;EACN,KAAK,SAAS;EACd,KAAK,gBAAgB;CACvB;CAEA,MACE,SACA,OACA,QACA,2BAA8C,CAAC,GACzC;EACN,MAAM,SAAS,KAAK;EACpB,MAAM,UAAU,QAAQ,WAAW,IAAI;EACvC,IAAI,CAAC,UAAU,CAAC,SACd;EAGF,IAAI,OAAO,UAAU,KAAA,KAAa,MAAM,UAAU,KAAK,WAAW;GAChE,KAAK,YAAY,MAAM;GACvB,KAAK,MAAM,UAAU,KAAK,WAAW,OAAO,GAC1C,IAAI,CAAC,OAAO,OACV,OAAO,eAAe;EAG5B;EACA,KAAK,sBAAsB,OAAO,MAAM;EAExC,MAAM,cAAc,QAChB,KAAK,qBAAqB,QAAQ,OAAO,OAAO,IAChD,KAAA;EACJ,MAAM,sBAAsB,IAAI,IAAI,wBAAwB;EAC5D,IAAI,aAAa,MAAM,WAAW,GAAG;GACnC,KAAK,cAAc,OAAO,UAAU,CAAC,GAAG,mBAAmB;GAC3D;EACF;EAEA,MAAM,QAAQ,WAAW,QAAQ,oBAAoB;EACrD,QAAQ,aAAa,OAAO,GAAG,GAAG,OAAO,GAAG,CAAC;EAC7C,QAAQ,eAAe;EAEvB,QAAQ,YAAY,8BAA8B,QAAQ,KAAK;EAC/D,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,OAAO,QAAQ,OAAO,OAAO,SAAS,KAAK;EACpE;EAEA,IAAI,CAAC,OACH;EAGF,KAAK,SAAS,SAAS,OAAO,SAAS,WAAW;EAClD,KAAK,WACH,SACA,MAAM,UAAU,CAAC,GACjB,SACA,aACA,mBACF;CACF;CAEA,SACE,SACA,OACA,SACA,aACM;EACN,IAAI,aAAa;GACf,KAAK,MAAM,CAAC,GAAG,WAAW,YAAY,MAAM;IAC1C,MAAM,MAAM,MAAM,KAAK,MAAM,CAAC;IAC9B,KAAK,QAAQ,SAAS,OAAO,SAAS,KAAK,GAAG,MAAM;GACtD;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,SAAS,KAAK,CAAC;EAC9C;CACF;CAEA,QACE,SACA,OACA,SACA,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,SAAS,GAAG,GAAG,MAAM,MAAM,KAAK;EACzD;CACF;CAEA,WACE,SACA,QACA,SACA,aACA,qBACM;EACN,MAAM,oCAAoB,IAAI,IAAY;EAC1C,KAAK,MAAM,SAAS,QAAQ;GAC1B,IAAI,CAAC,wBAAwB,KAAK,GAChC;GAEF,KAAK,UACH,SACA,OACA,SACA,aACA,mBACA,mBACF;EACF;EACA,KAAK,yBAAyB,iBAAiB;CACjD;CAEA,cACE,QACA,qBACM;EACN,MAAM,oCAAoB,IAAI,IAAY;EAC1C,KAAK,MAAM,SAAS,QAAQ;GAC1B,IAAI,CAAC,wBAAwB,KAAK,GAChC;GAEF,KAAK,YAAY,OAAO,mBAAmB,mBAAmB;EAChE;EACA,KAAK,yBAAyB,iBAAiB;CACjD;CAEA,UACE,SACA,OACA,SACA,aACA,mBACA,qBACM;EACN,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;EAGF,MAAM,eAAe,KAAK,YACxB,OACA,mBACA,mBACF;EACA,IAAI,CAAC,cACH;EAGF,IACE,eACG,CAAC,8BAA8B,aAAa,OAAO,OAAO,WAAW,UAAU,GAElF;EAGF,QAAQ,KAAK;EACb,QAAQ,UAAU;EAClB,QAAQ,KACN,QAAQ,QAAQ,WAChB,QAAQ,QAAQ,YAChB,YAAY,QAAQ,WACpB,aAAa,QAAQ,UACvB;EACA,QAAQ,KAAK;EAIb,QAAQ,cAAc,uBAAuB,MAAM,OAAO;EAC1D,QAAQ,UACN,cACA,UAAU,QAAQ,WAClB,UAAU,QAAQ,YAClB,cAAc,QAAQ,WACtB,eAAe,QAAQ,UACzB;EACA,QAAQ,QAAQ;CAClB;CAEA,YACE,OACA,mBACA,qBAC+B;EAC/B,IAAI,SAAS,KAAK,WAAW,IAAI,MAAM,EAAE;EACzC,IAAI,QAAQ,OACV,OAAO,OAAO;EAGhB,MAAM,4BAA4B,MAAM,eAAe,KAAA,KAClD,oBAAoB,OAAO,MAAM,EAAE;EACxC,IACE,MAAM,eAAe,KAAA,MACjB,QAAQ,YAAY,KAAA,KAAa,4BACrC;GACA,IAAI,CAAC,KAAK,wBAAwB,MAAM,IAAI,MAAM,UAAU,GAC1D;GAEF,SAAS;IACP,SAAS,MAAM;IACf,SAAS;GACX;GACA,KAAK,mBAAmB,MAAM,IAAI,MAAM;EAC1C,OAAO,IAAI,CAAC,QAAQ;GAClB,IAAI,CAAC,yBAAyB,MAAM,EAAE,GACpC;GAEF,IAAI,CAAC,KAAK,wBAAwB,MAAM,EAAE,GACxC;GAEF,SAAS,EAAE,cAAc,MAAM;GAC/B,KAAK,mBAAmB,MAAM,IAAI,MAAM;GACxC,kBAAkB,IAAI,MAAM,EAAE;GAC9B;EACF;EAEA,MAAM,WAAW,OAAO,WAAW;EACnC,IAAI,OAAO,YAAY,KAAA,GAAW;GAChC,IAAI,CAAC,OAAO,cAAc;IACxB,OAAO,eAAe;IACtB,kBAAkB,IAAI,MAAM,EAAE;GAChC;GACA;EACF;EACA,IAAI,YAAA,GAAuC;GACzC,IAAI,CAAC,OAAO,cAAc;IACxB,OAAO,eAAe;IACtB,kBAAkB,IAAI,MAAM,EAAE;GAChC;GACA;EACF;EAEA,IAAI,CAAC,OAAO,SAAS;GACnB,MAAM,eAAe,WAAW;GAChC,MAAM,UAAU,KAAK,aAAa,OAAO,SAAS,MAAM,QAAQ,MAAM,EAAE;GACxE,OAAO,UAAU;GACjB,OAAO,UAAU;GACjB,QAAa,MAAM,iBAAiB;IAElC,IADe,KAAK,WAAW,IAAI,MAAM,EAChC,CAAC,EAAE,YAAY,SACtB;IAEF,KAAK,sBAAsB,MAAM,EAAE;IACnC,KAAK,WAAW,IAAI,MAAM,IAAI,EAAE,OAAO,aAAa,CAAC;IACrD,KAAK,cAAc;GACrB,CAAC,CAAC,CAAC,YAAY;IACb,MAAM,SAAS,KAAK,WAAW,IAAI,MAAM,EAAE;IAC3C,IAAI,QAAQ,YAAY,SACtB;IAEF,OAAO,UAAU,KAAA;IACjB,KACG,OAAO,WAAW,MAAA,KAChB,CAAC,OAAO,cACX;KACA,OAAO,eAAe;KACtB,IAAI,yBAAyB,MAAM,EAAE,GACnC,KAAK,yBAAyB,MAAM,EAAE;IAE1C;IACA,KAAK,cAAc;GACrB,CAAC;EACH;CAGF;CAEA,wBACE,IACA,SACS;EACT,MAAM,WAAW,KAAK,WAAW,IAAI,EAAE;EACvC,MAAM,4BAA4B,UAAU,QACxC,IACA,UAAU,SAAS,UAAU;EACjC,MAAM,wBAAwB,KAAK,mCAC/B,6BACC,SAAS,UAAU;EACxB,QACG,KAAK,mBAAmB,IAAI,EAAE,KAC1B,KAAK,mBAAmB,OAAA,QAC1B,yBAAA;CAEP;CAEA,mBACE,IACA,QACM;EACN,MAAM,WAAW,KAAK,WAAW,IAAI,EAAE;EACvC,IAAI,KAAK,mBAAmB,IAAI,EAAE,GAChC,KAAK,oCAAoC,UAAU,SAAS,UAAU;EAExE,KAAK,WAAW,IAAI,IAAI,MAAM;EAC9B,KAAK,mBAAmB,IAAI,EAAE;EAC9B,KAAK,oCAAoC,OAAO,SAAS,UAAU;EACnE,KAAK,2BAA2B,OAAO,EAAE;CAC3C;CAEA,sBACE,IACM;EACN,IAAI,CAAC,KAAK,mBAAmB,OAAO,EAAE,GACpC;EAEF,KAAK,oCACH,KAAK,WAAW,IAAI,EAAE,CAAC,EAAE,SAAS,UAAU;EAC9C,KAAK,2BAA2B,OAAO,EAAE;CAC3C;CAEA,sBACE,QACM;EACN,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,SAAS,UAAU,CAAC,GAAG;GAChC,IAAI,CAAC,KAAK,mBAAmB,IAAI,MAAM,EAAE,GACvC;GAEF,IAAI,wBAAwB,KAAK,GAC/B,aAAa,IAAI,MAAM,EAAE;EAE7B;EACA,KAAK,MAAM,MAAM,KAAK,oBAAoB;GACxC,IAAI,aAAa,IAAI,EAAE,GACrB;GAEF,KAAK,sBAAsB,EAAE;GAC7B,KAAK,WAAW,OAAO,EAAE;EAC3B;CACF;CAEA,yBACE,KACM;EACN,IAAI,IAAI,SAAS,GACf;EAEF,MAAM,eAAe,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK;EACnC,KAAK,+BACH,cACA,KAAK,mBAAmB,YAAY,CACtC;CACF;CAEA,yBACE,IACM;EACN,KAAK,2BAA2B,IAAI,EAAE;EACtC,IAAI,KAAK,2BACP;EAEF,KAAK,4BAA4B;EACjC,qBAAqB;GACnB,KAAK,4BAA4B;GACjC,MAAM,MAAM,CAAC,GAAG,KAAK,0BAA0B,CAAC,CAAC,KAAK;GACtD,KAAK,2BAA2B,MAAM;GACtC,IAAI,IAAI,SAAS,GACf,KAAK,+BACH,KACA,KAAK,mBAAmB,GAAG,CAC7B;EAEJ,CAAC;CACH;CAEA,+BACE,cACA,aACM;EAGN,MAAM,cAAc,IAAI,IACtB,MAAM,QAAQ,WAAW,IAAI,cAAc,YAC7C;EACA,KAAK,MAAM,MAAM,cAAc;GAC7B,MAAM,SAAS,KAAK,WAAW,IAAI,EAAE;GACrC,IAAI,UAAU,CAAC,OAAO,OACpB,OAAO,eAAe,YAAY,IAAI,EAAE;EAE5C;CACF;CAEA,SACE,SACA,SACA,GACA,GACA,MACA,MACA,OACM;EACN,MAAM,QAAQ,IAAI,QAAQ;EAC1B,MAAM,QAAQ,IAAI,QAAQ;EAC1B,MAAM,QAAQ,KAAK,IAAI,GAAG,IAAI,IAAI,QAAQ;EAC1C,MAAM,aAAa,0BAA0B,OAAO,QAAQ,KAAK;EACjE,MAAM,aAAa,0BAA0B,OAAO,QAAQ,KAAK;EACjE,MAAM,UAAU,OAAO,WAAW;EAElC,IAAI,YAAY;GACd,QAAQ,cAAc;GACtB,QAAQ,YAAY;GACpB,QAAQ,SAAS,OAAO,OAAO,OAAO,QAAQ,UAAU;EAC1D;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,QAAQ;GAClB,CAAC,GAAG;IACF,QAAQ,OAAO,aAAa,QAAQ,OAAO,KAAK;IAChD,QAAQ,SACN,MACA,OACA,QAAQ,KAAK,OAAO,QAAQ,aAAa,QAAQ,MAAM,YAAY,CAAC,IAAI,CAC1E;GACF;EACF;EAEA,KAAK,aAAa,SAAS,SAAS,OAAO,OAAO,OAAO,OAAO,WAAW,aAAa,UAAU;EAClG,KAAK,aAAa,SAAS,SAAS,OAAO,OAAO,OAAO,OAAO,eAAe,UAAU,UAAU;EACnG,QAAQ,cAAc;CACxB;CAEA,qBACE,QACA,OACA,SACyB;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,SAAS,SAAS,GAAG,KAAK,MAAM,KAAK,CAAC;IACjD,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,SAAS,SAAS,YAAY,KAAK,aAAa,UAAU,CAAC;IACtE,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,aACE,SACA,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,QAAQ,aAAa,IACzB,IAAI,KAAK,MAAM,QAAQ,aAAa,CAAC;EACzC,QAAQ,UAAU;EAClB,QAAQ,OAAO,GAAG,KAAK;EACvB,QAAQ,OAAO,IAAI,OAAO,KAAK;EAC/B,QAAQ,OAAO;EACf,QAAQ,YAAY,CAAC,CAAC;CACxB;AACF;AAEA,SAAS,uBACP,SACQ;CACR,IAAI,YAAY,KAAA,KAAa,CAAC,OAAO,SAAS,OAAO,GACnD,OAAO;CAET,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC;AACzC;AAEA,SAAS,wBACP,OACS;CACT,MAAM,KAAK,aAAa,gBAAgB,MAAM;CAC9C,MAAM,KAAK,WAAW,cAAc,MAAM;CAC1C,OAAO,uBAAuB,MAAM,MAAM,KACrC,cAAc,KACd,eAAe,KACf,YAAY,KACZ,aAAa;AACpB;;;;;;AAOA,SAAgB,aACd,eACA,OACQ;CACR,MAAM,WAAW,OAAO,MAAM;CAG9B,OAAO,IAFS,WAAW,OAAO,IAAI,YAAY,MAClC,WAAW,OAAO,IAAI,SAAS,KACnB,cAAc,SAAS,KAAK,cAAc;AACxE;AAEA,SAAS,SACP,SACA,GACA,GACA,MACW;CACX,OAAO;EACL,GAAG,IAAI,QAAQ;EACf,GAAG,IAAI,QAAQ;EACf,OAAO,KAAK,IAAI,GAAG,IAAI,IAAI,QAAQ;EACnC,QAAQ,QAAQ;CAClB;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,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"}
@@ -203,6 +203,7 @@ var DomSurfacePainter = class {
203
203
  entry.image.style.top = `${(boundsY - clipY) * metrics.cellHeight}px`;
204
204
  entry.image.style.width = `${boundsWidth * metrics.cellWidth}px`;
205
205
  entry.image.style.height = `${boundsHeight * metrics.cellHeight}px`;
206
+ entry.image.style.opacity = String(normalizedImageOpacity(image.opacity));
206
207
  if (image.dataBase64) {
207
208
  const source = `data:image/${image.format};base64,${image.dataBase64}`;
208
209
  if (entry.source !== source) {
@@ -225,6 +226,10 @@ var DomSurfacePainter = class {
225
226
  }
226
227
  }
227
228
  };
229
+ function normalizedImageOpacity(opacity) {
230
+ if (opacity === void 0 || !Number.isFinite(opacity)) return 1;
231
+ return Math.max(0, Math.min(1, opacity));
232
+ }
228
233
  /**
229
234
  * A change key over everything the rendered tree bakes into element styles —
230
235
  * grid geometry, font, and theme colors. A key change invalidates every
@@ -1 +1 @@
1
- {"version":3,"file":"DomSurfacePainter.js","names":[],"sources":["../../src/DomSurfacePainter.ts"],"sourcesContent":["import { fontForStyle } from \"./CanvasSurfacePainter.ts\";\nimport {\n resolvedSurfaceBackground,\n resolvedSurfaceForeground,\n type SurfaceMetrics,\n type WebHostSurfacePainter,\n} from \"./SurfaceRenderer.ts\";\nimport { webTUITerminalBackgroundColor } from \"./WebHostTerminalStyle.ts\";\nimport {\n isSupportedImageFormat,\n normalizeScalingMode,\n} from \"./normalizeWireTokens.ts\";\nimport {\n isWebHostImageRecoveryId,\n type WebHostImagePayloadRequestHandler,\n type WebHostSurfaceDamage,\n type WebHostSurfaceFrame,\n type WebHostSurfaceImage,\n type WebHostSurfaceLineStyle,\n type WebHostSurfaceStyle,\n} from \"./WebHostSurfaceTransport.ts\";\nimport {\n registerDomSurfacePainterConformanceControl,\n} from \"./SurfacePainterConformanceControl.ts\";\n\ninterface RenderedImage {\n container: HTMLElement;\n image: HTMLElement;\n source: string;\n}\n\nexport interface DomSurfacePainterOptions {\n /**\n * Reports supported, positive-area image IDs absent from the retained DOM\n * cache. Returning the admitted subset lets bounded transports keep overflow\n * IDs eligible on the next presentation frame; `void` preserves legacy\n * all-accepted behavior for custom hosts.\n */\n onImagePayloadMiss?: WebHostImagePayloadRequestHandler;\n}\n\n/**\n * Draws SwiftTUI surface frames as a DOM element tree instead of canvas\n * pixels: one absolutely positioned row container per grid row, one `<span>`\n * per styled cell run, and `<img>` elements for surface images.\n *\n * Rendering cells as real text buys what canvas cannot offer — the browser's\n * own font shaping and fallback (emoji, CJK), crisp text at any page zoom, an\n * inspectable tree, and native text selection — at the cost of pixel-exact\n * box-drawing seams, which render as font glyphs here rather than the canvas\n * painter's hand-drawn strokes.\n *\n * Damage handling mirrors the canvas painter at row granularity: a frame\n * carrying scoped damage rebuilds only the touched rows' elements; geometry\n * or style changes force a full rebuild. Grid alignment across a run is kept\n * exact by stretching each glyph advance to the cell width via\n * `letter-spacing`, measured once per font/size pair.\n */\nexport class DomSurfacePainter implements WebHostSurfacePainter {\n private readonly onImagePayloadMiss: WebHostImagePayloadRequestHandler;\n private root?: HTMLElement;\n private rowsLayer?: HTMLElement;\n private imagesLayer?: HTMLElement;\n private rowElements: HTMLElement[] = [];\n private renderedImages = new Map<string, RenderedImage>();\n private appliedMetricsKey?: string;\n private renderedGridKey?: string;\n private hasRenderedFrame = false;\n private letterSpacing?: { key: string; value: string };\n private reportedMissingImageIds = new Set<string>();\n private lastImageRecoveryFrame?: WebHostSurfaceFrame;\n private lastEpoch?: number;\n\n constructor(options: DomSurfacePainterOptions = {}) {\n this.onImagePayloadMiss = options.onImagePayloadMiss ?? (() => {});\n registerDomSurfacePainterConformanceControl(this, {\n evictImages: (ids) => {\n for (const id of ids) {\n this.renderedImages.get(id)?.container.remove();\n this.renderedImages.delete(id);\n this.reportedMissingImageIds.delete(id);\n }\n },\n visibleImageIDs: () => [...this.renderedImages.keys()].sort(),\n });\n }\n\n /**\n * Binds the container the painter renders into. The runtime owns the\n * container's size; the painter owns everything inside it.\n */\n attach(\n root: HTMLElement\n ): void {\n this.root = root;\n\n const rowsLayer = createElement(\"div\");\n rowsLayer.className = \"webhost-scene__surface-rows\";\n fillContainer(rowsLayer.style);\n\n const imagesLayer = createElement(\"div\");\n imagesLayer.className = \"webhost-scene__surface-images\";\n fillContainer(imagesLayer.style);\n imagesLayer.style.pointerEvents = \"none\";\n\n this.rowsLayer = rowsLayer;\n this.imagesLayer = imagesLayer;\n root.replaceChildren(rowsLayer, imagesLayer);\n this.rowElements = [];\n this.renderedImages = new Map();\n this.appliedMetricsKey = undefined;\n this.renderedGridKey = undefined;\n this.hasRenderedFrame = false;\n this.reportedMissingImageIds.clear();\n this.lastImageRecoveryFrame = undefined;\n this.lastEpoch = undefined;\n }\n\n paint(\n metrics: SurfaceMetrics,\n frame: WebHostSurfaceFrame | undefined,\n damage?: WebHostSurfaceDamage,\n _recoveredImagePayloadIds?: readonly string[]\n ): void {\n const root = this.root;\n const rowsLayer = this.rowsLayer;\n if (!root || !rowsLayer) {\n return;\n }\n\n if (frame?.epoch !== undefined && frame.epoch !== this.lastEpoch) {\n this.lastEpoch = frame.epoch;\n this.reportedMissingImageIds.clear();\n }\n\n const metricsKey = metricsKeyFor(metrics);\n const metricsChanged = metricsKey !== this.appliedMetricsKey;\n if (metricsChanged) {\n this.applyRootStyle(root, metrics);\n this.appliedMetricsKey = metricsKey;\n }\n\n if (!frame) {\n this.rowElements = [];\n rowsLayer.replaceChildren();\n this.lastImageRecoveryFrame = undefined;\n this.reconcileImages([], metrics, false);\n this.renderedGridKey = undefined;\n this.hasRenderedFrame = false;\n return;\n }\n\n const gridKey = `${frame.width}x${frame.height}x${frame.rows.length}`;\n const fullRepaint = metricsChanged\n || !this.hasRenderedFrame\n || gridKey !== this.renderedGridKey\n || !damage\n || damage.requiresFullTextRepaint\n || damage.requiresFullGraphicsReplay;\n this.renderedGridKey = gridKey;\n\n if (fullRepaint) {\n for (let y = this.rowElements.length; y > frame.rows.length; y -= 1) {\n this.rowElements[y - 1]?.remove();\n }\n this.rowElements.length = Math.min(this.rowElements.length, frame.rows.length);\n for (let y = 0; y < frame.rows.length; y += 1) {\n this.rebuildRow(y, frame, metrics);\n }\n } else {\n for (const [row] of damage.textRows) {\n if (row < 0 || row >= frame.rows.length) {\n continue;\n }\n this.rebuildRow(row, frame, metrics);\n }\n }\n\n const allowRecoveryRequests = frame !== this.lastImageRecoveryFrame;\n this.lastImageRecoveryFrame = frame;\n this.reconcileImages(frame.images ?? [], metrics, allowRecoveryRequests);\n this.hasRenderedFrame = true;\n }\n\n private rebuildRow(\n y: number,\n frame: WebHostSurfaceFrame,\n metrics: SurfaceMetrics\n ): void {\n const rowElement = this.ensureRowElement(y, metrics);\n const children: HTMLElement[] = [];\n for (const [x, text, span, styleIndex] of frame.rows[y] ?? []) {\n const cellElement = buildCellElement(\n x,\n text,\n span,\n frame.styles[styleIndex] ?? undefined,\n metrics\n );\n if (cellElement) {\n children.push(cellElement);\n }\n }\n rowElement.replaceChildren(...children);\n }\n\n private ensureRowElement(\n y: number,\n metrics: SurfaceMetrics\n ): HTMLElement {\n let rowElement = this.rowElements[y];\n if (!rowElement) {\n rowElement = createElement(\"div\");\n rowElement.className = \"webhost-scene__surface-row\";\n rowElement.style.position = \"absolute\";\n rowElement.style.left = \"0\";\n this.rowElements[y] = rowElement;\n this.rowsLayer?.appendChild(rowElement);\n }\n rowElement.style.top = `${y * metrics.cellHeight}px`;\n rowElement.style.height = `${metrics.cellHeight}px`;\n rowElement.style.width = `${metrics.columns * metrics.cellWidth}px`;\n return rowElement;\n }\n\n private applyRootStyle(\n root: HTMLElement,\n metrics: SurfaceMetrics\n ): void {\n const style = root.style;\n style.position = \"relative\";\n style.overflow = \"hidden\";\n style.background = webTUITerminalBackgroundColor(metrics.style);\n style.font = fontForStyle(metrics.style);\n // Set after `font`: the shorthand resets line-height, and the grid needs\n // every row to be exactly one cell tall.\n style.lineHeight = `${metrics.cellHeight}px`;\n style.letterSpacing = this.letterSpacingFor(metrics);\n // Ligature-capable monospace fonts would merge runs like \"->\" into one\n // glyph and break the column grid.\n style.fontVariantLigatures = \"none\";\n style.userSelect = \"text\";\n }\n\n /**\n * The per-glyph advance correction that stretches the font's natural\n * monospace advance to exactly `cellWidth`, so long runs stay on the cell\n * grid instead of drifting by the sub-pixel remainder of the runtime's\n * ceil'd cell measurement.\n */\n private letterSpacingFor(\n metrics: SurfaceMetrics\n ): string {\n const font = fontForStyle(metrics.style);\n const key = `${font}|${metrics.cellWidth}`;\n if (this.letterSpacing?.key === key) {\n return this.letterSpacing.value;\n }\n\n let value = \"0px\";\n const canvas = createElement(\"canvas\") as HTMLCanvasElement;\n const context = canvas.getContext?.(\"2d\");\n if (context) {\n context.font = font;\n const advance = context.measureText(\"W\").width;\n const correction = metrics.cellWidth - advance;\n if (advance > 0 && Math.abs(correction) >= 0.01) {\n value = `${Math.round(correction * 1000) / 1000}px`;\n }\n }\n\n this.letterSpacing = { key, value };\n return value;\n }\n\n private reconcileImages(\n images: WebHostSurfaceImage[],\n metrics: SurfaceMetrics,\n allowRecoveryRequests: boolean\n ): void {\n const layer = this.imagesLayer;\n if (!layer) {\n return;\n }\n\n const next = new Map<string, RenderedImage>();\n const currentMissingImageIds = new Set<string>();\n const newlyMissingImageIds = new Set<string>();\n for (const rawImage of images) {\n if (!isSupportedImageFormat(rawImage.format)) {\n continue;\n }\n const image = {\n ...rawImage,\n scalingMode: normalizeScalingMode(rawImage.scalingMode),\n };\n const [boundsX, boundsY, boundsWidth, boundsHeight] = image.bounds;\n const [clipX, clipY, clipWidth, clipHeight] = image.visibleBounds;\n const existing = this.renderedImages.get(image.id);\n if (\n boundsWidth <= 0\n || boundsHeight <= 0\n || clipWidth <= 0\n || clipHeight <= 0\n ) {\n continue;\n }\n if (!existing && image.dataBase64 === undefined) {\n if (!isWebHostImageRecoveryId(image.id)) {\n continue;\n }\n currentMissingImageIds.add(image.id);\n if (!this.reportedMissingImageIds.has(image.id)) {\n newlyMissingImageIds.add(image.id);\n }\n continue;\n }\n\n const entry = existing ?? makeImageEntry();\n entry.container.style.left = `${clipX * metrics.cellWidth}px`;\n entry.container.style.top = `${clipY * metrics.cellHeight}px`;\n entry.container.style.width = `${clipWidth * metrics.cellWidth}px`;\n entry.container.style.height = `${clipHeight * metrics.cellHeight}px`;\n entry.image.style.left = `${(boundsX - clipX) * metrics.cellWidth}px`;\n entry.image.style.top = `${(boundsY - clipY) * metrics.cellHeight}px`;\n entry.image.style.width = `${boundsWidth * metrics.cellWidth}px`;\n entry.image.style.height = `${boundsHeight * metrics.cellHeight}px`;\n\n if (image.dataBase64) {\n const source = `data:image/${image.format};base64,${image.dataBase64}`;\n if (entry.source !== source) {\n entry.image.setAttribute(\"src\", source);\n entry.source = source;\n }\n }\n if (!existing) {\n layer.appendChild(entry.container);\n }\n next.set(image.id, entry);\n }\n\n for (const [id, entry] of this.renderedImages) {\n if (!next.has(id)) {\n entry.container.remove();\n }\n }\n this.renderedImages = next;\n for (const id of this.reportedMissingImageIds) {\n if (!currentMissingImageIds.has(id)) {\n this.reportedMissingImageIds.delete(id);\n }\n }\n if (allowRecoveryRequests && newlyMissingImageIds.size > 0) {\n const candidateIds = [...newlyMissingImageIds].sort();\n const admittedIds = this.onImagePayloadMiss(candidateIds);\n const acceptedIds = Array.isArray(admittedIds)\n ? admittedIds\n : candidateIds;\n const candidates = new Set(candidateIds);\n for (const id of acceptedIds) {\n if (candidates.has(id)) {\n this.reportedMissingImageIds.add(id);\n }\n }\n }\n }\n}\n\n/**\n * A change key over everything the rendered tree bakes into element styles —\n * grid geometry, font, and theme colors. A key change invalidates every\n * rendered row, so the next paint restyles the root and rebuilds in full.\n */\nfunction metricsKeyFor(\n metrics: SurfaceMetrics\n): string {\n return [\n metrics.columns,\n metrics.rows,\n metrics.cellWidth,\n metrics.cellHeight,\n fontForStyle(metrics.style),\n metrics.style.theme.foreground,\n metrics.style.theme.background,\n metrics.style.theme.windowBackground,\n metrics.style.backgroundOpacity,\n ].join(\"|\");\n}\n\nfunction buildCellElement(\n x: number,\n text: string,\n span: number,\n style: WebHostSurfaceStyle | undefined,\n metrics: SurfaceMetrics\n): HTMLElement | undefined {\n const background = resolvedSurfaceBackground(style, metrics.style);\n const hasDecoration = Boolean(style?.underline || style?.strikethrough);\n if (!background && !hasDecoration && text.trim() === \"\") {\n return undefined;\n }\n\n const element = createElement(\"span\");\n element.textContent = text;\n const elementStyle = element.style;\n elementStyle.position = \"absolute\";\n elementStyle.left = `${x * metrics.cellWidth}px`;\n elementStyle.top = \"0\";\n elementStyle.width = `${Math.max(1, span) * metrics.cellWidth}px`;\n elementStyle.height = \"100%\";\n elementStyle.whiteSpace = \"pre\";\n elementStyle.color = resolvedSurfaceForeground(style, metrics.style);\n if (background) {\n elementStyle.backgroundColor = background;\n }\n\n const emphasis = style?.em ?? 0;\n if (emphasis & 1) {\n elementStyle.fontWeight = \"700\";\n }\n if (emphasis & 2) {\n elementStyle.fontStyle = \"italic\";\n }\n\n const opacity = style?.opacity ?? 1;\n if (opacity !== 1) {\n elementStyle.opacity = String(opacity);\n }\n\n applyTextDecoration(elementStyle, style);\n return element;\n}\n\nfunction applyTextDecoration(\n elementStyle: CSSStyleDeclaration,\n style: WebHostSurfaceStyle | undefined\n): void {\n const lines: string[] = [];\n if (style?.underline) {\n lines.push(\"underline\");\n }\n if (style?.strikethrough) {\n lines.push(\"line-through\");\n }\n if (lines.length === 0) {\n return;\n }\n\n elementStyle.textDecorationLine = lines.join(\" \");\n const pattern = style?.underline?.pattern ?? style?.strikethrough?.pattern;\n elementStyle.textDecorationStyle = decorationStyleFor(pattern);\n // CSS shares one decoration color across both lines; the underline's color\n // wins when the app styles them differently.\n const color = style?.underline?.color ?? style?.strikethrough?.color;\n if (color) {\n elementStyle.textDecorationColor = color;\n }\n}\n\nfunction decorationStyleFor(\n pattern: WebHostSurfaceLineStyle[\"pattern\"] | undefined\n): string {\n switch (pattern) {\n case \"dot\":\n return \"dotted\";\n case \"dash\":\n case \"dashDot\":\n case \"dashDotDot\":\n return \"dashed\";\n case \"double\":\n return \"double\";\n case \"curly\":\n return \"wavy\";\n default:\n return \"solid\";\n }\n}\n\nfunction fillContainer(\n style: CSSStyleDeclaration\n): void {\n style.position = \"absolute\";\n style.left = \"0\";\n style.top = \"0\";\n style.width = \"100%\";\n style.height = \"100%\";\n}\n\nfunction makeImageEntry(): RenderedImage {\n const container = createElement(\"div\");\n container.className = \"webhost-scene__surface-image\";\n container.style.position = \"absolute\";\n container.style.overflow = \"hidden\";\n\n const image = createElement(\"img\");\n image.style.position = \"absolute\";\n image.setAttribute(\"alt\", \"\");\n image.setAttribute(\"draggable\", \"false\");\n container.appendChild(image);\n return { container, image, source: \"\" };\n}\n\nfunction createElement(\n tagName: string\n): HTMLElement {\n if (typeof document === \"undefined\") {\n throw new Error(\"document is not available\");\n }\n return document.createElement(tagName);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DomSurfacePainter.js","names":[],"sources":["../../src/DomSurfacePainter.ts"],"sourcesContent":["import { fontForStyle } from \"./CanvasSurfacePainter.ts\";\nimport {\n resolvedSurfaceBackground,\n resolvedSurfaceForeground,\n type SurfaceMetrics,\n type WebHostSurfacePainter,\n} from \"./SurfaceRenderer.ts\";\nimport { webTUITerminalBackgroundColor } from \"./WebHostTerminalStyle.ts\";\nimport {\n isSupportedImageFormat,\n normalizeScalingMode,\n} from \"./normalizeWireTokens.ts\";\nimport {\n isWebHostImageRecoveryId,\n type WebHostImagePayloadRequestHandler,\n type WebHostSurfaceDamage,\n type WebHostSurfaceFrame,\n type WebHostSurfaceImage,\n type WebHostSurfaceLineStyle,\n type WebHostSurfaceStyle,\n} from \"./WebHostSurfaceTransport.ts\";\nimport {\n registerDomSurfacePainterConformanceControl,\n} from \"./SurfacePainterConformanceControl.ts\";\n\ninterface RenderedImage {\n container: HTMLElement;\n image: HTMLElement;\n source: string;\n}\n\nexport interface DomSurfacePainterOptions {\n /**\n * Reports supported, positive-area image IDs absent from the retained DOM\n * cache. Returning the admitted subset lets bounded transports keep overflow\n * IDs eligible on the next presentation frame; `void` preserves legacy\n * all-accepted behavior for custom hosts.\n */\n onImagePayloadMiss?: WebHostImagePayloadRequestHandler;\n}\n\n/**\n * Draws SwiftTUI surface frames as a DOM element tree instead of canvas\n * pixels: one absolutely positioned row container per grid row, one `<span>`\n * per styled cell run, and `<img>` elements for surface images.\n *\n * Rendering cells as real text buys what canvas cannot offer — the browser's\n * own font shaping and fallback (emoji, CJK), crisp text at any page zoom, an\n * inspectable tree, and native text selection — at the cost of pixel-exact\n * box-drawing seams, which render as font glyphs here rather than the canvas\n * painter's hand-drawn strokes.\n *\n * Damage handling mirrors the canvas painter at row granularity: a frame\n * carrying scoped damage rebuilds only the touched rows' elements; geometry\n * or style changes force a full rebuild. Grid alignment across a run is kept\n * exact by stretching each glyph advance to the cell width via\n * `letter-spacing`, measured once per font/size pair.\n */\nexport class DomSurfacePainter implements WebHostSurfacePainter {\n private readonly onImagePayloadMiss: WebHostImagePayloadRequestHandler;\n private root?: HTMLElement;\n private rowsLayer?: HTMLElement;\n private imagesLayer?: HTMLElement;\n private rowElements: HTMLElement[] = [];\n private renderedImages = new Map<string, RenderedImage>();\n private appliedMetricsKey?: string;\n private renderedGridKey?: string;\n private hasRenderedFrame = false;\n private letterSpacing?: { key: string; value: string };\n private reportedMissingImageIds = new Set<string>();\n private lastImageRecoveryFrame?: WebHostSurfaceFrame;\n private lastEpoch?: number;\n\n constructor(options: DomSurfacePainterOptions = {}) {\n this.onImagePayloadMiss = options.onImagePayloadMiss ?? (() => {});\n registerDomSurfacePainterConformanceControl(this, {\n evictImages: (ids) => {\n for (const id of ids) {\n this.renderedImages.get(id)?.container.remove();\n this.renderedImages.delete(id);\n this.reportedMissingImageIds.delete(id);\n }\n },\n visibleImageIDs: () => [...this.renderedImages.keys()].sort(),\n });\n }\n\n /**\n * Binds the container the painter renders into. The runtime owns the\n * container's size; the painter owns everything inside it.\n */\n attach(\n root: HTMLElement\n ): void {\n this.root = root;\n\n const rowsLayer = createElement(\"div\");\n rowsLayer.className = \"webhost-scene__surface-rows\";\n fillContainer(rowsLayer.style);\n\n const imagesLayer = createElement(\"div\");\n imagesLayer.className = \"webhost-scene__surface-images\";\n fillContainer(imagesLayer.style);\n imagesLayer.style.pointerEvents = \"none\";\n\n this.rowsLayer = rowsLayer;\n this.imagesLayer = imagesLayer;\n root.replaceChildren(rowsLayer, imagesLayer);\n this.rowElements = [];\n this.renderedImages = new Map();\n this.appliedMetricsKey = undefined;\n this.renderedGridKey = undefined;\n this.hasRenderedFrame = false;\n this.reportedMissingImageIds.clear();\n this.lastImageRecoveryFrame = undefined;\n this.lastEpoch = undefined;\n }\n\n paint(\n metrics: SurfaceMetrics,\n frame: WebHostSurfaceFrame | undefined,\n damage?: WebHostSurfaceDamage,\n _recoveredImagePayloadIds?: readonly string[]\n ): void {\n const root = this.root;\n const rowsLayer = this.rowsLayer;\n if (!root || !rowsLayer) {\n return;\n }\n\n if (frame?.epoch !== undefined && frame.epoch !== this.lastEpoch) {\n this.lastEpoch = frame.epoch;\n this.reportedMissingImageIds.clear();\n }\n\n const metricsKey = metricsKeyFor(metrics);\n const metricsChanged = metricsKey !== this.appliedMetricsKey;\n if (metricsChanged) {\n this.applyRootStyle(root, metrics);\n this.appliedMetricsKey = metricsKey;\n }\n\n if (!frame) {\n this.rowElements = [];\n rowsLayer.replaceChildren();\n this.lastImageRecoveryFrame = undefined;\n this.reconcileImages([], metrics, false);\n this.renderedGridKey = undefined;\n this.hasRenderedFrame = false;\n return;\n }\n\n const gridKey = `${frame.width}x${frame.height}x${frame.rows.length}`;\n const fullRepaint = metricsChanged\n || !this.hasRenderedFrame\n || gridKey !== this.renderedGridKey\n || !damage\n || damage.requiresFullTextRepaint\n || damage.requiresFullGraphicsReplay;\n this.renderedGridKey = gridKey;\n\n if (fullRepaint) {\n for (let y = this.rowElements.length; y > frame.rows.length; y -= 1) {\n this.rowElements[y - 1]?.remove();\n }\n this.rowElements.length = Math.min(this.rowElements.length, frame.rows.length);\n for (let y = 0; y < frame.rows.length; y += 1) {\n this.rebuildRow(y, frame, metrics);\n }\n } else {\n for (const [row] of damage.textRows) {\n if (row < 0 || row >= frame.rows.length) {\n continue;\n }\n this.rebuildRow(row, frame, metrics);\n }\n }\n\n const allowRecoveryRequests = frame !== this.lastImageRecoveryFrame;\n this.lastImageRecoveryFrame = frame;\n this.reconcileImages(frame.images ?? [], metrics, allowRecoveryRequests);\n this.hasRenderedFrame = true;\n }\n\n private rebuildRow(\n y: number,\n frame: WebHostSurfaceFrame,\n metrics: SurfaceMetrics\n ): void {\n const rowElement = this.ensureRowElement(y, metrics);\n const children: HTMLElement[] = [];\n for (const [x, text, span, styleIndex] of frame.rows[y] ?? []) {\n const cellElement = buildCellElement(\n x,\n text,\n span,\n frame.styles[styleIndex] ?? undefined,\n metrics\n );\n if (cellElement) {\n children.push(cellElement);\n }\n }\n rowElement.replaceChildren(...children);\n }\n\n private ensureRowElement(\n y: number,\n metrics: SurfaceMetrics\n ): HTMLElement {\n let rowElement = this.rowElements[y];\n if (!rowElement) {\n rowElement = createElement(\"div\");\n rowElement.className = \"webhost-scene__surface-row\";\n rowElement.style.position = \"absolute\";\n rowElement.style.left = \"0\";\n this.rowElements[y] = rowElement;\n this.rowsLayer?.appendChild(rowElement);\n }\n rowElement.style.top = `${y * metrics.cellHeight}px`;\n rowElement.style.height = `${metrics.cellHeight}px`;\n rowElement.style.width = `${metrics.columns * metrics.cellWidth}px`;\n return rowElement;\n }\n\n private applyRootStyle(\n root: HTMLElement,\n metrics: SurfaceMetrics\n ): void {\n const style = root.style;\n style.position = \"relative\";\n style.overflow = \"hidden\";\n style.background = webTUITerminalBackgroundColor(metrics.style);\n style.font = fontForStyle(metrics.style);\n // Set after `font`: the shorthand resets line-height, and the grid needs\n // every row to be exactly one cell tall.\n style.lineHeight = `${metrics.cellHeight}px`;\n style.letterSpacing = this.letterSpacingFor(metrics);\n // Ligature-capable monospace fonts would merge runs like \"->\" into one\n // glyph and break the column grid.\n style.fontVariantLigatures = \"none\";\n style.userSelect = \"text\";\n }\n\n /**\n * The per-glyph advance correction that stretches the font's natural\n * monospace advance to exactly `cellWidth`, so long runs stay on the cell\n * grid instead of drifting by the sub-pixel remainder of the runtime's\n * ceil'd cell measurement.\n */\n private letterSpacingFor(\n metrics: SurfaceMetrics\n ): string {\n const font = fontForStyle(metrics.style);\n const key = `${font}|${metrics.cellWidth}`;\n if (this.letterSpacing?.key === key) {\n return this.letterSpacing.value;\n }\n\n let value = \"0px\";\n const canvas = createElement(\"canvas\") as HTMLCanvasElement;\n const context = canvas.getContext?.(\"2d\");\n if (context) {\n context.font = font;\n const advance = context.measureText(\"W\").width;\n const correction = metrics.cellWidth - advance;\n if (advance > 0 && Math.abs(correction) >= 0.01) {\n value = `${Math.round(correction * 1000) / 1000}px`;\n }\n }\n\n this.letterSpacing = { key, value };\n return value;\n }\n\n private reconcileImages(\n images: WebHostSurfaceImage[],\n metrics: SurfaceMetrics,\n allowRecoveryRequests: boolean\n ): void {\n const layer = this.imagesLayer;\n if (!layer) {\n return;\n }\n\n const next = new Map<string, RenderedImage>();\n const currentMissingImageIds = new Set<string>();\n const newlyMissingImageIds = new Set<string>();\n for (const rawImage of images) {\n if (!isSupportedImageFormat(rawImage.format)) {\n continue;\n }\n const image = {\n ...rawImage,\n scalingMode: normalizeScalingMode(rawImage.scalingMode),\n };\n const [boundsX, boundsY, boundsWidth, boundsHeight] = image.bounds;\n const [clipX, clipY, clipWidth, clipHeight] = image.visibleBounds;\n const existing = this.renderedImages.get(image.id);\n if (\n boundsWidth <= 0\n || boundsHeight <= 0\n || clipWidth <= 0\n || clipHeight <= 0\n ) {\n continue;\n }\n if (!existing && image.dataBase64 === undefined) {\n if (!isWebHostImageRecoveryId(image.id)) {\n continue;\n }\n currentMissingImageIds.add(image.id);\n if (!this.reportedMissingImageIds.has(image.id)) {\n newlyMissingImageIds.add(image.id);\n }\n continue;\n }\n\n const entry = existing ?? makeImageEntry();\n entry.container.style.left = `${clipX * metrics.cellWidth}px`;\n entry.container.style.top = `${clipY * metrics.cellHeight}px`;\n entry.container.style.width = `${clipWidth * metrics.cellWidth}px`;\n entry.container.style.height = `${clipHeight * metrics.cellHeight}px`;\n entry.image.style.left = `${(boundsX - clipX) * metrics.cellWidth}px`;\n entry.image.style.top = `${(boundsY - clipY) * metrics.cellHeight}px`;\n entry.image.style.width = `${boundsWidth * metrics.cellWidth}px`;\n entry.image.style.height = `${boundsHeight * metrics.cellHeight}px`;\n entry.image.style.opacity = String(normalizedImageOpacity(image.opacity));\n\n if (image.dataBase64) {\n const source = `data:image/${image.format};base64,${image.dataBase64}`;\n if (entry.source !== source) {\n entry.image.setAttribute(\"src\", source);\n entry.source = source;\n }\n }\n if (!existing) {\n layer.appendChild(entry.container);\n }\n next.set(image.id, entry);\n }\n\n for (const [id, entry] of this.renderedImages) {\n if (!next.has(id)) {\n entry.container.remove();\n }\n }\n this.renderedImages = next;\n for (const id of this.reportedMissingImageIds) {\n if (!currentMissingImageIds.has(id)) {\n this.reportedMissingImageIds.delete(id);\n }\n }\n if (allowRecoveryRequests && newlyMissingImageIds.size > 0) {\n const candidateIds = [...newlyMissingImageIds].sort();\n const admittedIds = this.onImagePayloadMiss(candidateIds);\n const acceptedIds = Array.isArray(admittedIds)\n ? admittedIds\n : candidateIds;\n const candidates = new Set(candidateIds);\n for (const id of acceptedIds) {\n if (candidates.has(id)) {\n this.reportedMissingImageIds.add(id);\n }\n }\n }\n }\n}\n\nfunction normalizedImageOpacity(\n opacity: number | undefined\n): number {\n if (opacity === undefined || !Number.isFinite(opacity)) {\n return 1;\n }\n return Math.max(0, Math.min(1, opacity));\n}\n\n/**\n * A change key over everything the rendered tree bakes into element styles —\n * grid geometry, font, and theme colors. A key change invalidates every\n * rendered row, so the next paint restyles the root and rebuilds in full.\n */\nfunction metricsKeyFor(\n metrics: SurfaceMetrics\n): string {\n return [\n metrics.columns,\n metrics.rows,\n metrics.cellWidth,\n metrics.cellHeight,\n fontForStyle(metrics.style),\n metrics.style.theme.foreground,\n metrics.style.theme.background,\n metrics.style.theme.windowBackground,\n metrics.style.backgroundOpacity,\n ].join(\"|\");\n}\n\nfunction buildCellElement(\n x: number,\n text: string,\n span: number,\n style: WebHostSurfaceStyle | undefined,\n metrics: SurfaceMetrics\n): HTMLElement | undefined {\n const background = resolvedSurfaceBackground(style, metrics.style);\n const hasDecoration = Boolean(style?.underline || style?.strikethrough);\n if (!background && !hasDecoration && text.trim() === \"\") {\n return undefined;\n }\n\n const element = createElement(\"span\");\n element.textContent = text;\n const elementStyle = element.style;\n elementStyle.position = \"absolute\";\n elementStyle.left = `${x * metrics.cellWidth}px`;\n elementStyle.top = \"0\";\n elementStyle.width = `${Math.max(1, span) * metrics.cellWidth}px`;\n elementStyle.height = \"100%\";\n elementStyle.whiteSpace = \"pre\";\n elementStyle.color = resolvedSurfaceForeground(style, metrics.style);\n if (background) {\n elementStyle.backgroundColor = background;\n }\n\n const emphasis = style?.em ?? 0;\n if (emphasis & 1) {\n elementStyle.fontWeight = \"700\";\n }\n if (emphasis & 2) {\n elementStyle.fontStyle = \"italic\";\n }\n\n const opacity = style?.opacity ?? 1;\n if (opacity !== 1) {\n elementStyle.opacity = String(opacity);\n }\n\n applyTextDecoration(elementStyle, style);\n return element;\n}\n\nfunction applyTextDecoration(\n elementStyle: CSSStyleDeclaration,\n style: WebHostSurfaceStyle | undefined\n): void {\n const lines: string[] = [];\n if (style?.underline) {\n lines.push(\"underline\");\n }\n if (style?.strikethrough) {\n lines.push(\"line-through\");\n }\n if (lines.length === 0) {\n return;\n }\n\n elementStyle.textDecorationLine = lines.join(\" \");\n const pattern = style?.underline?.pattern ?? style?.strikethrough?.pattern;\n elementStyle.textDecorationStyle = decorationStyleFor(pattern);\n // CSS shares one decoration color across both lines; the underline's color\n // wins when the app styles them differently.\n const color = style?.underline?.color ?? style?.strikethrough?.color;\n if (color) {\n elementStyle.textDecorationColor = color;\n }\n}\n\nfunction decorationStyleFor(\n pattern: WebHostSurfaceLineStyle[\"pattern\"] | undefined\n): string {\n switch (pattern) {\n case \"dot\":\n return \"dotted\";\n case \"dash\":\n case \"dashDot\":\n case \"dashDotDot\":\n return \"dashed\";\n case \"double\":\n return \"double\";\n case \"curly\":\n return \"wavy\";\n default:\n return \"solid\";\n }\n}\n\nfunction fillContainer(\n style: CSSStyleDeclaration\n): void {\n style.position = \"absolute\";\n style.left = \"0\";\n style.top = \"0\";\n style.width = \"100%\";\n style.height = \"100%\";\n}\n\nfunction makeImageEntry(): RenderedImage {\n const container = createElement(\"div\");\n container.className = \"webhost-scene__surface-image\";\n container.style.position = \"absolute\";\n container.style.overflow = \"hidden\";\n\n const image = createElement(\"img\");\n image.style.position = \"absolute\";\n image.setAttribute(\"alt\", \"\");\n image.setAttribute(\"draggable\", \"false\");\n container.appendChild(image);\n return { container, image, source: \"\" };\n}\n\nfunction createElement(\n tagName: string\n): HTMLElement {\n if (typeof document === \"undefined\") {\n throw new Error(\"document is not available\");\n }\n return document.createElement(tagName);\n}\n"],"mappings":"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"}
@@ -66,6 +66,8 @@ interface WebHostSurfaceImage {
66
66
  bounds: WebHostSurfaceRect;
67
67
  visibleBounds: WebHostSurfaceRect;
68
68
  scalingMode: string;
69
+ /** Effective placement alpha. Absent on legacy records and therefore 1. */
70
+ opacity?: number;
69
71
  pixelSize?: WebHostSurfaceSize;
70
72
  dataBase64?: string;
71
73
  }
@@ -482,7 +482,7 @@ function isWebHostSurfaceImages(value) {
482
482
  function isWebHostSurfaceImage(value) {
483
483
  if (!value || typeof value !== "object") return false;
484
484
  const image = value;
485
- return typeof image.id === "string" && isWebHostSurfaceImageFormat(image.format) && isWebHostSurfaceRect(image.bounds) && isWebHostSurfaceRect(image.visibleBounds) && isWebHostSurfaceScalingMode(image.scalingMode) && (image.pixelSize === void 0 || isWebHostSurfaceSize(image.pixelSize)) && (image.dataBase64 === void 0 || typeof image.dataBase64 === "string");
485
+ return typeof image.id === "string" && isWebHostSurfaceImageFormat(image.format) && isWebHostSurfaceRect(image.bounds) && isWebHostSurfaceRect(image.visibleBounds) && isWebHostSurfaceScalingMode(image.scalingMode) && (image.opacity === void 0 || typeof image.opacity === "number" && Number.isFinite(image.opacity)) && (image.pixelSize === void 0 || isWebHostSurfaceSize(image.pixelSize)) && (image.dataBase64 === void 0 || typeof image.dataBase64 === "string");
486
486
  }
487
487
  function isWebHostSurfaceDamage(value) {
488
488
  if (!value || typeof value !== "object") return false;
@@ -1 +1 @@
1
- {"version":3,"file":"WebHostSurfaceTransport.js","names":[],"sources":["../../src/WebHostSurfaceTransport.ts"],"sourcesContent":["/**\n * Browser consumer for SwiftTUI's host wire. The normative cross-host\n * contract lives upstream:\n * https://github.com/SwiftTUI/swift-tui/blob/main/docs/HOST-WIRE-CONTRACT.md\n */\nimport {\n encodeWebHostTerminalRenderStyleBase64,\n type WebHostTerminalStyle,\n} from \"./WebHostTerminalStyle.ts\";\n\nexport interface WebHostSurfaceStyle {\n fg?: string;\n bg?: string;\n em?: number;\n underline?: WebHostSurfaceLineStyle;\n strikethrough?: WebHostSurfaceLineStyle;\n opacity?: number;\n}\n\nexport interface WebHostSurfaceLineStyle {\n pattern: \"solid\" | \"dot\" | \"dash\" | \"dashDot\" | \"dashDotDot\" | \"double\" | \"curly\";\n color?: string;\n}\n\nexport type WebHostSurfaceCell = [\n x: number,\n text: string,\n span: number,\n styleIndex: number,\n];\n\nexport type WebHostSurfaceRect = [\n x: number,\n y: number,\n width: number,\n height: number,\n];\n\nexport type WebHostSurfaceSize = [\n width: number,\n height: number,\n];\n\nexport type WebHostAccessibilityPoint = [\n x: number,\n y: number,\n];\n\nexport type WebHostAccessibilityLiveRegion = string;\n\nexport interface WebHostAccessibilityNode {\n id: string;\n parentId?: string;\n rect: WebHostSurfaceRect;\n role: string;\n label?: string;\n hint?: string;\n /** omitted when false */\n hidden?: boolean;\n liveRegion?: WebHostAccessibilityLiveRegion;\n cursorAnchor?: WebHostAccessibilityPoint;\n isFocused?: boolean;\n}\n\n/**\n * One hyperlink run within a row: [x, spanWidth, linkTargetIndex]. The index\n * points into the frame's deduplicated `linkTargets` table.\n */\nexport type WebHostSurfaceLinkRun = [\n x: number,\n span: number,\n targetIndex: number,\n];\n\n/** Hyperlink runs for one row: [rowIndex, runs]. */\nexport type WebHostSurfaceLinkRow = [\n row: number,\n runs: WebHostSurfaceLinkRun[],\n];\n\nexport type WebHostFocusSemantics = string;\n\n/**\n * The settled focus presentation for a committed frame — the same derivation\n * the Android host consumes (`prefersTextInput` gates its IME).\n */\nexport interface WebHostFocusPresentation {\n focusedIdentity?: string;\n semantics: WebHostFocusSemantics;\n prefersTextInput: boolean;\n hasFocusedRegion: boolean;\n}\n\nexport interface WebHostAccessibilityAnnouncement {\n message: string;\n politeness: WebHostAccessibilityLiveRegion;\n}\n\nexport type WebHostSurfaceImageFormat = string;\n\n/**\n * Reports locally unresolved image IDs to a recovery-capable host. The first\n * branch lets bounded hosts return the admitted subset; the explicit legacy\n * void branch preserves contextual typing for concise callbacks that return\n * incidental values such as `array.push(...)`.\n */\nexport type WebHostImagePayloadRequestHandler =\n | ((ids: readonly string[]) => readonly string[])\n | ((ids: readonly string[]) => void);\n\nexport interface WebHostSurfaceImage {\n id: string;\n format: WebHostSurfaceImageFormat;\n bounds: WebHostSurfaceRect;\n visibleBounds: WebHostSurfaceRect;\n scalingMode: string;\n pixelSize?: WebHostSurfaceSize;\n dataBase64?: string;\n}\n\nexport type WebHostSurfaceDamageRange = [\n start: number,\n end: number,\n];\n\nexport type WebHostSurfaceDamageTextRow = [\n row: number,\n ranges: WebHostSurfaceDamageRange[],\n];\n\nexport interface WebHostSurfaceDamage {\n textRows: WebHostSurfaceDamageTextRow[];\n requiresFullTextRepaint: boolean;\n requiresFullGraphicsReplay: boolean;\n}\n\n/**\n * Per-region scroll extent published with each frame so the host can implement\n * scroll-chaining: capture the wheel only while the region under the pointer can\n * still scroll in the wheel's direction, otherwise let it fall through to the\n * page. The host recomputes the per-direction headroom from `offset`/`content`/\n * the viewport `rect`, mirroring SwiftTUI's\n * `min(max(0, offset), max(0, content - viewport))` clamp.\n */\nexport interface WebHostScrollRegion {\n /** identity path — same key space as accessibility node ids */\n id: string;\n /** viewport rect in cells: [x, y, width, height] */\n rect: WebHostSurfaceRect;\n /** current clamped scroll offset in cells: [x, y] */\n offset: WebHostAccessibilityPoint;\n /** total content size in cells: [width, height] */\n content: WebHostSurfaceSize;\n}\n\nexport interface WebHostSurfaceFrame {\n version: 1 | 2;\n epoch?: number;\n gen?: number;\n sequence?: number;\n width: number;\n height: number;\n styles: Array<WebHostSurfaceStyle | null>;\n rows: WebHostSurfaceCell[][];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport type WebHostSurfaceDeltaRow = [\n row: number,\n cells: WebHostSurfaceCell[],\n];\n\nexport interface WebHostSurfaceDeltaFrame {\n version: 3;\n encoding: \"delta\";\n epoch?: number;\n gen?: number;\n baselineGen?: number;\n sequence?: number;\n width: number;\n height: number;\n /**\n * With `stylesBase` present, only the styles this record *added*: index `i`\n * of this array is table index `stylesBase + i`. Absent `stylesBase`, the\n * complete accumulated table, as deployed decoders expect.\n */\n styles: Array<WebHostSurfaceStyle | null>;\n /**\n * Where `styles` splices onto the retained table. Present only when the\n * client declared `styleAppend`, which is why it is negotiated rather than\n * additive: reading `styles` as a whole table when this is present\n * mis-indexes every style in the record.\n */\n stylesBase?: number;\n deltaRows: WebHostSurfaceDeltaRow[];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport interface WebHostRuntimeIssue {\n severity: \"warning\" | \"error\";\n code: string;\n message: string;\n description: string;\n identity?: string;\n source?: string;\n}\n\nexport interface WebHostFrameDiagnosticRecord {\n format: \"swift-tui-frame-diagnostics-v1\";\n header: string[];\n fields: string[];\n}\n\nexport type WebHostOutputRecord =\n | { type: \"surface\"; frame: WebHostSurfaceFrame }\n | { type: \"clipboard\"; text: string }\n | { type: \"runtimeIssue\"; issue: WebHostRuntimeIssue }\n | { type: \"frameDiagnostic\"; diagnostic: WebHostFrameDiagnosticRecord }\n | { type: \"surfaceDropped\"; reason: \"noBaseline\" | \"staleBaseline\" }\n | { type: \"text\"; text: string };\n\nexport type WebHostResyncRequest =\n | { scope: \"keyframe\" }\n | { scope: \"images\"; ids?: string[] };\n\nexport interface WebHostOutputSink {\n presentSurface(\n frame: WebHostSurfaceFrame,\n recoveredImagePayloadIds?: readonly string[]\n ): void;\n writeClipboard?(text: string): void | Promise<void>;\n notifyRuntimeIssue?(issue: WebHostRuntimeIssue): void;\n recordFrameDiagnostic?(diagnostic: WebHostFrameDiagnosticRecord): void;\n writeOutput?(text: string): void;\n writeError?(text: string): void;\n}\n\nexport interface WebHostKeyInput {\n key:\n | \"return\"\n | \"space\"\n | \"tab\"\n | \"arrowLeft\"\n | \"arrowRight\"\n | \"arrowUp\"\n | \"arrowDown\"\n | \"backspace\"\n | \"escape\"\n | \"home\"\n | \"end\"\n | \"character\";\n character?: string;\n modifiers?: number;\n}\n\nexport interface WebHostMouseInput {\n kind: \"down\" | \"up\" | \"moved\" | \"dragged\" | \"scrolled\";\n x: number;\n y: number;\n button?: \"primary\" | \"middle\" | \"secondary\";\n deltaX?: number;\n deltaY?: number;\n modifiers?: number;\n}\n\nconst recordPrefix = \"\\u001E\";\nconst textEncoder = new TextEncoder();\n\n/** Limits wire-derived recovery state and keeps every single-ID WASI request bounded. */\nexport const MAX_IMAGE_RECOVERY_ID_BYTES = 1_024;\nexport const MAX_OUTSTANDING_IMAGE_RECOVERY_IDS = 1_024;\n\nexport function isWebHostImageRecoveryId(\n id: string\n): boolean {\n return id.length > 0\n && id.length <= MAX_IMAGE_RECOVERY_ID_BYTES\n && textEncoder.encode(id).byteLength <= MAX_IMAGE_RECOVERY_ID_BYTES;\n}\n\n/**\n * The newest `surface` record version this runtime understands. Unknown\n * additive object keys are ignored by design (older runtimes render newer\n * frames), but a frame declaring a NEWER version than this is surfaced as an\n * error-severity runtime issue instead of silently degrading to a text\n * diagnostic — silent version skew is the failure mode this guards against\n * (F57).\n */\nexport const SUPPORTED_SURFACE_VERSION = 3;\n\nexport class WebHostOutputDecoder {\n private readonly textDecoder = new TextDecoder();\n private bufferedText = \"\";\n private lastSurfaceFrame?: WebHostSurfaceFrame;\n private lastEpoch?: number;\n private lastGen?: number;\n private lastPresentedEpoch?: number;\n private keyframeResyncOutstanding = false;\n private keyframeResyncPending = false;\n private readonly imageResyncOutstandingIds = new Set<string>();\n private readonly imageResyncPendingIds = new Set<string>();\n\n feed(\n chunk: Uint8Array\n ): WebHostOutputRecord[] {\n this.bufferedText += this.textDecoder.decode(chunk, { stream: true });\n const records: WebHostOutputRecord[] = [];\n\n while (true) {\n const newlineIndex = this.bufferedText.indexOf(\"\\n\");\n if (newlineIndex < 0) {\n break;\n }\n\n const line = this.bufferedText.slice(0, newlineIndex);\n this.bufferedText = this.bufferedText.slice(newlineIndex + 1);\n records.push(this.decodeLine(line));\n }\n\n if (this.bufferedText.length > 4096 && !this.bufferedText.startsWith(recordPrefix)) {\n records.push({ type: \"text\", text: this.bufferedText });\n this.bufferedText = \"\";\n }\n\n return records;\n }\n\n flush(): WebHostOutputRecord[] {\n if (!this.bufferedText) {\n return [];\n }\n const text = this.bufferedText;\n this.bufferedText = \"\";\n return [this.decodeLine(text)];\n }\n\n takeResyncRequest(\n maximumEncodedBytes?: number\n ): WebHostResyncRequest | undefined {\n if (this.keyframeResyncPending) {\n this.keyframeResyncPending = false;\n return { scope: \"keyframe\" };\n }\n if (this.imageResyncPendingIds.size === 0) {\n return undefined;\n }\n\n const sortedIds = [...this.imageResyncPendingIds].sort();\n const { ids, rejectedIds } = boundedImageResyncIds(\n sortedIds,\n maximumEncodedBytes\n );\n for (const id of rejectedIds) {\n this.imageResyncPendingIds.delete(id);\n this.imageResyncOutstandingIds.delete(id);\n }\n for (const id of ids) {\n this.imageResyncPendingIds.delete(id);\n }\n if (ids.length === 0) {\n return undefined;\n }\n return { scope: \"images\", ids };\n }\n\n /**\n * Adds locally unresolved image IDs to this epoch's recovery set. IDs remain\n * outstanding after delivery, so repeat painter misses cannot create storms;\n * a payload-bearing record or epoch re-anchor releases them. Returns the IDs\n * now tracked (new or already outstanding); callers should suppress repeats\n * only for this admitted subset.\n */\n requestImagePayloads(\n ids: Iterable<string>\n ): readonly string[] {\n const acceptedIds = new Set<string>();\n for (const id of ids) {\n if (!isWebHostImageRecoveryId(id)) {\n continue;\n }\n if (this.imageResyncOutstandingIds.has(id)) {\n acceptedIds.add(id);\n continue;\n }\n if (\n this.imageResyncOutstandingIds.size\n >= MAX_OUTSTANDING_IMAGE_RECOVERY_IDS\n ) {\n continue;\n }\n this.imageResyncOutstandingIds.add(id);\n this.imageResyncPendingIds.add(id);\n acceptedIds.add(id);\n }\n return [...acceptedIds];\n }\n\n /**\n * Advances image recovery immediately before one decoded surface reaches its\n * presenter. Payload arrival and epoch reset therefore follow delivery order\n * rather than `feed`'s parse-ahead order across a multi-record chunk. The\n * return value identifies payloads that answer an outstanding image request,\n * so presenters can open exactly one fresh local decode generation even when\n * content-addressed retransmission uses identical bytes.\n */\n prepareToPresentSurface(\n frame: WebHostSurfaceFrame\n ): readonly string[] {\n const recoveredImagePayloadIds = this.recoveredImagePayloadIds(frame.images);\n this.resetImageResyncForEpoch(frame.epoch);\n this.sweepImageResyncForPresentedImages(frame.images);\n this.clearArrivedImagePayloads(frame.images);\n if (frame.epoch !== undefined) {\n this.lastPresentedEpoch = frame.epoch;\n }\n return recoveredImagePayloadIds;\n }\n\n resyncRequestDeliveryFailed(\n request: WebHostResyncRequest\n ): void {\n if (request.scope === \"keyframe\") {\n if (this.keyframeResyncOutstanding) {\n this.keyframeResyncPending = true;\n }\n return;\n }\n\n for (const id of request.ids ?? []) {\n if (this.imageResyncOutstandingIds.has(id)) {\n this.imageResyncPendingIds.add(id);\n }\n }\n }\n\n private decodeLine(\n line: string\n ): WebHostOutputRecord {\n if (line.startsWith(`${recordPrefix}clipboard:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}clipboard:`.length));\n if (isWebHostClipboardRecord(record)) {\n return { type: \"clipboard\", text: record.text };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}runtimeIssue:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}runtimeIssue:`.length));\n if (isWebHostRuntimeIssue(record)) {\n return { type: \"runtimeIssue\", issue: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}frameDiagnostic:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}frameDiagnostic:`.length));\n if (isWebHostFrameDiagnosticRecord(record)) {\n return { type: \"frameDiagnostic\", diagnostic: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (!line.startsWith(`${recordPrefix}surface:`)) {\n return { type: \"text\", text: `${line}\\n` };\n }\n\n try {\n const frame = JSON.parse(line.slice(`${recordPrefix}surface:`.length));\n if (declaresNewerSurfaceVersion(frame)) {\n return {\n type: \"runtimeIssue\",\n issue: {\n severity: \"error\",\n code: \"surface.unsupportedVersion\",\n message: `SwiftTUI surface version ${frame.version} is newer than the supported ${SUPPORTED_SURFACE_VERSION}`,\n description: \"The app emitted a surface record with version \"\n + `${frame.version}, but this @swifttui/web runtime understands `\n + `versions up to ${SUPPORTED_SURFACE_VERSION}. Update @swifttui/web `\n + \"to render it.\",\n },\n };\n }\n if (isWebHostSurfaceFrame(frame)) {\n this.lastSurfaceFrame = frame;\n this.lastEpoch = frame.epoch;\n this.lastGen = frame.gen;\n this.keyframeResyncOutstanding = false;\n this.keyframeResyncPending = false;\n return { type: \"surface\", frame };\n }\n if (isWebHostSurfaceDeltaFrame(frame)) {\n const carriesDeliveryStamps = frame.epoch !== undefined\n || frame.gen !== undefined\n || frame.baselineGen !== undefined;\n if (\n !this.lastSurfaceFrame\n || this.lastSurfaceFrame.width !== frame.width\n || this.lastSurfaceFrame.height !== frame.height\n ) {\n if (carriesDeliveryStamps) {\n this.requestKeyframeResync();\n }\n return { type: \"surfaceDropped\", reason: \"noBaseline\" };\n }\n if (\n carriesDeliveryStamps\n && (\n frame.epoch === undefined\n || frame.gen === undefined\n || frame.baselineGen === undefined\n || frame.epoch !== this.lastEpoch\n || frame.baselineGen !== this.lastGen\n )\n ) {\n this.requestKeyframeResync();\n return { type: \"surfaceDropped\", reason: \"staleBaseline\" };\n }\n const materialized = this.materializeDeltaFrame(frame);\n if (materialized) {\n this.lastSurfaceFrame = materialized;\n this.lastEpoch = frame.epoch;\n this.lastGen = frame.gen;\n return { type: \"surface\", frame: materialized };\n }\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n private requestKeyframeResync(): void {\n if (this.keyframeResyncOutstanding) {\n return;\n }\n this.keyframeResyncOutstanding = true;\n this.keyframeResyncPending = true;\n }\n\n private resetImageResyncForEpoch(\n epoch: number | undefined\n ): void {\n if (epoch === undefined || epoch === this.lastPresentedEpoch) {\n return;\n }\n this.imageResyncOutstandingIds.clear();\n this.imageResyncPendingIds.clear();\n }\n\n private clearArrivedImagePayloads(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n for (const image of images ?? []) {\n if (image.dataBase64 === undefined) {\n continue;\n }\n this.imageResyncOutstandingIds.delete(image.id);\n this.imageResyncPendingIds.delete(image.id);\n }\n }\n\n private recoveredImagePayloadIds(\n images: WebHostSurfaceImage[] | undefined\n ): string[] {\n const recoveredIds = new Set<string>();\n for (const image of images ?? []) {\n if (\n image.dataBase64 !== undefined\n && this.imageResyncOutstandingIds.has(image.id)\n ) {\n recoveredIds.add(image.id);\n }\n }\n return [...recoveredIds].sort();\n }\n\n private sweepImageResyncForPresentedImages(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n const presentedIds = new Set<string>();\n for (const image of images ?? []) {\n if (this.imageResyncOutstandingIds.has(image.id)) {\n presentedIds.add(image.id);\n }\n }\n for (const id of this.imageResyncOutstandingIds) {\n if (presentedIds.has(id)) {\n continue;\n }\n this.imageResyncOutstandingIds.delete(id);\n this.imageResyncPendingIds.delete(id);\n }\n }\n\n private materializeDeltaFrame(\n frame: WebHostSurfaceDeltaFrame\n ): WebHostSurfaceFrame | undefined {\n const baseline = this.lastSurfaceFrame;\n if (!baseline) {\n return undefined;\n }\n\n const styles = this.materializeDeltaStyles(frame, baseline);\n if (!styles) {\n return undefined;\n }\n\n const rows = baseline.rows.slice();\n for (const [row, cells] of frame.deltaRows) {\n if (!Number.isSafeInteger(row) || row < 0 || row >= frame.height) {\n return undefined;\n }\n rows[row] = cells;\n }\n\n return {\n version: baseline.version,\n epoch: frame.epoch,\n gen: frame.gen,\n sequence: frame.sequence,\n width: frame.width,\n height: frame.height,\n styles,\n rows,\n images: frame.images,\n damage: frame.damage,\n accessibilityTree: frame.accessibilityTree,\n accessibilityAnnouncements: frame.accessibilityAnnouncements,\n scrollRegions: frame.scrollRegions,\n links: frame.links,\n linkTargets: frame.linkTargets,\n focusPresentation: frame.focusPresentation,\n preferredGridWidth: frame.preferredGridWidth,\n preferredGridHeight: frame.preferredGridHeight,\n };\n }\n\n /**\n * The delta's style table: either the record's own complete table, or the\n * negotiated append spliced onto the baseline's.\n *\n * A `stylesBase` that does not name the end of the retained table is a\n * structural break, not a recoverable one — splicing at the wrong offset\n * silently repaints cells in the wrong style, which is worse than refusing\n * the record and asking for a keyframe.\n */\n private materializeDeltaStyles(\n frame: WebHostSurfaceDeltaFrame,\n baseline: WebHostSurfaceFrame\n ): Array<WebHostSurfaceStyle | null> | undefined {\n if (frame.stylesBase === undefined) {\n return frame.styles;\n }\n if (frame.stylesBase !== baseline.styles.length) {\n return undefined;\n }\n return baseline.styles.concat(frame.styles);\n }\n}\n\nfunction boundedImageResyncIds(\n sortedIds: string[],\n maximumEncodedBytes: number | undefined\n): {\n ids: string[];\n rejectedIds: string[];\n} {\n if (\n maximumEncodedBytes === undefined\n || !Number.isFinite(maximumEncodedBytes)\n ) {\n return { ids: sortedIds, rejectedIds: [] };\n }\n\n const emptyRequestBytes = encodeResyncControlMessage({\n scope: \"images\",\n ids: [],\n }).byteLength;\n const normalizedMaximum = Math.max(0, Math.floor(maximumEncodedBytes));\n const ids: string[] = [];\n const rejectedIds: string[] = [];\n let encodedBytes = emptyRequestBytes;\n for (const id of sortedIds) {\n const separatorBytes = ids.length === 0 ? 0 : 1;\n const idBytes = textEncoder.encode(JSON.stringify(id)).byteLength;\n if (encodedBytes + separatorBytes + idBytes > normalizedMaximum) {\n if (ids.length === 0) {\n rejectedIds.push(id);\n continue;\n }\n break;\n }\n ids.push(id);\n encodedBytes += separatorBytes + idBytes;\n if (encodedBytes >= normalizedMaximum) {\n break;\n }\n }\n return { ids, rejectedIds };\n}\n\nfunction declaresNewerSurfaceVersion(\n value: unknown\n): value is { version: number } {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const version = (value as { version?: unknown }).version;\n return typeof version === \"number\"\n && Number.isSafeInteger(version)\n && version > SUPPORTED_SURFACE_VERSION;\n}\n\nfunction isWebHostClipboardRecord(\n value: unknown\n): value is { text: string } {\n return !!value && typeof value === \"object\" && typeof (value as { text?: unknown }).text === \"string\";\n}\n\nfunction isWebHostRuntimeIssue(\n value: unknown\n): value is WebHostRuntimeIssue {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostRuntimeIssue>;\n return (record.severity === \"warning\" || record.severity === \"error\")\n && typeof record.code === \"string\"\n && typeof record.message === \"string\"\n && typeof record.description === \"string\"\n && (record.identity === undefined || typeof record.identity === \"string\")\n && (record.source === undefined || typeof record.source === \"string\");\n}\n\nfunction isWebHostFrameDiagnosticRecord(\n value: unknown\n): value is WebHostFrameDiagnosticRecord {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostFrameDiagnosticRecord>;\n return record.format === \"swift-tui-frame-diagnostics-v1\"\n && Array.isArray(record.header)\n && record.header.every((field) => typeof field === \"string\")\n && Array.isArray(record.fields)\n && record.fields.every((field) => typeof field === \"string\");\n}\n\nexport function encodeResizeControlMessage(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n): Uint8Array {\n const normalizedColumns = Math.max(1, Math.round(columns));\n const normalizedRows = Math.max(1, Math.round(rows));\n if (cellWidth && cellHeight) {\n return textEncoder.encode(\n `${recordPrefix}resize:${normalizedColumns}:${normalizedRows}:${Math.max(1, Math.round(cellWidth))}:${Math.max(1, Math.round(cellHeight))}\\n`\n );\n }\n\n return textEncoder.encode(`${recordPrefix}resize:${normalizedColumns}:${normalizedRows}\\n`);\n}\n\nexport function encodeRenderStyleControlMessage(\n style: WebHostTerminalStyle\n): Uint8Array {\n const encoded = encodeWebHostTerminalRenderStyleBase64(style);\n return textEncoder.encode(`${recordPrefix}style:${encoded}\\n`);\n}\n\nexport function encodeCapabilitiesControlMessage(): Uint8Array {\n // The client's wire-capability declaration, sent once after the socket\n // opens. The declaration is truthful today: this decoder materializes v3\n // delta frames (`materializeDeltaFrame`). Byte shape and key order are\n // pinned by the cross-repo fixture Fixtures/Transport/web-caps-record.txt\n // — swift-tui's input parser consumes the identical bytes, and the\n // coordination root's transport_fixture_sync gate keeps the copies in\n // lockstep. Servers that predate the record drop it silently, so the\n // session degrades to today's full-frame defaults.\n //\n // Capabilities are named feature bits. The retired `maxWebSurfaceVersion`\n // key declared a decoder version ceiling, which was only ever read as\n // \"accepts delta or not\" and duplicated — more weakly — the version check\n // this decoder already performs on every record (SUPPORTED_SURFACE_VERSION).\n // Servers still expecting it skip unknown keys, so dropping it is safe in\n // both directions.\n //\n // `styleAppend` is declared here because `materializeDeltaStyles` splices a\n // delta's `styles` onto the retained table when `stylesBase` is present.\n // Declaring it is truthful by construction: this decoder and this record are\n // the same release. Measured at Stage SV, the full retransmit it replaces was\n // 69.7% of late-record bytes in a style-churning epoch.\n return textEncoder.encode(\n `${recordPrefix}caps:{\"acceptsDeltaFrames\":true,\"styleAppend\":true}\\n`\n );\n}\n\n/**\n * The page's pointer-paradigm declaration.\n *\n * `panning=1` tells the app that this client scrolls by dragging content\n * directly, which is how touch devices and coarse-pointer browsers behave and\n * is not how a desktop mouse behaves. The app uses it to decide whether a drag\n * on a scroll view's background pans it, and whether a drag that starts on a\n * control may be taken over by an enclosing scroll view.\n *\n * Payload is `key=value` tokens rather than positional fields so a later\n * pointer fact can join the record without changing its arity; the Swift\n * parser skips keys it does not know. Unlike `caps:`, this record is not a\n * once-per-connection epoch marker — the device can change (a tablet docked to\n * a mouse), so it may be sent again at any time. Servers that predate the\n * record drop it and keep the desktop paradigm.\n */\nexport function encodePointerCapabilitiesControlMessage(\n supportsScrollPanning: boolean\n): Uint8Array {\n return textEncoder.encode(\n `${recordPrefix}pointer:panning=${supportsScrollPanning ? 1 : 0}\\n`\n );\n}\n\nexport function encodeResyncControlMessage(\n request: WebHostResyncRequest\n): Uint8Array {\n const payload = request.scope === \"keyframe\"\n ? { scope: \"keyframe\" }\n : {\n scope: \"images\",\n ...(request.ids === undefined ? {} : { ids: request.ids }),\n };\n return textEncoder.encode(\n `${recordPrefix}resync:${JSON.stringify(payload)}\\n`\n );\n}\n\nexport function encodeKeyInputMessage(\n input: WebHostKeyInput\n): Uint8Array {\n const modifiers = Math.max(0, Math.round(input.modifiers ?? 0));\n if (input.key === \"character\") {\n return textEncoder.encode(\n `${recordPrefix}key:character:${encodeURIComponent(input.character ?? \"\")}:${modifiers}\\n`\n );\n }\n return textEncoder.encode(`${recordPrefix}key:${input.key}:${modifiers}\\n`);\n}\n\nexport function encodePasteInputMessage(\n text: string\n): Uint8Array {\n return textEncoder.encode(`${recordPrefix}paste:${encodeURIComponent(text)}\\n`);\n}\n\nexport function encodeMouseInputMessage(\n input: WebHostMouseInput\n): Uint8Array {\n return textEncoder.encode(\n recordPrefix + [\n \"mouse\",\n input.kind,\n formatCellCoordinate(input.x),\n formatCellCoordinate(input.y),\n input.button ?? \"none\",\n Math.round(input.deltaX ?? 0),\n Math.round(input.deltaY ?? 0),\n Math.max(0, Math.round(input.modifiers ?? 0)),\n ].join(\":\") + \"\\n\"\n );\n}\n\nfunction formatCellCoordinate(\n value: number\n): string {\n return Number.isFinite(value) ? String(value) : \"0\";\n}\n\nfunction isWebHostSurfaceFrame(\n value: unknown\n): value is WebHostSurfaceFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceFrame>;\n return (frame.version === 1 || frame.version === 2)\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.rows)\n && frame.rows.every(isWebHostSurfaceRow)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\nfunction isWebHostSurfaceDeltaFrame(\n value: unknown\n): value is WebHostSurfaceDeltaFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceDeltaFrame>;\n return frame.version === 3\n && frame.encoding === \"delta\"\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.deltaRows)\n && frame.deltaRows.every(isWebHostSurfaceDeltaRow)\n && isOptionalSafeInteger(frame.baselineGen)\n && isOptionalSafeInteger(frame.stylesBase)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\n/**\n * The F19 additive fields shared by the full and delta record shapes. Absent\n * means \"feature not present\" — servers older than the field omit it.\n */\nfunction hasValidAdditiveFrameFields(\n frame: Partial<WebHostSurfaceFrame> | Partial<WebHostSurfaceDeltaFrame>\n): boolean {\n return isOptionalSafeInteger(frame.epoch)\n && isOptionalSafeInteger(frame.gen)\n && (frame.links === undefined || isWebHostSurfaceLinks(frame.links))\n && (frame.linkTargets === undefined || isWebHostSurfaceLinkTargets(frame.linkTargets))\n && (\n frame.focusPresentation === undefined\n || isWebHostFocusPresentation(frame.focusPresentation)\n )\n && (\n frame.preferredGridWidth === undefined\n || (Number.isSafeInteger(frame.preferredGridWidth) && frame.preferredGridWidth >= 0)\n )\n && (\n frame.preferredGridHeight === undefined\n || (Number.isSafeInteger(frame.preferredGridHeight) && frame.preferredGridHeight >= 0)\n );\n}\n\nfunction isOptionalSafeInteger(\n value: unknown\n): boolean {\n return value === undefined || Number.isSafeInteger(value);\n}\n\nfunction isWebHostSurfaceLinks(\n value: unknown\n): value is WebHostSurfaceLinkRow[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceLinkRow);\n}\n\nfunction isWebHostSurfaceLinkRow(\n value: unknown\n): value is WebHostSurfaceLinkRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceLinkRun);\n}\n\nfunction isWebHostSurfaceLinkRun(\n value: unknown\n): value is WebHostSurfaceLinkRun {\n if (!Array.isArray(value) || value.length !== 3) {\n return false;\n }\n const [x, span, targetIndex] = value as number[];\n return Number.isSafeInteger(x)\n && x >= 0\n && Number.isSafeInteger(span)\n && span >= 1\n && Number.isSafeInteger(targetIndex)\n && targetIndex >= 0;\n}\n\nfunction isWebHostSurfaceLinkTargets(\n value: unknown\n): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isWebHostFocusPresentation(\n value: unknown\n): value is WebHostFocusPresentation {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const presentation = value as Partial<WebHostFocusPresentation>;\n return (\n presentation.focusedIdentity === undefined\n || typeof presentation.focusedIdentity === \"string\"\n )\n && typeof presentation.semantics === \"string\"\n && typeof presentation.prefersTextInput === \"boolean\"\n && typeof presentation.hasFocusedRegion === \"boolean\";\n}\n\nfunction isWebHostSurfaceDeltaRow(\n value: unknown\n): value is WebHostSurfaceDeltaRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && isWebHostSurfaceRow(value[1]);\n}\n\nfunction isWebHostSurfaceRow(\n value: unknown\n): value is WebHostSurfaceCell[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceCell);\n}\n\nfunction isWebHostSurfaceCell(\n value: unknown\n): value is WebHostSurfaceCell {\n return Array.isArray(value)\n && value.length === 4\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && typeof value[1] === \"string\"\n && Number.isSafeInteger(value[2])\n && value[2] >= 1\n && Number.isSafeInteger(value[3])\n && value[3] >= 0;\n}\n\nfunction isWebHostAccessibilityNodes(\n value: unknown\n): value is WebHostAccessibilityNode[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityNode);\n}\n\nfunction isWebHostAccessibilityNode(\n value: unknown\n): value is WebHostAccessibilityNode {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const node = value as Partial<WebHostAccessibilityNode>;\n return typeof node.id === \"string\"\n && (node.parentId === undefined || typeof node.parentId === \"string\")\n && isWebHostSurfaceRect(node.rect)\n && typeof node.role === \"string\"\n && (node.label === undefined || typeof node.label === \"string\")\n && (node.hint === undefined || typeof node.hint === \"string\")\n && (node.hidden === undefined || typeof node.hidden === \"boolean\")\n && (node.liveRegion === undefined || typeof node.liveRegion === \"string\")\n && (node.cursorAnchor === undefined || isWebHostAccessibilityPoint(node.cursorAnchor))\n && (node.isFocused === undefined || typeof node.isFocused === \"boolean\");\n}\n\nfunction isWebHostAccessibilityPoint(\n value: unknown\n): value is WebHostAccessibilityPoint {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostAccessibilityAnnouncements(\n value: unknown\n): value is WebHostAccessibilityAnnouncement[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityAnnouncement);\n}\n\nfunction isWebHostAccessibilityAnnouncement(\n value: unknown\n): value is WebHostAccessibilityAnnouncement {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const announcement = value as Partial<WebHostAccessibilityAnnouncement>;\n return typeof announcement.message === \"string\"\n && typeof announcement.politeness === \"string\";\n}\n\nfunction isWebHostSurfaceImages(\n value: unknown\n): value is WebHostSurfaceImage[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceImage);\n}\n\nfunction isWebHostSurfaceImage(\n value: unknown\n): value is WebHostSurfaceImage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const image = value as Partial<WebHostSurfaceImage>;\n return typeof image.id === \"string\"\n && isWebHostSurfaceImageFormat(image.format)\n && isWebHostSurfaceRect(image.bounds)\n && isWebHostSurfaceRect(image.visibleBounds)\n && isWebHostSurfaceScalingMode(image.scalingMode)\n && (image.pixelSize === undefined || isWebHostSurfaceSize(image.pixelSize))\n && (image.dataBase64 === undefined || typeof image.dataBase64 === \"string\");\n}\n\nfunction isWebHostSurfaceDamage(\n value: unknown\n): value is WebHostSurfaceDamage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const damage = value as Partial<WebHostSurfaceDamage>;\n return Array.isArray(damage.textRows)\n && damage.textRows.every(isWebHostSurfaceDamageTextRow)\n && typeof damage.requiresFullTextRepaint === \"boolean\"\n && typeof damage.requiresFullGraphicsReplay === \"boolean\";\n}\n\nfunction isWebHostSurfaceDamageTextRow(\n value: unknown\n): value is WebHostSurfaceDamageTextRow {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceDamageRange);\n}\n\nfunction isWebHostSurfaceDamageRange(\n value: unknown\n): value is WebHostSurfaceDamageRange {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && typeof value[1] === \"number\";\n}\n\nfunction isWebHostSurfaceImageFormat(\n value: unknown\n): value is WebHostSurfaceImageFormat {\n return typeof value === \"string\";\n}\n\nfunction isWebHostScrollRegions(\n value: unknown\n): value is WebHostScrollRegion[] {\n return Array.isArray(value) && value.every(isWebHostScrollRegion);\n}\n\nfunction isWebHostScrollRegion(\n value: unknown\n): value is WebHostScrollRegion {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const region = value as Partial<WebHostScrollRegion>;\n return typeof region.id === \"string\"\n && isWebHostSurfaceRect(region.rect)\n && isWebHostSurfaceSize(region.offset)\n && isWebHostSurfaceSize(region.content);\n}\n\nfunction isWebHostSurfaceRect(\n value: unknown\n): value is WebHostSurfaceRect {\n return Array.isArray(value)\n && value.length === 4\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceSize(\n value: unknown\n): value is WebHostSurfaceSize {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceScalingMode(\n value: unknown\n): value is WebHostSurfaceImage[\"scalingMode\"] {\n return typeof value === \"string\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WebHostSurfaceTransport.js","names":[],"sources":["../../src/WebHostSurfaceTransport.ts"],"sourcesContent":["/**\n * Browser consumer for SwiftTUI's host wire. The normative cross-host\n * contract lives upstream:\n * https://github.com/SwiftTUI/swift-tui/blob/main/docs/HOST-WIRE-CONTRACT.md\n */\nimport {\n encodeWebHostTerminalRenderStyleBase64,\n type WebHostTerminalStyle,\n} from \"./WebHostTerminalStyle.ts\";\n\nexport interface WebHostSurfaceStyle {\n fg?: string;\n bg?: string;\n em?: number;\n underline?: WebHostSurfaceLineStyle;\n strikethrough?: WebHostSurfaceLineStyle;\n opacity?: number;\n}\n\nexport interface WebHostSurfaceLineStyle {\n pattern: \"solid\" | \"dot\" | \"dash\" | \"dashDot\" | \"dashDotDot\" | \"double\" | \"curly\";\n color?: string;\n}\n\nexport type WebHostSurfaceCell = [\n x: number,\n text: string,\n span: number,\n styleIndex: number,\n];\n\nexport type WebHostSurfaceRect = [\n x: number,\n y: number,\n width: number,\n height: number,\n];\n\nexport type WebHostSurfaceSize = [\n width: number,\n height: number,\n];\n\nexport type WebHostAccessibilityPoint = [\n x: number,\n y: number,\n];\n\nexport type WebHostAccessibilityLiveRegion = string;\n\nexport interface WebHostAccessibilityNode {\n id: string;\n parentId?: string;\n rect: WebHostSurfaceRect;\n role: string;\n label?: string;\n hint?: string;\n /** omitted when false */\n hidden?: boolean;\n liveRegion?: WebHostAccessibilityLiveRegion;\n cursorAnchor?: WebHostAccessibilityPoint;\n isFocused?: boolean;\n}\n\n/**\n * One hyperlink run within a row: [x, spanWidth, linkTargetIndex]. The index\n * points into the frame's deduplicated `linkTargets` table.\n */\nexport type WebHostSurfaceLinkRun = [\n x: number,\n span: number,\n targetIndex: number,\n];\n\n/** Hyperlink runs for one row: [rowIndex, runs]. */\nexport type WebHostSurfaceLinkRow = [\n row: number,\n runs: WebHostSurfaceLinkRun[],\n];\n\nexport type WebHostFocusSemantics = string;\n\n/**\n * The settled focus presentation for a committed frame — the same derivation\n * the Android host consumes (`prefersTextInput` gates its IME).\n */\nexport interface WebHostFocusPresentation {\n focusedIdentity?: string;\n semantics: WebHostFocusSemantics;\n prefersTextInput: boolean;\n hasFocusedRegion: boolean;\n}\n\nexport interface WebHostAccessibilityAnnouncement {\n message: string;\n politeness: WebHostAccessibilityLiveRegion;\n}\n\nexport type WebHostSurfaceImageFormat = string;\n\n/**\n * Reports locally unresolved image IDs to a recovery-capable host. The first\n * branch lets bounded hosts return the admitted subset; the explicit legacy\n * void branch preserves contextual typing for concise callbacks that return\n * incidental values such as `array.push(...)`.\n */\nexport type WebHostImagePayloadRequestHandler =\n | ((ids: readonly string[]) => readonly string[])\n | ((ids: readonly string[]) => void);\n\nexport interface WebHostSurfaceImage {\n id: string;\n format: WebHostSurfaceImageFormat;\n bounds: WebHostSurfaceRect;\n visibleBounds: WebHostSurfaceRect;\n scalingMode: string;\n /** Effective placement alpha. Absent on legacy records and therefore 1. */\n opacity?: number;\n pixelSize?: WebHostSurfaceSize;\n dataBase64?: string;\n}\n\nexport type WebHostSurfaceDamageRange = [\n start: number,\n end: number,\n];\n\nexport type WebHostSurfaceDamageTextRow = [\n row: number,\n ranges: WebHostSurfaceDamageRange[],\n];\n\nexport interface WebHostSurfaceDamage {\n textRows: WebHostSurfaceDamageTextRow[];\n requiresFullTextRepaint: boolean;\n requiresFullGraphicsReplay: boolean;\n}\n\n/**\n * Per-region scroll extent published with each frame so the host can implement\n * scroll-chaining: capture the wheel only while the region under the pointer can\n * still scroll in the wheel's direction, otherwise let it fall through to the\n * page. The host recomputes the per-direction headroom from `offset`/`content`/\n * the viewport `rect`, mirroring SwiftTUI's\n * `min(max(0, offset), max(0, content - viewport))` clamp.\n */\nexport interface WebHostScrollRegion {\n /** identity path — same key space as accessibility node ids */\n id: string;\n /** viewport rect in cells: [x, y, width, height] */\n rect: WebHostSurfaceRect;\n /** current clamped scroll offset in cells: [x, y] */\n offset: WebHostAccessibilityPoint;\n /** total content size in cells: [width, height] */\n content: WebHostSurfaceSize;\n}\n\nexport interface WebHostSurfaceFrame {\n version: 1 | 2;\n epoch?: number;\n gen?: number;\n sequence?: number;\n width: number;\n height: number;\n styles: Array<WebHostSurfaceStyle | null>;\n rows: WebHostSurfaceCell[][];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport type WebHostSurfaceDeltaRow = [\n row: number,\n cells: WebHostSurfaceCell[],\n];\n\nexport interface WebHostSurfaceDeltaFrame {\n version: 3;\n encoding: \"delta\";\n epoch?: number;\n gen?: number;\n baselineGen?: number;\n sequence?: number;\n width: number;\n height: number;\n /**\n * With `stylesBase` present, only the styles this record *added*: index `i`\n * of this array is table index `stylesBase + i`. Absent `stylesBase`, the\n * complete accumulated table, as deployed decoders expect.\n */\n styles: Array<WebHostSurfaceStyle | null>;\n /**\n * Where `styles` splices onto the retained table. Present only when the\n * client declared `styleAppend`, which is why it is negotiated rather than\n * additive: reading `styles` as a whole table when this is present\n * mis-indexes every style in the record.\n */\n stylesBase?: number;\n deltaRows: WebHostSurfaceDeltaRow[];\n images?: WebHostSurfaceImage[];\n damage?: WebHostSurfaceDamage;\n accessibilityTree?: WebHostAccessibilityNode[];\n accessibilityAnnouncements?: WebHostAccessibilityAnnouncement[];\n scrollRegions?: WebHostScrollRegion[];\n links?: WebHostSurfaceLinkRow[];\n linkTargets?: string[];\n focusPresentation?: WebHostFocusPresentation;\n preferredGridWidth?: number;\n preferredGridHeight?: number;\n}\n\nexport interface WebHostRuntimeIssue {\n severity: \"warning\" | \"error\";\n code: string;\n message: string;\n description: string;\n identity?: string;\n source?: string;\n}\n\nexport interface WebHostFrameDiagnosticRecord {\n format: \"swift-tui-frame-diagnostics-v1\";\n header: string[];\n fields: string[];\n}\n\nexport type WebHostOutputRecord =\n | { type: \"surface\"; frame: WebHostSurfaceFrame }\n | { type: \"clipboard\"; text: string }\n | { type: \"runtimeIssue\"; issue: WebHostRuntimeIssue }\n | { type: \"frameDiagnostic\"; diagnostic: WebHostFrameDiagnosticRecord }\n | { type: \"surfaceDropped\"; reason: \"noBaseline\" | \"staleBaseline\" }\n | { type: \"text\"; text: string };\n\nexport type WebHostResyncRequest =\n | { scope: \"keyframe\" }\n | { scope: \"images\"; ids?: string[] };\n\nexport interface WebHostOutputSink {\n presentSurface(\n frame: WebHostSurfaceFrame,\n recoveredImagePayloadIds?: readonly string[]\n ): void;\n writeClipboard?(text: string): void | Promise<void>;\n notifyRuntimeIssue?(issue: WebHostRuntimeIssue): void;\n recordFrameDiagnostic?(diagnostic: WebHostFrameDiagnosticRecord): void;\n writeOutput?(text: string): void;\n writeError?(text: string): void;\n}\n\nexport interface WebHostKeyInput {\n key:\n | \"return\"\n | \"space\"\n | \"tab\"\n | \"arrowLeft\"\n | \"arrowRight\"\n | \"arrowUp\"\n | \"arrowDown\"\n | \"backspace\"\n | \"escape\"\n | \"home\"\n | \"end\"\n | \"character\";\n character?: string;\n modifiers?: number;\n}\n\nexport interface WebHostMouseInput {\n kind: \"down\" | \"up\" | \"moved\" | \"dragged\" | \"scrolled\";\n x: number;\n y: number;\n button?: \"primary\" | \"middle\" | \"secondary\";\n deltaX?: number;\n deltaY?: number;\n modifiers?: number;\n}\n\nconst recordPrefix = \"\\u001E\";\nconst textEncoder = new TextEncoder();\n\n/** Limits wire-derived recovery state and keeps every single-ID WASI request bounded. */\nexport const MAX_IMAGE_RECOVERY_ID_BYTES = 1_024;\nexport const MAX_OUTSTANDING_IMAGE_RECOVERY_IDS = 1_024;\n\nexport function isWebHostImageRecoveryId(\n id: string\n): boolean {\n return id.length > 0\n && id.length <= MAX_IMAGE_RECOVERY_ID_BYTES\n && textEncoder.encode(id).byteLength <= MAX_IMAGE_RECOVERY_ID_BYTES;\n}\n\n/**\n * The newest `surface` record version this runtime understands. Unknown\n * additive object keys are ignored by design (older runtimes render newer\n * frames), but a frame declaring a NEWER version than this is surfaced as an\n * error-severity runtime issue instead of silently degrading to a text\n * diagnostic — silent version skew is the failure mode this guards against\n * (F57).\n */\nexport const SUPPORTED_SURFACE_VERSION = 3;\n\nexport class WebHostOutputDecoder {\n private readonly textDecoder = new TextDecoder();\n private bufferedText = \"\";\n private lastSurfaceFrame?: WebHostSurfaceFrame;\n private lastEpoch?: number;\n private lastGen?: number;\n private lastPresentedEpoch?: number;\n private keyframeResyncOutstanding = false;\n private keyframeResyncPending = false;\n private readonly imageResyncOutstandingIds = new Set<string>();\n private readonly imageResyncPendingIds = new Set<string>();\n\n feed(\n chunk: Uint8Array\n ): WebHostOutputRecord[] {\n this.bufferedText += this.textDecoder.decode(chunk, { stream: true });\n const records: WebHostOutputRecord[] = [];\n\n while (true) {\n const newlineIndex = this.bufferedText.indexOf(\"\\n\");\n if (newlineIndex < 0) {\n break;\n }\n\n const line = this.bufferedText.slice(0, newlineIndex);\n this.bufferedText = this.bufferedText.slice(newlineIndex + 1);\n records.push(this.decodeLine(line));\n }\n\n if (this.bufferedText.length > 4096 && !this.bufferedText.startsWith(recordPrefix)) {\n records.push({ type: \"text\", text: this.bufferedText });\n this.bufferedText = \"\";\n }\n\n return records;\n }\n\n flush(): WebHostOutputRecord[] {\n if (!this.bufferedText) {\n return [];\n }\n const text = this.bufferedText;\n this.bufferedText = \"\";\n return [this.decodeLine(text)];\n }\n\n takeResyncRequest(\n maximumEncodedBytes?: number\n ): WebHostResyncRequest | undefined {\n if (this.keyframeResyncPending) {\n this.keyframeResyncPending = false;\n return { scope: \"keyframe\" };\n }\n if (this.imageResyncPendingIds.size === 0) {\n return undefined;\n }\n\n const sortedIds = [...this.imageResyncPendingIds].sort();\n const { ids, rejectedIds } = boundedImageResyncIds(\n sortedIds,\n maximumEncodedBytes\n );\n for (const id of rejectedIds) {\n this.imageResyncPendingIds.delete(id);\n this.imageResyncOutstandingIds.delete(id);\n }\n for (const id of ids) {\n this.imageResyncPendingIds.delete(id);\n }\n if (ids.length === 0) {\n return undefined;\n }\n return { scope: \"images\", ids };\n }\n\n /**\n * Adds locally unresolved image IDs to this epoch's recovery set. IDs remain\n * outstanding after delivery, so repeat painter misses cannot create storms;\n * a payload-bearing record or epoch re-anchor releases them. Returns the IDs\n * now tracked (new or already outstanding); callers should suppress repeats\n * only for this admitted subset.\n */\n requestImagePayloads(\n ids: Iterable<string>\n ): readonly string[] {\n const acceptedIds = new Set<string>();\n for (const id of ids) {\n if (!isWebHostImageRecoveryId(id)) {\n continue;\n }\n if (this.imageResyncOutstandingIds.has(id)) {\n acceptedIds.add(id);\n continue;\n }\n if (\n this.imageResyncOutstandingIds.size\n >= MAX_OUTSTANDING_IMAGE_RECOVERY_IDS\n ) {\n continue;\n }\n this.imageResyncOutstandingIds.add(id);\n this.imageResyncPendingIds.add(id);\n acceptedIds.add(id);\n }\n return [...acceptedIds];\n }\n\n /**\n * Advances image recovery immediately before one decoded surface reaches its\n * presenter. Payload arrival and epoch reset therefore follow delivery order\n * rather than `feed`'s parse-ahead order across a multi-record chunk. The\n * return value identifies payloads that answer an outstanding image request,\n * so presenters can open exactly one fresh local decode generation even when\n * content-addressed retransmission uses identical bytes.\n */\n prepareToPresentSurface(\n frame: WebHostSurfaceFrame\n ): readonly string[] {\n const recoveredImagePayloadIds = this.recoveredImagePayloadIds(frame.images);\n this.resetImageResyncForEpoch(frame.epoch);\n this.sweepImageResyncForPresentedImages(frame.images);\n this.clearArrivedImagePayloads(frame.images);\n if (frame.epoch !== undefined) {\n this.lastPresentedEpoch = frame.epoch;\n }\n return recoveredImagePayloadIds;\n }\n\n resyncRequestDeliveryFailed(\n request: WebHostResyncRequest\n ): void {\n if (request.scope === \"keyframe\") {\n if (this.keyframeResyncOutstanding) {\n this.keyframeResyncPending = true;\n }\n return;\n }\n\n for (const id of request.ids ?? []) {\n if (this.imageResyncOutstandingIds.has(id)) {\n this.imageResyncPendingIds.add(id);\n }\n }\n }\n\n private decodeLine(\n line: string\n ): WebHostOutputRecord {\n if (line.startsWith(`${recordPrefix}clipboard:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}clipboard:`.length));\n if (isWebHostClipboardRecord(record)) {\n return { type: \"clipboard\", text: record.text };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}runtimeIssue:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}runtimeIssue:`.length));\n if (isWebHostRuntimeIssue(record)) {\n return { type: \"runtimeIssue\", issue: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (line.startsWith(`${recordPrefix}frameDiagnostic:`)) {\n try {\n const record = JSON.parse(line.slice(`${recordPrefix}frameDiagnostic:`.length));\n if (isWebHostFrameDiagnosticRecord(record)) {\n return { type: \"frameDiagnostic\", diagnostic: record };\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n if (!line.startsWith(`${recordPrefix}surface:`)) {\n return { type: \"text\", text: `${line}\\n` };\n }\n\n try {\n const frame = JSON.parse(line.slice(`${recordPrefix}surface:`.length));\n if (declaresNewerSurfaceVersion(frame)) {\n return {\n type: \"runtimeIssue\",\n issue: {\n severity: \"error\",\n code: \"surface.unsupportedVersion\",\n message: `SwiftTUI surface version ${frame.version} is newer than the supported ${SUPPORTED_SURFACE_VERSION}`,\n description: \"The app emitted a surface record with version \"\n + `${frame.version}, but this @swifttui/web runtime understands `\n + `versions up to ${SUPPORTED_SURFACE_VERSION}. Update @swifttui/web `\n + \"to render it.\",\n },\n };\n }\n if (isWebHostSurfaceFrame(frame)) {\n this.lastSurfaceFrame = frame;\n this.lastEpoch = frame.epoch;\n this.lastGen = frame.gen;\n this.keyframeResyncOutstanding = false;\n this.keyframeResyncPending = false;\n return { type: \"surface\", frame };\n }\n if (isWebHostSurfaceDeltaFrame(frame)) {\n const carriesDeliveryStamps = frame.epoch !== undefined\n || frame.gen !== undefined\n || frame.baselineGen !== undefined;\n if (\n !this.lastSurfaceFrame\n || this.lastSurfaceFrame.width !== frame.width\n || this.lastSurfaceFrame.height !== frame.height\n ) {\n if (carriesDeliveryStamps) {\n this.requestKeyframeResync();\n }\n return { type: \"surfaceDropped\", reason: \"noBaseline\" };\n }\n if (\n carriesDeliveryStamps\n && (\n frame.epoch === undefined\n || frame.gen === undefined\n || frame.baselineGen === undefined\n || frame.epoch !== this.lastEpoch\n || frame.baselineGen !== this.lastGen\n )\n ) {\n this.requestKeyframeResync();\n return { type: \"surfaceDropped\", reason: \"staleBaseline\" };\n }\n const materialized = this.materializeDeltaFrame(frame);\n if (materialized) {\n this.lastSurfaceFrame = materialized;\n this.lastEpoch = frame.epoch;\n this.lastGen = frame.gen;\n return { type: \"surface\", frame: materialized };\n }\n }\n } catch {\n // Fall through to the text path below so malformed output remains visible.\n }\n\n return { type: \"text\", text: `${line}\\n` };\n }\n\n private requestKeyframeResync(): void {\n if (this.keyframeResyncOutstanding) {\n return;\n }\n this.keyframeResyncOutstanding = true;\n this.keyframeResyncPending = true;\n }\n\n private resetImageResyncForEpoch(\n epoch: number | undefined\n ): void {\n if (epoch === undefined || epoch === this.lastPresentedEpoch) {\n return;\n }\n this.imageResyncOutstandingIds.clear();\n this.imageResyncPendingIds.clear();\n }\n\n private clearArrivedImagePayloads(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n for (const image of images ?? []) {\n if (image.dataBase64 === undefined) {\n continue;\n }\n this.imageResyncOutstandingIds.delete(image.id);\n this.imageResyncPendingIds.delete(image.id);\n }\n }\n\n private recoveredImagePayloadIds(\n images: WebHostSurfaceImage[] | undefined\n ): string[] {\n const recoveredIds = new Set<string>();\n for (const image of images ?? []) {\n if (\n image.dataBase64 !== undefined\n && this.imageResyncOutstandingIds.has(image.id)\n ) {\n recoveredIds.add(image.id);\n }\n }\n return [...recoveredIds].sort();\n }\n\n private sweepImageResyncForPresentedImages(\n images: WebHostSurfaceImage[] | undefined\n ): void {\n const presentedIds = new Set<string>();\n for (const image of images ?? []) {\n if (this.imageResyncOutstandingIds.has(image.id)) {\n presentedIds.add(image.id);\n }\n }\n for (const id of this.imageResyncOutstandingIds) {\n if (presentedIds.has(id)) {\n continue;\n }\n this.imageResyncOutstandingIds.delete(id);\n this.imageResyncPendingIds.delete(id);\n }\n }\n\n private materializeDeltaFrame(\n frame: WebHostSurfaceDeltaFrame\n ): WebHostSurfaceFrame | undefined {\n const baseline = this.lastSurfaceFrame;\n if (!baseline) {\n return undefined;\n }\n\n const styles = this.materializeDeltaStyles(frame, baseline);\n if (!styles) {\n return undefined;\n }\n\n const rows = baseline.rows.slice();\n for (const [row, cells] of frame.deltaRows) {\n if (!Number.isSafeInteger(row) || row < 0 || row >= frame.height) {\n return undefined;\n }\n rows[row] = cells;\n }\n\n return {\n version: baseline.version,\n epoch: frame.epoch,\n gen: frame.gen,\n sequence: frame.sequence,\n width: frame.width,\n height: frame.height,\n styles,\n rows,\n images: frame.images,\n damage: frame.damage,\n accessibilityTree: frame.accessibilityTree,\n accessibilityAnnouncements: frame.accessibilityAnnouncements,\n scrollRegions: frame.scrollRegions,\n links: frame.links,\n linkTargets: frame.linkTargets,\n focusPresentation: frame.focusPresentation,\n preferredGridWidth: frame.preferredGridWidth,\n preferredGridHeight: frame.preferredGridHeight,\n };\n }\n\n /**\n * The delta's style table: either the record's own complete table, or the\n * negotiated append spliced onto the baseline's.\n *\n * A `stylesBase` that does not name the end of the retained table is a\n * structural break, not a recoverable one — splicing at the wrong offset\n * silently repaints cells in the wrong style, which is worse than refusing\n * the record and asking for a keyframe.\n */\n private materializeDeltaStyles(\n frame: WebHostSurfaceDeltaFrame,\n baseline: WebHostSurfaceFrame\n ): Array<WebHostSurfaceStyle | null> | undefined {\n if (frame.stylesBase === undefined) {\n return frame.styles;\n }\n if (frame.stylesBase !== baseline.styles.length) {\n return undefined;\n }\n return baseline.styles.concat(frame.styles);\n }\n}\n\nfunction boundedImageResyncIds(\n sortedIds: string[],\n maximumEncodedBytes: number | undefined\n): {\n ids: string[];\n rejectedIds: string[];\n} {\n if (\n maximumEncodedBytes === undefined\n || !Number.isFinite(maximumEncodedBytes)\n ) {\n return { ids: sortedIds, rejectedIds: [] };\n }\n\n const emptyRequestBytes = encodeResyncControlMessage({\n scope: \"images\",\n ids: [],\n }).byteLength;\n const normalizedMaximum = Math.max(0, Math.floor(maximumEncodedBytes));\n const ids: string[] = [];\n const rejectedIds: string[] = [];\n let encodedBytes = emptyRequestBytes;\n for (const id of sortedIds) {\n const separatorBytes = ids.length === 0 ? 0 : 1;\n const idBytes = textEncoder.encode(JSON.stringify(id)).byteLength;\n if (encodedBytes + separatorBytes + idBytes > normalizedMaximum) {\n if (ids.length === 0) {\n rejectedIds.push(id);\n continue;\n }\n break;\n }\n ids.push(id);\n encodedBytes += separatorBytes + idBytes;\n if (encodedBytes >= normalizedMaximum) {\n break;\n }\n }\n return { ids, rejectedIds };\n}\n\nfunction declaresNewerSurfaceVersion(\n value: unknown\n): value is { version: number } {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const version = (value as { version?: unknown }).version;\n return typeof version === \"number\"\n && Number.isSafeInteger(version)\n && version > SUPPORTED_SURFACE_VERSION;\n}\n\nfunction isWebHostClipboardRecord(\n value: unknown\n): value is { text: string } {\n return !!value && typeof value === \"object\" && typeof (value as { text?: unknown }).text === \"string\";\n}\n\nfunction isWebHostRuntimeIssue(\n value: unknown\n): value is WebHostRuntimeIssue {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostRuntimeIssue>;\n return (record.severity === \"warning\" || record.severity === \"error\")\n && typeof record.code === \"string\"\n && typeof record.message === \"string\"\n && typeof record.description === \"string\"\n && (record.identity === undefined || typeof record.identity === \"string\")\n && (record.source === undefined || typeof record.source === \"string\");\n}\n\nfunction isWebHostFrameDiagnosticRecord(\n value: unknown\n): value is WebHostFrameDiagnosticRecord {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const record = value as Partial<WebHostFrameDiagnosticRecord>;\n return record.format === \"swift-tui-frame-diagnostics-v1\"\n && Array.isArray(record.header)\n && record.header.every((field) => typeof field === \"string\")\n && Array.isArray(record.fields)\n && record.fields.every((field) => typeof field === \"string\");\n}\n\nexport function encodeResizeControlMessage(\n columns: number,\n rows: number,\n cellWidth?: number,\n cellHeight?: number\n): Uint8Array {\n const normalizedColumns = Math.max(1, Math.round(columns));\n const normalizedRows = Math.max(1, Math.round(rows));\n if (cellWidth && cellHeight) {\n return textEncoder.encode(\n `${recordPrefix}resize:${normalizedColumns}:${normalizedRows}:${Math.max(1, Math.round(cellWidth))}:${Math.max(1, Math.round(cellHeight))}\\n`\n );\n }\n\n return textEncoder.encode(`${recordPrefix}resize:${normalizedColumns}:${normalizedRows}\\n`);\n}\n\nexport function encodeRenderStyleControlMessage(\n style: WebHostTerminalStyle\n): Uint8Array {\n const encoded = encodeWebHostTerminalRenderStyleBase64(style);\n return textEncoder.encode(`${recordPrefix}style:${encoded}\\n`);\n}\n\nexport function encodeCapabilitiesControlMessage(): Uint8Array {\n // The client's wire-capability declaration, sent once after the socket\n // opens. The declaration is truthful today: this decoder materializes v3\n // delta frames (`materializeDeltaFrame`). Byte shape and key order are\n // pinned by the cross-repo fixture Fixtures/Transport/web-caps-record.txt\n // — swift-tui's input parser consumes the identical bytes, and the\n // coordination root's transport_fixture_sync gate keeps the copies in\n // lockstep. Servers that predate the record drop it silently, so the\n // session degrades to today's full-frame defaults.\n //\n // Capabilities are named feature bits. The retired `maxWebSurfaceVersion`\n // key declared a decoder version ceiling, which was only ever read as\n // \"accepts delta or not\" and duplicated — more weakly — the version check\n // this decoder already performs on every record (SUPPORTED_SURFACE_VERSION).\n // Servers still expecting it skip unknown keys, so dropping it is safe in\n // both directions.\n //\n // `styleAppend` is declared here because `materializeDeltaStyles` splices a\n // delta's `styles` onto the retained table when `stylesBase` is present.\n // Declaring it is truthful by construction: this decoder and this record are\n // the same release. Measured at Stage SV, the full retransmit it replaces was\n // 69.7% of late-record bytes in a style-churning epoch.\n return textEncoder.encode(\n `${recordPrefix}caps:{\"acceptsDeltaFrames\":true,\"styleAppend\":true}\\n`\n );\n}\n\n/**\n * The page's pointer-paradigm declaration.\n *\n * `panning=1` tells the app that this client scrolls by dragging content\n * directly, which is how touch devices and coarse-pointer browsers behave and\n * is not how a desktop mouse behaves. The app uses it to decide whether a drag\n * on a scroll view's background pans it, and whether a drag that starts on a\n * control may be taken over by an enclosing scroll view.\n *\n * Payload is `key=value` tokens rather than positional fields so a later\n * pointer fact can join the record without changing its arity; the Swift\n * parser skips keys it does not know. Unlike `caps:`, this record is not a\n * once-per-connection epoch marker — the device can change (a tablet docked to\n * a mouse), so it may be sent again at any time. Servers that predate the\n * record drop it and keep the desktop paradigm.\n */\nexport function encodePointerCapabilitiesControlMessage(\n supportsScrollPanning: boolean\n): Uint8Array {\n return textEncoder.encode(\n `${recordPrefix}pointer:panning=${supportsScrollPanning ? 1 : 0}\\n`\n );\n}\n\nexport function encodeResyncControlMessage(\n request: WebHostResyncRequest\n): Uint8Array {\n const payload = request.scope === \"keyframe\"\n ? { scope: \"keyframe\" }\n : {\n scope: \"images\",\n ...(request.ids === undefined ? {} : { ids: request.ids }),\n };\n return textEncoder.encode(\n `${recordPrefix}resync:${JSON.stringify(payload)}\\n`\n );\n}\n\nexport function encodeKeyInputMessage(\n input: WebHostKeyInput\n): Uint8Array {\n const modifiers = Math.max(0, Math.round(input.modifiers ?? 0));\n if (input.key === \"character\") {\n return textEncoder.encode(\n `${recordPrefix}key:character:${encodeURIComponent(input.character ?? \"\")}:${modifiers}\\n`\n );\n }\n return textEncoder.encode(`${recordPrefix}key:${input.key}:${modifiers}\\n`);\n}\n\nexport function encodePasteInputMessage(\n text: string\n): Uint8Array {\n return textEncoder.encode(`${recordPrefix}paste:${encodeURIComponent(text)}\\n`);\n}\n\nexport function encodeMouseInputMessage(\n input: WebHostMouseInput\n): Uint8Array {\n return textEncoder.encode(\n recordPrefix + [\n \"mouse\",\n input.kind,\n formatCellCoordinate(input.x),\n formatCellCoordinate(input.y),\n input.button ?? \"none\",\n Math.round(input.deltaX ?? 0),\n Math.round(input.deltaY ?? 0),\n Math.max(0, Math.round(input.modifiers ?? 0)),\n ].join(\":\") + \"\\n\"\n );\n}\n\nfunction formatCellCoordinate(\n value: number\n): string {\n return Number.isFinite(value) ? String(value) : \"0\";\n}\n\nfunction isWebHostSurfaceFrame(\n value: unknown\n): value is WebHostSurfaceFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceFrame>;\n return (frame.version === 1 || frame.version === 2)\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.rows)\n && frame.rows.every(isWebHostSurfaceRow)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\nfunction isWebHostSurfaceDeltaFrame(\n value: unknown\n): value is WebHostSurfaceDeltaFrame {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const frame = value as Partial<WebHostSurfaceDeltaFrame>;\n return frame.version === 3\n && frame.encoding === \"delta\"\n && (\n frame.sequence === undefined\n || (Number.isSafeInteger(frame.sequence) && frame.sequence >= 0)\n )\n && typeof frame.width === \"number\"\n && typeof frame.height === \"number\"\n && Array.isArray(frame.styles)\n && Array.isArray(frame.deltaRows)\n && frame.deltaRows.every(isWebHostSurfaceDeltaRow)\n && isOptionalSafeInteger(frame.baselineGen)\n && isOptionalSafeInteger(frame.stylesBase)\n && (frame.images === undefined || isWebHostSurfaceImages(frame.images))\n && (frame.damage === undefined || isWebHostSurfaceDamage(frame.damage))\n && (\n frame.accessibilityTree === undefined\n || isWebHostAccessibilityNodes(frame.accessibilityTree)\n )\n && (\n frame.accessibilityAnnouncements === undefined\n || isWebHostAccessibilityAnnouncements(frame.accessibilityAnnouncements)\n )\n && (frame.scrollRegions === undefined || isWebHostScrollRegions(frame.scrollRegions))\n && hasValidAdditiveFrameFields(frame);\n}\n\n/**\n * The F19 additive fields shared by the full and delta record shapes. Absent\n * means \"feature not present\" — servers older than the field omit it.\n */\nfunction hasValidAdditiveFrameFields(\n frame: Partial<WebHostSurfaceFrame> | Partial<WebHostSurfaceDeltaFrame>\n): boolean {\n return isOptionalSafeInteger(frame.epoch)\n && isOptionalSafeInteger(frame.gen)\n && (frame.links === undefined || isWebHostSurfaceLinks(frame.links))\n && (frame.linkTargets === undefined || isWebHostSurfaceLinkTargets(frame.linkTargets))\n && (\n frame.focusPresentation === undefined\n || isWebHostFocusPresentation(frame.focusPresentation)\n )\n && (\n frame.preferredGridWidth === undefined\n || (Number.isSafeInteger(frame.preferredGridWidth) && frame.preferredGridWidth >= 0)\n )\n && (\n frame.preferredGridHeight === undefined\n || (Number.isSafeInteger(frame.preferredGridHeight) && frame.preferredGridHeight >= 0)\n );\n}\n\nfunction isOptionalSafeInteger(\n value: unknown\n): boolean {\n return value === undefined || Number.isSafeInteger(value);\n}\n\nfunction isWebHostSurfaceLinks(\n value: unknown\n): value is WebHostSurfaceLinkRow[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceLinkRow);\n}\n\nfunction isWebHostSurfaceLinkRow(\n value: unknown\n): value is WebHostSurfaceLinkRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceLinkRun);\n}\n\nfunction isWebHostSurfaceLinkRun(\n value: unknown\n): value is WebHostSurfaceLinkRun {\n if (!Array.isArray(value) || value.length !== 3) {\n return false;\n }\n const [x, span, targetIndex] = value as number[];\n return Number.isSafeInteger(x)\n && x >= 0\n && Number.isSafeInteger(span)\n && span >= 1\n && Number.isSafeInteger(targetIndex)\n && targetIndex >= 0;\n}\n\nfunction isWebHostSurfaceLinkTargets(\n value: unknown\n): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isWebHostFocusPresentation(\n value: unknown\n): value is WebHostFocusPresentation {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const presentation = value as Partial<WebHostFocusPresentation>;\n return (\n presentation.focusedIdentity === undefined\n || typeof presentation.focusedIdentity === \"string\"\n )\n && typeof presentation.semantics === \"string\"\n && typeof presentation.prefersTextInput === \"boolean\"\n && typeof presentation.hasFocusedRegion === \"boolean\";\n}\n\nfunction isWebHostSurfaceDeltaRow(\n value: unknown\n): value is WebHostSurfaceDeltaRow {\n return Array.isArray(value)\n && value.length === 2\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && isWebHostSurfaceRow(value[1]);\n}\n\nfunction isWebHostSurfaceRow(\n value: unknown\n): value is WebHostSurfaceCell[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceCell);\n}\n\nfunction isWebHostSurfaceCell(\n value: unknown\n): value is WebHostSurfaceCell {\n return Array.isArray(value)\n && value.length === 4\n && Number.isSafeInteger(value[0])\n && value[0] >= 0\n && typeof value[1] === \"string\"\n && Number.isSafeInteger(value[2])\n && value[2] >= 1\n && Number.isSafeInteger(value[3])\n && value[3] >= 0;\n}\n\nfunction isWebHostAccessibilityNodes(\n value: unknown\n): value is WebHostAccessibilityNode[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityNode);\n}\n\nfunction isWebHostAccessibilityNode(\n value: unknown\n): value is WebHostAccessibilityNode {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const node = value as Partial<WebHostAccessibilityNode>;\n return typeof node.id === \"string\"\n && (node.parentId === undefined || typeof node.parentId === \"string\")\n && isWebHostSurfaceRect(node.rect)\n && typeof node.role === \"string\"\n && (node.label === undefined || typeof node.label === \"string\")\n && (node.hint === undefined || typeof node.hint === \"string\")\n && (node.hidden === undefined || typeof node.hidden === \"boolean\")\n && (node.liveRegion === undefined || typeof node.liveRegion === \"string\")\n && (node.cursorAnchor === undefined || isWebHostAccessibilityPoint(node.cursorAnchor))\n && (node.isFocused === undefined || typeof node.isFocused === \"boolean\");\n}\n\nfunction isWebHostAccessibilityPoint(\n value: unknown\n): value is WebHostAccessibilityPoint {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostAccessibilityAnnouncements(\n value: unknown\n): value is WebHostAccessibilityAnnouncement[] {\n return Array.isArray(value) && value.every(isWebHostAccessibilityAnnouncement);\n}\n\nfunction isWebHostAccessibilityAnnouncement(\n value: unknown\n): value is WebHostAccessibilityAnnouncement {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const announcement = value as Partial<WebHostAccessibilityAnnouncement>;\n return typeof announcement.message === \"string\"\n && typeof announcement.politeness === \"string\";\n}\n\nfunction isWebHostSurfaceImages(\n value: unknown\n): value is WebHostSurfaceImage[] {\n return Array.isArray(value) && value.every(isWebHostSurfaceImage);\n}\n\nfunction isWebHostSurfaceImage(\n value: unknown\n): value is WebHostSurfaceImage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const image = value as Partial<WebHostSurfaceImage>;\n return typeof image.id === \"string\"\n && isWebHostSurfaceImageFormat(image.format)\n && isWebHostSurfaceRect(image.bounds)\n && isWebHostSurfaceRect(image.visibleBounds)\n && isWebHostSurfaceScalingMode(image.scalingMode)\n && (\n image.opacity === undefined\n || (typeof image.opacity === \"number\" && Number.isFinite(image.opacity))\n )\n && (image.pixelSize === undefined || isWebHostSurfaceSize(image.pixelSize))\n && (image.dataBase64 === undefined || typeof image.dataBase64 === \"string\");\n}\n\nfunction isWebHostSurfaceDamage(\n value: unknown\n): value is WebHostSurfaceDamage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const damage = value as Partial<WebHostSurfaceDamage>;\n return Array.isArray(damage.textRows)\n && damage.textRows.every(isWebHostSurfaceDamageTextRow)\n && typeof damage.requiresFullTextRepaint === \"boolean\"\n && typeof damage.requiresFullGraphicsReplay === \"boolean\";\n}\n\nfunction isWebHostSurfaceDamageTextRow(\n value: unknown\n): value is WebHostSurfaceDamageTextRow {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && Array.isArray(value[1])\n && value[1].every(isWebHostSurfaceDamageRange);\n}\n\nfunction isWebHostSurfaceDamageRange(\n value: unknown\n): value is WebHostSurfaceDamageRange {\n return Array.isArray(value)\n && value.length === 2\n && typeof value[0] === \"number\"\n && typeof value[1] === \"number\";\n}\n\nfunction isWebHostSurfaceImageFormat(\n value: unknown\n): value is WebHostSurfaceImageFormat {\n return typeof value === \"string\";\n}\n\nfunction isWebHostScrollRegions(\n value: unknown\n): value is WebHostScrollRegion[] {\n return Array.isArray(value) && value.every(isWebHostScrollRegion);\n}\n\nfunction isWebHostScrollRegion(\n value: unknown\n): value is WebHostScrollRegion {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const region = value as Partial<WebHostScrollRegion>;\n return typeof region.id === \"string\"\n && isWebHostSurfaceRect(region.rect)\n && isWebHostSurfaceSize(region.offset)\n && isWebHostSurfaceSize(region.content);\n}\n\nfunction isWebHostSurfaceRect(\n value: unknown\n): value is WebHostSurfaceRect {\n return Array.isArray(value)\n && value.length === 4\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceSize(\n value: unknown\n): value is WebHostSurfaceSize {\n return Array.isArray(value)\n && value.length === 2\n && value.every((entry) => typeof entry === \"number\");\n}\n\nfunction isWebHostSurfaceScalingMode(\n value: unknown\n): value is WebHostSurfaceImage[\"scalingMode\"] {\n return typeof value === \"string\";\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swifttui/web",
3
- "version": "0.8.9",
3
+ "version": "0.8.11",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -65,6 +65,6 @@
65
65
  "@bjorn3/browser_wasi_shim": "^0.4.2"
66
66
  },
67
67
  "devDependencies": {
68
- "@types/bun": "1.3.13"
68
+ "@types/bun": "1.3.14"
69
69
  }
70
70
  }