@opendata-ai/openchart-vanilla 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/svg-renderer.ts +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/export.ts","../src/graph/simulation-worker-url.ts","../src/graph-mount.ts","../src/graph/canvas-renderer.ts","../src/graph/zoom.ts","../src/graph/interaction.ts","../src/graph/keyboard.ts","../src/graph/search.ts","../src/graph/simulation.ts","../src/graph/spatial-index.ts","../src/resize-observer.ts","../src/tooltip.ts","../src/mount.ts","../src/svg-renderer.ts","../src/renderers/table-cells.ts","../src/table-keyboard.ts","../src/table-mount.ts","../src/table-renderer.ts"],"sourcesContent":["/**\n * Export utilities: serialize charts to SVG, PNG, or CSV.\n *\n * - SVG: serializes the rendered DOM element via XMLSerializer\n * - PNG: renders SVG to canvas, then extracts as Blob\n * - CSV: converts a data array to comma-separated text\n */\n\n/**\n * Serialize an SVG element to an XML string.\n *\n * @param svgElement - The rendered SVG element to serialize.\n * @returns The SVG markup as a string.\n */\nexport function exportSVG(svgElement: SVGElement): string {\n const serializer = new XMLSerializer();\n return serializer.serializeToString(svgElement);\n}\n\nexport interface PNGExportOptions {\n /** DPI scaling factor. Defaults to 2 for retina-quality output. */\n dpi?: number;\n}\n\n/**\n * Render an SVG element to a PNG Blob via a canvas.\n *\n * @param svgElement - The rendered SVG element.\n * @param options - Optional DPI scaling.\n * @returns A Promise resolving to the PNG Blob.\n */\nexport async function exportPNG(svgElement: SVGElement, options?: PNGExportOptions): Promise<Blob> {\n const dpi = options?.dpi ?? 2;\n const svgString = exportSVG(svgElement);\n\n const width = parseFloat(svgElement.getAttribute('width') || '600');\n const height = parseFloat(svgElement.getAttribute('height') || '400');\n\n const canvas = document.createElement('canvas');\n canvas.width = width * dpi;\n canvas.height = height * dpi;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Canvas 2D context not available');\n }\n\n ctx.scale(dpi, dpi);\n\n const img = new Image();\n const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n\n return new Promise<Blob>((resolve, reject) => {\n img.onload = () => {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n\n canvas.toBlob((result) => {\n if (result) {\n resolve(result);\n } else {\n reject(new Error('Canvas toBlob returned null'));\n }\n }, 'image/png');\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load SVG as image'));\n };\n\n img.src = url;\n });\n}\n\nexport interface JPGExportOptions extends PNGExportOptions {\n /** JPEG quality from 0 to 1. Defaults to 0.92. */\n quality?: number;\n}\n\n/**\n * Render an SVG element to a JPEG Blob via a canvas.\n *\n * Same pipeline as exportPNG but outputs JPEG with configurable quality.\n * The canvas is filled with white before drawing to avoid transparent\n * backgrounds rendering as black in JPEG format.\n *\n * @param svgElement - The rendered SVG element.\n * @param options - Optional DPI scaling and JPEG quality.\n * @returns A Promise resolving to the JPEG Blob.\n */\nexport async function exportJPG(svgElement: SVGElement, options?: JPGExportOptions): Promise<Blob> {\n const dpi = options?.dpi ?? 2;\n const quality = options?.quality ?? 0.92;\n const svgString = exportSVG(svgElement);\n\n const width = parseFloat(svgElement.getAttribute('width') || '600');\n const height = parseFloat(svgElement.getAttribute('height') || '400');\n\n const canvas = document.createElement('canvas');\n canvas.width = width * dpi;\n canvas.height = height * dpi;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Canvas 2D context not available');\n }\n\n // Fill white background since JPEG doesn't support transparency\n ctx.fillStyle = '#ffffff';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n ctx.scale(dpi, dpi);\n\n const img = new Image();\n const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n\n return new Promise<Blob>((resolve, reject) => {\n img.onload = () => {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n\n canvas.toBlob(\n (result) => {\n if (result) {\n resolve(result);\n } else {\n reject(new Error('Canvas toBlob returned null'));\n }\n },\n 'image/jpeg',\n quality,\n );\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load SVG as image'));\n };\n\n img.src = url;\n });\n}\n\n/**\n * Convert an array of data objects to a CSV string.\n *\n * Uses the keys from the first row as column headers.\n * Values are quoted if they contain commas, quotes, or newlines.\n *\n * @param data - Array of row objects.\n * @returns CSV-formatted string.\n */\nexport function exportCSV(data: Record<string, unknown>[]): string {\n if (data.length === 0) return '';\n\n const headers = Object.keys(data[0]);\n const rows = [headers.map(csvEscape).join(',')];\n\n for (const row of data) {\n const values = headers.map((h) => csvEscape(String(row[h] ?? '')));\n rows.push(values.join(','));\n }\n\n return rows.join('\\n');\n}\n\nfunction csvEscape(value: string): string {\n if (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n","/**\n * Creates a Web Worker running the force simulation.\n *\n * Uses the `new URL` + `import.meta.url` pattern recognized by Vite, Webpack 5,\n * Parcel, and esbuild. The bundler resolves the worker file path at build time\n * and handles the asset accordingly.\n *\n * - Vite dev (Ladle): resolves src/graph/simulation-worker.ts directly, serves\n * it as a native ES module worker with on-the-fly TypeScript transform.\n * - Production (tsup + bun build): dist/simulation-worker.js is a self-contained\n * IIFE produced by `bun build`. The consuming app's bundler copies it as an\n * asset and rewrites the URL.\n *\n * Usage:\n * import { createSimulationWorker } from '@opendata-ai/openchart-vanilla';\n * const worker = createSimulationWorker();\n * worker.postMessage({ type: 'init', nodes, links, width: 800, height: 600 });\n * worker.onmessage = (e) => console.log(e.data);\n */\n\n/**\n * Path that resolves in Vite dev (workspace source) to the .ts file.\n * In production dist/, the consuming bundler resolves to simulation-worker.js\n * which sits alongside index.js in the dist folder.\n */\nconst workerUrl = new URL('./simulation-worker.ts', import.meta.url);\n\nexport function createSimulationWorker(): Worker {\n return new Worker(workerUrl, { type: 'module' });\n}\n","/**\n * Graph mount API: the main entry point for vanilla JS graph usage.\n *\n * createGraph() takes a container, GraphSpec, and options, compiles the graph,\n * creates a force simulation, canvas renderer, spatial index, interaction\n * manager, and search manager, then runs an animation loop driven by\n * simulation ticks. Returns a GraphInstance with update/search/zoom/destroy.\n */\n\nimport type { CompileOptions, DarkMode, GraphSpec, ThemeConfig } from '@opendata-ai/openchart-core';\nimport type {\n CompiledGraphEdge,\n CompiledGraphNode,\n GraphCompilation,\n} from '@opendata-ai/openchart-engine';\nimport { compileGraph } from '@opendata-ai/openchart-engine';\n\nimport { GraphCanvasRenderer } from './graph/canvas-renderer';\nimport { GraphInteractionManager } from './graph/interaction';\nimport { attachGraphKeyboardNav } from './graph/keyboard';\nimport { GraphSearchManager } from './graph/search';\nimport { SimulationManager } from './graph/simulation';\nimport { SpatialIndex } from './graph/spatial-index';\nimport type { GraphRenderState, PositionedEdge, PositionedNode } from './graph/types';\nimport type { SimEdge, SimNode } from './graph/worker-protocol';\nimport { ZoomTransform } from './graph/zoom';\nimport { observeResize } from './resize-observer';\nimport { createTooltipManager, type TooltipManager } from './tooltip';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface GraphMountOptions {\n theme?: ThemeConfig;\n darkMode?: DarkMode;\n responsive?: boolean;\n /** Show the built-in tooltip on node/edge hover. Defaults to true. */\n tooltip?: boolean;\n /** Show the built-in legend. Defaults to true. */\n legend?: boolean;\n onNodeClick?: (node: Record<string, unknown>) => void;\n onNodeDoubleClick?: (node: Record<string, unknown>) => void;\n onNodeHover?: (node: Record<string, unknown> | null) => void;\n onEdgeHover?: (edge: Record<string, unknown> | null) => void;\n onSelectionChange?: (nodeIds: string[]) => void;\n}\n\nexport interface GraphInstance {\n update(spec: GraphSpec): void;\n /** Re-compile encoding/legend/chrome without restarting the simulation. Preserves node positions. */\n updateVisuals(spec: GraphSpec): void;\n search(query: string): void;\n clearSearch(): void;\n zoomToFit(): void;\n zoomToNode(nodeId: string): void;\n selectNode(nodeId: string): void;\n getSelectedNodes(): string[];\n resize(): void;\n destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Dark mode resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveDarkMode(mode?: DarkMode): boolean {\n if (mode === 'force') return true;\n if (mode === 'off' || mode === undefined) return false;\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Main API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a graph instance from a spec and mount it into a container.\n *\n * @param container - The DOM element to render into.\n * @param spec - The graph spec.\n * @param options - Mount options.\n * @returns A GraphInstance with update/search/zoom/destroy methods.\n */\nexport function createGraph(\n container: HTMLElement,\n spec: GraphSpec,\n options?: GraphMountOptions,\n): GraphInstance {\n let currentSpec = spec;\n let compilation: GraphCompilation;\n let destroyed = false;\n\n // DOM elements\n let wrapper: HTMLElement | null = null;\n let canvas: HTMLCanvasElement | null = null;\n let chromeEl: HTMLElement | null = null;\n let legendEl: HTMLElement | null = null;\n\n // Subsystems\n let renderer: GraphCanvasRenderer | null = null;\n let simulation: SimulationManager | null = null;\n const spatialIndex = new SpatialIndex();\n let interactionManager: GraphInteractionManager | null = null;\n const searchManager = new GraphSearchManager();\n let tooltipManager: TooltipManager | null = null;\n let cleanupKeyboard: (() => void) | null = null;\n let disconnectResize: (() => void) | null = null;\n\n // State\n let positionedNodes: PositionedNode[] = [];\n let positionedEdges: PositionedEdge[] = [];\n let adjacencyMap = new Map<string, Set<string>>();\n let hoveredNodeId: string | null = null;\n let hoveredEdgeId: string | null = null;\n let selectedNodeIds = new Set<string>();\n let animFrameId: number | null = null;\n let needsRender = false;\n let isGesturing = false;\n let gestureTimeout: ReturnType<typeof setTimeout> | null = null;\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n function markGesture(): void {\n isGesturing = true;\n if (gestureTimeout !== null) clearTimeout(gestureTimeout);\n gestureTimeout = setTimeout(() => {\n isGesturing = false;\n gestureTimeout = null;\n needsRender = true;\n scheduleRender();\n }, 150);\n }\n\n function getContainerDimensions(): { width: number; height: number } {\n const rect = container.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n function compile(): GraphCompilation {\n const { width, height } = getContainerDimensions();\n const darkMode = resolveDarkMode(options?.darkMode);\n\n const compileOpts: CompileOptions = {\n width,\n height,\n theme: options?.theme,\n darkMode,\n };\n\n return compileGraph(currentSpec, compileOpts);\n }\n\n function buildAdjacencyMap(edges: CompiledGraphEdge[]): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n for (const edge of edges) {\n if (!map.has(edge.source)) map.set(edge.source, new Set());\n if (!map.has(edge.target)) map.set(edge.target, new Set());\n map.get(edge.source)!.add(edge.target);\n map.get(edge.target)!.add(edge.source);\n }\n return map;\n }\n\n function toSimNodes(nodes: CompiledGraphNode[]): SimNode[] {\n return nodes.map((n) => ({\n id: n.id,\n radius: n.radius,\n community: n.community,\n }));\n }\n\n function toSimEdges(edges: CompiledGraphEdge[]): SimEdge[] {\n return edges.map((e) => ({\n source: e.source,\n target: e.target,\n }));\n }\n\n /**\n * Look up a node's data from the compilation by id.\n * Falls back to an empty object if not found.\n */\n function nodeDataById(nodeId: string): Record<string, unknown> {\n const node = compilation.nodes.find((n) => n.id === nodeId);\n return node?.data ?? {};\n }\n\n /**\n * Point-to-line-segment distance for edge hit testing.\n * Returns the shortest distance from point (px, py) to the segment (ax, ay)-(bx, by).\n */\n function pointToSegmentDist(\n px: number,\n py: number,\n ax: number,\n ay: number,\n bx: number,\n by: number,\n ): number {\n const dx = bx - ax;\n const dy = by - ay;\n const lenSq = dx * dx + dy * dy;\n if (lenSq === 0) return Math.hypot(px - ax, py - ay);\n const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / lenSq));\n return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));\n }\n\n /**\n * Find the edge closest to a graph-space point, within a threshold.\n * Returns an edge key \"source->target\" or null.\n */\n function hitTestEdge(graphX: number, graphY: number, threshold: number): string | null {\n let bestDist = threshold;\n let bestEdgeId: string | null = null;\n\n for (const edge of positionedEdges) {\n const dist = pointToSegmentDist(\n graphX,\n graphY,\n edge.sourceX,\n edge.sourceY,\n edge.targetX,\n edge.targetY,\n );\n if (dist < bestDist) {\n bestDist = dist;\n bestEdgeId = `${edge.source}->${edge.target}`;\n }\n }\n\n return bestEdgeId;\n }\n\n /**\n * Look up edge data by edge id (\"source->target\").\n */\n function edgeDataById(edgeId: string): Record<string, unknown> | null {\n const [source, target] = edgeId.split('->');\n const edge = compilation.edges.find((e) => e.source === source && e.target === target);\n return edge?.data ?? null;\n }\n\n // ---------------------------------------------------------------------------\n // DOM creation\n // ---------------------------------------------------------------------------\n\n function createDOM(): void {\n const { width, height } = getContainerDimensions();\n const isDark = resolveDarkMode(options?.darkMode);\n\n // Wrapper\n wrapper = document.createElement('div');\n wrapper.className = 'viz-graph-wrapper';\n if (isDark) {\n container.classList.add('viz-dark');\n } else {\n container.classList.remove('viz-dark');\n }\n\n // Chrome (title, subtitle)\n chromeEl = document.createElement('div');\n chromeEl.className = 'viz-graph-chrome';\n renderChrome();\n wrapper.appendChild(chromeEl);\n\n // Canvas\n canvas = document.createElement('canvas');\n canvas.className = 'viz-graph-canvas';\n canvas.setAttribute('role', 'img');\n if (compilation.a11y?.altText) {\n canvas.setAttribute('aria-label', compilation.a11y.altText);\n }\n wrapper.appendChild(canvas);\n\n // Legend\n if (options?.legend !== false) {\n legendEl = document.createElement('div');\n legendEl.className = 'viz-graph-legend';\n renderLegend();\n wrapper.appendChild(legendEl);\n }\n\n container.appendChild(wrapper);\n\n // Size the canvas\n const chromeHeight = chromeEl.getBoundingClientRect().height || 0;\n const canvasHeight = Math.max(height - chromeHeight, 200);\n renderer = new GraphCanvasRenderer(canvas);\n renderer.resize(width, canvasHeight);\n }\n\n function renderChrome(): void {\n if (!chromeEl) return;\n let html = '';\n\n if (compilation.chrome.title) {\n html += `<h2 class=\"viz-title\">${escapeHtml(compilation.chrome.title.text)}</h2>`;\n }\n if (compilation.chrome.subtitle) {\n html += `<p class=\"viz-subtitle\">${escapeHtml(compilation.chrome.subtitle.text)}</p>`;\n }\n\n chromeEl.innerHTML = html;\n\n // Hide chrome if empty\n if (!html) {\n chromeEl.style.display = 'none';\n } else {\n chromeEl.style.display = '';\n }\n }\n\n function renderLegend(): void {\n if (!legendEl) return;\n\n const entries = compilation.legend.entries;\n if (entries.length === 0) {\n legendEl.style.display = 'none';\n return;\n }\n\n legendEl.style.display = '';\n let html = '';\n for (const entry of entries) {\n html += '<div class=\"viz-graph-legend-item\">';\n html += `<span class=\"viz-graph-legend-swatch\" style=\"background:${escapeHtml(entry.color)}\"></span>`;\n html += `<span>${escapeHtml(entry.label)}</span>`;\n html += '</div>';\n }\n legendEl.innerHTML = html;\n }\n\n // ---------------------------------------------------------------------------\n // Simulation and animation\n // ---------------------------------------------------------------------------\n\n function initSimulation(): void {\n const simNodes = toSimNodes(compilation.nodes);\n const simEdges = toSimEdges(compilation.edges);\n const config = compilation.simulationConfig;\n\n simulation = SimulationManager.create(simNodes, simEdges, {\n chargeStrength: config.chargeStrength,\n linkDistance: config.linkDistance,\n clustering: config.clustering,\n alphaDecay: config.alphaDecay,\n velocityDecay: config.velocityDecay,\n collisionRadius: config.collisionRadius,\n collisionPadding: config.collisionPadding,\n linkStrength: config.linkStrength,\n centerForce: config.centerForce,\n });\n\n simulation.onTick((positions, _alpha) => {\n if (destroyed) return;\n\n // Build position lookup\n const posMap = new Map<string, { x: number; y: number }>();\n for (const p of positions) {\n posMap.set(p.id, { x: p.x, y: p.y });\n }\n\n // Build positioned nodes\n positionedNodes = compilation.nodes.map((node) => {\n const pos = posMap.get(node.id) ?? { x: 0, y: 0 };\n return { ...node, x: pos.x, y: pos.y };\n });\n\n // Build positioned edges\n positionedEdges = compilation.edges.map((edge) => {\n const src = posMap.get(edge.source) ?? { x: 0, y: 0 };\n const tgt = posMap.get(edge.target) ?? { x: 0, y: 0 };\n return {\n ...edge,\n sourceX: src.x,\n sourceY: src.y,\n targetX: tgt.x,\n targetY: tgt.y,\n };\n });\n\n // Rebuild spatial index\n spatialIndex.rebuild(positionedNodes);\n\n needsRender = true;\n scheduleRender();\n });\n\n simulation.onSettled(() => {\n // One final fit after simulation settles\n if (canvas && positionedNodes.length > 0 && interactionManager) {\n const { width: cw, height: ch } = getCanvasDimensions();\n const fitTransform = ZoomTransform.fitBounds(positionedNodes, cw, ch);\n interactionManager.setTransform(fitTransform);\n needsRender = true;\n scheduleRender();\n }\n });\n }\n\n function getCanvasDimensions(): { width: number; height: number } {\n if (!canvas) return { width: 600, height: 400 };\n const rect = canvas.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n function scheduleRender(): void {\n if (animFrameId !== null || destroyed) return;\n animFrameId = requestAnimationFrame(renderFrame);\n }\n\n function renderFrame(): void {\n animFrameId = null;\n if (destroyed || !renderer || !interactionManager) return;\n\n if (needsRender) {\n needsRender = false;\n\n const transform = interactionManager.getTransform();\n const state: GraphRenderState = {\n nodes: positionedNodes,\n edges: positionedEdges,\n transform: { x: transform.x, y: transform.y, k: transform.k },\n hoveredNodeId,\n hoveredEdgeId,\n selectedNodeIds,\n adjacencyMap,\n theme: compilation.theme,\n searchMatches: searchManager.getMatches(),\n isGesturing,\n };\n\n renderer.render(state);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Interaction wiring\n // ---------------------------------------------------------------------------\n\n function initInteraction(): void {\n if (!canvas) return;\n\n if (options?.tooltip !== false) {\n tooltipManager = createTooltipManager(wrapper!);\n }\n\n interactionManager = new GraphInteractionManager(canvas, spatialIndex, {\n onTransformChange(_transform) {\n markGesture();\n needsRender = true;\n scheduleRender();\n },\n onHoverChange(nodeId) {\n hoveredNodeId = nodeId;\n needsRender = true;\n scheduleRender();\n\n // Fire onNodeHover callback\n if (nodeId) {\n options?.onNodeHover?.(nodeDataById(nodeId));\n } else {\n options?.onNodeHover?.(null);\n }\n\n // Show or hide tooltip\n if (nodeId && tooltipManager) {\n // Clear edge hover when hovering a node\n if (hoveredEdgeId) {\n hoveredEdgeId = null;\n options?.onEdgeHover?.(null);\n }\n const content = compilation.tooltipDescriptors.get(nodeId);\n if (content) {\n const node = positionedNodes.find((n) => n.id === nodeId);\n if (node && interactionManager) {\n const screen = interactionManager.getTransform().graphToScreen(node.x, node.y);\n tooltipManager.show(content, screen.x, screen.y);\n }\n }\n } else if (!nodeId) {\n // Tooltip hiding handled in onBackgroundHover (edge may show tooltip)\n // If no edge hover happens, tooltip stays hidden\n tooltipManager?.hide();\n }\n },\n onBackgroundHover(graphX, graphY, screenX, screenY) {\n // Edge hit testing: check proximity to edge line segments\n const transform = interactionManager?.getTransform();\n const threshold = 5 / (transform?.k ?? 1); // 5px in screen space\n const edgeId = hitTestEdge(graphX, graphY, threshold);\n\n if (edgeId !== hoveredEdgeId) {\n hoveredEdgeId = edgeId;\n needsRender = true;\n scheduleRender();\n\n if (edgeId) {\n const data = edgeDataById(edgeId);\n options?.onEdgeHover?.(data);\n\n // Show edge tooltip\n if (tooltipManager && data) {\n const fields = Object.entries(data)\n .filter(([key]) => key !== 'source' && key !== 'target')\n .filter(([, value]) => value != null)\n .map(([key, value]) => ({\n label: key,\n value: typeof value === 'number' ? value.toLocaleString() : String(value),\n }));\n\n const [source, target] = edgeId.split('->');\n tooltipManager.show({ title: `${source} → ${target}`, fields }, screenX, screenY);\n }\n } else {\n options?.onEdgeHover?.(null);\n tooltipManager?.hide();\n }\n }\n },\n onSelectionChange(nodeIds) {\n selectedNodeIds = new Set(nodeIds);\n needsRender = true;\n scheduleRender();\n options?.onSelectionChange?.(nodeIds);\n\n // Fire onNodeClick for the most recently added node\n if (nodeIds.length > 0) {\n const lastId = nodeIds[nodeIds.length - 1];\n options?.onNodeClick?.(nodeDataById(lastId));\n }\n },\n onNodeDragStart(nodeId) {\n // Pin at the node's current position to avoid visual snap to origin\n const node = positionedNodes.find((n) => n.id === nodeId);\n const x = node?.x ?? 0;\n const y = node?.y ?? 0;\n simulation?.pinNode(nodeId, x, y);\n canvas?.classList.add('viz-graph-canvas--dragging');\n },\n onNodeDrag(nodeId, x, y) {\n simulation?.dragNode(nodeId, x, y);\n },\n onNodeDragEnd(nodeId) {\n simulation?.unpinNode(nodeId);\n canvas?.classList.remove('viz-graph-canvas--dragging');\n },\n onDoubleClick(nodeId) {\n options?.onNodeDoubleClick?.(nodeDataById(nodeId));\n },\n });\n\n // Wire keyboard navigation\n cleanupKeyboard = attachGraphKeyboardNav({\n canvas,\n getNodes: () => positionedNodes,\n getSelectedIds: () => [...selectedNodeIds],\n getAdjacency: () => adjacencyMap,\n onSelect(nodeId) {\n selectedNodeIds = new Set([nodeId]);\n needsRender = true;\n scheduleRender();\n options?.onNodeClick?.(nodeDataById(nodeId));\n options?.onSelectionChange?.([nodeId]);\n },\n onDeselect() {\n selectedNodeIds.clear();\n needsRender = true;\n scheduleRender();\n options?.onSelectionChange?.([]);\n },\n onZoom(direction) {\n if (!interactionManager || !canvas) return;\n const t = interactionManager.getTransform();\n const { width: cw, height: ch } = getCanvasDimensions();\n const factor = direction === 'in' ? 1.2 : 0.8;\n const newK = t.k * factor;\n const newTransform = t.zoomAt(newK, cw / 2, ch / 2);\n interactionManager.setTransform(newTransform);\n needsRender = true;\n scheduleRender();\n },\n onFitAll() {\n zoomToFit();\n },\n });\n\n // Handle node clicks (from interaction manager selection change wiring above)\n // We catch clicks via the interaction manager's onSelectionChange callback\n }\n\n // ---------------------------------------------------------------------------\n // Public API methods\n // ---------------------------------------------------------------------------\n\n function search(query: string): void {\n if (destroyed) return;\n searchManager.search(query, positionedNodes);\n needsRender = true;\n scheduleRender();\n }\n\n function clearSearch(): void {\n if (destroyed) return;\n searchManager.clearSearch();\n needsRender = true;\n scheduleRender();\n }\n\n function zoomToFit(): void {\n if (destroyed || !interactionManager || positionedNodes.length === 0) return;\n const { width: cw, height: ch } = getCanvasDimensions();\n const fitTransform = ZoomTransform.fitBounds(positionedNodes, cw, ch);\n interactionManager.setTransform(fitTransform);\n needsRender = true;\n scheduleRender();\n }\n\n function zoomToNode(nodeId: string): void {\n if (destroyed || !interactionManager || !canvas) return;\n const node = positionedNodes.find((n) => n.id === nodeId);\n if (!node) return;\n\n const { width: cw, height: ch } = getCanvasDimensions();\n // Zoom to 2x and center on node\n const k = 2;\n const tx = cw / 2 - node.x * k;\n const ty = ch / 2 - node.y * k;\n const newTransform = new ZoomTransform(tx, ty, k);\n interactionManager.setTransform(newTransform);\n needsRender = true;\n scheduleRender();\n }\n\n function selectNode(nodeId: string): void {\n if (destroyed) return;\n selectedNodeIds = new Set([nodeId]);\n needsRender = true;\n scheduleRender();\n options?.onSelectionChange?.([nodeId]);\n }\n\n function getSelectedNodes(): string[] {\n return [...selectedNodeIds];\n }\n\n function doResize(): void {\n if (destroyed || !canvas || !renderer || !wrapper) return;\n const { width, height } = getContainerDimensions();\n const chromeHeight = chromeEl?.getBoundingClientRect().height || 0;\n const canvasHeight = Math.max(height - chromeHeight, 200);\n renderer.resize(width, canvasHeight);\n needsRender = true;\n scheduleRender();\n }\n\n function update(newSpec: GraphSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n\n // Tear down old simulation + interaction\n teardownSubsystems();\n\n // Recompile\n compilation = compile();\n adjacencyMap = buildAdjacencyMap(compilation.edges);\n\n // Update DOM chrome/legend\n renderChrome();\n renderLegend();\n\n // Reinit\n initSimulation();\n initInteraction();\n\n // Reset state\n hoveredNodeId = null;\n hoveredEdgeId = null;\n selectedNodeIds = new Set();\n searchManager.clearSearch();\n }\n\n function updateVisuals(newSpec: GraphSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n\n // Build a position lookup from current positioned nodes\n const posMap = new Map<string, { x: number; y: number }>();\n for (const node of positionedNodes) {\n posMap.set(node.id, { x: node.x, y: node.y });\n }\n\n // Recompile with new spec (encoding, chrome, nodeOverrides, etc.)\n compilation = compile();\n adjacencyMap = buildAdjacencyMap(compilation.edges);\n\n // Transfer positions to new compiled nodes\n positionedNodes = compilation.nodes.map((node) => {\n const pos = posMap.get(node.id) ?? { x: 0, y: 0 };\n return { ...node, x: pos.x, y: pos.y };\n });\n\n // Rebuild positioned edges from existing positions\n positionedEdges = compilation.edges.map((edge) => {\n const src = posMap.get(edge.source) ?? { x: 0, y: 0 };\n const tgt = posMap.get(edge.target) ?? { x: 0, y: 0 };\n return {\n ...edge,\n sourceX: src.x,\n sourceY: src.y,\n targetX: tgt.x,\n targetY: tgt.y,\n };\n });\n\n // Rebuild spatial index with updated visuals\n spatialIndex.rebuild(positionedNodes);\n\n // Update DOM chrome/legend\n renderChrome();\n renderLegend();\n\n // Re-render canvas without restarting simulation\n needsRender = true;\n scheduleRender();\n }\n\n function teardownSubsystems(): void {\n if (animFrameId !== null) {\n cancelAnimationFrame(animFrameId);\n animFrameId = null;\n }\n if (cleanupKeyboard) {\n cleanupKeyboard();\n cleanupKeyboard = null;\n }\n interactionManager?.destroy();\n interactionManager = null;\n simulation?.destroy();\n simulation = null;\n tooltipManager?.destroy();\n tooltipManager = null;\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (gestureTimeout !== null) {\n clearTimeout(gestureTimeout);\n gestureTimeout = null;\n }\n\n teardownSubsystems();\n\n if (disconnectResize) {\n disconnectResize();\n disconnectResize = null;\n }\n\n if (wrapper?.parentNode) {\n wrapper.parentNode.removeChild(wrapper);\n }\n wrapper = null;\n canvas = null;\n chromeEl = null;\n legendEl = null;\n renderer = null;\n\n container.classList.remove('viz-dark');\n }\n\n // ---------------------------------------------------------------------------\n // Initialize\n // ---------------------------------------------------------------------------\n\n try {\n compilation = compile();\n adjacencyMap = buildAdjacencyMap(compilation.edges);\n createDOM();\n initSimulation();\n initInteraction();\n } catch (err) {\n console.error('[viz] Graph mount failed:', err);\n // Return a no-op instance so callers don't crash\n return {\n update() {},\n updateVisuals() {},\n search() {},\n clearSearch() {},\n zoomToFit() {},\n zoomToNode() {},\n selectNode() {},\n getSelectedNodes: () => [],\n resize() {},\n destroy() {},\n };\n }\n\n // Responsive resize\n if (options?.responsive !== false) {\n disconnectResize = observeResize(container, () => {\n doResize();\n });\n }\n\n return {\n update,\n updateVisuals,\n search,\n clearSearch,\n zoomToFit,\n zoomToNode,\n selectNode,\n getSelectedNodes,\n resize: doResize,\n destroy,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Util\n// ---------------------------------------------------------------------------\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","/**\n * Canvas 2D renderer for force-directed graph visualization.\n *\n * Stateless renderer: receives a GraphRenderState each frame and draws it.\n * Handles DPR scaling, viewport culling, LOD labels, dark mode glow effects,\n * and batched drawing for performance at 10k+ nodes.\n *\n * Performance strategy:\n * - Edges batched by (stroke, strokeWidth, dash) key → one stroke() per group\n * - Nodes batched by fill color → one fill() per color group\n * - Node strokes batched by stroke color\n * - Labels and glow skipped during active pan/zoom gestures\n */\n\nimport type { GraphRenderState, PositionedEdge, PositionedNode } from './types';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst LABEL_FONT_MIN = 8;\nconst LABEL_FONT_MAX = 12;\nconst EDGE_ALPHA_DEFAULT = 0.35;\nconst EDGE_ALPHA_CONNECTED = 1.0;\nconst EDGE_ALPHA_DIMMED = 0.05;\nconst SEARCH_NON_MATCH_ALPHA = 0.15;\nconst GLOW_NODE_THRESHOLD = 2000;\nconst GLOW_RADIUS_MULTIPLIER = 1.3;\nconst GLOW_ALPHA = 0.15;\nconst CULL_MARGIN = 50;\nconst TWO_PI = Math.PI * 2;\n\n/** Minimum node radius in screen pixels. Keeps nodes visible when zoomed out. */\nconst MIN_SCREEN_RADIUS = 2.5;\n\n/** Minimum canvas width to render the brand watermark. */\nconst BRAND_MIN_WIDTH = 120;\n\n// ---------------------------------------------------------------------------\n// Helpers (exported for testing)\n// ---------------------------------------------------------------------------\n\n/**\n * Compute label visibility threshold from zoom level.\n * At zoom 0.2 (zoomed out): threshold ~1.0 (only top ~5% visible).\n * At zoom 2.0+: threshold ~0.0 (all visible).\n */\nexport function labelThreshold(zoom: number): number {\n const t = Math.max(0, Math.min(1, (zoom - 0.2) / 1.8));\n return 1 - t;\n}\n\n/** Compute visible rect in graph coordinates from canvas size + transform. */\nexport function visibleRect(\n canvasWidth: number,\n canvasHeight: number,\n transform: { x: number; y: number; k: number },\n margin: number = CULL_MARGIN,\n): { minX: number; minY: number; maxX: number; maxY: number } {\n const { x, y, k } = transform;\n return {\n minX: (-x - margin) / k,\n minY: (-y - margin) / k,\n maxX: (canvasWidth - x + margin) / k,\n maxY: (canvasHeight - y + margin) / k,\n };\n}\n\n/** Check if a node falls within the visible rect. */\nfunction nodeInView(\n node: PositionedNode,\n rect: { minX: number; minY: number; maxX: number; maxY: number },\n): boolean {\n return (\n node.x + node.radius >= rect.minX &&\n node.x - node.radius <= rect.maxX &&\n node.y + node.radius >= rect.minY &&\n node.y - node.radius <= rect.maxY\n );\n}\n\n/** Check if an edge has at least one endpoint in view. */\nfunction edgeInView(\n edge: PositionedEdge,\n rect: { minX: number; minY: number; maxX: number; maxY: number },\n): boolean {\n return (\n (edge.sourceX >= rect.minX &&\n edge.sourceX <= rect.maxX &&\n edge.sourceY >= rect.minY &&\n edge.sourceY <= rect.maxY) ||\n (edge.targetX >= rect.minX &&\n edge.targetX <= rect.maxX &&\n edge.targetY >= rect.minY &&\n edge.targetY <= rect.maxY)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Dash patterns for edge styles\n// ---------------------------------------------------------------------------\n\nconst DASH_PATTERNS: Record<string, number[]> = {\n solid: [],\n dashed: [6, 4],\n dotted: [2, 3],\n};\n\n// ---------------------------------------------------------------------------\n// GraphCanvasRenderer\n// ---------------------------------------------------------------------------\n\nexport class GraphCanvasRenderer {\n private canvas: HTMLCanvasElement;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via this-destructuring\n private ctx: CanvasRenderingContext2D;\n private dpr: number;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via this-destructuring\n private cssWidth = 0;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via this-destructuring\n private cssHeight = 0;\n\n constructor(canvas: HTMLCanvasElement) {\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d')!;\n this.dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n }\n\n /** Update canvas dimensions with DPR scaling. CSS size stays at css values. */\n resize(width: number, height: number): void {\n this.cssWidth = width;\n this.cssHeight = height;\n this.canvas.width = width * this.dpr;\n this.canvas.height = height * this.dpr;\n this.canvas.style.width = `${width}px`;\n this.canvas.style.height = `${height}px`;\n }\n\n /** Clear canvas and render the full graph state. */\n render(state: GraphRenderState): void {\n const { ctx, dpr, cssWidth, cssHeight } = this;\n const {\n nodes,\n edges,\n transform,\n hoveredNodeId,\n hoveredEdgeId,\n selectedNodeIds,\n adjacencyMap,\n theme,\n searchMatches,\n isGesturing,\n } = state;\n\n const hasActiveNode = hoveredNodeId !== null || selectedNodeIds.size > 0;\n const activeNodeIds = new Set<string>();\n if (hoveredNodeId) activeNodeIds.add(hoveredNodeId);\n for (const id of selectedNodeIds) activeNodeIds.add(id);\n\n // Collect all nodes connected to any active node\n const connectedNodeIds = new Set<string>();\n for (const id of activeNodeIds) {\n connectedNodeIds.add(id);\n const neighbors = adjacencyMap.get(id);\n if (neighbors) {\n for (const nid of neighbors) connectedNodeIds.add(nid);\n }\n }\n\n // Viewport culling\n const rect = visibleRect(cssWidth, cssHeight, transform);\n const visibleNodes = nodes.filter((n) => nodeInView(n, rect));\n const visibleEdges = edges.filter((e) => edgeInView(e, rect));\n\n const isDark = theme.isDark;\n const showGlow = isDark && !isGesturing && visibleNodes.length < GLOW_NODE_THRESHOLD;\n const threshold = labelThreshold(transform.k);\n // Minimum radius in graph coordinates so nodes stay visible when zoomed out\n const minRadius = MIN_SCREEN_RADIUS / transform.k;\n\n // -- Clear and apply transform --\n ctx.save();\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, cssWidth, cssHeight);\n\n // Fill background (skip if transparent to let page background show through)\n if (theme.colors.background !== 'transparent') {\n ctx.fillStyle = theme.colors.background;\n ctx.fillRect(0, 0, cssWidth, cssHeight);\n }\n\n ctx.translate(transform.x, transform.y);\n ctx.scale(transform.k, transform.k);\n\n // -- Draw edges (batched by style key) --\n this.drawEdgesBatched(\n ctx,\n visibleEdges,\n hasActiveNode,\n connectedNodeIds,\n isGesturing ? null : searchMatches,\n hoveredEdgeId,\n );\n\n // -- Draw nodes (batched by fill color) --\n this.drawNodesBatched(\n ctx,\n visibleNodes,\n hoveredNodeId,\n selectedNodeIds,\n isGesturing ? null : searchMatches,\n showGlow,\n theme,\n minRadius,\n );\n\n // -- Draw labels (skipped during gestures) --\n if (!isGesturing) {\n this.drawLabels(\n ctx,\n visibleNodes,\n threshold,\n hoveredNodeId,\n selectedNodeIds,\n searchMatches,\n transform.k,\n theme,\n );\n }\n\n ctx.restore();\n\n // Brand watermark in screen coordinates (unaffected by pan/zoom)\n this.drawBrand(ctx, cssWidth, cssHeight, theme);\n }\n\n // -------------------------------------------------------------------------\n // Brand rendering\n // -------------------------------------------------------------------------\n\n private drawBrand(\n ctx: CanvasRenderingContext2D,\n w: number,\n h: number,\n theme: GraphRenderState['theme'],\n ): void {\n if (w < BRAND_MIN_WIDTH) return;\n const { dpr } = this;\n ctx.save();\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n const padding = theme.spacing.padding;\n const x = w - padding;\n const y = h - padding;\n ctx.font = `600 20px ${theme.fonts.family}`;\n ctx.fillStyle = theme.colors.axis;\n ctx.globalAlpha = 0.55;\n ctx.textAlign = 'right';\n ctx.textBaseline = 'alphabetic';\n ctx.fillText('OpenData', x, y);\n ctx.restore();\n }\n\n // -------------------------------------------------------------------------\n // Batched edge drawing\n // -------------------------------------------------------------------------\n\n private drawEdgesBatched(\n ctx: CanvasRenderingContext2D,\n edges: PositionedEdge[],\n hasActiveNode: boolean,\n connectedNodeIds: Set<string>,\n searchMatches: Set<string> | null,\n hoveredEdgeId: string | null,\n ): void {\n // Classify edges by alpha level, then batch by visual style within each level\n const dimmedEdges: PositionedEdge[] = [];\n const defaultEdges: PositionedEdge[] = [];\n const connectedEdges: PositionedEdge[] = [];\n let hoveredEdge: PositionedEdge | null = null;\n\n for (const edge of edges) {\n const edgeId = `${edge.source}->${edge.target}`;\n if (edgeId === hoveredEdgeId) {\n hoveredEdge = edge;\n continue; // Draw hovered edge last, on top\n }\n\n const isConnected =\n hasActiveNode && connectedNodeIds.has(edge.source) && connectedNodeIds.has(edge.target);\n const isDimmed = hasActiveNode && !isConnected;\n\n if (isConnected) {\n connectedEdges.push(edge);\n } else if (isDimmed) {\n dimmedEdges.push(edge);\n } else {\n defaultEdges.push(edge);\n }\n }\n\n // Draw dimmed first, then default, then connected (on top)\n this.drawEdgeGroupBatched(ctx, dimmedEdges, EDGE_ALPHA_DIMMED, searchMatches);\n this.drawEdgeGroupBatched(ctx, defaultEdges, EDGE_ALPHA_DEFAULT, searchMatches);\n this.drawEdgeGroupBatched(ctx, connectedEdges, EDGE_ALPHA_CONNECTED, searchMatches);\n\n // Draw hovered edge on top with highlight\n if (hoveredEdge) {\n const dash = DASH_PATTERNS[hoveredEdge.style] ?? DASH_PATTERNS.solid;\n ctx.setLineDash(dash);\n ctx.strokeStyle = hoveredEdge.stroke;\n ctx.lineWidth = hoveredEdge.strokeWidth * 2;\n ctx.globalAlpha = EDGE_ALPHA_CONNECTED;\n ctx.beginPath();\n ctx.moveTo(hoveredEdge.sourceX, hoveredEdge.sourceY);\n ctx.lineTo(hoveredEdge.targetX, hoveredEdge.targetY);\n ctx.stroke();\n ctx.setLineDash([]);\n ctx.globalAlpha = 1;\n }\n }\n\n /**\n * Draw a group of edges at a given alpha, batched by (stroke, strokeWidth, style).\n * When search is inactive, all edges of the same style are drawn in a single path.\n * When search is active, edges split by search-match status for alpha dimming.\n */\n private drawEdgeGroupBatched(\n ctx: CanvasRenderingContext2D,\n edges: PositionedEdge[],\n alpha: number,\n searchMatches: Set<string> | null,\n ): void {\n if (edges.length === 0) return;\n\n // Group by visual key: stroke + strokeWidth + style\n const groups = new Map<string, PositionedEdge[]>();\n for (const edge of edges) {\n const key = `${edge.stroke}|${edge.strokeWidth}|${edge.style}`;\n let group = groups.get(key);\n if (!group) {\n group = [];\n groups.set(key, group);\n }\n group.push(edge);\n }\n\n for (const [, group] of groups) {\n const sample = group[0];\n const dash = DASH_PATTERNS[sample.style] ?? DASH_PATTERNS.solid;\n ctx.setLineDash(dash);\n ctx.strokeStyle = sample.stroke;\n ctx.lineWidth = sample.strokeWidth;\n\n if (!searchMatches) {\n // Fast path: single batched path for all edges in this group\n ctx.globalAlpha = alpha;\n ctx.beginPath();\n for (const edge of group) {\n ctx.moveTo(edge.sourceX, edge.sourceY);\n ctx.lineTo(edge.targetX, edge.targetY);\n }\n ctx.stroke();\n } else {\n // Search active: split into matched and non-matched batches\n ctx.globalAlpha = alpha;\n ctx.beginPath();\n let hasMatched = false;\n\n const nonMatchPath: PositionedEdge[] = [];\n\n for (const edge of group) {\n const srcMatch = searchMatches.has(edge.source);\n const tgtMatch = searchMatches.has(edge.target);\n if (srcMatch || tgtMatch) {\n ctx.moveTo(edge.sourceX, edge.sourceY);\n ctx.lineTo(edge.targetX, edge.targetY);\n hasMatched = true;\n } else {\n nonMatchPath.push(edge);\n }\n }\n if (hasMatched) ctx.stroke();\n\n // Draw non-matching edges dimmed\n if (nonMatchPath.length > 0) {\n ctx.globalAlpha = SEARCH_NON_MATCH_ALPHA * alpha;\n ctx.beginPath();\n for (const edge of nonMatchPath) {\n ctx.moveTo(edge.sourceX, edge.sourceY);\n ctx.lineTo(edge.targetX, edge.targetY);\n }\n ctx.stroke();\n }\n }\n }\n\n ctx.setLineDash([]);\n ctx.globalAlpha = 1;\n }\n\n // -------------------------------------------------------------------------\n // Batched node drawing\n // -------------------------------------------------------------------------\n\n private drawNodesBatched(\n ctx: CanvasRenderingContext2D,\n nodes: PositionedNode[],\n hoveredNodeId: string | null,\n selectedNodeIds: Set<string>,\n searchMatches: Set<string> | null,\n showGlow: boolean,\n theme: GraphRenderState['theme'],\n minRadius: number,\n ): void {\n // Separate special nodes (hovered/selected) from bulk nodes.\n // Special nodes need individual treatment; bulk nodes get batched by color.\n const bulkNodes: PositionedNode[] = [];\n const specialNodes: PositionedNode[] = [];\n\n for (const node of nodes) {\n if (node.id === hoveredNodeId || selectedNodeIds.has(node.id)) {\n specialNodes.push(node);\n } else {\n bulkNodes.push(node);\n }\n }\n\n // Helper: effective radius clamped to minimum screen size\n const r = (node: PositionedNode) => Math.max(node.radius, minRadius);\n\n // --- Glow pass (dark mode only, before fills) ---\n if (showGlow) {\n this.drawGlowBatched(ctx, bulkNodes, searchMatches, minRadius);\n }\n\n // --- Bulk fill pass: batch by fill color ---\n const fillGroups = new Map<string, PositionedNode[]>();\n for (const node of bulkNodes) {\n let group = fillGroups.get(node.fill);\n if (!group) {\n group = [];\n fillGroups.set(node.fill, group);\n }\n group.push(node);\n }\n\n if (!searchMatches) {\n // Fast path: no search dimming\n ctx.globalAlpha = 1;\n for (const [fill, group] of fillGroups) {\n ctx.fillStyle = fill;\n ctx.beginPath();\n for (const node of group) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.fill();\n }\n } else {\n // Search active: split each color group into matched/dimmed batches\n for (const [fill, group] of fillGroups) {\n ctx.fillStyle = fill;\n\n // Matched nodes\n ctx.globalAlpha = 1;\n ctx.beginPath();\n let hasMatched = false;\n const dimmedNodes: PositionedNode[] = [];\n\n for (const node of group) {\n if (searchMatches.has(node.id)) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n hasMatched = true;\n } else {\n dimmedNodes.push(node);\n }\n }\n if (hasMatched) ctx.fill();\n\n // Dimmed nodes\n if (dimmedNodes.length > 0) {\n ctx.globalAlpha = SEARCH_NON_MATCH_ALPHA;\n ctx.beginPath();\n for (const node of dimmedNodes) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.fill();\n }\n }\n }\n\n // --- Bulk stroke pass: batch by stroke color ---\n const strokeGroups = new Map<string, PositionedNode[]>();\n for (const node of bulkNodes) {\n const key = `${node.stroke}|${node.strokeWidth}`;\n let group = strokeGroups.get(key);\n if (!group) {\n group = [];\n strokeGroups.set(key, group);\n }\n group.push(node);\n }\n\n for (const [key, group] of strokeGroups) {\n const [stroke, widthStr] = key.split('|');\n ctx.strokeStyle = stroke;\n ctx.lineWidth = parseFloat(widthStr);\n\n if (!searchMatches) {\n ctx.globalAlpha = 1;\n ctx.beginPath();\n for (const node of group) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.stroke();\n } else {\n // Split matched/dimmed for strokes too\n ctx.globalAlpha = 1;\n ctx.beginPath();\n let hasMatched = false;\n const dimmedNodes: PositionedNode[] = [];\n\n for (const node of group) {\n if (searchMatches.has(node.id)) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n hasMatched = true;\n } else {\n dimmedNodes.push(node);\n }\n }\n if (hasMatched) ctx.stroke();\n\n if (dimmedNodes.length > 0) {\n ctx.globalAlpha = SEARCH_NON_MATCH_ALPHA;\n ctx.beginPath();\n for (const node of dimmedNodes) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.stroke();\n }\n }\n }\n\n // --- Special nodes (hovered/selected) drawn individually ---\n for (const node of specialNodes) {\n const isHovered = node.id === hoveredNodeId;\n const isSelected = selectedNodeIds.has(node.id);\n const dimmed = searchMatches !== null && !searchMatches.has(node.id);\n const baseRadius = Math.max(node.radius, minRadius);\n const radius = isHovered ? baseRadius * 1.15 : baseRadius;\n\n ctx.globalAlpha = dimmed ? SEARCH_NON_MATCH_ALPHA : 1;\n\n // Glow for special nodes\n if (showGlow && !dimmed) {\n ctx.beginPath();\n ctx.arc(node.x, node.y, radius * GLOW_RADIUS_MULTIPLIER, 0, TWO_PI);\n ctx.fillStyle = node.fill;\n ctx.globalAlpha = GLOW_ALPHA;\n ctx.fill();\n ctx.globalAlpha = dimmed ? SEARCH_NON_MATCH_ALPHA : 1;\n }\n\n // Fill\n ctx.beginPath();\n ctx.arc(node.x, node.y, radius, 0, TWO_PI);\n ctx.fillStyle = isHovered ? brighten(node.fill) : node.fill;\n ctx.fill();\n\n // Stroke\n ctx.strokeStyle = node.stroke;\n ctx.lineWidth = node.strokeWidth;\n ctx.stroke();\n\n // Selection ring\n if (isSelected) {\n ctx.beginPath();\n ctx.arc(node.x, node.y, radius + 3, 0, TWO_PI);\n ctx.strokeStyle = theme.colors.categorical[0] ?? '#3b82f6';\n ctx.lineWidth = 2;\n ctx.stroke();\n }\n }\n\n ctx.globalAlpha = 1;\n }\n\n /** Batch glow circles by fill color. */\n private drawGlowBatched(\n ctx: CanvasRenderingContext2D,\n nodes: PositionedNode[],\n searchMatches: Set<string> | null,\n minRadius: number,\n ): void {\n const glowGroups = new Map<string, PositionedNode[]>();\n for (const node of nodes) {\n if (searchMatches && !searchMatches.has(node.id)) continue;\n let group = glowGroups.get(node.fill);\n if (!group) {\n group = [];\n glowGroups.set(node.fill, group);\n }\n group.push(node);\n }\n\n ctx.globalAlpha = GLOW_ALPHA;\n for (const [fill, group] of glowGroups) {\n ctx.fillStyle = fill;\n ctx.beginPath();\n for (const node of group) {\n const gr = Math.max(node.radius, minRadius) * GLOW_RADIUS_MULTIPLIER;\n ctx.moveTo(node.x + gr, node.y);\n ctx.arc(node.x, node.y, gr, 0, TWO_PI);\n }\n ctx.fill();\n }\n ctx.globalAlpha = 1;\n }\n\n // -------------------------------------------------------------------------\n // Labels (drawn individually, skipped during gestures)\n // -------------------------------------------------------------------------\n\n private drawLabels(\n ctx: CanvasRenderingContext2D,\n nodes: PositionedNode[],\n threshold: number,\n hoveredNodeId: string | null,\n selectedNodeIds: Set<string>,\n searchMatches: Set<string> | null,\n zoom: number,\n theme: GraphRenderState['theme'],\n ): void {\n // Font size inversely scaled by zoom, clamped to readable range\n const rawSize = 10 / zoom;\n const fontSize = Math.max(LABEL_FONT_MIN, Math.min(LABEL_FONT_MAX, rawSize));\n\n ctx.font = `${fontSize}px ${theme.fonts.family}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'top';\n\n for (const node of nodes) {\n if (!node.label) continue;\n\n const isHovered = node.id === hoveredNodeId;\n const isSelected = selectedNodeIds.has(node.id);\n const forced = isHovered || isSelected;\n const dimmed = searchMatches !== null && !searchMatches.has(node.id);\n\n // LOD: skip labels below threshold unless forced\n if (!forced && node.labelPriority < threshold) continue;\n\n ctx.globalAlpha = dimmed ? SEARCH_NON_MATCH_ALPHA : 1;\n\n const labelY = node.y + node.radius + 3;\n\n // Halo for readability: stroke behind text in the background color\n // so labels stay legible over edges and other nodes.\n if (theme.colors.background !== 'transparent') {\n ctx.strokeStyle = theme.colors.background;\n } else {\n // Transparent bg: infer page background from text luminance.\n // Light text = dark page, dark text = light page.\n ctx.strokeStyle = isLightColor(theme.colors.text)\n ? 'rgba(0, 0, 0, 0.7)'\n : 'rgba(255, 255, 255, 0.85)';\n }\n ctx.lineWidth = 3;\n ctx.lineJoin = 'round';\n ctx.miterLimit = 2;\n ctx.strokeText(node.label, node.x, labelY);\n\n ctx.fillStyle = theme.colors.text;\n ctx.fillText(node.label, node.x, labelY);\n }\n\n ctx.globalAlpha = 1;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Color helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Brighten a hex/rgb color by ~20% for hover effect.\n * Quick and dirty approach: parse hex, lighten each channel.\n */\nfunction brighten(color: string): string {\n // Handle rgb(r,g,b) or rgb(r, g, b)\n const rgbMatch = color.match(/^rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/);\n if (rgbMatch) {\n const r = Math.min(255, parseInt(rgbMatch[1], 10) + 40);\n const g = Math.min(255, parseInt(rgbMatch[2], 10) + 40);\n const b = Math.min(255, parseInt(rgbMatch[3], 10) + 40);\n return `rgb(${r},${g},${b})`;\n }\n\n // Handle hex colors (#rgb and #rrggbb)\n const hex = color.replace('#', '');\n const full =\n hex.length === 3\n ? hex\n .split('')\n .map((c) => c + c)\n .join('')\n : hex;\n\n if (full.length === 6) {\n const r = Math.min(255, parseInt(full.slice(0, 2), 16) + 40);\n const g = Math.min(255, parseInt(full.slice(2, 4), 16) + 40);\n const b = Math.min(255, parseInt(full.slice(4, 6), 16) + 40);\n return `rgb(${r},${g},${b})`;\n }\n\n return color;\n}\n\n/**\n * Returns true if a color is perceptually light (luminance > 0.5).\n * Used to pick a contrasting halo color for labels on transparent backgrounds.\n */\nfunction isLightColor(color: string): boolean {\n const hex = color.replace('#', '');\n const full =\n hex.length === 3\n ? hex\n .split('')\n .map((c) => c + c)\n .join('')\n : hex;\n if (full.length !== 6) return false;\n const r = parseInt(full.slice(0, 2), 16) / 255;\n const g = parseInt(full.slice(2, 4), 16) / 255;\n const b = parseInt(full.slice(4, 6), 16) / 255;\n const toLinear = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\n return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b) > 0.5;\n}\n","/**\n * Immutable zoom transform for graph pan/zoom.\n *\n * Provides coordinate conversion between screen space (canvas pixels)\n * and graph space (simulation coordinates). All mutations return new\n * instances rather than modifying in place.\n */\n\nimport type { PositionedNode } from './types';\n\nexport class ZoomTransform {\n constructor(\n readonly x: number,\n readonly y: number,\n readonly k: number,\n ) {}\n\n /** Convert screen coordinates to graph coordinates. */\n screenToGraph(sx: number, sy: number): { x: number; y: number } {\n return {\n x: (sx - this.x) / this.k,\n y: (sy - this.y) / this.k,\n };\n }\n\n /** Convert graph coordinates to screen coordinates. */\n graphToScreen(gx: number, gy: number): { x: number; y: number } {\n return {\n x: gx * this.k + this.x,\n y: gy * this.k + this.y,\n };\n }\n\n /**\n * Zoom to a target scale, keeping the given screen-space pivot\n * point fixed (content under the cursor stays under the cursor).\n */\n zoomAt(targetK: number, pivotX: number, pivotY: number): ZoomTransform {\n // The graph point under the pivot should remain at the same screen position.\n // Before: pivotX = gx * k + x => gx = (pivotX - x) / k\n // After: pivotX = gx * targetK + newX => newX = pivotX - gx * targetK\n const gx = (pivotX - this.x) / this.k;\n const gy = (pivotY - this.y) / this.k;\n return new ZoomTransform(pivotX - gx * targetK, pivotY - gy * targetK, targetK);\n }\n\n /** Pan by a screen-space delta. */\n pan(dx: number, dy: number): ZoomTransform {\n return new ZoomTransform(this.x + dx, this.y + dy, this.k);\n }\n\n /**\n * Compute a transform that fits all nodes within the given canvas\n * dimensions with the specified padding.\n */\n static fitBounds(\n nodes: PositionedNode[],\n canvasW: number,\n canvasH: number,\n padding: number = 40,\n ): ZoomTransform {\n if (nodes.length === 0) {\n return ZoomTransform.identity();\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const n of nodes) {\n const r = n.radius;\n if (n.x - r < minX) minX = n.x - r;\n if (n.y - r < minY) minY = n.y - r;\n if (n.x + r > maxX) maxX = n.x + r;\n if (n.y + r > maxY) maxY = n.y + r;\n }\n\n const graphW = maxX - minX;\n const graphH = maxY - minY;\n\n if (graphW === 0 && graphH === 0) {\n // All nodes at the same point; just center\n return new ZoomTransform(canvasW / 2 - minX, canvasH / 2 - minY, 1);\n }\n\n const availW = canvasW - padding * 2;\n const availH = canvasH - padding * 2;\n const k = Math.min(availW / graphW, availH / graphH);\n\n // Center the graph in the canvas\n const cx = (minX + maxX) / 2;\n const cy = (minY + maxY) / 2;\n const tx = canvasW / 2 - cx * k;\n const ty = canvasH / 2 - cy * k;\n\n return new ZoomTransform(tx, ty, k);\n }\n\n /** Identity transform (no pan, no zoom). */\n static identity(): ZoomTransform {\n return new ZoomTransform(0, 0, 1);\n }\n}\n","/**\n * Graph interaction manager.\n *\n * Handles mouse/touch events on the canvas and translates them into\n * high-level graph interactions: pan, zoom, hover, select, drag nodes.\n * Uses the spatial index for hit testing and ZoomTransform for coordinate\n * conversion.\n */\n\nimport type { SpatialIndex } from './spatial-index';\nimport { ZoomTransform } from './zoom';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst ZOOM_MIN = 0.05;\nconst ZOOM_MAX = 15;\nconst ZOOM_STEP = -0.001;\nconst HIT_DISTANCE = 5;\n\n// ---------------------------------------------------------------------------\n// Callback interface\n// ---------------------------------------------------------------------------\n\nexport interface InteractionCallbacks {\n onTransformChange(transform: ZoomTransform): void;\n onHoverChange(nodeId: string | null): void;\n /** Called during mouse move when no node is hit, with graph-space coordinates for edge hit testing. */\n onBackgroundHover?(graphX: number, graphY: number, screenX: number, screenY: number): void;\n onSelectionChange(nodeIds: string[]): void;\n onNodeDragStart(nodeId: string): void;\n onNodeDrag(nodeId: string, x: number, y: number): void;\n onNodeDragEnd(nodeId: string): void;\n onDoubleClick(nodeId: string): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal state\n// ---------------------------------------------------------------------------\n\ninterface DragState {\n nodeId: string;\n started: boolean;\n}\n\ninterface PanState {\n startX: number;\n startY: number;\n}\n\n// ---------------------------------------------------------------------------\n// GraphInteractionManager\n// ---------------------------------------------------------------------------\n\nexport class GraphInteractionManager {\n private canvas: HTMLCanvasElement;\n private spatialIndex: SpatialIndex;\n private callbacks: InteractionCallbacks;\n private transform = ZoomTransform.identity();\n\n private dragState: DragState | null = null;\n private panState: PanState | null = null;\n private mousedownNodeId: string | null = null;\n private selectedIds: Set<string> = new Set();\n\n // Touch state\n private lastTouchDist: number | null = null;\n private lastTouchCenter: { x: number; y: number } | null = null;\n\n // Bound handlers for cleanup\n private boundWheel: (e: WheelEvent) => void;\n private boundMouseDown: (e: MouseEvent) => void;\n private boundMouseMove: (e: MouseEvent) => void;\n private boundMouseUp: (e: MouseEvent) => void;\n private boundDblClick: (e: MouseEvent) => void;\n private boundTouchStart: (e: TouchEvent) => void;\n private boundTouchMove: (e: TouchEvent) => void;\n private boundTouchEnd: (e: TouchEvent) => void;\n private boundMouseLeave: (e: MouseEvent) => void;\n\n constructor(\n canvas: HTMLCanvasElement,\n spatialIndex: SpatialIndex,\n callbacks: InteractionCallbacks,\n ) {\n this.canvas = canvas;\n this.spatialIndex = spatialIndex;\n this.callbacks = callbacks;\n\n // Bind handlers\n this.boundWheel = this.onWheel.bind(this);\n this.boundMouseDown = this.onMouseDown.bind(this);\n this.boundMouseMove = this.onMouseMove.bind(this);\n this.boundMouseUp = this.onMouseUp.bind(this);\n this.boundMouseLeave = this.onMouseLeave.bind(this);\n this.boundDblClick = this.onDblClick.bind(this);\n this.boundTouchStart = this.onTouchStart.bind(this);\n this.boundTouchMove = this.onTouchMove.bind(this);\n this.boundTouchEnd = this.onTouchEnd.bind(this);\n\n // Attach event listeners\n canvas.addEventListener('wheel', this.boundWheel, { passive: false });\n canvas.addEventListener('mousedown', this.boundMouseDown);\n canvas.addEventListener('mousemove', this.boundMouseMove);\n canvas.addEventListener('mouseup', this.boundMouseUp);\n canvas.addEventListener('mouseleave', this.boundMouseLeave);\n canvas.addEventListener('dblclick', this.boundDblClick);\n canvas.addEventListener('touchstart', this.boundTouchStart, {\n passive: false,\n });\n canvas.addEventListener('touchmove', this.boundTouchMove, {\n passive: false,\n });\n canvas.addEventListener('touchend', this.boundTouchEnd);\n }\n\n setTransform(transform: ZoomTransform): void {\n this.transform = transform;\n }\n\n getTransform(): ZoomTransform {\n return this.transform;\n }\n\n destroy(): void {\n this.canvas.removeEventListener('wheel', this.boundWheel);\n this.canvas.removeEventListener('mousedown', this.boundMouseDown);\n this.canvas.removeEventListener('mousemove', this.boundMouseMove);\n this.canvas.removeEventListener('mouseup', this.boundMouseUp);\n this.canvas.removeEventListener('mouseleave', this.boundMouseLeave);\n this.canvas.removeEventListener('dblclick', this.boundDblClick);\n this.canvas.removeEventListener('touchstart', this.boundTouchStart);\n this.canvas.removeEventListener('touchmove', this.boundTouchMove);\n this.canvas.removeEventListener('touchend', this.boundTouchEnd);\n }\n\n // -------------------------------------------------------------------------\n // Mouse handlers\n // -------------------------------------------------------------------------\n\n private canvasXY(e: MouseEvent): { x: number; y: number } {\n const rect = this.canvas.getBoundingClientRect();\n return { x: e.clientX - rect.left, y: e.clientY - rect.top };\n }\n\n private hitTest(screenX: number, screenY: number): string | null {\n const graph = this.transform.screenToGraph(screenX, screenY);\n const node = this.spatialIndex.findNearest(graph.x, graph.y, HIT_DISTANCE / this.transform.k);\n return node?.id ?? null;\n }\n\n private onWheel(e: WheelEvent): void {\n e.preventDefault();\n const { x, y } = this.canvasXY(e);\n const factor = e.deltaY * ZOOM_STEP;\n const newK = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, this.transform.k * (1 + factor)));\n this.transform = this.transform.zoomAt(newK, x, y);\n this.callbacks.onTransformChange(this.transform);\n }\n\n private onMouseDown(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n const hitId = this.hitTest(x, y);\n\n if (hitId) {\n // Start potential node drag\n this.dragState = { nodeId: hitId, started: false };\n this.mousedownNodeId = hitId;\n } else {\n // Start pan\n this.panState = { startX: x, startY: y };\n this.mousedownNodeId = null;\n }\n }\n\n private onMouseMove(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n\n if (this.dragState) {\n const graph = this.transform.screenToGraph(x, y);\n if (!this.dragState.started) {\n this.dragState.started = true;\n this.callbacks.onNodeDragStart(this.dragState.nodeId);\n }\n this.callbacks.onNodeDrag(this.dragState.nodeId, graph.x, graph.y);\n return;\n }\n\n if (this.panState) {\n const dx = x - this.panState.startX;\n const dy = y - this.panState.startY;\n this.transform = this.transform.pan(dx, dy);\n this.panState = { startX: x, startY: y };\n this.callbacks.onTransformChange(this.transform);\n return;\n }\n\n // Hover detection\n const hitId = this.hitTest(x, y);\n this.callbacks.onHoverChange(hitId);\n\n // If no node hit, check edges via callback\n if (!hitId) {\n const graph = this.transform.screenToGraph(x, y);\n this.callbacks.onBackgroundHover?.(graph.x, graph.y, x, y);\n }\n\n // Update cursor\n this.canvas.style.cursor = hitId ? 'pointer' : 'default';\n }\n\n private onMouseUp(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n\n if (this.dragState) {\n if (this.dragState.started) {\n this.callbacks.onNodeDragEnd(this.dragState.nodeId);\n } else {\n // Was a click on a node (no drag movement)\n this.handleNodeClick(this.dragState.nodeId, e.shiftKey);\n }\n this.dragState = null;\n return;\n }\n\n if (this.panState) {\n this.panState = null;\n\n // If mouse up is on background (no node), treat as background click\n if (!this.mousedownNodeId) {\n const hitId = this.hitTest(x, y);\n if (!hitId) {\n // Background click: clear selection\n this.selectedIds.clear();\n this.callbacks.onSelectionChange([]);\n }\n }\n return;\n }\n }\n\n private onDblClick(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n const hitId = this.hitTest(x, y);\n if (hitId) {\n this.callbacks.onDoubleClick(hitId);\n }\n }\n\n private onMouseLeave(_e: MouseEvent): void {\n this.callbacks.onHoverChange(null);\n this.canvas.style.cursor = 'default';\n\n // Cancel any in-progress pan\n if (this.panState) {\n this.panState = null;\n }\n }\n\n private handleNodeClick(nodeId: string, shiftKey: boolean): void {\n if (shiftKey) {\n // Toggle node in multi-select\n if (this.selectedIds.has(nodeId)) {\n this.selectedIds.delete(nodeId);\n } else {\n this.selectedIds.add(nodeId);\n }\n } else {\n // Single select\n this.selectedIds.clear();\n this.selectedIds.add(nodeId);\n }\n\n this.callbacks.onSelectionChange([...this.selectedIds]);\n }\n\n // -------------------------------------------------------------------------\n // Touch handlers\n // -------------------------------------------------------------------------\n\n private onTouchStart(e: TouchEvent): void {\n e.preventDefault();\n\n if (e.touches.length === 2) {\n // Pinch-zoom start\n const [t0, t1] = [e.touches[0], e.touches[1]];\n this.lastTouchDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);\n this.lastTouchCenter = {\n x: (t0.clientX + t1.clientX) / 2,\n y: (t0.clientY + t1.clientY) / 2,\n };\n } else if (e.touches.length === 1) {\n const touch = e.touches[0];\n const rect = this.canvas.getBoundingClientRect();\n const x = touch.clientX - rect.left;\n const y = touch.clientY - rect.top;\n\n const hitId = this.hitTest(x, y);\n if (hitId) {\n this.mousedownNodeId = hitId;\n } else {\n this.panState = { startX: x, startY: y };\n this.mousedownNodeId = null;\n }\n }\n }\n\n private onTouchMove(e: TouchEvent): void {\n e.preventDefault();\n\n if (e.touches.length === 2 && this.lastTouchDist !== null) {\n const [t0, t1] = [e.touches[0], e.touches[1]];\n const newDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);\n const rect = this.canvas.getBoundingClientRect();\n const centerX = (t0.clientX + t1.clientX) / 2 - rect.left;\n const centerY = (t0.clientY + t1.clientY) / 2 - rect.top;\n\n const scale = newDist / this.lastTouchDist;\n const newK = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, this.transform.k * scale));\n this.transform = this.transform.zoomAt(newK, centerX, centerY);\n\n // Pan from center movement\n if (this.lastTouchCenter) {\n const dx = centerX - (this.lastTouchCenter.x - rect.left);\n const dy = centerY - (this.lastTouchCenter.y - rect.top);\n this.transform = this.transform.pan(dx, dy);\n }\n\n this.lastTouchDist = newDist;\n this.lastTouchCenter = {\n x: (t0.clientX + t1.clientX) / 2,\n y: (t0.clientY + t1.clientY) / 2,\n };\n this.callbacks.onTransformChange(this.transform);\n } else if (e.touches.length === 1 && this.panState) {\n const touch = e.touches[0];\n const rect = this.canvas.getBoundingClientRect();\n const x = touch.clientX - rect.left;\n const y = touch.clientY - rect.top;\n\n const dx = x - this.panState.startX;\n const dy = y - this.panState.startY;\n this.transform = this.transform.pan(dx, dy);\n this.panState = { startX: x, startY: y };\n this.callbacks.onTransformChange(this.transform);\n }\n }\n\n private onTouchEnd(e: TouchEvent): void {\n if (e.touches.length === 0) {\n // Tap-select\n if (this.mousedownNodeId && !this.panState) {\n this.handleNodeClick(this.mousedownNodeId, false);\n } else if (!this.mousedownNodeId && this.panState) {\n // Background tap: clear selection\n this.selectedIds.clear();\n this.callbacks.onSelectionChange([]);\n }\n\n this.panState = null;\n this.mousedownNodeId = null;\n this.lastTouchDist = null;\n this.lastTouchCenter = null;\n }\n }\n}\n","/**\n * Keyboard navigation for the graph canvas.\n *\n * Provides accessible keyboard control: Tab to focus, arrow keys to\n * navigate between adjacent nodes (following edges), Enter to select,\n * Escape to clear, +/- to zoom, Home to fit all, / to focus search.\n */\n\nimport type { PositionedNode } from './types';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface KeyboardNavOptions {\n canvas: HTMLCanvasElement;\n getNodes(): PositionedNode[];\n getSelectedIds(): string[];\n getAdjacency(): Map<string, Set<string>>;\n onSelect(nodeId: string): void;\n onDeselect(): void;\n onZoom(direction: 'in' | 'out'): void;\n onFitAll(): void;\n onFocusSearch?(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Attach keyboard navigation to a graph canvas.\n * Returns a cleanup function that removes all listeners.\n */\nexport function attachGraphKeyboardNav(options: KeyboardNavOptions): () => void {\n const {\n canvas,\n getNodes,\n getSelectedIds,\n getAdjacency,\n onSelect,\n onDeselect,\n onZoom,\n onFitAll,\n onFocusSearch,\n } = options;\n\n let focusedNodeId: string | null = null;\n\n // Make canvas focusable\n if (!canvas.hasAttribute('tabindex')) {\n canvas.setAttribute('tabindex', '0');\n }\n\n function findNodeById(id: string): PositionedNode | undefined {\n return getNodes().find((n) => n.id === id);\n }\n\n /**\n * Given a set of neighbor node ids, pick the one that best matches\n * the arrow key direction relative to the current focused node.\n */\n function pickDirectionalNeighbor(\n fromNode: PositionedNode,\n neighborIds: Set<string>,\n direction: 'up' | 'down' | 'left' | 'right',\n ): string | null {\n const nodes = getNodes();\n const candidates = nodes.filter((n) => neighborIds.has(n.id));\n if (candidates.length === 0) return null;\n\n // Score each candidate by how well it matches the desired direction\n let best: PositionedNode | null = null;\n let bestScore = -Infinity;\n\n for (const c of candidates) {\n const dx = c.x - fromNode.x;\n const dy = c.y - fromNode.y;\n let score: number;\n\n switch (direction) {\n case 'right':\n score = dx - Math.abs(dy) * 0.5;\n break;\n case 'left':\n score = -dx - Math.abs(dy) * 0.5;\n break;\n case 'down':\n score = dy - Math.abs(dx) * 0.5;\n break;\n case 'up':\n score = -dy - Math.abs(dx) * 0.5;\n break;\n }\n\n if (score > bestScore) {\n bestScore = score;\n best = c;\n }\n }\n\n return best?.id ?? null;\n }\n\n function onKeyDown(e: KeyboardEvent): void {\n switch (e.key) {\n case 'Tab': {\n // Focus first/selected node\n const selected = getSelectedIds();\n const nodes = getNodes();\n if (nodes.length === 0) return;\n\n if (selected.length > 0) {\n focusedNodeId = selected[0];\n } else if (!focusedNodeId || !findNodeById(focusedNodeId)) {\n focusedNodeId = nodes[0].id;\n }\n\n e.preventDefault();\n break;\n }\n\n case 'ArrowUp':\n case 'ArrowDown':\n case 'ArrowLeft':\n case 'ArrowRight': {\n if (!focusedNodeId) return;\n e.preventDefault();\n\n const focusedNode = findNodeById(focusedNodeId);\n if (!focusedNode) return;\n\n const adjacency = getAdjacency();\n const neighbors = adjacency.get(focusedNodeId);\n if (!neighbors || neighbors.size === 0) return;\n\n const dirMap: Record<string, 'up' | 'down' | 'left' | 'right'> = {\n ArrowUp: 'up',\n ArrowDown: 'down',\n ArrowLeft: 'left',\n ArrowRight: 'right',\n };\n\n const nextId = pickDirectionalNeighbor(focusedNode, neighbors, dirMap[e.key]);\n if (nextId) {\n focusedNodeId = nextId;\n onSelect(nextId);\n }\n break;\n }\n\n case 'Enter': {\n if (focusedNodeId) {\n e.preventDefault();\n const selected = getSelectedIds();\n if (selected.includes(focusedNodeId)) {\n onDeselect();\n } else {\n onSelect(focusedNodeId);\n }\n }\n break;\n }\n\n case 'Escape': {\n e.preventDefault();\n focusedNodeId = null;\n onDeselect();\n break;\n }\n\n case '+':\n case '=': {\n e.preventDefault();\n onZoom('in');\n break;\n }\n\n case '-':\n case '_': {\n e.preventDefault();\n onZoom('out');\n break;\n }\n\n case 'Home': {\n e.preventDefault();\n onFitAll();\n break;\n }\n\n case '/': {\n if (onFocusSearch) {\n e.preventDefault();\n onFocusSearch();\n }\n break;\n }\n }\n }\n\n canvas.addEventListener('keydown', onKeyDown);\n\n // Return cleanup function\n return () => {\n canvas.removeEventListener('keydown', onKeyDown);\n };\n}\n","/**\n * Graph search manager.\n *\n * Provides case-insensitive substring matching against node labels/ids.\n * Returns a Set of matching node ids that the renderer uses to dim\n * non-matching nodes and edges.\n */\n\nexport class GraphSearchManager {\n private matchedIds: Set<string> | null = null;\n\n /**\n * Search for nodes matching the query string.\n * Returns a Set of matching node ids, or an empty set if nothing matches.\n */\n search(query: string, nodes: Array<{ id: string; label?: string }>): Set<string> {\n const q = query.toLowerCase().trim();\n\n if (q === '') {\n this.matchedIds = null;\n return new Set();\n }\n\n const matches = new Set<string>();\n for (const node of nodes) {\n const label = (node.label ?? '').toLowerCase();\n const id = node.id.toLowerCase();\n if (label.includes(q) || id.includes(q)) {\n matches.add(node.id);\n }\n }\n\n this.matchedIds = matches;\n return matches;\n }\n\n /**\n * Clear the current search.\n * Returns null to indicate no active search.\n */\n clearSearch(): Set<string> | null {\n this.matchedIds = null;\n return null;\n }\n\n /** Get the current set of matched ids, or null if no search is active. */\n getMatches(): Set<string> | null {\n return this.matchedIds;\n }\n}\n","/**\n * SimulationManager: spawns a Web Worker for the force simulation,\n * or falls back to synchronous d3-force on the main thread.\n *\n * Synchronous fallback is used when:\n * - Web Workers are unavailable (SSR, test environments)\n * - Node count is below 200 (worker overhead not worth it)\n *\n * The sync path caps at 300 ticks to prevent blocking the main thread.\n */\n\nimport {\n forceCenter,\n forceCollide,\n forceLink,\n forceManyBody,\n forceSimulation,\n forceX,\n forceY,\n type Simulation,\n type SimulationNodeDatum,\n} from 'd3-force';\n\nimport type { SimEdge, SimNode, WorkerOutMessage, WorkerSimulationConfig } from './worker-protocol';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SYNC_THRESHOLD = 200;\nconst SYNC_MAX_TICKS = 300;\n\n// ---------------------------------------------------------------------------\n// Internal node shape for sync simulation\n// ---------------------------------------------------------------------------\n\ninterface SyncNode extends SimulationNodeDatum {\n id: string;\n radius: number;\n community?: string;\n fx?: number | null;\n fy?: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Cluster force (duplicated in simulation-worker.ts for the Web Worker path.\n// Worker can't import from workspace packages, so both copies must stay in sync.)\n// ---------------------------------------------------------------------------\n\nfunction forceCluster(nodes: SyncNode[], strength: number) {\n return (alpha: number) => {\n const cx = new Map<string, number>();\n const cy = new Map<string, number>();\n const count = new Map<string, number>();\n\n for (const node of nodes) {\n if (!node.community) continue;\n const c = node.community;\n cx.set(c, (cx.get(c) ?? 0) + (node.x ?? 0));\n cy.set(c, (cy.get(c) ?? 0) + (node.y ?? 0));\n count.set(c, (count.get(c) ?? 0) + 1);\n }\n\n for (const [c, n] of count) {\n cx.set(c, cx.get(c)! / n);\n cy.set(c, cy.get(c)! / n);\n }\n\n const k = strength * alpha;\n for (const node of nodes) {\n if (!node.community) continue;\n const targetX = cx.get(node.community)!;\n const targetY = cy.get(node.community)!;\n node.vx = (node.vx ?? 0) + (targetX - (node.x ?? 0)) * k;\n node.vy = (node.vy ?? 0) + (targetY - (node.y ?? 0)) * k;\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// SimulationManager\n// ---------------------------------------------------------------------------\n\ntype TickCallback = (positions: Array<{ id: string; x: number; y: number }>, alpha: number) => void;\n\ntype SettledCallback = () => void;\n\nexport class SimulationManager {\n private worker: Worker | null = null;\n private syncSim: Simulation<SyncNode, undefined> | null = null;\n private syncNodes: SyncNode[] = [];\n private syncNodeMap: Map<string, SyncNode> = new Map();\n private tickCb: TickCallback | null = null;\n private settledCb: SettledCallback | null = null;\n private destroyed = false;\n\n // Stored for worker->sync fallback\n private initNodes: SimNode[] = [];\n private initEdges: SimEdge[] = [];\n private initConfig: WorkerSimulationConfig | null = null;\n\n private constructor() {}\n\n /**\n * Create a SimulationManager. Uses Web Worker for large graphs,\n * synchronous fallback for small graphs or when Worker unavailable.\n */\n static create(\n nodes: SimNode[],\n edges: SimEdge[],\n config: WorkerSimulationConfig,\n ): SimulationManager {\n const mgr = new SimulationManager();\n\n const useWorker = typeof Worker !== 'undefined' && nodes.length >= SYNC_THRESHOLD;\n\n if (useWorker) {\n mgr.initWorker(nodes, edges, config);\n } else {\n mgr.initSync(nodes, edges, config);\n }\n\n return mgr;\n }\n\n /** Register a callback for position updates. */\n onTick(cb: TickCallback): void {\n this.tickCb = cb;\n }\n\n /** Register a callback for when the simulation has settled. */\n onSettled(cb: SettledCallback): void {\n this.settledCb = cb;\n }\n\n /** Reheat the simulation. */\n reheat(alpha?: number): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'reheat', alpha });\n } else if (this.syncSim) {\n this.syncSim.alpha(alpha ?? 0.3).restart();\n this.runSyncTicks();\n }\n }\n\n /** Pin a node to fixed x/y coordinates. */\n pinNode(id: string, x: number, y: number): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'pin', nodeId: id, x, y });\n } else {\n const node = this.syncNodeMap.get(id);\n if (node) {\n node.fx = x;\n node.fy = y;\n }\n }\n }\n\n /** Unpin a node (free it from fixed position). */\n unpinNode(id: string): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'unpin', nodeId: id });\n } else {\n const node = this.syncNodeMap.get(id);\n if (node) {\n node.fx = null;\n node.fy = null;\n }\n }\n }\n\n /** Drag a node (pins it and reheats slightly). */\n dragNode(id: string, x: number, y: number): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'drag', nodeId: id, x, y });\n } else {\n const node = this.syncNodeMap.get(id);\n if (node) {\n node.fx = x;\n node.fy = y;\n }\n if (this.syncSim && this.syncSim.alpha() < 0.1) {\n this.syncSim.alpha(0.1).restart();\n this.runSyncTicks();\n }\n }\n }\n\n /** Tear down the simulation and release resources. */\n destroy(): void {\n this.destroyed = true;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'stop' });\n this.worker.terminate();\n this.worker = null;\n }\n\n if (this.syncSim) {\n this.syncSim.stop();\n this.syncSim = null;\n }\n\n this.tickCb = null;\n this.settledCb = null;\n }\n\n // -------------------------------------------------------------------------\n // Worker path\n // -------------------------------------------------------------------------\n\n private initWorker(nodes: SimNode[], edges: SimEdge[], config: WorkerSimulationConfig): void {\n // Store for fallback if worker fails to load\n this.initNodes = nodes;\n this.initEdges = edges;\n this.initConfig = config;\n\n try {\n const workerUrl = new URL('./simulation-worker.ts', import.meta.url);\n this.worker = new Worker(workerUrl, { type: 'module' });\n } catch {\n // Worker construction failed (e.g. SSR or restrictive CSP)\n console.warn('[SimulationManager] Worker creation failed, using sync fallback');\n this.initSync(nodes, edges, config);\n return;\n }\n\n this.worker.onmessage = (event: MessageEvent<WorkerOutMessage>) => {\n if (this.destroyed) return;\n const msg = event.data;\n\n switch (msg.type) {\n case 'positions':\n this.tickCb?.(msg.nodes, msg.alpha);\n break;\n case 'settled':\n this.settledCb?.();\n break;\n case 'error':\n console.error('[SimulationManager] Worker error:', msg.message);\n break;\n }\n };\n\n this.worker.onerror = () => {\n // Worker failed to load (e.g. MIME type error in dev, missing file).\n // Terminate and fall back to synchronous simulation.\n if (this.destroyed) return;\n console.warn('[SimulationManager] Worker failed to load, falling back to sync');\n this.worker?.terminate();\n this.worker = null;\n this.initSync(this.initNodes, this.initEdges, this.initConfig!);\n };\n\n this.worker.postMessage({ type: 'init', nodes, edges, config });\n }\n\n // -------------------------------------------------------------------------\n // Synchronous fallback\n // -------------------------------------------------------------------------\n\n private initSync(nodes: SimNode[], edges: SimEdge[], config: WorkerSimulationConfig): void {\n this.syncNodes = nodes.map((n) => ({\n id: n.id,\n x: n.x,\n y: n.y,\n radius: n.radius,\n community: n.community,\n }));\n\n this.syncNodeMap = new Map(this.syncNodes.map((n) => [n.id, n]));\n\n const linkForce = forceLink(edges.map((e) => ({ ...e })))\n .id((d) => (d as SyncNode).id)\n .distance(config.linkDistance);\n if (config.linkStrength != null) {\n linkForce.strength(config.linkStrength);\n }\n\n const padding = config.collisionPadding ?? 2;\n\n this.syncSim = forceSimulation<SyncNode>(this.syncNodes)\n .force('link', linkForce)\n .force('charge', forceManyBody().strength(config.chargeStrength))\n .force(\n 'collide',\n forceCollide<SyncNode>().radius((d) => d.radius + padding),\n )\n // Weak gravity keeps disconnected nodes from drifting far from center\n .force('gravityX', forceX<SyncNode>(0).strength(0.05))\n .force('gravityY', forceY<SyncNode>(0).strength(0.05))\n .alphaDecay(config.alphaDecay)\n .velocityDecay(config.velocityDecay)\n .stop(); // Don't auto-run; we tick manually\n\n // Center force (default true)\n if (config.centerForce !== false) {\n this.syncSim.force('center', forceCenter(0, 0));\n }\n\n // Add clustering force if configured\n if (config.clustering) {\n const clusterFn = forceCluster(this.syncNodes, config.clustering.strength);\n // d3 calls force functions with (alpha) on each tick\n this.syncSim.force('cluster', clusterFn as unknown as ReturnType<typeof forceCenter>);\n }\n\n // Defer initial delivery: callbacks aren't wired yet at create() time\n this.runSyncTicks(true);\n }\n\n /**\n * Run ticks synchronously and fire callbacks.\n * @param deferred - When true, deliver results via microtask. Used for the\n * initial run where callbacks haven't been registered yet. Subsequent\n * calls (reheat, drag) fire synchronously since callbacks are already set.\n */\n private runSyncTicks(deferred = false): void {\n if (!this.syncSim || this.destroyed) return;\n\n const sim = this.syncSim;\n for (let i = 0; i < SYNC_MAX_TICKS; i++) {\n sim.tick();\n if (sim.alpha() < 0.001) break;\n }\n\n const positions = this.syncNodes.map((n) => ({\n id: n.id,\n x: n.x ?? 0,\n y: n.y ?? 0,\n }));\n const alpha = sim.alpha();\n const settled = alpha < 0.001;\n\n const deliver = () => {\n if (this.destroyed) return;\n this.tickCb?.(positions, alpha);\n if (settled) {\n this.settledCb?.();\n }\n };\n\n if (deferred) {\n // Initial run: callbacks not registered yet, defer to microtask\n queueMicrotask(deliver);\n } else {\n deliver();\n }\n }\n}\n","/**\n * Spatial index for fast node hit-testing.\n *\n * Wraps d3-quadtree to provide findNearest (accounts for node radius)\n * and findInRect queries. Tracks a generation counter to avoid unnecessary\n * rebuilds when positions haven't changed.\n */\n\nimport { type Quadtree, quadtree } from 'd3-quadtree';\nimport type { PositionedNode } from './types';\n\nexport class SpatialIndex {\n private tree: Quadtree<PositionedNode> | null = null;\n private nodes: PositionedNode[] = [];\n private maxRadius = 0;\n private generation = 0;\n\n /** Rebuild the quadtree from the current set of positioned nodes. */\n rebuild(nodes: PositionedNode[]): void {\n this.nodes = nodes;\n this.maxRadius = 0;\n for (const n of nodes) {\n if (n.radius > this.maxRadius) this.maxRadius = n.radius;\n }\n\n this.tree = quadtree<PositionedNode>()\n .x((d) => d.x)\n .y((d) => d.y)\n .addAll(nodes);\n this.generation++;\n }\n\n /** Current generation counter. Increments on each rebuild. */\n getGeneration(): number {\n return this.generation;\n }\n\n /**\n * Find the nearest node to (x, y) within maxDistance.\n * Accounts for node radius: a hit occurs if the point is inside\n * the node circle (distance to center < node.radius), or if the\n * edge-to-edge distance is within maxDistance.\n */\n findNearest(x: number, y: number, maxDistance: number = Infinity): PositionedNode | null {\n if (!this.tree || this.nodes.length === 0) return null;\n\n // The effective search radius for the quadtree needs to include\n // the largest node radius, because we might be \"inside\" a large node\n // whose center is far from our search point.\n const searchRadius = maxDistance + this.maxRadius;\n\n let best: PositionedNode | null = null;\n let bestEffectiveDist = maxDistance + this.maxRadius + 1;\n\n this.tree.visit((node, x0, y0, x1, y1) => {\n // Closest point in this quad to our target\n const closestX = Math.max(x0, Math.min(x, x1));\n const closestY = Math.max(y0, Math.min(y, y1));\n const quadDist = Math.hypot(closestX - x, closestY - y);\n\n // Prune: if the closest edge of this quad is beyond searchRadius, skip\n if (quadDist > searchRadius) return true;\n\n // Check leaf data\n if (!node.length) {\n let current = node;\n do {\n const d = current.data;\n if (d) {\n const dist = Math.hypot(d.x - x, d.y - y);\n // Effective distance: subtract the node's radius.\n // If we're inside the circle, effective distance is 0.\n const effectiveDist = Math.max(0, dist - d.radius);\n if (effectiveDist <= maxDistance && effectiveDist < bestEffectiveDist) {\n bestEffectiveDist = effectiveDist;\n best = d;\n }\n }\n } while ((current = current.next!));\n }\n\n return false;\n });\n\n return best;\n }\n\n /** Find all nodes whose centers fall within the given rectangle. */\n findInRect(x1: number, y1: number, x2: number, y2: number): PositionedNode[] {\n if (!this.tree) return [];\n\n const minX = Math.min(x1, x2);\n const minY = Math.min(y1, y2);\n const maxX = Math.max(x1, x2);\n const maxY = Math.max(y1, y2);\n\n const results: PositionedNode[] = [];\n\n this.tree.visit((node, qx0, qy0, qx1, qy1) => {\n // If quad doesn't overlap the search rect, skip\n if (qx0 > maxX || qx1 < minX || qy0 > maxY || qy1 < minY) {\n return true;\n }\n\n // Check leaf nodes\n if (!node.length) {\n let current = node;\n do {\n const d = current.data;\n if (d && d.x >= minX && d.x <= maxX && d.y >= minY && d.y <= maxY) {\n results.push(d);\n }\n } while ((current = current.next!));\n }\n\n return false;\n });\n\n return results;\n }\n}\n","/**\n * Resize observer: thin wrapper around ResizeObserver with debounce.\n *\n * Watches a container element for size changes and calls back with\n * the new width and height. Debounced at ~60fps (16ms) to avoid\n * excessive re-renders during drag resizes.\n */\n\nconst DEBOUNCE_MS = 16;\n\n/**\n * Observe a container element for size changes.\n *\n * @param container - The element to watch.\n * @param callback - Called with (width, height) when size changes.\n * @returns A cleanup function that disconnects the observer.\n */\nexport function observeResize(\n container: HTMLElement,\n callback: (width: number, height: number) => void,\n): () => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new ResizeObserver((entries) => {\n // Debounce to ~60fps\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n callback(width, height);\n }\n timeoutId = null;\n }, DEBOUNCE_MS);\n });\n\n observer.observe(container);\n\n return () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n observer.disconnect();\n };\n}\n","/**\n * Tooltip manager: creates and positions a floating tooltip element.\n *\n * Shows tooltip content near the mouse/touch position with viewport\n * edge avoidance. Touch support via tap-to-show, tap-outside-to-hide.\n */\n\nimport type { TooltipContent } from '@opendata-ai/openchart-core';\n\nexport interface TooltipManager {\n /** Show the tooltip with content at a given position. */\n show(content: TooltipContent, x: number, y: number): void;\n /** Hide the tooltip. */\n hide(): void;\n /** Remove the tooltip element and clean up event listeners. */\n destroy(): void;\n}\n\nconst TOOLTIP_OFFSET = 12;\n\n/**\n * Create a tooltip manager attached to a container element.\n *\n * The manager creates a floating div positioned relative to the container.\n * Content is rendered as a title line with optional color indicator,\n * followed by a compact list of field-value pairs.\n *\n * @param container - The parent element for the tooltip.\n * @returns TooltipManager with show/hide/destroy methods.\n */\nexport function createTooltipManager(container: HTMLElement): TooltipManager {\n const tooltip = document.createElement('div');\n tooltip.className = 'viz-tooltip';\n tooltip.setAttribute('role', 'tooltip');\n\n container.style.position = container.style.position || 'relative';\n container.appendChild(tooltip);\n\n // Hide on tap-outside for touch devices\n const handleDocumentTouch = (e: Event): void => {\n if (!container.contains(e.target as Node)) {\n hide();\n }\n };\n document.addEventListener('touchstart', handleDocumentTouch);\n\n function show(content: TooltipContent, x: number, y: number): void {\n let html = '';\n\n // Title row: optional color dot + title text\n if (content.title) {\n const titleColor = content.fields.find((f) => f.color)?.color;\n html += '<div class=\"viz-tooltip-header\">';\n if (titleColor) {\n html += `<span class=\"viz-tooltip-dot\" style=\"background:${esc(titleColor)}\"></span>`;\n }\n html += `<span class=\"viz-tooltip-title\">${esc(content.title)}</span>`;\n html += '</div>';\n }\n\n // Field rows\n if (content.fields.length > 0) {\n html += '<div class=\"viz-tooltip-body\">';\n for (const field of content.fields) {\n html += '<div class=\"viz-tooltip-row\">';\n html += `<span class=\"viz-tooltip-label\">${esc(field.label)}</span>`;\n html += `<span class=\"viz-tooltip-value\">${esc(field.value)}</span>`;\n html += '</div>';\n }\n html += '</div>';\n }\n\n tooltip.innerHTML = html;\n tooltip.style.display = 'block';\n\n // Position with viewport edge avoidance\n const containerRect = container.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n\n let left = x + TOOLTIP_OFFSET;\n let top = y + TOOLTIP_OFFSET;\n\n // Flip horizontal if overflowing right\n if (left + tooltipRect.width > containerRect.width) {\n left = x - tooltipRect.width - TOOLTIP_OFFSET;\n }\n // Flip vertical if overflowing bottom\n if (top + tooltipRect.height > containerRect.height) {\n top = y - tooltipRect.height - TOOLTIP_OFFSET;\n }\n\n // Clamp to container bounds\n left = Math.max(0, Math.min(left, containerRect.width - tooltipRect.width));\n top = Math.max(0, Math.min(top, containerRect.height - tooltipRect.height));\n\n tooltip.style.left = `${left}px`;\n tooltip.style.top = `${top}px`;\n }\n\n function hide(): void {\n tooltip.style.display = 'none';\n }\n\n function destroy(): void {\n document.removeEventListener('touchstart', handleDocumentTouch);\n if (tooltip.parentNode) {\n tooltip.parentNode.removeChild(tooltip);\n }\n }\n\n return { show, hide, destroy };\n}\n\nfunction esc(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","/**\n * Mount API: the main entry point for vanilla JS usage.\n *\n * createChart() takes a container, spec, and options, compiles the chart,\n * renders it as SVG, sets up responsive resizing, tooltip interaction\n * (mouse/touch/keyboard), keyboard navigation between data points,\n * and returns a ChartInstance with update/resize/export/destroy methods.\n */\n\nimport type {\n Annotation,\n AnnotationOffset,\n ChartEventHandlers,\n ChartLayout,\n ChartSpec,\n ChromeKey,\n CompileOptions,\n DarkMode,\n ElementEdit,\n GraphSpec,\n MeasureTextFn,\n RangeAnnotation,\n RefLineAnnotation,\n TextAnnotation,\n ThemeConfig,\n TooltipContent,\n} from '@opendata-ai/openchart-core';\nimport { compileChart } from '@opendata-ai/openchart-engine';\nimport { exportCSV, exportJPG, exportPNG, exportSVG, type JPGExportOptions } from './export';\nimport { observeResize } from './resize-observer';\nimport { renderChartSVG } from './svg-renderer';\nimport { createTooltipManager, type TooltipManager } from './tooltip';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MountOptions extends ChartEventHandlers {\n /** Theme overrides. */\n theme?: ThemeConfig;\n /** Dark mode setting: \"auto\" (system pref), \"force\", or \"off\". */\n darkMode?: DarkMode;\n /** Callback when a data point is clicked. @deprecated Use onMarkClick instead. */\n onDataPointClick?: (data: Record<string, unknown>) => void;\n /** Enable responsive resizing. Defaults to true. */\n responsive?: boolean;\n}\n\nexport interface ExportOptions extends JPGExportOptions {\n // Extensible for future formats (extends JPGExportOptions which extends PNGExportOptions)\n}\n\nexport interface ChartInstance {\n /** Re-compile and re-render with a new spec. */\n update(spec: ChartSpec | GraphSpec): void;\n /** Re-compile at current container dimensions. */\n resize(): void;\n /** Export the chart. */\n export(format: 'svg'): string;\n export(format: 'png', options?: ExportOptions): Promise<Blob>;\n export(format: 'jpg', options?: ExportOptions): Promise<Blob>;\n export(format: 'csv'): string;\n export(format: 'svg' | 'png' | 'jpg' | 'csv', options?: ExportOptions): string | Promise<Blob>;\n /** Remove all DOM elements and disconnect observers. */\n destroy(): void;\n /** The current compiled layout (for hooks / debugging). */\n readonly layout: ChartLayout;\n}\n\n// ---------------------------------------------------------------------------\n// Dark mode resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveDarkMode(mode?: DarkMode): boolean {\n if (mode === 'force') return true;\n if (mode === 'off' || mode === undefined) return false;\n // \"auto\": check system preference\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// measureText via hidden canvas\n// ---------------------------------------------------------------------------\n\nfunction createMeasureText(): MeasureTextFn {\n let canvas: HTMLCanvasElement | null = null;\n let ctx: CanvasRenderingContext2D | null = null;\n\n return (\n text: string,\n fontSize: number,\n fontWeight?: number,\n ): { width: number; height: number } => {\n if (!canvas) {\n canvas = document.createElement('canvas');\n ctx = canvas.getContext('2d');\n }\n if (!ctx) {\n // Fallback: heuristic estimation\n return { width: text.length * fontSize * 0.6, height: fontSize * 1.2 };\n }\n\n const weight = fontWeight ?? 400;\n ctx.font = `${weight} ${fontSize}px Inter, sans-serif`;\n const metrics = ctx.measureText(text);\n return {\n width: metrics.width,\n height: fontSize * 1.2,\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tooltip event wiring\n// ---------------------------------------------------------------------------\n\n/**\n * Wire tooltip events on mark elements inside an SVG.\n * Returns a cleanup function to remove all listeners.\n */\nfunction wireTooltipEvents(\n svg: SVGElement,\n tooltipDescriptors: Map<string, TooltipContent>,\n tooltipManager: TooltipManager,\n): () => void {\n const markElements = svg.querySelectorAll('[data-mark-id]');\n const cleanups: Array<() => void> = [];\n\n for (const el of markElements) {\n const markId = el.getAttribute('data-mark-id');\n if (!markId) continue;\n\n const content = tooltipDescriptors.get(markId);\n if (!content) continue;\n\n // Mouse enter -> show tooltip\n const handleMouseEnter = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n const x = mouseEvent.clientX - svgRect.left;\n const y = mouseEvent.clientY - svgRect.top;\n tooltipManager.show(content, x, y);\n };\n\n // Mouse move -> reposition tooltip\n const handleMouseMove = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n const x = mouseEvent.clientX - svgRect.left;\n const y = mouseEvent.clientY - svgRect.top;\n tooltipManager.show(content, x, y);\n };\n\n // Mouse leave -> hide tooltip\n const handleMouseLeave = () => {\n tooltipManager.hide();\n };\n\n // Touch: tap to show\n const handleTouchStart = (e: Event) => {\n const touchEvent = e as TouchEvent;\n if (touchEvent.touches.length > 0) {\n const touch = touchEvent.touches[0];\n const svgRect = svg.getBoundingClientRect();\n const x = touch.clientX - svgRect.left;\n const y = touch.clientY - svgRect.top;\n tooltipManager.show(content, x, y);\n }\n };\n\n el.addEventListener('mouseenter', handleMouseEnter);\n el.addEventListener('mousemove', handleMouseMove);\n el.addEventListener('mouseleave', handleMouseLeave);\n el.addEventListener('touchstart', handleTouchStart);\n\n cleanups.push(() => {\n el.removeEventListener('mouseenter', handleMouseEnter);\n el.removeEventListener('mousemove', handleMouseMove);\n el.removeEventListener('mouseleave', handleMouseLeave);\n el.removeEventListener('touchstart', handleTouchStart);\n });\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Chart event wiring (click, hover, leave on marks; legend toggle; annotation click)\n// ---------------------------------------------------------------------------\n\n/**\n * Build a map from data-mark-id to { datum, series } so event handlers\n * can look up the data row associated with a clicked/hovered mark element.\n */\nfunction buildMarkDataMap(\n layout: ChartLayout,\n): Map<string, { datum: Record<string, unknown>; series?: string }> {\n const map = new Map<string, { datum: Record<string, unknown>; series?: string }>();\n\n for (let i = 0; i < layout.marks.length; i++) {\n const mark = layout.marks[i];\n switch (mark.type) {\n case 'line':\n map.set(`line-${mark.seriesKey ?? i}`, {\n // For line marks, data is an array. Use the first row as representative.\n datum: mark.data[0] ?? {},\n series: mark.seriesKey,\n });\n break;\n case 'area':\n map.set(`area-${mark.seriesKey ?? i}`, {\n datum: mark.data[0] ?? {},\n series: mark.seriesKey,\n });\n break;\n case 'rect':\n map.set(`rect-${i}`, { datum: mark.data });\n break;\n case 'arc':\n map.set(`arc-${i}`, { datum: mark.data });\n break;\n case 'point':\n map.set(`point-${i}`, { datum: mark.data });\n break;\n }\n }\n\n return map;\n}\n\n/**\n * Wire chart event handlers (onMarkClick, onMarkHover, onMarkLeave) to mark\n * elements, onLegendToggle to legend entries, and onAnnotationClick to annotation\n * elements inside an SVG.\n *\n * Returns a cleanup function to remove all listeners.\n */\nfunction wireChartEvents(\n svg: SVGElement,\n layout: ChartLayout,\n specAnnotations: import('@opendata-ai/openchart-core').Annotation[],\n handlers: ChartEventHandlers,\n): () => void {\n const cleanups: Array<() => void> = [];\n const markDataMap = buildMarkDataMap(layout);\n\n // Wire mark click/hover/leave events\n if (handlers.onMarkClick || handlers.onMarkHover || handlers.onMarkLeave) {\n const markElements = svg.querySelectorAll('[data-mark-id]');\n\n for (const el of markElements) {\n const markId = el.getAttribute('data-mark-id');\n if (!markId) continue;\n\n const markInfo = markDataMap.get(markId);\n if (!markInfo) continue;\n\n const series = markInfo.series ?? el.getAttribute('data-series') ?? undefined;\n\n if (handlers.onMarkClick) {\n const handleClick = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n handlers.onMarkClick!({\n datum: markInfo.datum,\n series,\n position: {\n x: mouseEvent.clientX - svgRect.left,\n y: mouseEvent.clientY - svgRect.top,\n },\n event: mouseEvent,\n });\n };\n el.addEventListener('click', handleClick);\n cleanups.push(() => el.removeEventListener('click', handleClick));\n }\n\n if (handlers.onMarkHover) {\n const handleEnter = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n handlers.onMarkHover!({\n datum: markInfo.datum,\n series,\n position: {\n x: mouseEvent.clientX - svgRect.left,\n y: mouseEvent.clientY - svgRect.top,\n },\n event: mouseEvent,\n });\n };\n el.addEventListener('mouseenter', handleEnter);\n cleanups.push(() => el.removeEventListener('mouseenter', handleEnter));\n }\n\n if (handlers.onMarkLeave) {\n const handleLeave = () => {\n handlers.onMarkLeave!();\n };\n el.addEventListener('mouseleave', handleLeave);\n cleanups.push(() => el.removeEventListener('mouseleave', handleLeave));\n }\n }\n }\n\n // Wire annotation click events\n if (handlers.onAnnotationClick) {\n const annotationElements = svg.querySelectorAll('.viz-annotation');\n\n for (let i = 0; i < annotationElements.length; i++) {\n const el = annotationElements[i];\n const specAnnotation = specAnnotations[i];\n if (!specAnnotation) continue;\n\n const handleClick = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n handlers.onAnnotationClick!(specAnnotation, mouseEvent);\n };\n\n el.addEventListener('click', handleClick);\n cleanups.push(() => el.removeEventListener('click', handleClick));\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared drag handler utility\n// ---------------------------------------------------------------------------\n\ninterface DragConfig {\n element: SVGElement;\n svg: SVGSVGElement;\n onMove: (dx: number, dy: number) => void;\n onEnd: (dx: number, dy: number, moved: boolean) => void;\n setDragging: (dragging: boolean) => void;\n threshold?: number; // default: 3\n}\n\n/**\n * Reusable drag handler for SVG elements.\n * Handles mouse and touch events, viewBox scaling, threshold detection,\n * click suppression after drag, and cursor state.\n *\n * Returns a cleanup function that removes all listeners.\n */\nfunction createDragHandler(config: DragConfig): () => void {\n const { element, svg, onMove, onEnd, setDragging, threshold = 3 } = config;\n const cleanups: Array<() => void> = [];\n\n // Track active document listeners so cleanup can remove them mid-drag\n let activeDocMouseMove: ((e: MouseEvent) => void) | null = null;\n let activeDocMouseUp: ((e: MouseEvent) => void) | null = null;\n let activeDocTouchMove: ((e: TouchEvent) => void) | null = null;\n let activeDocTouchEnd: ((e: TouchEvent) => void) | null = null;\n let activeDocTouchCancel: ((e: TouchEvent) => void) | null = null;\n\n function getScale(): { scaleX: number; scaleY: number } {\n const viewBox = svg.viewBox?.baseVal;\n const svgRect = svg.getBoundingClientRect();\n return {\n scaleX: viewBox?.width && svgRect.width ? viewBox.width / svgRect.width : 1,\n scaleY: viewBox?.height && svgRect.height ? viewBox.height / svgRect.height : 1,\n };\n }\n\n function startDrag(startX: number, startY: number): void {\n setDragging(true);\n const { scaleX, scaleY } = getScale();\n\n element.style.cursor = 'grabbing';\n // Prevent text selection during drag\n svg.style.userSelect = 'none';\n\n const handleMove = (clientX: number, clientY: number) => {\n const dx = (clientX - startX) * scaleX;\n const dy = (clientY - startY) * scaleY;\n onMove(dx, dy);\n };\n\n const cleanupDocListeners = () => {\n if (activeDocMouseMove) {\n document.removeEventListener('mousemove', activeDocMouseMove);\n activeDocMouseMove = null;\n }\n if (activeDocMouseUp) {\n document.removeEventListener('mouseup', activeDocMouseUp);\n activeDocMouseUp = null;\n }\n if (activeDocTouchMove) {\n document.removeEventListener('touchmove', activeDocTouchMove);\n activeDocTouchMove = null;\n }\n if (activeDocTouchEnd) {\n document.removeEventListener('touchend', activeDocTouchEnd);\n activeDocTouchEnd = null;\n }\n if (activeDocTouchCancel) {\n document.removeEventListener('touchcancel', activeDocTouchCancel);\n activeDocTouchCancel = null;\n }\n };\n\n const handleEnd = (clientX: number, clientY: number) => {\n const dx = (clientX - startX) * scaleX;\n const dy = (clientY - startY) * scaleY;\n const moved = Math.abs(dx) > threshold || Math.abs(dy) > threshold;\n\n onEnd(dx, dy, moved);\n\n // Suppress click if drag actually moved\n if (moved) {\n element.addEventListener(\n 'click',\n (clickE) => {\n clickE.stopPropagation();\n },\n { capture: true, once: true },\n );\n }\n\n element.style.cursor = 'grab';\n svg.style.userSelect = '';\n\n cleanupDocListeners();\n setDragging(false);\n };\n\n // Mouse listeners\n const onMouseMove = (moveEvent: MouseEvent) => {\n handleMove(moveEvent.clientX, moveEvent.clientY);\n };\n const onMouseUp = (upEvent: MouseEvent) => {\n handleEnd(upEvent.clientX, upEvent.clientY);\n };\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n activeDocMouseMove = onMouseMove;\n activeDocMouseUp = onMouseUp;\n\n // Touch listeners\n const onTouchMove = (moveEvent: TouchEvent) => {\n if (moveEvent.touches.length > 0) {\n moveEvent.preventDefault();\n handleMove(moveEvent.touches[0].clientX, moveEvent.touches[0].clientY);\n }\n };\n const onTouchEnd = (endEvent: TouchEvent) => {\n const touch = endEvent.changedTouches[0];\n if (touch) {\n handleEnd(touch.clientX, touch.clientY);\n } else {\n handleEnd(startX, startY);\n }\n };\n document.addEventListener('touchmove', onTouchMove, { passive: false });\n document.addEventListener('touchend', onTouchEnd);\n document.addEventListener('touchcancel', onTouchEnd);\n activeDocTouchMove = onTouchMove;\n activeDocTouchEnd = onTouchEnd;\n activeDocTouchCancel = onTouchEnd;\n }\n\n // Mouse down handler\n const handleMouseDown = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n mouseEvent.preventDefault();\n startDrag(mouseEvent.clientX, mouseEvent.clientY);\n };\n\n // Touch start handler\n const handleTouchStart = (e: Event) => {\n const touchEvent = e as TouchEvent;\n if (touchEvent.touches.length === 1) {\n touchEvent.preventDefault();\n startDrag(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);\n }\n };\n\n element.addEventListener('mousedown', handleMouseDown);\n element.addEventListener('touchstart', handleTouchStart, { passive: false });\n cleanups.push(() => {\n element.removeEventListener('mousedown', handleMouseDown);\n element.removeEventListener('touchstart', handleTouchStart);\n });\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n // Clean up any active document listeners (mid-drag unmount)\n if (activeDocMouseMove) {\n document.removeEventListener('mousemove', activeDocMouseMove);\n activeDocMouseMove = null;\n }\n if (activeDocMouseUp) {\n document.removeEventListener('mouseup', activeDocMouseUp);\n activeDocMouseUp = null;\n }\n if (activeDocTouchMove) {\n document.removeEventListener('touchmove', activeDocTouchMove);\n activeDocTouchMove = null;\n }\n if (activeDocTouchEnd) {\n document.removeEventListener('touchend', activeDocTouchEnd);\n activeDocTouchEnd = null;\n }\n if (activeDocTouchCancel) {\n document.removeEventListener('touchcancel', activeDocTouchCancel);\n activeDocTouchCancel = null;\n }\n // Restore user-select in case of mid-drag cleanup\n svg.style.userSelect = '';\n };\n}\n\n// ---------------------------------------------------------------------------\n// Annotation drag editing\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag-to-reposition on text annotation labels.\n * Only activates for text annotations (not range or refline).\n * During drag, applies a CSS transform for real-time visual feedback and\n * counter-adjusts straight connector endpoints so the data-point end stays fixed.\n * On mouseup, fires the callback with the updated offset values.\n *\n * Returns a cleanup function to remove all listeners.\n */\nfunction wireAnnotationDrag(\n svg: SVGElement,\n specAnnotations: Annotation[],\n onAnnotationEdit:\n | ((annotation: TextAnnotation, updatedOffset: AnnotationOffset) => void)\n | undefined,\n onEdit: ((edit: ElementEdit) => void) | undefined,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const annotationElements = svg.querySelectorAll('.viz-annotation-text');\n const cleanups: Array<() => void> = [];\n\n for (const el of annotationElements) {\n const indexStr = el.getAttribute('data-annotation-index');\n if (indexStr === null) continue;\n\n const index = Number(indexStr);\n const specAnnotation = specAnnotations[index];\n if (!specAnnotation || specAnnotation.type !== 'text') continue;\n\n const textAnnotation = specAnnotation as TextAnnotation;\n const annotationG = el as SVGGElement;\n\n // Visual affordance: show grab cursor\n annotationG.style.cursor = 'grab';\n\n // Stash connector info for real-time updates during drag\n const connectorLine = annotationG.querySelector('line.viz-annotation-connector');\n const origX2 = connectorLine ? Number(connectorLine.getAttribute('x2')) : 0;\n const origY2 = connectorLine ? Number(connectorLine.getAttribute('y2')) : 0;\n\n // For curved connectors, stash path/polygon elements to hide during drag\n const curvedPath = annotationG.querySelector('path.viz-annotation-connector');\n const arrowhead = annotationG.querySelector('polygon.viz-annotation-connector');\n const hasCurvedConnector = curvedPath !== null;\n\n const origDx = textAnnotation.offset?.dx ?? 0;\n const origDy = textAnnotation.offset?.dy ?? 0;\n\n const cleanup = createDragHandler({\n element: annotationG,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n // Move the entire annotation group\n annotationG.setAttribute('transform', `translate(${dx}, ${dy})`);\n\n // For straight connectors, counter-adjust the data-point end\n if (connectorLine && !hasCurvedConnector) {\n connectorLine.setAttribute('x2', String(origX2 - dx));\n connectorLine.setAttribute('y2', String(origY2 - dy));\n }\n\n // Hide curved connector elements during drag\n if (hasCurvedConnector) {\n if (curvedPath) curvedPath.setAttribute('display', 'none');\n if (arrowhead) arrowhead.setAttribute('display', 'none');\n }\n },\n onEnd: (dx, dy, moved) => {\n // Clean up visual state\n annotationG.removeAttribute('transform');\n\n // Restore straight connector to original values\n if (connectorLine && !hasCurvedConnector) {\n connectorLine.setAttribute('x2', String(origX2));\n connectorLine.setAttribute('y2', String(origY2));\n }\n\n // Restore curved connector elements\n if (hasCurvedConnector) {\n if (curvedPath) curvedPath.removeAttribute('display');\n if (arrowhead) arrowhead.removeAttribute('display');\n }\n\n if (moved) {\n const newOffset: AnnotationOffset = {\n dx: origDx + dx,\n dy: origDy + dy,\n };\n // Fire legacy callback\n onAnnotationEdit?.(textAnnotation, newOffset);\n // Fire unified edit callback\n onEdit?.({ type: 'annotation', annotation: textAnnotation, offset: newOffset });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Connector endpoint drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on connector endpoint handles for text annotations.\n * Dynamically creates invisible handle circles at connector endpoints\n * so they only exist when editing is active (not in every chart).\n * During drag, updates the handle position and the connector line endpoints.\n * On end, fires onEdit with the accumulated endpoint offset.\n *\n * Shows handles on hover over the parent annotation group.\n * Returns a cleanup function that removes handles and all listeners.\n */\nfunction wireConnectorEndpointDrag(\n svg: SVGElement,\n specAnnotations: Annotation[],\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const SVG_NS = 'http://www.w3.org/2000/svg';\n const cleanups: Array<() => void> = [];\n const annotationGroups = svg.querySelectorAll('.viz-annotation-text');\n\n for (const el of annotationGroups) {\n const annotationG = el as SVGGElement;\n const indexStr = annotationG.getAttribute('data-annotation-index');\n if (indexStr === null) continue;\n\n const index = Number(indexStr);\n const specAnnotation = specAnnotations[index];\n if (!specAnnotation || specAnnotation.type !== 'text') continue;\n\n const textAnnotation = specAnnotation as TextAnnotation;\n\n // Find connector line or curved connector to determine endpoints\n const connectorLine = annotationG.querySelector('line.viz-annotation-connector');\n const curvedPath = annotationG.querySelector('path.viz-annotation-connector');\n if (!connectorLine && !curvedPath) continue;\n\n // Determine connector endpoint positions from the connector element\n let fromX: number, fromY: number, toX: number, toY: number;\n if (connectorLine) {\n fromX = Number(connectorLine.getAttribute('x1'));\n fromY = Number(connectorLine.getAttribute('y1'));\n toX = Number(connectorLine.getAttribute('x2'));\n toY = Number(connectorLine.getAttribute('y2'));\n } else {\n // For curved connectors, get positions from the path data\n // The path starts at M x y, so parse the first coordinates\n const pathD = curvedPath!.getAttribute('d') ?? '';\n const mMatch = pathD.match(/M\\s*([\\d.e+-]+)\\s+([\\d.e+-]+)/);\n fromX = mMatch ? Number(mMatch[1]) : 0;\n fromY = mMatch ? Number(mMatch[2]) : 0;\n // For curved connectors, the arrow polygon has the target\n const arrowhead = annotationG.querySelector('polygon.viz-annotation-connector');\n const points = arrowhead?.getAttribute('points') ?? '';\n const firstPoint = points.split(' ')[0] ?? '0,0';\n const [px, py] = firstPoint.split(',');\n toX = Number(px);\n toY = Number(py);\n }\n\n // Create handles dynamically\n const endpoints: Array<{ name: 'from' | 'to'; cx: number; cy: number }> = [\n { name: 'from', cx: fromX, cy: fromY },\n { name: 'to', cx: toX, cy: toY },\n ];\n\n const createdHandles: SVGCircleElement[] = [];\n\n for (const ep of endpoints) {\n const handleEl = document.createElementNS(SVG_NS, 'circle') as SVGCircleElement;\n handleEl.setAttribute('class', 'viz-connector-handle');\n handleEl.setAttribute('data-endpoint', ep.name);\n handleEl.setAttribute('cx', String(ep.cx));\n handleEl.setAttribute('cy', String(ep.cy));\n handleEl.setAttribute('r', '4');\n handleEl.setAttribute('opacity', '0');\n handleEl.setAttribute('fill', 'currentColor');\n handleEl.setAttribute('stroke', 'currentColor');\n annotationG.appendChild(handleEl);\n createdHandles.push(handleEl);\n\n const origCx = ep.cx;\n const origCy = ep.cy;\n\n // Prevent parent annotation drag from firing\n const stopProp = (e: Event) => {\n e.stopPropagation();\n };\n handleEl.addEventListener('mousedown', stopProp);\n handleEl.addEventListener('touchstart', stopProp);\n cleanups.push(() => {\n handleEl.removeEventListener('mousedown', stopProp);\n handleEl.removeEventListener('touchstart', stopProp);\n });\n\n const cleanup = createDragHandler({\n element: handleEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n handleEl.setAttribute('cx', String(origCx + dx));\n handleEl.setAttribute('cy', String(origCy + dy));\n\n if (connectorLine) {\n if (ep.name === 'from') {\n connectorLine.setAttribute('x1', String(origCx + dx));\n connectorLine.setAttribute('y1', String(origCy + dy));\n } else {\n connectorLine.setAttribute('x2', String(origCx + dx));\n connectorLine.setAttribute('y2', String(origCy + dy));\n }\n }\n },\n onEnd: (dx, dy, moved) => {\n handleEl.setAttribute('cx', String(origCx));\n handleEl.setAttribute('cy', String(origCy));\n\n if (connectorLine) {\n if (ep.name === 'from') {\n connectorLine.setAttribute('x1', String(origCx));\n connectorLine.setAttribute('y1', String(origCy));\n } else {\n connectorLine.setAttribute('x2', String(origCx));\n connectorLine.setAttribute('y2', String(origCy));\n }\n }\n\n if (moved) {\n const existingOffset = textAnnotation.connectorOffset?.[ep.name];\n const origEndDx = existingOffset?.dx ?? 0;\n const origEndDy = existingOffset?.dy ?? 0;\n onEdit({\n type: 'annotation-connector',\n annotation: textAnnotation,\n endpoint: ep.name,\n offset: { dx: origEndDx + dx, dy: origEndDy + dy },\n });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n // Wire hover to show/hide handles\n const showHandles = () => {\n for (const h of createdHandles) {\n h.setAttribute('opacity', '0.6');\n }\n };\n const hideHandles = () => {\n for (const h of createdHandles) {\n h.setAttribute('opacity', '0');\n }\n };\n\n annotationG.addEventListener('mouseenter', showHandles);\n annotationG.addEventListener('mouseleave', hideHandles);\n cleanups.push(() => {\n annotationG.removeEventListener('mouseenter', showHandles);\n annotationG.removeEventListener('mouseleave', hideHandles);\n // Remove dynamically created handles\n for (const h of createdHandles) {\n h.remove();\n }\n });\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Range/refline annotation label drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on range and refline annotation labels.\n * On drag end, fires onEdit with the label offset.\n * Returns a cleanup function.\n */\nfunction wireAnnotationLabelDrag(\n svg: SVGElement,\n specAnnotations: Annotation[],\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const cleanups: Array<() => void> = [];\n\n // Target range and refline annotation labels\n const selectors = [\n '.viz-annotation-range .viz-annotation-label',\n '.viz-annotation-refline .viz-annotation-label',\n ];\n\n for (const selector of selectors) {\n const labels = svg.querySelectorAll(selector);\n\n for (const label of labels) {\n const annotationG = label.closest('.viz-annotation') as SVGGElement | null;\n if (!annotationG) continue;\n\n const indexStr = annotationG.getAttribute('data-annotation-index');\n if (indexStr === null) continue;\n\n const index = Number(indexStr);\n const specAnnotation = specAnnotations[index];\n if (!specAnnotation) continue;\n\n const labelEl = label as SVGTextElement;\n labelEl.style.cursor = 'grab';\n\n const isRange = specAnnotation.type === 'range';\n const existingLabelOffset = isRange\n ? (specAnnotation as RangeAnnotation).labelOffset\n : (specAnnotation as RefLineAnnotation).labelOffset;\n const origLabelDx = existingLabelOffset?.dx ?? 0;\n const origLabelDy = existingLabelOffset?.dy ?? 0;\n\n const cleanup = createDragHandler({\n element: labelEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n if (isRange) {\n onEdit({\n type: 'range-label',\n annotation: specAnnotation as RangeAnnotation,\n labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy },\n });\n } else {\n onEdit({\n type: 'refline-label',\n annotation: specAnnotation as RefLineAnnotation,\n labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy },\n });\n }\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Chrome text drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on chrome text elements (title, subtitle, source, byline, footer).\n * On drag end, fires onEdit with the chrome key, text, and offset.\n * Returns a cleanup function.\n */\nfunction wireChromeDrag(\n svg: SVGElement,\n spec: ChartSpec | GraphSpec,\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const chromeTexts = svg.querySelectorAll('.viz-chrome text[data-chrome-key]');\n const cleanups: Array<() => void> = [];\n\n // Read existing chrome offsets from the spec\n const chromeConfig = 'chrome' in spec ? spec.chrome : undefined;\n\n for (const el of chromeTexts) {\n const textEl = el as SVGTextElement;\n const key = textEl.getAttribute('data-chrome-key') as ChromeKey;\n if (!key) continue;\n\n // Read existing offset for this chrome element\n const chromeEntry = chromeConfig?.[key];\n const existingOffset =\n typeof chromeEntry === 'object' && chromeEntry !== null ? chromeEntry.offset : undefined;\n const origChromeDx = existingOffset?.dx ?? 0;\n const origChromeDy = existingOffset?.dy ?? 0;\n\n textEl.style.cursor = 'grab';\n\n const cleanup = createDragHandler({\n element: textEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (textEl as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (textEl as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n onEdit({\n type: 'chrome',\n key,\n text: textEl.textContent ?? '',\n offset: { dx: origChromeDx + dx, dy: origChromeDy + dy },\n });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Legend drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on the legend group.\n * Click suppression prevents legend toggle from firing after a drag.\n * On drag end, fires onEdit with the legend offset.\n * Returns a cleanup function.\n */\nfunction wireLegendDrag(\n svg: SVGElement,\n spec: ChartSpec | GraphSpec,\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const legendG = svg.querySelector('.viz-legend') as SVGGElement | null;\n if (!legendG) return () => {};\n\n const cleanups: Array<() => void> = [];\n\n // Read existing legend offset from the spec\n const legendConfig = 'legend' in spec ? spec.legend : undefined;\n const origLegendDx = legendConfig?.offset?.dx ?? 0;\n const origLegendDy = legendConfig?.offset?.dy ?? 0;\n\n // Set grab cursor on the legend background, not on entry elements\n legendG.style.cursor = 'grab';\n\n const cleanup = createDragHandler({\n element: legendG,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (legendG as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (legendG as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n onEdit({ type: 'legend', offset: { dx: origLegendDx + dx, dy: origLegendDy + dy } });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Series label drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on series label elements (.viz-mark-label[data-series]).\n * On drag end, fires onEdit with the series name and offset.\n * Returns a cleanup function.\n */\nfunction wireSeriesLabelDrag(\n svg: SVGElement,\n spec: ChartSpec | GraphSpec,\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const labels = svg.querySelectorAll('.viz-mark-label');\n const cleanups: Array<() => void> = [];\n\n // Read existing label offsets from the spec\n const labelsConfig = 'labels' in spec ? spec.labels : undefined;\n\n for (const label of labels) {\n const labelEl = label as SVGTextElement;\n // Check label itself first, then fall back to the parent mark group's data-series\n const series =\n labelEl.getAttribute('data-series') ??\n labelEl.closest('[data-series]')?.getAttribute('data-series');\n if (!series) continue;\n\n // Read existing offset for this series label\n const existingSeriesOffset = labelsConfig?.offsets?.[series];\n const origSeriesDx = existingSeriesOffset?.dx ?? 0;\n const origSeriesDy = existingSeriesOffset?.dy ?? 0;\n\n labelEl.style.cursor = 'grab';\n\n const cleanup = createDragHandler({\n element: labelEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n onEdit({\n type: 'series-label',\n series,\n offset: { dx: origSeriesDx + dx, dy: origSeriesDy + dy },\n });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Legend interactivity\n// ---------------------------------------------------------------------------\n\n/**\n * Wire click handlers on legend entries to toggle series visibility.\n * Fires onEdit with { type: 'legend-toggle', series, hidden } for each toggle,\n * and optionally calls the legacy onLegendToggle callback.\n * Legend entries for hidden series stay visible but dimmed (opacity 0.3).\n * Returns a cleanup function.\n */\nfunction wireLegendInteraction(\n svg: SVGElement,\n _layout: ChartLayout,\n onLegendToggle?: (series: string, visible: boolean) => void,\n onEdit?: (edit: ElementEdit) => void,\n): () => void {\n const legendEntries = svg.querySelectorAll('[data-legend-index]');\n const cleanups: Array<() => void> = [];\n\n // Track which series are hidden\n const hiddenSeries = new Set<string>();\n\n for (const entry of legendEntries) {\n const handleClick = () => {\n const label = entry.getAttribute('data-legend-label');\n if (!label) return;\n\n if (hiddenSeries.has(label)) {\n hiddenSeries.delete(label);\n entry.setAttribute('opacity', '1');\n entry.setAttribute('aria-label', `${label}: visible`);\n onLegendToggle?.(label, true);\n onEdit?.({ type: 'legend-toggle', series: label, hidden: false });\n } else {\n hiddenSeries.add(label);\n entry.setAttribute('opacity', '0.3');\n entry.setAttribute('aria-label', `${label}: hidden`);\n onLegendToggle?.(label, false);\n onEdit?.({ type: 'legend-toggle', series: label, hidden: true });\n }\n\n // Toggle visibility of marks with matching series.\n // Uses the data-series attribute set by the SVG renderer, which works\n // for all mark types (line, area, rect, arc, point).\n const marks = svg.querySelectorAll('.viz-mark');\n for (const mark of marks) {\n const seriesName = mark.getAttribute('data-series');\n if (!seriesName) continue;\n\n if (hiddenSeries.has(seriesName)) {\n (mark as SVGElement).style.display = 'none';\n } else {\n (mark as SVGElement).style.display = '';\n }\n }\n };\n\n entry.addEventListener('click', handleClick);\n cleanups.push(() => entry.removeEventListener('click', handleClick));\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard navigation\n// ---------------------------------------------------------------------------\n\n/**\n * Wire keyboard navigation on the SVG element.\n * Arrow keys move focus between mark elements. Enter/Space shows tooltip.\n * Escape hides tooltip. Returns a cleanup function.\n */\nfunction wireKeyboardNav(\n svg: SVGElement,\n container: HTMLElement,\n tooltipDescriptors: Map<string, TooltipContent>,\n tooltipManager: TooltipManager,\n layout: ChartLayout,\n): () => void {\n // Make container focusable\n container.setAttribute('tabindex', '0');\n container.setAttribute('aria-roledescription', 'chart');\n container.setAttribute('aria-label', layout.a11y.altText);\n\n // Collect navigable mark elements (those with tooltip content)\n const markElements: SVGElement[] = [];\n const allMarkEls = svg.querySelectorAll('[data-mark-id]');\n for (const el of allMarkEls) {\n const markId = el.getAttribute('data-mark-id');\n if (markId && tooltipDescriptors.has(markId)) {\n markElements.push(el as SVGElement);\n }\n }\n\n let focusIndex = -1;\n\n function highlightMark(index: number): void {\n // Remove previous highlight\n if (focusIndex >= 0 && focusIndex < markElements.length) {\n markElements[focusIndex].classList.remove('viz-mark-focused');\n markElements[focusIndex].removeAttribute('aria-selected');\n }\n\n focusIndex = index;\n\n if (focusIndex >= 0 && focusIndex < markElements.length) {\n const el = markElements[focusIndex];\n el.classList.add('viz-mark-focused');\n el.setAttribute('aria-selected', 'true');\n }\n }\n\n function showTooltipForFocused(): void {\n if (focusIndex < 0 || focusIndex >= markElements.length) return;\n\n const el = markElements[focusIndex];\n const markId = el.getAttribute('data-mark-id');\n if (!markId) return;\n\n const content = tooltipDescriptors.get(markId);\n if (!content) return;\n\n // Position tooltip near the mark element\n const bbox = el.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const x = bbox.left + bbox.width / 2 - containerRect.left;\n const y = bbox.top - containerRect.top;\n tooltipManager.show(content, x, y);\n }\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (markElements.length === 0) return;\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowDown': {\n e.preventDefault();\n const next = focusIndex < markElements.length - 1 ? focusIndex + 1 : 0;\n highlightMark(next);\n showTooltipForFocused();\n break;\n }\n case 'ArrowLeft':\n case 'ArrowUp': {\n e.preventDefault();\n const prev = focusIndex > 0 ? focusIndex - 1 : markElements.length - 1;\n highlightMark(prev);\n showTooltipForFocused();\n break;\n }\n case 'Enter':\n case ' ': {\n e.preventDefault();\n if (focusIndex >= 0) {\n showTooltipForFocused();\n } else if (markElements.length > 0) {\n highlightMark(0);\n showTooltipForFocused();\n }\n break;\n }\n case 'Escape': {\n e.preventDefault();\n tooltipManager.hide();\n highlightMark(-1);\n break;\n }\n }\n };\n\n container.addEventListener('keydown', handleKeyDown);\n\n return () => {\n container.removeEventListener('keydown', handleKeyDown);\n container.removeAttribute('tabindex');\n container.removeAttribute('aria-roledescription');\n container.removeAttribute('aria-label');\n };\n}\n\n// ---------------------------------------------------------------------------\n// Hidden data table for screen readers\n// ---------------------------------------------------------------------------\n\n/**\n * Create a visually-hidden data table from the chart's a11y fallback data.\n * Returns the table element (append to container) and a cleanup function.\n */\nfunction createScreenReaderTable(\n layout: ChartLayout,\n container: HTMLElement,\n): HTMLTableElement | null {\n const data = layout.a11y.dataTableFallback;\n if (!data || data.length === 0) return null;\n\n const table = document.createElement('table');\n table.className = 'viz-sr-only';\n table.setAttribute('role', 'table');\n table.setAttribute('aria-label', `Data table: ${layout.a11y.altText}`);\n\n // First row is headers\n if (data.length > 0) {\n const thead = document.createElement('thead');\n const headerRow = document.createElement('tr');\n const headers = data[0] as unknown[];\n for (const header of headers) {\n const th = document.createElement('th');\n th.textContent = String(header ?? '');\n th.setAttribute('scope', 'col');\n headerRow.appendChild(th);\n }\n thead.appendChild(headerRow);\n table.appendChild(thead);\n }\n\n // Remaining rows are data\n if (data.length > 1) {\n const tbody = document.createElement('tbody');\n for (let i = 1; i < data.length; i++) {\n const tr = document.createElement('tr');\n const cells = data[i] as unknown[];\n for (const cell of cells) {\n const td = document.createElement('td');\n td.textContent = String(cell ?? '');\n tr.appendChild(td);\n }\n tbody.appendChild(tr);\n }\n table.appendChild(tbody);\n }\n\n container.appendChild(table);\n return table;\n}\n\n// ---------------------------------------------------------------------------\n// Main API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a chart instance from a spec and mount it into a container.\n *\n * @param container - The DOM element to render into.\n * @param spec - The visualization spec.\n * @param options - Mount options (theme, darkMode, responsive, etc.).\n * @returns A ChartInstance with update/resize/export/destroy methods.\n */\nexport function createChart(\n container: HTMLElement,\n spec: ChartSpec | GraphSpec,\n options?: MountOptions,\n): ChartInstance {\n let currentSpec: ChartSpec | GraphSpec = spec;\n let currentLayout: ChartLayout;\n let svgElement: SVGElement | null = null;\n let tooltipManager: TooltipManager | null = null;\n let disconnectResize: (() => void) | null = null;\n let cleanupTooltipEvents: (() => void) | null = null;\n let cleanupKeyboardNav: (() => void) | null = null;\n let cleanupLegend: (() => void) | null = null;\n let cleanupChartEvents: (() => void) | null = null;\n let cleanupAnnotationDrag: (() => void) | null = null;\n let cleanupEditDrags: (() => void) | null = null;\n let srTable: HTMLTableElement | null = null;\n let destroyed = false;\n let isDragging = false;\n let pendingRender = false;\n\n const measureText = createMeasureText();\n\n function compile(): ChartLayout {\n const { width, height } = getContainerDimensions();\n const darkMode = resolveDarkMode(options?.darkMode);\n\n const compileOpts: CompileOptions = {\n width,\n height,\n theme: options?.theme,\n darkMode,\n measureText,\n };\n\n return compileChart(currentSpec, compileOpts);\n }\n\n function getContainerDimensions(): { width: number; height: number } {\n const rect = container.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n function render(): void {\n // Defer re-render if a drag is in progress to avoid destroying the dragged element\n if (isDragging) {\n pendingRender = true;\n return;\n }\n\n // Clean up previous render\n if (cleanupTooltipEvents) {\n cleanupTooltipEvents();\n cleanupTooltipEvents = null;\n }\n if (cleanupKeyboardNav) {\n cleanupKeyboardNav();\n cleanupKeyboardNav = null;\n }\n if (cleanupLegend) {\n cleanupLegend();\n cleanupLegend = null;\n }\n if (cleanupChartEvents) {\n cleanupChartEvents();\n cleanupChartEvents = null;\n }\n if (cleanupAnnotationDrag) {\n cleanupAnnotationDrag();\n cleanupAnnotationDrag = null;\n }\n if (cleanupEditDrags) {\n cleanupEditDrags();\n cleanupEditDrags = null;\n }\n if (svgElement?.parentNode) {\n svgElement.parentNode.removeChild(svgElement);\n }\n if (tooltipManager) {\n tooltipManager.destroy();\n }\n if (srTable?.parentNode) {\n srTable.parentNode.removeChild(srTable);\n srTable = null;\n }\n\n currentLayout = compile();\n svgElement = renderChartSVG(currentLayout, container);\n tooltipManager = createTooltipManager(container);\n\n // Wire tooltip events on mark elements\n cleanupTooltipEvents = wireTooltipEvents(\n svgElement,\n currentLayout.tooltipDescriptors,\n tooltipManager,\n );\n\n // Wire keyboard navigation\n cleanupKeyboardNav = wireKeyboardNav(\n svgElement,\n container,\n currentLayout.tooltipDescriptors,\n tooltipManager,\n currentLayout,\n );\n\n // Wire legend interactivity\n cleanupLegend = wireLegendInteraction(\n svgElement,\n currentLayout,\n options?.onLegendToggle,\n options?.onEdit,\n );\n\n // Wire chart event handlers (mark click/hover/leave, annotation click)\n if (\n options?.onMarkClick ||\n options?.onMarkHover ||\n options?.onMarkLeave ||\n options?.onAnnotationClick\n ) {\n const specAnnotations: import('@opendata-ai/openchart-core').Annotation[] =\n 'annotations' in currentSpec && Array.isArray(currentSpec.annotations)\n ? currentSpec.annotations\n : [];\n cleanupChartEvents = wireChartEvents(svgElement, currentLayout, specAnnotations, options);\n }\n\n // Shared setDragging callback for all drag handlers\n const setDragging = (dragging: boolean) => {\n isDragging = dragging;\n if (!dragging && pendingRender) {\n pendingRender = false;\n render();\n }\n };\n\n // Shared annotation list for drag handlers (computed once)\n const dragAnnotations: Annotation[] =\n 'annotations' in currentSpec && Array.isArray(currentSpec.annotations)\n ? currentSpec.annotations\n : [];\n\n // Wire annotation drag editing (activates when onAnnotationEdit or onEdit is provided)\n if (options?.onAnnotationEdit || options?.onEdit) {\n cleanupAnnotationDrag = wireAnnotationDrag(\n svgElement,\n dragAnnotations,\n options?.onAnnotationEdit,\n options?.onEdit,\n setDragging,\n );\n }\n\n // Wire all edit drag handlers when onEdit is provided\n if (options?.onEdit) {\n const editCleanups: Array<() => void> = [];\n\n // Connector endpoint drag\n editCleanups.push(\n wireConnectorEndpointDrag(svgElement, dragAnnotations, options.onEdit, setDragging),\n );\n\n // Range/refline annotation label drag\n editCleanups.push(\n wireAnnotationLabelDrag(svgElement, dragAnnotations, options.onEdit, setDragging),\n );\n\n // Chrome text drag\n editCleanups.push(wireChromeDrag(svgElement, currentSpec, options.onEdit, setDragging));\n\n // Legend drag\n editCleanups.push(wireLegendDrag(svgElement, currentSpec, options.onEdit, setDragging));\n\n // Series label drag\n editCleanups.push(wireSeriesLabelDrag(svgElement, currentSpec, options.onEdit, setDragging));\n\n cleanupEditDrags = () => {\n for (const cleanup of editCleanups) {\n cleanup();\n }\n };\n }\n\n // Create hidden data table for screen readers\n srTable = createScreenReaderTable(currentLayout, container);\n\n // Apply container classes for CSS variable scoping and dark mode\n container.classList.add('viz-root');\n const isDark = resolveDarkMode(options?.darkMode);\n if (isDark) {\n container.classList.add('viz-dark');\n } else {\n container.classList.remove('viz-dark');\n }\n }\n\n function update(newSpec: ChartSpec | GraphSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n render();\n }\n\n function resize(): void {\n if (destroyed) return;\n render();\n }\n\n function doExport(format: 'svg'): string;\n function doExport(format: 'png', exportOptions?: ExportOptions): Promise<Blob>;\n function doExport(format: 'jpg', exportOptions?: ExportOptions): Promise<Blob>;\n function doExport(format: 'csv'): string;\n function doExport(\n format: 'svg' | 'png' | 'jpg' | 'csv',\n exportOptions?: ExportOptions,\n ): string | Promise<Blob> {\n if (!svgElement) {\n throw new Error('Chart is not rendered yet');\n }\n\n switch (format) {\n case 'svg':\n return exportSVG(svgElement);\n case 'png':\n return exportPNG(svgElement, exportOptions);\n case 'jpg':\n return exportJPG(svgElement, exportOptions);\n case 'csv':\n return exportCSV(\n 'data' in currentSpec && Array.isArray(currentSpec.data) ? currentSpec.data : [],\n );\n default:\n throw new Error(`Unsupported export format: ${format}`);\n }\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (cleanupTooltipEvents) {\n cleanupTooltipEvents();\n cleanupTooltipEvents = null;\n }\n if (cleanupKeyboardNav) {\n cleanupKeyboardNav();\n cleanupKeyboardNav = null;\n }\n if (cleanupLegend) {\n cleanupLegend();\n cleanupLegend = null;\n }\n if (cleanupChartEvents) {\n cleanupChartEvents();\n cleanupChartEvents = null;\n }\n if (cleanupAnnotationDrag) {\n cleanupAnnotationDrag();\n cleanupAnnotationDrag = null;\n }\n if (cleanupEditDrags) {\n cleanupEditDrags();\n cleanupEditDrags = null;\n }\n if (disconnectResize) {\n disconnectResize();\n disconnectResize = null;\n }\n if (tooltipManager) {\n tooltipManager.destroy();\n tooltipManager = null;\n }\n if (svgElement?.parentNode) {\n svgElement.parentNode.removeChild(svgElement);\n svgElement = null;\n }\n if (srTable?.parentNode) {\n srTable.parentNode.removeChild(srTable);\n srTable = null;\n }\n container.classList.remove('viz-dark');\n container.classList.remove('viz-root');\n }\n\n // Initial render\n render();\n\n // Set up responsive resize\n if (options?.responsive !== false) {\n disconnectResize = observeResize(container, () => {\n resize();\n });\n }\n\n return {\n update,\n resize,\n export: doExport,\n destroy,\n get layout() {\n return currentLayout;\n },\n };\n}\n","/**\n * SVG renderer: converts a ChartLayout into SVG DOM elements.\n *\n * Creates an <svg> element with viewBox matching layout dimensions,\n * renders chrome (title/subtitle/source), axes, marks, annotations,\n * and legend. All styling via inline SVG attributes from layout data.\n *\n * Mark rendering dispatches per mark type with dedicated renderers\n * for line, area, rect, arc, and point marks.\n */\n\nimport type {\n ArcMark,\n AreaMark,\n AxisLayout,\n ChartLayout,\n LegendLayout,\n LineMark,\n Mark,\n Point,\n PointMark,\n RectMark,\n ResolvedAnnotation,\n ResolvedChromeElement,\n TextStyle,\n} from '@opendata-ai/openchart-core';\nimport { estimateTextWidth } from '@opendata-ai/openchart-core';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\n/**\n * Compute the vertical extent of x-axis labels below the chart area.\n * Accounts for rotated tick labels which need more vertical space.\n */\nfunction computeXAxisExtent(layout: ChartLayout): number {\n const xAxis = layout.axes.x;\n if (!xAxis) return 0;\n\n if (xAxis.tickAngle && Math.abs(xAxis.tickAngle) > 10) {\n // Rotated labels: estimate height from the longest tick label.\n const fontSize = xAxis.tickLabelStyle.fontSize;\n const fontWeight = xAxis.tickLabelStyle.fontWeight;\n const angleRad = Math.abs(xAxis.tickAngle) * (Math.PI / 180);\n let maxLabelWidth = 40;\n for (const tick of xAxis.ticks) {\n const w = estimateTextWidth(tick.label, fontSize, fontWeight);\n if (w > maxLabelWidth) maxLabelWidth = w;\n }\n const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);\n return xAxis.label ? rotatedHeight + 20 : rotatedHeight;\n }\n\n return xAxis.label ? 48 : 26;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createSVGElement(tag: string): SVGElement {\n return document.createElementNS(SVG_NS, tag);\n}\n\nfunction setAttrs(el: SVGElement, attrs: Record<string, string | number>): void {\n for (const [key, value] of Object.entries(attrs)) {\n el.setAttribute(key, String(value));\n }\n}\n\nfunction applyTextStyle(el: SVGElement, style: TextStyle): void {\n setAttrs(el, {\n 'font-family': style.fontFamily,\n 'font-size': style.fontSize,\n 'font-weight': style.fontWeight,\n });\n // Use inline style for fill so it takes priority over CSS class defaults\n // (e.g. .viz-title { fill: var(--viz-text) } which would override attributes)\n (el as SVGElement & ElementCSSInlineStyle).style.setProperty('fill', style.fill);\n if (style.textAnchor) {\n el.setAttribute('text-anchor', style.textAnchor);\n }\n if (style.dominantBaseline) {\n el.setAttribute('dominant-baseline', style.dominantBaseline);\n }\n if (style.fontVariant) {\n el.setAttribute('font-variant', style.fontVariant);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Chrome rendering\n// ---------------------------------------------------------------------------\n\nfunction renderChromeElement(\n parent: SVGElement,\n element: ResolvedChromeElement,\n className: string,\n chromeKey: string,\n): void {\n const text = createSVGElement('text');\n setAttrs(text, { x: element.x, y: element.y });\n applyTextStyle(text, element.style);\n text.setAttribute('class', className);\n text.setAttribute('data-chrome-key', chromeKey);\n text.textContent = element.text;\n parent.appendChild(text);\n}\n\nfunction renderChrome(parent: SVGElement, layout: ChartLayout): void {\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-chrome');\n\n const { chrome } = layout;\n\n // Top chrome: render at their stored y positions (already absolute)\n if (chrome.title) {\n renderChromeElement(g, chrome.title, 'viz-title', 'title');\n }\n if (chrome.subtitle) {\n renderChromeElement(g, chrome.subtitle, 'viz-subtitle', 'subtitle');\n }\n\n // Bottom chrome starts below x-axis labels/title, not at chart area bottom.\n // Accounts for rotated tick labels which need more vertical space.\n const xAxisExtent = computeXAxisExtent(layout);\n const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;\n if (chrome.source) {\n renderChromeElement(\n g,\n { ...chrome.source, y: bottomOffset + chrome.source.y },\n 'viz-source',\n 'source',\n );\n }\n if (chrome.byline) {\n renderChromeElement(\n g,\n { ...chrome.byline, y: bottomOffset + chrome.byline.y },\n 'viz-byline',\n 'byline',\n );\n }\n if (chrome.footer) {\n renderChromeElement(\n g,\n { ...chrome.footer, y: bottomOffset + chrome.footer.y },\n 'viz-footer',\n 'footer',\n );\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Axis rendering\n// ---------------------------------------------------------------------------\n\nfunction renderAxis(\n parent: SVGElement,\n axis: AxisLayout,\n orientation: 'x' | 'y',\n layout: ChartLayout,\n): void {\n const g = createSVGElement('g');\n g.setAttribute('class', `viz-axis viz-axis-${orientation}`);\n\n const { area } = layout;\n\n // Only draw axis line for x-axis (bottom baseline).\n // Horizontal gridlines already guide y-values, so the vertical y-axis line is redundant.\n if (orientation === 'x') {\n const line = createSVGElement('line');\n line.setAttribute('class', 'viz-axis-line');\n setAttrs(line, {\n x1: axis.start.x,\n y1: axis.start.y,\n x2: axis.end.x,\n y2: axis.end.y,\n stroke: layout.theme.colors.axis,\n 'stroke-width': 1,\n });\n g.appendChild(line);\n }\n\n // Ticks and labels\n // Tick positions are absolute pixel coordinates from D3 scales whose range\n // was set to [chartArea.x, chartArea.x + chartArea.width] (and similarly for y).\n // Don't add area.x/area.y again or you'll double-offset everything.\n for (const tick of axis.ticks) {\n if (orientation === 'x') {\n // Label (no tick marks -- gridlines provide sufficient reference)\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-axis-tick');\n\n if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {\n // Rotated labels: anchor at the rotation pivot point\n const labelX = tick.position;\n const labelY = area.y + area.height + 6;\n setAttrs(label, {\n x: labelX,\n y: labelY,\n 'text-anchor': axis.tickAngle < 0 ? 'end' : 'start',\n 'dominant-baseline': 'central',\n transform: `rotate(${axis.tickAngle}, ${labelX}, ${labelY})`,\n });\n } else {\n setAttrs(label, {\n x: tick.position,\n y: area.y + area.height + 14,\n 'text-anchor': 'middle',\n });\n }\n\n applyTextStyle(label, axis.tickLabelStyle);\n label.textContent = tick.label;\n g.appendChild(label);\n } else {\n // Label (no tick marks -- gridlines provide sufficient reference)\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-axis-tick');\n setAttrs(label, {\n x: area.x - 6,\n y: tick.position,\n 'text-anchor': 'end',\n 'dominant-baseline': 'central',\n });\n applyTextStyle(label, axis.tickLabelStyle);\n label.textContent = tick.label;\n g.appendChild(label);\n }\n }\n\n // Gridlines (positions are also absolute from the scales)\n for (const gridline of axis.gridlines) {\n const gl = createSVGElement('line');\n gl.setAttribute('class', 'viz-gridline');\n if (orientation === 'y') {\n setAttrs(gl, {\n x1: area.x,\n y1: gridline.position,\n x2: area.x + area.width,\n y2: gridline.position,\n stroke: layout.theme.colors.gridline,\n 'stroke-width': 1,\n 'stroke-opacity': 0.35,\n });\n } else {\n setAttrs(gl, {\n x1: gridline.position,\n y1: area.y,\n x2: gridline.position,\n y2: area.y + area.height,\n stroke: layout.theme.colors.gridline,\n 'stroke-width': 1,\n 'stroke-opacity': 0.35,\n });\n }\n g.appendChild(gl);\n }\n\n // Axis label\n if (axis.label && axis.labelStyle) {\n const axisLabel = createSVGElement('text');\n axisLabel.setAttribute('class', 'viz-axis-title');\n applyTextStyle(axisLabel, axis.labelStyle);\n axisLabel.textContent = axis.label;\n\n if (orientation === 'x') {\n // Position axis title below tick labels. For rotated labels, compute\n // the vertical extent of the rotated ticks and place the title below.\n let titleY = area.y + area.height + 35;\n if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {\n const angleRad = Math.abs(axis.tickAngle) * (Math.PI / 180);\n let maxLabelWidth = 40;\n for (const tick of axis.ticks) {\n const w = estimateTextWidth(\n tick.label,\n axis.tickLabelStyle.fontSize,\n axis.tickLabelStyle.fontWeight,\n );\n if (w > maxLabelWidth) maxLabelWidth = w;\n }\n const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);\n titleY = area.y + area.height + rotatedHeight + 14;\n }\n setAttrs(axisLabel, {\n x: area.x + area.width / 2,\n y: titleY,\n 'text-anchor': 'middle',\n });\n } else {\n // Rotated y-axis label\n setAttrs(axisLabel, {\n x: area.x - 45,\n y: area.y + area.height / 2,\n 'text-anchor': 'middle',\n transform: `rotate(-90, ${area.x - 45}, ${area.y + area.height / 2})`,\n });\n }\n g.appendChild(axisLabel);\n }\n\n parent.appendChild(g);\n}\n\nfunction renderAxes(parent: SVGElement, layout: ChartLayout): void {\n if (layout.axes.x) {\n renderAxis(parent, layout.axes.x, 'x', layout);\n }\n if (layout.axes.y) {\n renderAxis(parent, layout.axes.y, 'y', layout);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mark rendering (dispatch per mark type)\n// ---------------------------------------------------------------------------\n\ntype MarkRenderer<T extends Mark> = (mark: T, index: number) => SVGElement;\n\nconst markRenderers: Record<string, MarkRenderer<Mark>> = {};\n\n/**\n * Register a mark renderer for a specific mark type.\n * Built-in renderers are registered below for all chart types.\n */\nexport function registerMarkRenderer<T extends Mark>(\n type: T['type'],\n renderer: MarkRenderer<T>,\n): void {\n markRenderers[type] = renderer as MarkRenderer<Mark>;\n}\n\nfunction renderLineMark(mark: LineMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `line-${mark.seriesKey ?? index}`);\n g.setAttribute('class', 'viz-mark viz-mark-line');\n\n if (mark.points.length > 1) {\n const path = createSVGElement('path');\n // Use the pre-computed D3 curve path when available (smooth monotone),\n // otherwise fall back to straight M/L segments.\n const d =\n mark.path ?? mark.points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' ');\n setAttrs(path, {\n d,\n fill: 'none',\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth,\n });\n if (mark.strokeDasharray) {\n path.setAttribute('stroke-dasharray', mark.strokeDasharray);\n }\n if (mark.opacity != null) {\n path.setAttribute('opacity', String(mark.opacity));\n }\n g.appendChild(path);\n }\n\n // Render end-of-line label if present and visible\n if (mark.label?.visible) {\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-mark-label');\n if (mark.seriesKey) {\n label.setAttribute('data-series', mark.seriesKey);\n }\n setAttrs(label, { x: mark.label.x, y: mark.label.y });\n applyTextStyle(label, mark.label.style);\n label.textContent = mark.label.text;\n g.appendChild(label);\n\n // Render connector line if label was offset from anchor\n if (mark.label.connector) {\n const connector = createSVGElement('line');\n connector.setAttribute('class', 'viz-mark-connector');\n setAttrs(connector, {\n x1: mark.label.connector.from.x,\n y1: mark.label.connector.from.y,\n x2: mark.label.connector.to.x,\n y2: mark.label.connector.to.y,\n stroke: mark.label.connector.stroke,\n 'stroke-width': 1,\n 'stroke-opacity': 0.5,\n });\n g.appendChild(connector);\n }\n }\n\n return g;\n}\n\nfunction renderAreaMark(mark: AreaMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `area-${mark.seriesKey ?? index}`);\n g.setAttribute('class', 'viz-mark viz-mark-area');\n\n if (mark.path) {\n // Area fill: the full closed shape (top line + baseline), no stroke\n const fill = createSVGElement('path');\n setAttrs(fill, {\n d: mark.path,\n fill: mark.fill,\n 'fill-opacity': mark.fillOpacity,\n stroke: 'none',\n });\n g.appendChild(fill);\n\n // Top-line stroke: only along the data points, not the baseline\n if (mark.stroke && mark.topPath) {\n const strokePath = createSVGElement('path');\n setAttrs(strokePath, {\n d: mark.topPath,\n fill: 'none',\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth ?? 1,\n });\n g.appendChild(strokePath);\n }\n }\n\n return g;\n}\n\nfunction renderRectMark(mark: RectMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `rect-${index}`);\n g.setAttribute('class', 'viz-mark viz-mark-rect');\n\n const rect = createSVGElement('rect');\n setAttrs(rect, {\n x: mark.x,\n y: mark.y,\n width: mark.width,\n height: mark.height,\n fill: mark.fill,\n });\n if (mark.stroke) {\n rect.setAttribute('stroke', mark.stroke);\n }\n if (mark.strokeWidth) {\n rect.setAttribute('stroke-width', String(mark.strokeWidth));\n }\n if (mark.cornerRadius) {\n setAttrs(rect, { rx: mark.cornerRadius, ry: mark.cornerRadius });\n }\n g.appendChild(rect);\n\n // Render value label if present and visible\n if (mark.label?.visible) {\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-mark-label');\n setAttrs(label, { x: mark.label.x, y: mark.label.y });\n applyTextStyle(label, mark.label.style);\n label.textContent = mark.label.text;\n g.appendChild(label);\n }\n\n return g;\n}\n\nfunction renderArcMark(mark: ArcMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `arc-${index}`);\n g.setAttribute('class', 'viz-mark viz-mark-arc');\n g.setAttribute('transform', `translate(${mark.center.x},${mark.center.y})`);\n\n const path = createSVGElement('path');\n setAttrs(path, {\n d: mark.path,\n fill: mark.fill,\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth,\n });\n g.appendChild(path);\n\n // Render label if present and visible\n if (mark.label?.visible) {\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-mark-label');\n // Label position is in absolute coords, but we're in a translated group,\n // so subtract the center offset\n setAttrs(label, {\n x: mark.label.x - mark.center.x,\n y: mark.label.y - mark.center.y,\n });\n applyTextStyle(label, mark.label.style);\n label.textContent = mark.label.text;\n g.appendChild(label);\n }\n\n return g;\n}\n\nfunction renderPointMark(mark: PointMark, index: number): SVGElement {\n const circle = createSVGElement('circle');\n circle.setAttribute('data-mark-id', `point-${index}`);\n circle.setAttribute('class', 'viz-mark viz-mark-point');\n setAttrs(circle, {\n cx: mark.cx,\n cy: mark.cy,\n r: mark.r,\n fill: mark.fill,\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth,\n });\n if (mark.fillOpacity !== undefined) {\n circle.setAttribute('fill-opacity', String(mark.fillOpacity));\n }\n return circle;\n}\n\n// Register built-in renderers\nregisterMarkRenderer('line', renderLineMark as MarkRenderer<Mark>);\nregisterMarkRenderer('area', renderAreaMark as MarkRenderer<Mark>);\nregisterMarkRenderer('rect', renderRectMark as MarkRenderer<Mark>);\nregisterMarkRenderer('arc', renderArcMark as MarkRenderer<Mark>);\nregisterMarkRenderer('point', renderPointMark as MarkRenderer<Mark>);\n\n/** Extract series name from a mark for legend toggle matching. */\nfunction getMarkSeries(mark: Mark): string | undefined {\n // Line and area marks have an explicit seriesKey\n if (mark.type === 'line' || mark.type === 'area') {\n return mark.seriesKey;\n }\n // For arc marks, the category name is the first part of the aria label (before ':')\n if (mark.type === 'arc') {\n return mark.aria.label.split(':')[0]?.trim();\n }\n // For rect/point, the aria label may be \"category: value\" or \"category, group: value\".\n // The series name is the category part (before the colon).\n if (mark.aria?.label) {\n const beforeColon = mark.aria.label.split(':')[0]?.trim();\n if (beforeColon) return beforeColon;\n }\n return undefined;\n}\n\nfunction renderMarks(parent: SVGElement, layout: ChartLayout): void {\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-marks');\n\n for (let i = 0; i < layout.marks.length; i++) {\n const mark = layout.marks[i];\n const renderer = markRenderers[mark.type];\n if (renderer) {\n const el = renderer(mark, i);\n // Add ARIA label if present\n if (mark.aria?.label) {\n el.setAttribute('aria-label', mark.aria.label);\n }\n // Add data-series attribute for legend toggle matching\n const series = getMarkSeries(mark);\n if (series) {\n el.setAttribute('data-series', series);\n }\n g.appendChild(el);\n }\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Annotation rendering\n// ---------------------------------------------------------------------------\n\nfunction renderAnnotations(parent: SVGElement, layout: ChartLayout): void {\n if (layout.annotations.length === 0) return;\n\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-annotations');\n\n // Annotations are already sorted by zIndex from the engine, so render in order\n for (let i = 0; i < layout.annotations.length; i++) {\n renderAnnotation(g, layout.annotations[i], i);\n }\n\n parent.appendChild(g);\n}\n\n/**\n * Render a curved arrow connector from a label to a data point.\n * Uses a cubic bezier that sweeps outward then curves toward the\n * target, with a triangular arrowhead at the tip.\n */\nfunction renderCurvedArrow(parent: SVGElement, from: Point, to: Point, stroke: string): void {\n // Pad above the target so the arrow doesn't sit right on the element.\n const pad = 6;\n const tipY = to.y - pad;\n\n const dy = tipY - from.y;\n const dist = Math.sqrt((to.x - from.x) ** 2 + dy ** 2) || 1;\n\n // Arrowhead geometry\n const arrowLen = 8;\n const arrowWidth = 4;\n\n // cp2 directly above target so arrow arrives pointing straight down.\n const bulge = Math.max(dist * 0.4, 35);\n const cp1x = from.x + bulge;\n const cp1y = from.y + dy * 0.35;\n const cp2x = to.x;\n const cp2y = tipY - Math.abs(dy) * 0.25;\n\n // Tangent at the tip (from cp2 → tip), used for arrowhead direction.\n const tx = to.x - cp2x;\n const ty = tipY - cp2y;\n const tLen = Math.sqrt(tx * tx + ty * ty) || 1;\n const ux = tx / tLen;\n const uy = ty / tLen;\n\n // End the curve path at the arrowhead BASE so the stroke doesn't\n // poke through the filled triangle.\n const baseX = to.x - ux * arrowLen;\n const baseY = tipY - uy * arrowLen;\n\n const path = createSVGElement('path');\n path.setAttribute('class', 'viz-annotation-connector');\n setAttrs(path, {\n d: `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${baseX} ${baseY}`,\n fill: 'none',\n stroke,\n 'stroke-width': 1.5,\n });\n parent.appendChild(path);\n\n // Arrowhead triangle: perpendicular to tangent direction.\n const px = -uy;\n const py = ux;\n\n const arrow = createSVGElement('polygon');\n arrow.setAttribute('class', 'viz-annotation-connector');\n setAttrs(arrow, {\n points: [\n `${to.x},${tipY}`,\n `${baseX + px * arrowWidth},${baseY + py * arrowWidth}`,\n `${baseX - px * arrowWidth},${baseY - py * arrowWidth}`,\n ].join(' '),\n fill: stroke,\n });\n parent.appendChild(arrow);\n}\n\nfunction renderAnnotation(parent: SVGElement, annotation: ResolvedAnnotation, index: number): void {\n const g = createSVGElement('g');\n g.setAttribute('class', `viz-annotation viz-annotation-${annotation.type}`);\n g.setAttribute('data-annotation-index', String(index));\n\n // Range rect\n if (annotation.rect) {\n const rect = createSVGElement('rect');\n rect.setAttribute('class', 'viz-annotation-range');\n setAttrs(rect, {\n x: annotation.rect.x,\n y: annotation.rect.y,\n width: annotation.rect.width,\n height: annotation.rect.height,\n });\n if (annotation.fill) rect.setAttribute('fill', annotation.fill);\n if (annotation.opacity !== undefined) {\n rect.setAttribute('fill-opacity', String(annotation.opacity));\n }\n g.appendChild(rect);\n }\n\n // Reference line\n if (annotation.line) {\n const line = createSVGElement('line');\n line.setAttribute('class', 'viz-annotation-line');\n setAttrs(line, {\n x1: annotation.line.start.x,\n y1: annotation.line.start.y,\n x2: annotation.line.end.x,\n y2: annotation.line.end.y,\n 'stroke-width': annotation.strokeWidth ?? 1,\n });\n if (annotation.stroke) line.setAttribute('stroke', annotation.stroke);\n if (annotation.strokeDasharray) {\n line.setAttribute('stroke-dasharray', annotation.strokeDasharray);\n }\n g.appendChild(line);\n }\n\n // Label with optional connector line\n if (annotation.label?.visible) {\n // Render connector first (behind the label text)\n if (annotation.label.connector) {\n const c = annotation.label.connector;\n if (c.style === 'caret') {\n // Small directional chevron centered in the gap between the label\n // text and the data mark, pointing toward the data.\n const pointsDown = c.to.y > c.from.y;\n const caretSize = 4;\n // c.from.y is near the text baseline, not the visual bottom.\n // Estimate the text bottom from the label's line count and font size.\n const labelLines = annotation.label.text.split('\\n');\n const labelFontSize = annotation.label.style.fontSize ?? 12;\n const labelLineHeight = labelFontSize * (annotation.label.style.lineHeight ?? 1.3);\n const textBottom =\n annotation.label.y + (labelLines.length - 1) * labelLineHeight + labelFontSize * 0.25;\n const textTop = annotation.label.y - labelFontSize;\n // Center caret in the gap between text edge and data point\n const gapEdge = pointsDown ? textBottom : textTop;\n const midY = (gapEdge + c.to.y) / 2;\n const tipX = c.to.x;\n const tipY = pointsDown ? midY + caretSize / 2 : midY - caretSize / 2;\n const baseY = pointsDown ? tipY - caretSize : tipY + caretSize;\n const path = createSVGElement('path');\n path.setAttribute('class', 'viz-annotation-connector');\n setAttrs(path, {\n d: `M${tipX - caretSize},${baseY} L${tipX},${tipY} L${tipX + caretSize},${baseY}`,\n fill: 'none',\n stroke: c.stroke,\n 'stroke-width': 1.5,\n 'stroke-opacity': 0.4,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n });\n g.appendChild(path);\n } else if (c.style === 'curve') {\n renderCurvedArrow(g, c.from, c.to, c.stroke);\n } else {\n const connector = createSVGElement('line');\n connector.setAttribute('class', 'viz-annotation-connector');\n setAttrs(connector, {\n x1: c.from.x,\n y1: c.from.y,\n x2: c.to.x,\n y2: c.to.y,\n stroke: c.stroke,\n 'stroke-width': 1,\n 'stroke-opacity': 0.5,\n });\n g.appendChild(connector);\n }\n }\n\n const text = createSVGElement('text');\n text.setAttribute('class', 'viz-annotation-label');\n setAttrs(text, { x: annotation.label.x, y: annotation.label.y });\n applyTextStyle(text, annotation.label.style);\n\n const lines = annotation.label.text.split('\\n');\n const fontSize = annotation.label.style.fontSize ?? 12;\n const lineHeight = fontSize * (annotation.label.style.lineHeight ?? 1.3);\n const isMultiLine = lines.length > 1;\n\n // Multi-line text uses center alignment for a cleaner look\n if (isMultiLine) {\n text.setAttribute('text-anchor', 'middle');\n for (let i = 0; i < lines.length; i++) {\n const tspan = createSVGElement('tspan');\n setAttrs(tspan, { x: annotation.label.x, dy: i === 0 ? 0 : lineHeight });\n tspan.textContent = lines[i];\n text.appendChild(tspan);\n }\n } else {\n text.textContent = annotation.label.text;\n }\n\n // Render background rect behind text if specified\n if (annotation.label.background) {\n const charWidth = fontSize * 0.55;\n const maxLineWidth = Math.max(...lines.map((l) => l.length)) * charWidth;\n const totalHeight = lines.length * lineHeight;\n const pad = 3;\n const bgX = isMultiLine\n ? annotation.label.x - maxLineWidth / 2 - pad\n : annotation.label.x - pad;\n const bgRect = createSVGElement('rect');\n bgRect.setAttribute('class', 'viz-annotation-bg');\n setAttrs(bgRect, {\n x: bgX,\n y: annotation.label.y - fontSize + (lineHeight - fontSize) / 2 - pad,\n width: maxLineWidth + pad * 2,\n height: totalHeight + pad * 2,\n fill: annotation.label.background,\n rx: 2,\n });\n g.appendChild(bgRect);\n }\n\n g.appendChild(text);\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Legend rendering\n// ---------------------------------------------------------------------------\n\nfunction renderLegend(parent: SVGElement, legend: LegendLayout): void {\n if (legend.entries.length === 0) return;\n\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-legend');\n g.setAttribute('role', 'list');\n g.setAttribute('aria-label', 'Chart legend');\n\n const isHorizontal = legend.position === 'top' || legend.position === 'bottom';\n let offsetX = legend.bounds.x;\n let offsetY = legend.bounds.y;\n\n for (let i = 0; i < legend.entries.length; i++) {\n const entry = legend.entries[i];\n const entryG = createSVGElement('g');\n entryG.setAttribute('class', 'viz-legend-entry');\n entryG.setAttribute('role', 'listitem');\n entryG.setAttribute('data-legend-index', String(i));\n entryG.setAttribute('data-legend-label', entry.label);\n entryG.setAttribute(\n 'aria-label',\n `${entry.label}: ${entry.active !== false ? 'visible' : 'hidden'}`,\n );\n entryG.setAttribute('style', 'cursor: pointer');\n\n // Apply dimming for inactive entries\n if (entry.active === false) {\n entryG.setAttribute('opacity', '0.3');\n }\n\n // Swatch\n if (entry.shape === 'circle') {\n const circle = createSVGElement('circle');\n setAttrs(circle, {\n cx: offsetX + legend.swatchSize / 2,\n cy: offsetY + legend.swatchSize / 2,\n r: legend.swatchSize / 2,\n fill: entry.color,\n });\n entryG.appendChild(circle);\n } else if (entry.shape === 'line') {\n // Line swatch: a short line segment with a dot in the middle\n const line = createSVGElement('line');\n setAttrs(line, {\n x1: offsetX,\n y1: offsetY + legend.swatchSize / 2,\n x2: offsetX + legend.swatchSize,\n y2: offsetY + legend.swatchSize / 2,\n stroke: entry.color,\n 'stroke-width': 2,\n });\n entryG.appendChild(line);\n // Small dot at center\n const dot = createSVGElement('circle');\n setAttrs(dot, {\n cx: offsetX + legend.swatchSize / 2,\n cy: offsetY + legend.swatchSize / 2,\n r: 2.5,\n fill: entry.color,\n });\n entryG.appendChild(dot);\n } else {\n const rect = createSVGElement('rect');\n setAttrs(rect, {\n x: offsetX,\n y: offsetY,\n width: legend.swatchSize,\n height: legend.swatchSize,\n fill: entry.color,\n rx: 2,\n });\n entryG.appendChild(rect);\n }\n\n // Label\n const label = createSVGElement('text');\n setAttrs(label, {\n x: offsetX + legend.swatchSize + legend.swatchGap,\n y: offsetY + legend.swatchSize / 2,\n 'dominant-baseline': 'central',\n });\n applyTextStyle(label, legend.labelStyle);\n label.textContent = entry.label;\n entryG.appendChild(label);\n\n g.appendChild(entryG);\n\n // Advance position for next entry\n if (isHorizontal) {\n const labelWidth = estimateTextWidth(\n entry.label,\n legend.labelStyle.fontSize,\n legend.labelStyle.fontWeight,\n );\n const entryWidth = legend.swatchSize + legend.swatchGap + labelWidth + legend.entryGap;\n offsetX += entryWidth;\n // Wrap to next line if exceeding bounds\n if (offsetX > legend.bounds.x + legend.bounds.width && i < legend.entries.length - 1) {\n offsetX = legend.bounds.x;\n offsetY += legend.swatchSize + 6;\n }\n } else {\n offsetY += legend.swatchSize + legend.entryGap;\n }\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Brand rendering\n// ---------------------------------------------------------------------------\n\nconst BRAND_FONT_SIZE = 11;\nconst BRAND_MIN_WIDTH = 120;\nconst BRAND_URL = 'https://tryopendata.ai';\nconst XLINK_NS = 'http://www.w3.org/1999/xlink';\n\n/**\n * Render the \"OpenData\" brand as a footer-row element, right-aligned on the\n * same baseline as the first bottom chrome text (source/byline/footer).\n * Uses the same font size as chrome source text so it blends in as a subtle\n * footer item rather than occupying independent visual space.\n */\nfunction renderBrand(parent: SVGElement, layout: ChartLayout): void {\n if (layout.dimensions.width < BRAND_MIN_WIDTH) return;\n\n const { width } = layout.dimensions;\n const padding = layout.theme.spacing.padding;\n const rightEdge = width - padding;\n const fill = layout.theme.colors.axis;\n\n // Vertically align with the first bottom chrome element.\n const { chrome } = layout;\n const xAxisExtent = computeXAxisExtent(layout);\n const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;\n const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;\n const chromeY = firstBottom\n ? bottomOffset + firstBottom.y\n : bottomOffset + layout.theme.spacing.chartToFooter;\n\n const a = createSVGElement('a');\n a.setAttribute('href', BRAND_URL);\n a.setAttributeNS(XLINK_NS, 'xlink:href', BRAND_URL);\n a.setAttribute('target', '_blank');\n a.setAttribute('rel', 'noopener');\n a.setAttribute('class', 'viz-chrome-ref');\n\n // \"Open\" in normal weight, \"Data\" in semibold, rendered as a single\n // right-aligned text element with two tspans.\n const text = createSVGElement('text');\n setAttrs(text, {\n x: rightEdge,\n y: chromeY,\n 'font-family': layout.theme.fonts.family,\n 'font-size': BRAND_FONT_SIZE,\n 'text-anchor': 'end',\n 'dominant-baseline': 'hanging',\n 'fill-opacity': 0.55,\n });\n (text as SVGElement & ElementCSSInlineStyle).style.setProperty('fill', fill);\n\n const openSpan = createSVGElement('tspan');\n openSpan.setAttribute('font-weight', '500');\n openSpan.textContent = 'Open';\n text.appendChild(openSpan);\n\n const dataSpan = createSVGElement('tspan');\n dataSpan.setAttribute('font-weight', '600');\n dataSpan.textContent = 'Data';\n text.appendChild(dataSpan);\n\n a.appendChild(text);\n parent.appendChild(a);\n}\n\n// ---------------------------------------------------------------------------\n// Main render function\n// ---------------------------------------------------------------------------\n\n/**\n * Render a compiled ChartLayout into an SVG element and append it to a container.\n *\n * @param layout - Compiled ChartLayout from compileChart().\n * @param container - DOM element to mount the SVG into.\n * @returns The created SVG element.\n */\nexport function renderChartSVG(layout: ChartLayout, container: HTMLElement): SVGElement {\n const { width, height } = layout.dimensions;\n\n const svg = createSVGElement('svg') as SVGSVGElement;\n setAttrs(svg, {\n viewBox: `0 0 ${width} ${height}`,\n xmlns: SVG_NS,\n });\n svg.setAttribute('role', layout.a11y.role);\n svg.setAttribute('aria-label', layout.a11y.altText);\n svg.setAttribute('class', 'viz-chart');\n\n // Background\n const bg = createSVGElement('rect');\n setAttrs(bg, {\n x: 0,\n y: 0,\n width,\n height,\n fill: layout.theme.colors.background,\n });\n svg.appendChild(bg);\n\n // Clip path to prevent marks (especially area fills) from overflowing\n // into the chrome region (title/subtitle). Extends full width so\n // end-of-line labels aren't clipped, but constrains vertically.\n const clipId = `viz-clip-${Math.random().toString(36).slice(2, 8)}`;\n const defs = createSVGElement('defs');\n const clipPath = createSVGElement('clipPath');\n clipPath.setAttribute('id', clipId);\n const clipRect = createSVGElement('rect');\n setAttrs(clipRect, {\n x: 0,\n y: layout.area.y,\n width,\n height: layout.area.height + 2,\n });\n clipPath.appendChild(clipRect);\n defs.appendChild(clipPath);\n svg.appendChild(defs);\n\n // Render layers in order (back to front)\n // Axes render outside clip (labels extend beyond chart area)\n renderAxes(svg, layout);\n\n // Marks are clipped to chart area so area fills don't cover chrome\n const clippedGroup = createSVGElement('g');\n clippedGroup.setAttribute('clip-path', `url(#${clipId})`);\n renderMarks(clippedGroup, layout);\n svg.appendChild(clippedGroup);\n\n renderAnnotations(svg, layout);\n renderLegend(svg, layout.legend);\n\n // Chrome renders on top so titles are never obscured by chart elements\n renderChrome(svg, layout);\n\n // Brand renders as a footer item, right-aligned on the source/footer row\n renderBrand(svg, layout);\n\n container.appendChild(svg);\n return svg;\n}\n","/**\n * Table cell renderers: produce DOM elements for each cell type.\n *\n * Each renderer takes a resolved TableCell and returns an HTMLElement\n * (typically a <td>) with appropriate content, styling, and classes.\n */\n\nimport type {\n BarTableCell,\n CategoryTableCell,\n FlagTableCell,\n HeatmapTableCell,\n ImageTableCell,\n SparklineData,\n SparklineTableCell,\n TableCell,\n TextTableCell,\n} from '@opendata-ai/openchart-core';\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\n/** Apply common cell styles (background, color, font weight, font variant). */\nfunction applyCellStyle(td: HTMLTableCellElement, cell: TableCell): void {\n if (cell.style.backgroundColor) {\n td.style.background = cell.style.backgroundColor;\n }\n if (cell.style.color) {\n td.style.color = cell.style.color;\n }\n if (cell.style.fontWeight) {\n td.style.fontWeight = String(cell.style.fontWeight);\n }\n if (cell.style.fontVariant) {\n td.style.fontVariant = cell.style.fontVariant;\n }\n if (cell.aria) {\n td.setAttribute('aria-label', cell.aria);\n }\n}\n\n/**\n * Convert a 2-char ISO 3166-1 alpha-2 country code to a flag emoji.\n * Each letter maps to a Regional Indicator Symbol (offset 0x1F1A5 from ASCII).\n */\nfunction countryToEmoji(code: string): string {\n return [...code.toUpperCase()]\n .map((c) => String.fromCodePoint(c.charCodeAt(0) + 0x1f1a5))\n .join('');\n}\n\n/**\n * Lookup map for common country names by ISO 3166-1 alpha-2 code.\n * Falls back to the raw code for unrecognized values.\n */\nconst COUNTRY_NAMES: Record<string, string> = {\n US: 'United States',\n CN: 'China',\n IN: 'India',\n ID: 'Indonesia',\n BR: 'Brazil',\n PK: 'Pakistan',\n NG: 'Nigeria',\n BD: 'Bangladesh',\n RU: 'Russia',\n MX: 'Mexico',\n JP: 'Japan',\n DE: 'Germany',\n GB: 'United Kingdom',\n FR: 'France',\n IT: 'Italy',\n CA: 'Canada',\n AU: 'Australia',\n KR: 'South Korea',\n ES: 'Spain',\n AR: 'Argentina',\n CO: 'Colombia',\n ZA: 'South Africa',\n TR: 'Turkey',\n SA: 'Saudi Arabia',\n UA: 'Ukraine',\n PL: 'Poland',\n NL: 'Netherlands',\n SE: 'Sweden',\n NO: 'Norway',\n DK: 'Denmark',\n FI: 'Finland',\n CH: 'Switzerland',\n AT: 'Austria',\n BE: 'Belgium',\n PT: 'Portugal',\n IE: 'Ireland',\n NZ: 'New Zealand',\n SG: 'Singapore',\n IL: 'Israel',\n AE: 'United Arab Emirates',\n EG: 'Egypt',\n TH: 'Thailand',\n VN: 'Vietnam',\n PH: 'Philippines',\n MY: 'Malaysia',\n CL: 'Chile',\n PE: 'Peru',\n CZ: 'Czech Republic',\n GR: 'Greece',\n HU: 'Hungary',\n RO: 'Romania',\n ET: 'Ethiopia',\n};\n\n/** Get a human-readable name for a country code. */\nfunction getCountryName(code: string): string {\n return COUNTRY_NAMES[code.toUpperCase()] || code.toUpperCase();\n}\n\n/**\n * Describe a sparkline trend for screen readers.\n * Compares first and last values to determine direction.\n */\nfunction describeSparklineTrend(data: SparklineData): string {\n if (data.type === 'line' && data.points.length >= 2) {\n const first = data.points[0].y;\n const last = data.points[data.points.length - 1].y;\n const count = data.points.length;\n if (last > first) return `Sparkline with ${count} points, trending upward`;\n if (last < first) return `Sparkline with ${count} points, trending downward`;\n return `Sparkline with ${count} points, roughly flat`;\n }\n if ((data.type === 'bar' || data.type === 'column') && data.bars.length > 0) {\n return `${data.type === 'column' ? 'Column' : 'Bar'} sparkline with ${data.bars.length} values`;\n }\n return 'Sparkline';\n}\n\n// ---------------------------------------------------------------------------\n// Cell renderers\n// ---------------------------------------------------------------------------\n\n/** Render a plain text cell. */\nexport function renderTextCell(cell: TextTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.textContent = cell.formattedValue;\n applyCellStyle(td, cell);\n return td;\n}\n\n/** Render a heatmap-colored cell. */\nexport function renderHeatmapCell(cell: HeatmapTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.textContent = cell.formattedValue;\n applyCellStyle(td, cell);\n return td;\n}\n\n/** Render a category-colored cell. */\nexport function renderCategoryCell(cell: CategoryTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.textContent = cell.formattedValue;\n applyCellStyle(td, cell);\n return td;\n}\n\n/** Render a cell with an inline bar visualization. */\nexport function renderBarCell(cell: BarTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.className = 'viz-table-bar';\n applyCellStyle(td, cell);\n\n const fill = document.createElement('div');\n fill.className = 'viz-table-bar-fill';\n fill.style.width = `${Math.round(cell.barWidth * 100)}%`;\n fill.style.left = `${Math.round(cell.barOffset * 100)}%`;\n fill.style.background = cell.barColor;\n td.appendChild(fill);\n\n const valueSpan = document.createElement('span');\n valueSpan.className = 'viz-table-bar-value';\n valueSpan.textContent = cell.formattedValue;\n td.appendChild(valueSpan);\n\n return td;\n}\n\n/**\n * Format a sparkline endpoint value for display.\n * Keeps it compact: no decimals for values >= 100, one decimal otherwise.\n */\nfunction formatSparklineValue(v: number): string {\n if (Math.abs(v) >= 1000) {\n return v.toLocaleString('en-US', { maximumFractionDigits: 0 });\n }\n if (Math.abs(v) >= 100) {\n return v.toFixed(0);\n }\n return v.toFixed(1);\n}\n\n/** Render a cell with an inline sparkline SVG. */\nexport function renderSparklineCell(cell: SparklineTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n applyCellStyle(td, cell);\n\n const sparklineData = cell.sparklineData;\n if (!sparklineData || sparklineData.count === 0) {\n td.textContent = cell.formattedValue || '';\n return td;\n }\n\n // Add aria-label describing the trend for screen readers\n const trendDescription = describeSparklineTrend(sparklineData);\n if (!td.getAttribute('aria-label')) {\n td.setAttribute('aria-label', trendDescription);\n }\n\n const wrapper = document.createElement('span');\n wrapper.className = 'viz-table-sparkline';\n\n const svgNS = 'http://www.w3.org/2000/svg';\n\n if (sparklineData.type === 'line') {\n // Infrographic-style sparkline: SVG polyline fills cell width via percentage x-coords.\n // Dots are HTML elements (not SVG circles) to avoid aspect-ratio distortion.\n // Labels are HTML below the SVG.\n const svgH = 28;\n const padY = 4;\n const lineH = svgH - padY * 2;\n\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('aria-hidden', 'true');\n svg.setAttribute('xmlns', svgNS);\n svg.style.height = `${svgH}px`;\n\n // Compute y positions in pixel space (no viewBox scaling)\n const yPositions = sparklineData.points.map((p) => padY + (1 - p.y) * lineH);\n\n // SVG polyline doesn't support % in points attribute, so use a viewBox\n // with a wide aspect ratio and map x to 0-1000 range. preserveAspectRatio=\"none\"\n // stretches the x-axis to fill the cell width. Y values stay in pixel space\n // since viewBox height matches the SVG height. Only the polyline is in the SVG;\n // dots are HTML elements to avoid circle distortion from the non-uniform scaling.\n const viewW = 1000;\n svg.setAttribute('viewBox', `0 0 ${viewW} ${svgH}`);\n svg.setAttribute('preserveAspectRatio', 'none');\n\n const ptsScaled = sparklineData.points.map((p, i) => ({\n x: p.x * viewW,\n y: yPositions[i],\n }));\n const scaledPointsStr = ptsScaled.map((p) => `${p.x},${p.y}`).join(' ');\n\n const polyline = document.createElementNS(svgNS, 'polyline');\n polyline.setAttribute('points', scaledPointsStr);\n polyline.setAttribute('fill', 'none');\n polyline.setAttribute('stroke', sparklineData.color);\n polyline.setAttribute('stroke-width', '1.5');\n polyline.setAttribute('stroke-linejoin', 'round');\n polyline.setAttribute('vector-effect', 'non-scaling-stroke');\n svg.appendChild(polyline);\n\n wrapper.appendChild(svg);\n\n // HTML dots at start and end (positioned absolutely over the SVG)\n const firstY = yPositions[0];\n const lastY = yPositions[yPositions.length - 1];\n const dotSize = 5;\n\n const startDot = document.createElement('span');\n startDot.className = 'viz-table-sparkline-dot';\n startDot.style.left = '0';\n startDot.style.top = `${firstY - dotSize / 2}px`;\n startDot.style.background = sparklineData.color;\n wrapper.appendChild(startDot);\n\n const endDot = document.createElement('span');\n endDot.className = 'viz-table-sparkline-dot';\n endDot.style.right = '0';\n endDot.style.top = `${lastY - dotSize / 2}px`;\n endDot.style.background = sparklineData.color;\n wrapper.appendChild(endDot);\n\n // HTML labels below the SVG, positioned at left and right edges\n const labelsRow = document.createElement('span');\n labelsRow.className = 'viz-table-sparkline-labels';\n labelsRow.style.color = sparklineData.color;\n\n const startLabel = document.createElement('span');\n startLabel.textContent = formatSparklineValue(sparklineData.startValue);\n labelsRow.appendChild(startLabel);\n\n const endLabel = document.createElement('span');\n endLabel.textContent = formatSparklineValue(sparklineData.endValue);\n labelsRow.appendChild(endLabel);\n\n wrapper.appendChild(labelsRow);\n } else if (sparklineData.type === 'column') {\n // Vertical bars at proportional heights\n const width = 80;\n const height = 20;\n const padding = 2;\n const innerW = width - padding * 2;\n const innerH = height - padding * 2;\n\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('width', String(width));\n svg.setAttribute('height', String(height));\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n svg.setAttribute('aria-hidden', 'true');\n\n const barCount = sparklineData.bars.length;\n if (barCount > 0) {\n const gap = 1;\n const barW = Math.max(1, (innerW - gap * (barCount - 1)) / barCount);\n\n for (let i = 0; i < barCount; i++) {\n const barH = Math.max(1, sparklineData.bars[i] * innerH);\n const x = padding + i * (barW + gap);\n const y = padding + innerH - barH;\n\n const rect = document.createElementNS(svgNS, 'rect');\n rect.setAttribute('x', String(x));\n rect.setAttribute('y', String(y));\n rect.setAttribute('width', String(barW));\n rect.setAttribute('height', String(barH));\n rect.setAttribute('rx', '1.5');\n rect.setAttribute('fill', sparklineData.color);\n svg.appendChild(rect);\n }\n }\n\n wrapper.appendChild(svg);\n } else {\n // 'bar' type: horizontal bars at proportional widths\n const width = 80;\n const height = 20;\n const padding = 2;\n const innerW = width - padding * 2;\n const innerH = height - padding * 2;\n\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('width', String(width));\n svg.setAttribute('height', String(height));\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n svg.setAttribute('aria-hidden', 'true');\n\n const barCount = sparklineData.bars.length;\n if (barCount > 0) {\n const gap = 1;\n const barH = Math.max(1, (innerH - gap * (barCount - 1)) / barCount);\n\n for (let i = 0; i < barCount; i++) {\n const barW = Math.max(1, sparklineData.bars[i] * innerW);\n const x = padding;\n const y = padding + i * (barH + gap);\n\n const rect = document.createElementNS(svgNS, 'rect');\n rect.setAttribute('x', String(x));\n rect.setAttribute('y', String(y));\n rect.setAttribute('width', String(barW));\n rect.setAttribute('height', String(barH));\n rect.setAttribute('rx', '1.5');\n rect.setAttribute('fill', sparklineData.color);\n svg.appendChild(rect);\n }\n }\n\n wrapper.appendChild(svg);\n }\n\n td.appendChild(wrapper);\n\n return td;\n}\n\n/** Render a cell with an image. */\nexport function renderImageCell(cell: ImageTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n applyCellStyle(td, cell);\n\n const wrapper = document.createElement('span');\n wrapper.className = `viz-table-image${cell.rounded ? ' viz-table-image-rounded' : ''}`;\n\n const img = document.createElement('img');\n img.src = cell.src;\n img.alt = cell.formattedValue || '';\n img.width = cell.imageWidth;\n img.height = cell.imageHeight;\n img.loading = 'lazy';\n\n wrapper.appendChild(img);\n td.appendChild(wrapper);\n\n return td;\n}\n\n/** Render a cell with a country flag emoji. */\nexport function renderFlagCell(cell: FlagTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n applyCellStyle(td, cell);\n\n const span = document.createElement('span');\n span.className = 'viz-table-flag';\n span.setAttribute('role', 'img');\n\n if (cell.countryCode && cell.countryCode.length === 2) {\n const countryName = getCountryName(cell.countryCode);\n span.textContent = countryToEmoji(cell.countryCode);\n span.setAttribute('aria-label', `Flag: ${countryName}`);\n } else {\n span.textContent = cell.formattedValue;\n span.setAttribute('aria-label', cell.formattedValue);\n }\n\n td.appendChild(span);\n\n return td;\n}\n\n// ---------------------------------------------------------------------------\n// Dispatcher\n// ---------------------------------------------------------------------------\n\n/** Render any table cell by dispatching on its cellType. */\nexport function renderCell(cell: TableCell): HTMLTableCellElement {\n switch (cell.cellType) {\n case 'text':\n return renderTextCell(cell);\n case 'heatmap':\n return renderHeatmapCell(cell);\n case 'category':\n return renderCategoryCell(cell);\n case 'bar':\n return renderBarCell(cell);\n case 'sparkline':\n return renderSparklineCell(cell);\n case 'image':\n return renderImageCell(cell);\n case 'flag':\n return renderFlagCell(cell);\n default:\n // Exhaustive check fallback\n return renderTextCell(cell as TextTableCell);\n }\n}\n","/**\n * Table keyboard navigation: arrow-key cell navigation, Enter to sort,\n * Escape to clear search, and aria-activedescendant management.\n *\n * Designed to be wired up by table-mount.ts after render. Returns a\n * cleanup function to remove listeners on re-render or destroy.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface KeyboardNavOptions {\n /** The wrapper element containing the whole table UI. */\n wrapper: HTMLElement;\n /** Callback to trigger sort on a column. */\n onSort: (columnKey: string) => void;\n /** Callback to clear search and return focus to the table body. */\n onClearSearch: () => void;\n /** Callback to announce text to screen readers via the live region. */\n onAnnounce: (message: string) => void;\n}\n\ninterface CellPosition {\n row: number;\n col: number;\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\n/**\n * Attach keyboard navigation to a rendered table.\n *\n * @returns A cleanup function that removes all event listeners.\n */\nexport function attachKeyboardNav(options: KeyboardNavOptions): () => void {\n const { wrapper, onSort, onClearSearch, onAnnounce } = options;\n\n let focusedCell: CellPosition = { row: -1, col: 0 };\n\n const table = wrapper.querySelector('table');\n if (!table) return () => {};\n\n const tbody = table.querySelector('tbody');\n const thead = table.querySelector('thead');\n if (!tbody || !thead) return () => {};\n\n // Make tbody focusable\n tbody.setAttribute('tabindex', '0');\n\n function getRows(): HTMLTableRowElement[] {\n if (!tbody) return [];\n return Array.from(tbody.querySelectorAll('tr'));\n }\n\n function getHeaderCells(): HTMLTableCellElement[] {\n if (!thead) return [];\n const headerRow = thead.querySelector('tr');\n if (!headerRow) return [];\n return Array.from(headerRow.querySelectorAll('th'));\n }\n\n function getCellsInRow(tr: HTMLTableRowElement): HTMLTableCellElement[] {\n return Array.from(tr.querySelectorAll('td'));\n }\n\n function getColCount(): number {\n const rows = getRows();\n if (rows.length === 0) return getHeaderCells().length;\n return getCellsInRow(rows[0]).length;\n }\n\n function clearFocusHighlight(): void {\n const prev = wrapper.querySelector('.viz-table-cell-focus');\n if (prev) {\n prev.classList.remove('viz-table-cell-focus');\n prev.removeAttribute('id');\n }\n }\n\n function setFocusedCell(row: number, col: number): void {\n clearFocusHighlight();\n const rows = getRows();\n const colCount = getColCount();\n\n // Clamp values\n if (rows.length === 0) return;\n row = Math.max(0, Math.min(row, rows.length - 1));\n col = Math.max(0, Math.min(col, colCount - 1));\n\n focusedCell = { row, col };\n\n // Highlight the cell\n const tr = rows[row];\n if (!tr) return;\n const cells = getCellsInRow(tr);\n const cell = cells[col];\n if (!cell) return;\n\n const cellId = `viz-cell-${row}-${col}`;\n cell.id = cellId;\n cell.classList.add('viz-table-cell-focus');\n cell.setAttribute('data-row', String(row));\n cell.setAttribute('data-col', String(col));\n\n // Set aria-activedescendant on tbody\n if (tbody) {\n tbody.setAttribute('aria-activedescendant', cellId);\n }\n\n // Scroll cell into view if needed\n cell.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n\n function handleTbodyFocus(): void {\n // When tbody receives focus, highlight the first cell (or restore last)\n if (focusedCell.row < 0) {\n setFocusedCell(0, 0);\n } else {\n setFocusedCell(focusedCell.row, focusedCell.col);\n }\n }\n\n function handleTbodyKeydown(e: KeyboardEvent): void {\n const rows = getRows();\n if (rows.length === 0) return;\n\n const colCount = getColCount();\n const { row, col } = focusedCell;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (row < rows.length - 1) {\n setFocusedCell(row + 1, col);\n }\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (row > 0) {\n setFocusedCell(row - 1, col);\n } else {\n // Move focus to the header\n focusHeaderCell(col);\n }\n break;\n case 'ArrowRight':\n e.preventDefault();\n if (col < colCount - 1) {\n setFocusedCell(row, col + 1);\n }\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (col > 0) {\n setFocusedCell(row, col - 1);\n }\n break;\n case 'Home':\n e.preventDefault();\n setFocusedCell(row, 0);\n break;\n case 'End':\n e.preventDefault();\n setFocusedCell(row, colCount - 1);\n break;\n }\n }\n\n // Header cell keyboard handling\n function focusHeaderCell(col: number): void {\n const headers = getHeaderCells();\n if (col >= 0 && col < headers.length) {\n clearFocusHighlight();\n headers[col].focus();\n }\n }\n\n function handleHeaderKeydown(e: KeyboardEvent): void {\n const th = e.currentTarget as HTMLTableCellElement;\n const headers = getHeaderCells();\n const colIndex = headers.indexOf(th);\n if (colIndex < 0) return;\n\n switch (e.key) {\n case 'ArrowRight':\n e.preventDefault();\n if (colIndex < headers.length - 1) {\n headers[colIndex + 1].focus();\n }\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (colIndex > 0) {\n headers[colIndex - 1].focus();\n }\n break;\n case 'ArrowDown':\n e.preventDefault();\n // Move focus to first body row at this column\n if (tbody) {\n tbody.focus();\n setFocusedCell(0, colIndex);\n }\n break;\n case 'Enter':\n case ' ': {\n e.preventDefault();\n const sortColumn = th.getAttribute('data-column');\n const sortBtn = th.querySelector('[data-sort-column]');\n if (sortColumn && sortBtn) {\n onSort(sortColumn);\n }\n break;\n }\n }\n }\n\n // Search escape handling\n const searchInput = wrapper.querySelector('.viz-table-search input') as HTMLInputElement | null;\n\n function handleSearchKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClearSearch();\n // Return focus to tbody\n if (tbody) {\n tbody.focus();\n onAnnounce('Search cleared');\n }\n }\n }\n\n // Wire up event listeners\n tbody.addEventListener('focus', handleTbodyFocus);\n tbody.addEventListener('keydown', handleTbodyKeydown as EventListener);\n\n // Make header cells focusable and wire keyboard\n const headerCells = getHeaderCells();\n for (const th of headerCells) {\n th.setAttribute('tabindex', '0');\n th.addEventListener('keydown', handleHeaderKeydown as EventListener);\n }\n\n if (searchInput) {\n searchInput.addEventListener('keydown', handleSearchKeydown as EventListener);\n }\n\n // Cleanup\n return () => {\n tbody.removeEventListener('focus', handleTbodyFocus);\n tbody.removeEventListener('keydown', handleTbodyKeydown as EventListener);\n\n for (const th of headerCells) {\n th.removeEventListener('keydown', handleHeaderKeydown as EventListener);\n }\n\n if (searchInput) {\n searchInput.removeEventListener('keydown', handleSearchKeydown as EventListener);\n }\n\n clearFocusHighlight();\n };\n}\n","/**\n * Table mount API: the main entry point for vanilla JS table usage.\n *\n * createTable() takes a container, spec, and options, compiles the table,\n * renders it as HTML, sets up responsive resizing, sort/search/pagination\n * interactivity, and returns a TableInstance with update/resize/export/destroy.\n *\n * Supports both controlled and uncontrolled modes:\n * - Uncontrolled (default): manages sort/search/page internally\n * - Controlled: reads state from externalState, fires onStateChange\n */\n\nimport type {\n CompileTableOptions,\n DarkMode,\n SortState,\n TableLayout,\n TableSpec,\n ThemeConfig,\n} from '@opendata-ai/openchart-core';\nimport { getBreakpoint } from '@opendata-ai/openchart-core';\nimport { compileTable } from '@opendata-ai/openchart-engine';\nimport { observeResize } from './resize-observer';\nimport { attachKeyboardNav } from './table-keyboard';\nimport { renderTable } from './table-renderer';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TableState {\n sort: SortState | null;\n search: string;\n page: number;\n}\n\nexport interface TableMountOptions {\n theme?: ThemeConfig;\n darkMode?: DarkMode;\n responsive?: boolean;\n onRowClick?: (row: Record<string, unknown>) => void;\n onStateChange?: (state: TableState) => void;\n externalState?: { sort?: SortState | null; search?: string; page?: number };\n}\n\nexport interface TableInstance {\n update(spec: TableSpec): void;\n resize(): void;\n export(format: 'csv'): string;\n getState(): TableState;\n setState(partial: Partial<TableState>): void;\n destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Dark mode resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveDarkMode(mode?: DarkMode): boolean {\n if (mode === 'force') return true;\n if (mode === 'off' || mode === undefined) return false;\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// CSV export\n// ---------------------------------------------------------------------------\n\nfunction csvEscape(value: string): string {\n if (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\n// Sort cycling\n// ---------------------------------------------------------------------------\n\n/**\n * Cycle sort state: clicking same column cycles none -> asc -> desc -> none.\n * Clicking a different column resets to asc.\n */\nfunction cycleSort(current: SortState | null, column: string): SortState | null {\n if (!current || current.column !== column) {\n return { column, direction: 'asc' };\n }\n if (current.direction === 'asc') {\n return { column, direction: 'desc' };\n }\n // desc -> none\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Main API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a table instance from a spec and mount it into a container.\n *\n * @param container - The DOM element to render into.\n * @param spec - The table spec.\n * @param options - Mount options.\n * @returns A TableInstance with update/resize/export/destroy methods.\n */\nexport function createTable(\n container: HTMLElement,\n spec: TableSpec,\n options?: TableMountOptions,\n): TableInstance {\n let currentSpec = spec;\n let currentLayout: TableLayout;\n let wrapperElement: HTMLElement | null = null;\n let disconnectResize: (() => void) | null = null;\n let cleanupKeyboard: (() => void) | null = null;\n let destroyed = false;\n\n // Internal state (used in uncontrolled mode)\n const internalState: TableState = {\n sort: null,\n search: '',\n page: 0,\n };\n\n // Debounce timers\n let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n let resizeDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const isControlled = options?.externalState !== undefined;\n\n function getState(): TableState {\n if (isControlled && options?.externalState) {\n return {\n sort: options.externalState.sort ?? null,\n search: options.externalState.search ?? '',\n page: options.externalState.page ?? 0,\n };\n }\n return { ...internalState };\n }\n\n function updateState(partial: Partial<TableState>): void {\n if (isControlled) {\n // In controlled mode, notify parent\n const current = getState();\n const next: TableState = {\n sort: partial.sort !== undefined ? partial.sort : current.sort,\n search: partial.search !== undefined ? partial.search : current.search,\n page: partial.page !== undefined ? partial.page : current.page,\n };\n options?.onStateChange?.(next);\n } else {\n // In uncontrolled mode, update internal state\n if (partial.sort !== undefined) internalState.sort = partial.sort;\n if (partial.search !== undefined) internalState.search = partial.search;\n if (partial.page !== undefined) internalState.page = partial.page;\n options?.onStateChange?.({ ...internalState });\n }\n }\n\n function compile(): TableLayout {\n const state = getState();\n const darkMode = resolveDarkMode(options?.darkMode);\n const { width } = getContainerDimensions();\n\n const compileOpts: CompileTableOptions = {\n width,\n height: 600,\n theme: options?.theme,\n darkMode,\n sort: state.sort ?? undefined,\n search: state.search || undefined,\n page: state.page,\n };\n\n return compileTable(currentSpec, compileOpts);\n }\n\n function getContainerDimensions(): { width: number; height: number } {\n const rect = container.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n /**\n * Announce a message to screen readers via the live region.\n */\n function announce(message: string): void {\n if (!wrapperElement) return;\n const liveRegion = wrapperElement.querySelector('.viz-table-live-region');\n if (liveRegion) {\n liveRegion.textContent = message;\n }\n }\n\n /**\n * Apply responsive breakpoint class based on container width.\n * Only auto-adds compact mode at small sizes. Never removes compact\n * if the spec explicitly requests it (layout.compact === true).\n */\n function applyBreakpointClass(): void {\n if (!wrapperElement) return;\n const { width } = getContainerDimensions();\n const bp = getBreakpoint(width);\n\n if (bp === 'compact' || bp === 'medium') {\n wrapperElement.classList.add('viz-table--compact');\n } else if (!currentLayout?.compact) {\n // Only remove compact if the spec didn't explicitly request it\n wrapperElement.classList.remove('viz-table--compact');\n }\n }\n\n function render(): void {\n if (destroyed) return;\n\n try {\n // Clean up previous keyboard nav\n if (cleanupKeyboard) {\n cleanupKeyboard();\n cleanupKeyboard = null;\n }\n\n // Clean up previous render\n if (wrapperElement?.parentNode) {\n wrapperElement.parentNode.removeChild(wrapperElement);\n wrapperElement = null;\n }\n\n currentLayout = compile();\n wrapperElement = renderTable(currentLayout, container);\n\n // Apply dark mode class\n const isDark = resolveDarkMode(options?.darkMode);\n if (isDark) {\n container.classList.add('viz-dark');\n } else {\n container.classList.remove('viz-dark');\n }\n\n // Apply responsive breakpoint\n applyBreakpointClass();\n\n // Add clickable class if onRowClick is provided\n if (options?.onRowClick) {\n wrapperElement.classList.add('viz-table--clickable');\n }\n\n // Wire up event handlers\n wireEvents();\n\n // Wire up keyboard navigation\n if (wrapperElement) {\n cleanupKeyboard = attachKeyboardNav({\n wrapper: wrapperElement,\n onSort: (columnKey: string) => {\n const state = getState();\n const newSort = cycleSort(state.sort, columnKey);\n updateState({ sort: newSort, page: 0 });\n\n // Announce sort change\n if (newSort) {\n const dir = newSort.direction === 'asc' ? 'ascending' : 'descending';\n announce(`Sorted by ${columnKey} ${dir}`);\n } else {\n announce('Sort cleared');\n }\n\n if (!isControlled) {\n rerender();\n }\n },\n onClearSearch: () => {\n updateState({ search: '', page: 0 });\n if (!isControlled) {\n rerender();\n }\n },\n onAnnounce: announce,\n });\n }\n } catch (err) {\n console.error('[viz] Table render failed:', err);\n }\n }\n\n function wireEvents(): void {\n if (!wrapperElement) return;\n\n // Sort click handlers on thead buttons\n const sortBtns = wrapperElement.querySelectorAll('[data-sort-column]');\n for (const btn of sortBtns) {\n btn.addEventListener('click', handleSortClick);\n }\n\n // Search input\n const searchInput = wrapperElement.querySelector(\n '.viz-table-search input',\n ) as HTMLInputElement | null;\n if (searchInput) {\n searchInput.addEventListener('input', handleSearchInput);\n }\n\n // Pagination buttons\n const pageButtons = wrapperElement.querySelectorAll('[data-page-action]');\n for (const btn of pageButtons) {\n btn.addEventListener('click', handlePageClick);\n }\n\n // Row click\n if (options?.onRowClick) {\n const rows = wrapperElement.querySelectorAll('tbody tr');\n for (const row of rows) {\n row.addEventListener('click', handleRowClick);\n }\n }\n }\n\n function handleSortClick(e: Event): void {\n const btn = e.currentTarget as HTMLElement;\n const column = btn.getAttribute('data-sort-column');\n if (!column) return;\n\n const state = getState();\n const newSort = cycleSort(state.sort, column);\n\n updateState({ sort: newSort, page: 0 });\n\n // Announce sort change for screen readers\n if (newSort) {\n const dir = newSort.direction === 'asc' ? 'ascending' : 'descending';\n announce(`Sorted by ${column} ${dir}`);\n } else {\n announce('Sort cleared');\n }\n\n if (!isControlled) {\n rerender();\n }\n }\n\n function handleSearchInput(e: Event): void {\n const input = e.target as HTMLInputElement;\n const query = input.value;\n\n if (searchDebounceTimer !== null) {\n clearTimeout(searchDebounceTimer);\n }\n\n searchDebounceTimer = setTimeout(() => {\n searchDebounceTimer = null;\n updateState({ search: query, page: 0 });\n\n if (!isControlled) {\n rerender();\n // Announce search result count\n const rowCount = currentLayout?.rows?.length ?? 0;\n if (query) {\n announce(`${rowCount} result${rowCount !== 1 ? 's' : ''} found`);\n }\n }\n }, 200);\n }\n\n function handlePageClick(e: Event): void {\n const btn = e.currentTarget as HTMLElement;\n const action = btn.getAttribute('data-page-action');\n const state = getState();\n\n if (action === 'prev' && state.page > 0) {\n updateState({ page: state.page - 1 });\n } else if (action === 'next') {\n updateState({ page: state.page + 1 });\n }\n\n if (!isControlled) {\n rerender();\n }\n }\n\n function handleRowClick(e: Event): void {\n const tr = e.currentTarget as HTMLElement;\n const rowId = tr.getAttribute('data-row-id');\n if (!rowId || !currentLayout) return;\n\n const row = currentLayout.rows.find((r) => r.id === rowId);\n if (row) {\n options?.onRowClick?.(row.data);\n }\n }\n\n /**\n * Re-render the table, preserving search input focus across the DOM rebuild.\n */\n function rerender(): void {\n if (destroyed) return;\n\n // Capture current search input state before re-render\n const searchInput = wrapperElement?.querySelector(\n '.viz-table-search input',\n ) as HTMLInputElement | null;\n const hadFocus = searchInput && document.activeElement === searchInput;\n const selectionStart = searchInput?.selectionStart ?? 0;\n const selectionEnd = searchInput?.selectionEnd ?? 0;\n\n render();\n\n // Restore search focus after re-render\n if (hadFocus) {\n const newInput = wrapperElement?.querySelector(\n '.viz-table-search input',\n ) as HTMLInputElement | null;\n if (newInput) {\n newInput.focus();\n newInput.setSelectionRange(selectionStart, selectionEnd);\n }\n }\n }\n\n function update(newSpec: TableSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n render();\n }\n\n function resize(): void {\n if (destroyed) return;\n render();\n }\n\n function doExport(format: 'csv'): string {\n if (format !== 'csv') {\n throw new Error(`Unsupported export format: ${format}`);\n }\n\n // Export all filtered/sorted data (not just current page)\n // Re-compile without pagination\n const state = getState();\n const darkMode = resolveDarkMode(options?.darkMode);\n const { width } = getContainerDimensions();\n\n const fullLayout = compileTable(currentSpec, {\n width,\n height: 600,\n theme: options?.theme,\n darkMode,\n sort: state.sort ?? undefined,\n search: state.search || undefined,\n // No page/pageSize: get all rows\n });\n\n const headers = fullLayout.columns.map((c) => c.label);\n const csvRows = [headers.map(csvEscape).join(',')];\n\n for (const row of fullLayout.rows) {\n const values = row.cells.map((cell) => csvEscape(cell.formattedValue));\n csvRows.push(values.join(','));\n }\n\n return csvRows.join('\\n');\n }\n\n function setState(partial: Partial<TableState>): void {\n if (destroyed) return;\n\n if (partial.sort !== undefined) internalState.sort = partial.sort;\n if (partial.search !== undefined) internalState.search = partial.search;\n if (partial.page !== undefined) internalState.page = partial.page;\n\n render();\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (cleanupKeyboard) {\n cleanupKeyboard();\n cleanupKeyboard = null;\n }\n if (searchDebounceTimer !== null) {\n clearTimeout(searchDebounceTimer);\n searchDebounceTimer = null;\n }\n if (resizeDebounceTimer !== null) {\n clearTimeout(resizeDebounceTimer);\n resizeDebounceTimer = null;\n }\n if (disconnectResize) {\n disconnectResize();\n disconnectResize = null;\n }\n if (wrapperElement?.parentNode) {\n wrapperElement.parentNode.removeChild(wrapperElement);\n wrapperElement = null;\n }\n container.classList.remove('viz-dark');\n }\n\n // Initial render\n render();\n\n // Set up responsive resize with breakpoint detection\n if (options?.responsive !== false) {\n disconnectResize = observeResize(container, () => {\n if (resizeDebounceTimer !== null) {\n clearTimeout(resizeDebounceTimer);\n }\n resizeDebounceTimer = setTimeout(() => {\n resizeDebounceTimer = null;\n // Update breakpoint class without full re-render when possible\n applyBreakpointClass();\n resize();\n }, 100);\n });\n }\n\n return {\n update,\n resize,\n export: doExport,\n getState,\n setState,\n destroy,\n };\n}\n","/**\n * Table renderer: produces semantic HTML from a TableLayout.\n *\n * renderTable() creates the full DOM structure: chrome, search bar,\n * scrollable table with sticky column support, pagination, and footer.\n * The returned element replaces or appends to the given container.\n */\n\nimport type { ResolvedColumn, TableLayout, TableRow } from '@opendata-ai/openchart-core';\nimport { renderCell } from './renderers/table-cells';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BRAND_URL = 'https://tryopendata.ai';\nconst BRAND_FONT_SIZE = 20;\n\n// ---------------------------------------------------------------------------\n// Chrome rendering\n// ---------------------------------------------------------------------------\n\n/** Create chrome (title/subtitle or source/footer) block. */\nfunction renderChromeBlock(\n layout: TableLayout,\n position: 'header' | 'footer',\n): HTMLDivElement | null {\n const chrome = layout.chrome;\n\n if (position === 'header') {\n if (!chrome.title && !chrome.subtitle) return null;\n\n const div = document.createElement('div');\n div.className = 'viz-chrome';\n\n if (chrome.title) {\n const h = document.createElement('div');\n h.className = 'viz-table-title';\n h.textContent = chrome.title.text;\n div.appendChild(h);\n }\n if (chrome.subtitle) {\n const sub = document.createElement('div');\n sub.className = 'viz-table-subtitle';\n sub.textContent = chrome.subtitle.text;\n div.appendChild(sub);\n }\n\n return div;\n }\n\n // Footer position\n if (!chrome.source && !chrome.footer) return null;\n\n const div = document.createElement('div');\n div.className = 'viz-chrome viz-chrome-footer';\n\n if (chrome.source) {\n const src = document.createElement('div');\n src.className = 'viz-table-source';\n src.textContent = chrome.source.text;\n div.appendChild(src);\n }\n if (chrome.footer) {\n const foot = document.createElement('div');\n foot.className = 'viz-table-footer-text';\n foot.textContent = chrome.footer.text;\n div.appendChild(foot);\n }\n\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Table head\n// ---------------------------------------------------------------------------\n\nfunction renderThead(\n columns: ResolvedColumn[],\n sort: TableLayout['sort'],\n): HTMLTableSectionElement {\n const thead = document.createElement('thead');\n const tr = document.createElement('tr');\n tr.setAttribute('role', 'row');\n\n for (const col of columns) {\n const th = document.createElement('th');\n th.setAttribute('scope', 'col');\n th.setAttribute('role', 'columnheader');\n th.style.textAlign = col.align;\n th.style.width = `${col.width}px`;\n\n // Sort state: use 'ascending'/'descending' per WAI-ARIA spec\n let ariaSortValue: string = 'none';\n if (sort && sort.column === col.key) {\n ariaSortValue = sort.direction === 'asc' ? 'ascending' : 'descending';\n }\n th.setAttribute('aria-sort', ariaSortValue);\n th.setAttribute('data-column', col.key);\n\n // Label\n const labelSpan = document.createTextNode(col.label);\n th.appendChild(labelSpan);\n\n // Sort button\n if (col.sortable) {\n const btn = document.createElement('button');\n btn.className = 'viz-table-sort-btn';\n btn.setAttribute('aria-label', `Sort by ${col.label}`);\n btn.setAttribute('data-sort-column', col.key);\n btn.type = 'button';\n th.appendChild(btn);\n }\n\n tr.appendChild(th);\n }\n\n thead.appendChild(tr);\n return thead;\n}\n\n// ---------------------------------------------------------------------------\n// Table body\n// ---------------------------------------------------------------------------\n\nfunction renderTbody(rows: TableRow[], columns: ResolvedColumn[]): HTMLTableSectionElement {\n const tbody = document.createElement('tbody');\n\n for (let r = 0; r < rows.length; r++) {\n const row = rows[r];\n const tr = document.createElement('tr');\n tr.setAttribute('role', 'row');\n tr.setAttribute('data-row-id', row.id);\n\n for (let c = 0; c < columns.length; c++) {\n const cell = row.cells[c];\n if (!cell) continue;\n\n const td = renderCell(cell);\n td.setAttribute('role', 'gridcell');\n td.style.textAlign = columns[c].align;\n tr.appendChild(td);\n }\n\n tbody.appendChild(tr);\n }\n\n return tbody;\n}\n\n// ---------------------------------------------------------------------------\n// Search bar\n// ---------------------------------------------------------------------------\n\nfunction renderSearchBar(layout: TableLayout): HTMLDivElement | null {\n if (!layout.search.enabled) return null;\n\n const div = document.createElement('div');\n div.className = 'viz-table-search';\n\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = layout.search.placeholder;\n input.setAttribute('aria-label', 'Search table');\n input.value = layout.search.query;\n\n div.appendChild(input);\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Pagination\n// ---------------------------------------------------------------------------\n\nfunction renderPagination(layout: TableLayout): HTMLDivElement | null {\n if (!layout.pagination) return null;\n\n const { page, pageSize, totalRows, totalPages } = layout.pagination;\n\n const div = document.createElement('div');\n div.className = 'viz-table-pagination';\n\n const info = document.createElement('span');\n info.className = 'viz-table-pagination-info';\n\n if (totalRows === 0) {\n info.textContent = 'No results';\n } else {\n const start = page * pageSize + 1;\n const end = Math.min((page + 1) * pageSize, totalRows);\n info.textContent = `Showing ${start}-${end} of ${totalRows}`;\n }\n\n div.appendChild(info);\n\n const btnGroup = document.createElement('span');\n btnGroup.className = 'viz-table-pagination-btns';\n\n const prevBtn = document.createElement('button');\n prevBtn.setAttribute('aria-label', 'Previous page');\n prevBtn.setAttribute('data-page-action', 'prev');\n prevBtn.textContent = 'Prev';\n prevBtn.disabled = page <= 0;\n btnGroup.appendChild(prevBtn);\n\n const nextBtn = document.createElement('button');\n nextBtn.setAttribute('aria-label', 'Next page');\n nextBtn.setAttribute('data-page-action', 'next');\n nextBtn.textContent = 'Next';\n nextBtn.disabled = page >= totalPages - 1;\n btnGroup.appendChild(nextBtn);\n\n div.appendChild(btnGroup);\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Empty state\n// ---------------------------------------------------------------------------\n\nfunction renderEmptyState(message: string): HTMLDivElement {\n const div = document.createElement('div');\n div.className = 'viz-table-empty';\n div.setAttribute('aria-live', 'polite');\n div.textContent = message;\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Main render\n// ---------------------------------------------------------------------------\n\n/**\n * Render a TableLayout into a full DOM structure.\n *\n * @param layout - The compiled table layout.\n * @param container - The container element to render into.\n * @returns The wrapper element that was created.\n */\nexport function renderTable(layout: TableLayout, container: HTMLElement): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'viz-table-wrapper';\n\n // Apply theme colors as CSS custom properties so table CSS picks them up.\n // Without this, dark-background themes show invisible text since the\n // CSS defaults (--viz-text etc.) are light-mode values.\n const { theme, chrome } = layout;\n if (theme) {\n const s = wrapper.style;\n s.setProperty('--viz-bg', theme.colors.background);\n s.setProperty('--viz-text', theme.colors.text);\n s.setProperty('--viz-text-secondary', theme.colors.axis ?? theme.colors.text);\n s.setProperty('--viz-text-muted', theme.colors.axis ?? theme.colors.text);\n s.setProperty('--viz-gridline', theme.colors.gridline);\n s.setProperty('--viz-border', theme.colors.gridline);\n s.setProperty('--viz-font-family', theme.fonts.family);\n s.fontFamily = theme.fonts.family;\n }\n\n // Set computed chrome CSS custom properties so chrome elements pick up\n // theme-resolved values via CSS fallbacks (e.g. --viz-title-computed-size).\n {\n const s = wrapper.style;\n if (chrome.title) {\n s.setProperty('--viz-title-computed-size', `${chrome.title.style.fontSize}px`);\n s.setProperty('--viz-title-computed-weight', String(chrome.title.style.fontWeight));\n s.setProperty('--viz-title-computed-color', chrome.title.style.fill);\n }\n if (chrome.subtitle) {\n s.setProperty('--viz-subtitle-computed-size', `${chrome.subtitle.style.fontSize}px`);\n s.setProperty('--viz-subtitle-computed-weight', String(chrome.subtitle.style.fontWeight));\n s.setProperty('--viz-subtitle-computed-color', chrome.subtitle.style.fill);\n }\n if (chrome.source) {\n s.setProperty('--viz-source-computed-size', `${chrome.source.style.fontSize}px`);\n s.setProperty('--viz-source-computed-color', chrome.source.style.fill);\n }\n if (chrome.footer) {\n s.setProperty('--viz-footer-computed-size', `${chrome.footer.style.fontSize}px`);\n s.setProperty('--viz-footer-computed-color', chrome.footer.style.fill);\n }\n }\n\n // Apply class modifiers\n if (layout.compact) {\n wrapper.classList.add('viz-table--compact');\n }\n\n // Header chrome\n const headerChrome = renderChromeBlock(layout, 'header');\n if (headerChrome) {\n wrapper.appendChild(headerChrome);\n }\n\n // Search bar\n const searchBar = renderSearchBar(layout);\n if (searchBar) {\n wrapper.appendChild(searchBar);\n }\n\n // Handle empty data\n if (layout.rows.length === 0) {\n const message = layout.search.query ? 'No results found' : 'No data';\n wrapper.appendChild(renderEmptyState(message));\n } else {\n // Scroll container\n const scroll = document.createElement('div');\n scroll.className = 'viz-table-scroll';\n\n // Table\n const table = document.createElement('table');\n table.setAttribute('role', 'grid');\n table.setAttribute('aria-label', layout.a11y.caption);\n\n if (layout.stickyFirstColumn) {\n table.classList.add('viz-table--sticky');\n }\n\n // Caption (screen reader only)\n const caption = document.createElement('caption');\n caption.className = 'viz-sr-only';\n caption.textContent = layout.a11y.summary;\n table.appendChild(caption);\n\n // Thead\n table.appendChild(renderThead(layout.columns, layout.sort));\n\n // Tbody\n table.appendChild(renderTbody(layout.rows, layout.columns));\n\n scroll.appendChild(table);\n wrapper.appendChild(scroll);\n }\n\n // Pagination\n const pagination = renderPagination(layout);\n if (pagination) {\n wrapper.appendChild(pagination);\n }\n\n // Footer chrome\n const footerChrome = renderChromeBlock(layout, 'footer');\n if (footerChrome) {\n wrapper.appendChild(footerChrome);\n }\n\n // Live region for screen reader announcements (sort changes, search results)\n const liveRegion = document.createElement('div');\n liveRegion.className = 'viz-table-live-region viz-sr-only';\n liveRegion.setAttribute('aria-live', 'polite');\n liveRegion.setAttribute('aria-atomic', 'true');\n liveRegion.setAttribute('role', 'status');\n wrapper.appendChild(liveRegion);\n\n // Brand watermark\n const brandColor = theme ? theme.colors.axis : '#999999';\n const brand = document.createElement('div');\n brand.className = 'viz-table-ref';\n brand.style.cssText = 'text-align: right; padding: 4px 8px;';\n const brandLink = document.createElement('a');\n brandLink.href = BRAND_URL;\n brandLink.target = '_blank';\n brandLink.rel = 'noopener';\n brandLink.style.cssText = `font-size: ${BRAND_FONT_SIZE}px; font-weight: 600; color: ${brandColor}; opacity: 0.55; text-decoration: none; font-family: ${theme ? theme.fonts.family : 'sans-serif'};`;\n brandLink.textContent = 'OpenData';\n brand.appendChild(brandLink);\n wrapper.appendChild(brand);\n\n container.appendChild(wrapper);\n return wrapper;\n}\n"],"mappings":"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","names":["workerUrl","renderChrome","renderLegend","BRAND_MIN_WIDTH","resolveDarkMode","SVG_NS","cleanup","BRAND_URL","BRAND_FONT_SIZE","div","resolveDarkMode","csvEscape"]}
|
|
1
|
+
{"version":3,"sources":["../src/export.ts","../src/graph/simulation-worker-url.ts","../src/graph-mount.ts","../src/graph/canvas-renderer.ts","../src/graph/zoom.ts","../src/graph/interaction.ts","../src/graph/keyboard.ts","../src/graph/search.ts","../src/graph/simulation.ts","../src/graph/spatial-index.ts","../src/resize-observer.ts","../src/tooltip.ts","../src/mount.ts","../src/svg-renderer.ts","../src/renderers/table-cells.ts","../src/table-keyboard.ts","../src/table-mount.ts","../src/table-renderer.ts"],"sourcesContent":["/**\n * Export utilities: serialize charts to SVG, PNG, or CSV.\n *\n * - SVG: serializes the rendered DOM element via XMLSerializer\n * - PNG: renders SVG to canvas, then extracts as Blob\n * - CSV: converts a data array to comma-separated text\n */\n\n/**\n * Serialize an SVG element to an XML string.\n *\n * @param svgElement - The rendered SVG element to serialize.\n * @returns The SVG markup as a string.\n */\nexport function exportSVG(svgElement: SVGElement): string {\n const serializer = new XMLSerializer();\n return serializer.serializeToString(svgElement);\n}\n\nexport interface PNGExportOptions {\n /** DPI scaling factor. Defaults to 2 for retina-quality output. */\n dpi?: number;\n}\n\n/**\n * Render an SVG element to a PNG Blob via a canvas.\n *\n * @param svgElement - The rendered SVG element.\n * @param options - Optional DPI scaling.\n * @returns A Promise resolving to the PNG Blob.\n */\nexport async function exportPNG(svgElement: SVGElement, options?: PNGExportOptions): Promise<Blob> {\n const dpi = options?.dpi ?? 2;\n const svgString = exportSVG(svgElement);\n\n const width = parseFloat(svgElement.getAttribute('width') || '600');\n const height = parseFloat(svgElement.getAttribute('height') || '400');\n\n const canvas = document.createElement('canvas');\n canvas.width = width * dpi;\n canvas.height = height * dpi;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Canvas 2D context not available');\n }\n\n ctx.scale(dpi, dpi);\n\n const img = new Image();\n const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n\n return new Promise<Blob>((resolve, reject) => {\n img.onload = () => {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n\n canvas.toBlob((result) => {\n if (result) {\n resolve(result);\n } else {\n reject(new Error('Canvas toBlob returned null'));\n }\n }, 'image/png');\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load SVG as image'));\n };\n\n img.src = url;\n });\n}\n\nexport interface JPGExportOptions extends PNGExportOptions {\n /** JPEG quality from 0 to 1. Defaults to 0.92. */\n quality?: number;\n}\n\n/**\n * Render an SVG element to a JPEG Blob via a canvas.\n *\n * Same pipeline as exportPNG but outputs JPEG with configurable quality.\n * The canvas is filled with white before drawing to avoid transparent\n * backgrounds rendering as black in JPEG format.\n *\n * @param svgElement - The rendered SVG element.\n * @param options - Optional DPI scaling and JPEG quality.\n * @returns A Promise resolving to the JPEG Blob.\n */\nexport async function exportJPG(svgElement: SVGElement, options?: JPGExportOptions): Promise<Blob> {\n const dpi = options?.dpi ?? 2;\n const quality = options?.quality ?? 0.92;\n const svgString = exportSVG(svgElement);\n\n const width = parseFloat(svgElement.getAttribute('width') || '600');\n const height = parseFloat(svgElement.getAttribute('height') || '400');\n\n const canvas = document.createElement('canvas');\n canvas.width = width * dpi;\n canvas.height = height * dpi;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Canvas 2D context not available');\n }\n\n // Fill white background since JPEG doesn't support transparency\n ctx.fillStyle = '#ffffff';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n ctx.scale(dpi, dpi);\n\n const img = new Image();\n const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n\n return new Promise<Blob>((resolve, reject) => {\n img.onload = () => {\n ctx.drawImage(img, 0, 0);\n URL.revokeObjectURL(url);\n\n canvas.toBlob(\n (result) => {\n if (result) {\n resolve(result);\n } else {\n reject(new Error('Canvas toBlob returned null'));\n }\n },\n 'image/jpeg',\n quality,\n );\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load SVG as image'));\n };\n\n img.src = url;\n });\n}\n\n/**\n * Convert an array of data objects to a CSV string.\n *\n * Uses the keys from the first row as column headers.\n * Values are quoted if they contain commas, quotes, or newlines.\n *\n * @param data - Array of row objects.\n * @returns CSV-formatted string.\n */\nexport function exportCSV(data: Record<string, unknown>[]): string {\n if (data.length === 0) return '';\n\n const headers = Object.keys(data[0]);\n const rows = [headers.map(csvEscape).join(',')];\n\n for (const row of data) {\n const values = headers.map((h) => csvEscape(String(row[h] ?? '')));\n rows.push(values.join(','));\n }\n\n return rows.join('\\n');\n}\n\nfunction csvEscape(value: string): string {\n if (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n","/**\n * Creates a Web Worker running the force simulation.\n *\n * Uses the `new URL` + `import.meta.url` pattern recognized by Vite, Webpack 5,\n * Parcel, and esbuild. The bundler resolves the worker file path at build time\n * and handles the asset accordingly.\n *\n * - Vite dev (Ladle): resolves src/graph/simulation-worker.ts directly, serves\n * it as a native ES module worker with on-the-fly TypeScript transform.\n * - Production (tsup + bun build): dist/simulation-worker.js is a self-contained\n * IIFE produced by `bun build`. The consuming app's bundler copies it as an\n * asset and rewrites the URL.\n *\n * Usage:\n * import { createSimulationWorker } from '@opendata-ai/openchart-vanilla';\n * const worker = createSimulationWorker();\n * worker.postMessage({ type: 'init', nodes, links, width: 800, height: 600 });\n * worker.onmessage = (e) => console.log(e.data);\n */\n\n/**\n * Path that resolves in Vite dev (workspace source) to the .ts file.\n * In production dist/, the consuming bundler resolves to simulation-worker.js\n * which sits alongside index.js in the dist folder.\n */\nconst workerUrl = new URL('./simulation-worker.ts', import.meta.url);\n\nexport function createSimulationWorker(): Worker {\n return new Worker(workerUrl, { type: 'module' });\n}\n","/**\n * Graph mount API: the main entry point for vanilla JS graph usage.\n *\n * createGraph() takes a container, GraphSpec, and options, compiles the graph,\n * creates a force simulation, canvas renderer, spatial index, interaction\n * manager, and search manager, then runs an animation loop driven by\n * simulation ticks. Returns a GraphInstance with update/search/zoom/destroy.\n */\n\nimport type { CompileOptions, DarkMode, GraphSpec, ThemeConfig } from '@opendata-ai/openchart-core';\nimport type {\n CompiledGraphEdge,\n CompiledGraphNode,\n GraphCompilation,\n} from '@opendata-ai/openchart-engine';\nimport { compileGraph } from '@opendata-ai/openchart-engine';\n\nimport { GraphCanvasRenderer } from './graph/canvas-renderer';\nimport { GraphInteractionManager } from './graph/interaction';\nimport { attachGraphKeyboardNav } from './graph/keyboard';\nimport { GraphSearchManager } from './graph/search';\nimport { SimulationManager } from './graph/simulation';\nimport { SpatialIndex } from './graph/spatial-index';\nimport type { GraphRenderState, PositionedEdge, PositionedNode } from './graph/types';\nimport type { SimEdge, SimNode } from './graph/worker-protocol';\nimport { ZoomTransform } from './graph/zoom';\nimport { observeResize } from './resize-observer';\nimport { createTooltipManager, type TooltipManager } from './tooltip';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface GraphMountOptions {\n theme?: ThemeConfig;\n darkMode?: DarkMode;\n responsive?: boolean;\n /** Show the built-in tooltip on node/edge hover. Defaults to true. */\n tooltip?: boolean;\n /** Show the built-in legend. Defaults to true. */\n legend?: boolean;\n onNodeClick?: (node: Record<string, unknown>) => void;\n onNodeDoubleClick?: (node: Record<string, unknown>) => void;\n onNodeHover?: (node: Record<string, unknown> | null) => void;\n onEdgeHover?: (edge: Record<string, unknown> | null) => void;\n onSelectionChange?: (nodeIds: string[]) => void;\n}\n\nexport interface GraphInstance {\n update(spec: GraphSpec): void;\n /** Re-compile encoding/legend/chrome without restarting the simulation. Preserves node positions. */\n updateVisuals(spec: GraphSpec): void;\n search(query: string): void;\n clearSearch(): void;\n zoomToFit(): void;\n zoomToNode(nodeId: string): void;\n selectNode(nodeId: string): void;\n getSelectedNodes(): string[];\n resize(): void;\n destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Dark mode resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveDarkMode(mode?: DarkMode): boolean {\n if (mode === 'force') return true;\n if (mode === 'off' || mode === undefined) return false;\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Main API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a graph instance from a spec and mount it into a container.\n *\n * @param container - The DOM element to render into.\n * @param spec - The graph spec.\n * @param options - Mount options.\n * @returns A GraphInstance with update/search/zoom/destroy methods.\n */\nexport function createGraph(\n container: HTMLElement,\n spec: GraphSpec,\n options?: GraphMountOptions,\n): GraphInstance {\n let currentSpec = spec;\n let compilation: GraphCompilation;\n let destroyed = false;\n\n // DOM elements\n let wrapper: HTMLElement | null = null;\n let canvas: HTMLCanvasElement | null = null;\n let chromeEl: HTMLElement | null = null;\n let legendEl: HTMLElement | null = null;\n\n // Subsystems\n let renderer: GraphCanvasRenderer | null = null;\n let simulation: SimulationManager | null = null;\n const spatialIndex = new SpatialIndex();\n let interactionManager: GraphInteractionManager | null = null;\n const searchManager = new GraphSearchManager();\n let tooltipManager: TooltipManager | null = null;\n let cleanupKeyboard: (() => void) | null = null;\n let disconnectResize: (() => void) | null = null;\n\n // State\n let positionedNodes: PositionedNode[] = [];\n let positionedEdges: PositionedEdge[] = [];\n let adjacencyMap = new Map<string, Set<string>>();\n let hoveredNodeId: string | null = null;\n let hoveredEdgeId: string | null = null;\n let selectedNodeIds = new Set<string>();\n let animFrameId: number | null = null;\n let needsRender = false;\n let isGesturing = false;\n let gestureTimeout: ReturnType<typeof setTimeout> | null = null;\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n function markGesture(): void {\n isGesturing = true;\n if (gestureTimeout !== null) clearTimeout(gestureTimeout);\n gestureTimeout = setTimeout(() => {\n isGesturing = false;\n gestureTimeout = null;\n needsRender = true;\n scheduleRender();\n }, 150);\n }\n\n function getContainerDimensions(): { width: number; height: number } {\n const rect = container.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n function compile(): GraphCompilation {\n const { width, height } = getContainerDimensions();\n const darkMode = resolveDarkMode(options?.darkMode);\n\n const compileOpts: CompileOptions = {\n width,\n height,\n theme: options?.theme,\n darkMode,\n };\n\n return compileGraph(currentSpec, compileOpts);\n }\n\n function buildAdjacencyMap(edges: CompiledGraphEdge[]): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n for (const edge of edges) {\n if (!map.has(edge.source)) map.set(edge.source, new Set());\n if (!map.has(edge.target)) map.set(edge.target, new Set());\n map.get(edge.source)!.add(edge.target);\n map.get(edge.target)!.add(edge.source);\n }\n return map;\n }\n\n function toSimNodes(nodes: CompiledGraphNode[]): SimNode[] {\n return nodes.map((n) => ({\n id: n.id,\n radius: n.radius,\n community: n.community,\n }));\n }\n\n function toSimEdges(edges: CompiledGraphEdge[]): SimEdge[] {\n return edges.map((e) => ({\n source: e.source,\n target: e.target,\n }));\n }\n\n /**\n * Look up a node's data from the compilation by id.\n * Falls back to an empty object if not found.\n */\n function nodeDataById(nodeId: string): Record<string, unknown> {\n const node = compilation.nodes.find((n) => n.id === nodeId);\n return node?.data ?? {};\n }\n\n /**\n * Point-to-line-segment distance for edge hit testing.\n * Returns the shortest distance from point (px, py) to the segment (ax, ay)-(bx, by).\n */\n function pointToSegmentDist(\n px: number,\n py: number,\n ax: number,\n ay: number,\n bx: number,\n by: number,\n ): number {\n const dx = bx - ax;\n const dy = by - ay;\n const lenSq = dx * dx + dy * dy;\n if (lenSq === 0) return Math.hypot(px - ax, py - ay);\n const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / lenSq));\n return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));\n }\n\n /**\n * Find the edge closest to a graph-space point, within a threshold.\n * Returns an edge key \"source->target\" or null.\n */\n function hitTestEdge(graphX: number, graphY: number, threshold: number): string | null {\n let bestDist = threshold;\n let bestEdgeId: string | null = null;\n\n for (const edge of positionedEdges) {\n const dist = pointToSegmentDist(\n graphX,\n graphY,\n edge.sourceX,\n edge.sourceY,\n edge.targetX,\n edge.targetY,\n );\n if (dist < bestDist) {\n bestDist = dist;\n bestEdgeId = `${edge.source}->${edge.target}`;\n }\n }\n\n return bestEdgeId;\n }\n\n /**\n * Look up edge data by edge id (\"source->target\").\n */\n function edgeDataById(edgeId: string): Record<string, unknown> | null {\n const [source, target] = edgeId.split('->');\n const edge = compilation.edges.find((e) => e.source === source && e.target === target);\n return edge?.data ?? null;\n }\n\n // ---------------------------------------------------------------------------\n // DOM creation\n // ---------------------------------------------------------------------------\n\n function createDOM(): void {\n const { width, height } = getContainerDimensions();\n const isDark = resolveDarkMode(options?.darkMode);\n\n // Wrapper\n wrapper = document.createElement('div');\n wrapper.className = 'viz-graph-wrapper';\n if (isDark) {\n container.classList.add('viz-dark');\n } else {\n container.classList.remove('viz-dark');\n }\n\n // Chrome (title, subtitle)\n chromeEl = document.createElement('div');\n chromeEl.className = 'viz-graph-chrome';\n renderChrome();\n wrapper.appendChild(chromeEl);\n\n // Canvas\n canvas = document.createElement('canvas');\n canvas.className = 'viz-graph-canvas';\n canvas.setAttribute('role', 'img');\n if (compilation.a11y?.altText) {\n canvas.setAttribute('aria-label', compilation.a11y.altText);\n }\n wrapper.appendChild(canvas);\n\n // Legend\n if (options?.legend !== false) {\n legendEl = document.createElement('div');\n legendEl.className = 'viz-graph-legend';\n renderLegend();\n wrapper.appendChild(legendEl);\n }\n\n container.appendChild(wrapper);\n\n // Size the canvas\n const chromeHeight = chromeEl.getBoundingClientRect().height || 0;\n const canvasHeight = Math.max(height - chromeHeight, 200);\n renderer = new GraphCanvasRenderer(canvas);\n renderer.resize(width, canvasHeight);\n }\n\n function renderChrome(): void {\n if (!chromeEl) return;\n let html = '';\n\n if (compilation.chrome.title) {\n html += `<h2 class=\"viz-title\">${escapeHtml(compilation.chrome.title.text)}</h2>`;\n }\n if (compilation.chrome.subtitle) {\n html += `<p class=\"viz-subtitle\">${escapeHtml(compilation.chrome.subtitle.text)}</p>`;\n }\n\n chromeEl.innerHTML = html;\n\n // Hide chrome if empty\n if (!html) {\n chromeEl.style.display = 'none';\n } else {\n chromeEl.style.display = '';\n }\n }\n\n function renderLegend(): void {\n if (!legendEl) return;\n\n const entries = compilation.legend.entries;\n if (entries.length === 0) {\n legendEl.style.display = 'none';\n return;\n }\n\n legendEl.style.display = '';\n let html = '';\n for (const entry of entries) {\n html += '<div class=\"viz-graph-legend-item\">';\n html += `<span class=\"viz-graph-legend-swatch\" style=\"background:${escapeHtml(entry.color)}\"></span>`;\n html += `<span>${escapeHtml(entry.label)}</span>`;\n html += '</div>';\n }\n legendEl.innerHTML = html;\n }\n\n // ---------------------------------------------------------------------------\n // Simulation and animation\n // ---------------------------------------------------------------------------\n\n function initSimulation(): void {\n const simNodes = toSimNodes(compilation.nodes);\n const simEdges = toSimEdges(compilation.edges);\n const config = compilation.simulationConfig;\n\n simulation = SimulationManager.create(simNodes, simEdges, {\n chargeStrength: config.chargeStrength,\n linkDistance: config.linkDistance,\n clustering: config.clustering,\n alphaDecay: config.alphaDecay,\n velocityDecay: config.velocityDecay,\n collisionRadius: config.collisionRadius,\n collisionPadding: config.collisionPadding,\n linkStrength: config.linkStrength,\n centerForce: config.centerForce,\n });\n\n simulation.onTick((positions, _alpha) => {\n if (destroyed) return;\n\n // Build position lookup\n const posMap = new Map<string, { x: number; y: number }>();\n for (const p of positions) {\n posMap.set(p.id, { x: p.x, y: p.y });\n }\n\n // Build positioned nodes\n positionedNodes = compilation.nodes.map((node) => {\n const pos = posMap.get(node.id) ?? { x: 0, y: 0 };\n return { ...node, x: pos.x, y: pos.y };\n });\n\n // Build positioned edges\n positionedEdges = compilation.edges.map((edge) => {\n const src = posMap.get(edge.source) ?? { x: 0, y: 0 };\n const tgt = posMap.get(edge.target) ?? { x: 0, y: 0 };\n return {\n ...edge,\n sourceX: src.x,\n sourceY: src.y,\n targetX: tgt.x,\n targetY: tgt.y,\n };\n });\n\n // Rebuild spatial index\n spatialIndex.rebuild(positionedNodes);\n\n needsRender = true;\n scheduleRender();\n });\n\n simulation.onSettled(() => {\n // One final fit after simulation settles\n if (canvas && positionedNodes.length > 0 && interactionManager) {\n const { width: cw, height: ch } = getCanvasDimensions();\n const fitTransform = ZoomTransform.fitBounds(positionedNodes, cw, ch);\n interactionManager.setTransform(fitTransform);\n needsRender = true;\n scheduleRender();\n }\n });\n }\n\n function getCanvasDimensions(): { width: number; height: number } {\n if (!canvas) return { width: 600, height: 400 };\n const rect = canvas.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n function scheduleRender(): void {\n if (animFrameId !== null || destroyed) return;\n animFrameId = requestAnimationFrame(renderFrame);\n }\n\n function renderFrame(): void {\n animFrameId = null;\n if (destroyed || !renderer || !interactionManager) return;\n\n if (needsRender) {\n needsRender = false;\n\n const transform = interactionManager.getTransform();\n const state: GraphRenderState = {\n nodes: positionedNodes,\n edges: positionedEdges,\n transform: { x: transform.x, y: transform.y, k: transform.k },\n hoveredNodeId,\n hoveredEdgeId,\n selectedNodeIds,\n adjacencyMap,\n theme: compilation.theme,\n searchMatches: searchManager.getMatches(),\n isGesturing,\n };\n\n renderer.render(state);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Interaction wiring\n // ---------------------------------------------------------------------------\n\n function initInteraction(): void {\n if (!canvas) return;\n\n if (options?.tooltip !== false) {\n tooltipManager = createTooltipManager(wrapper!);\n }\n\n interactionManager = new GraphInteractionManager(canvas, spatialIndex, {\n onTransformChange(_transform) {\n markGesture();\n needsRender = true;\n scheduleRender();\n },\n onHoverChange(nodeId) {\n hoveredNodeId = nodeId;\n needsRender = true;\n scheduleRender();\n\n // Fire onNodeHover callback\n if (nodeId) {\n options?.onNodeHover?.(nodeDataById(nodeId));\n } else {\n options?.onNodeHover?.(null);\n }\n\n // Show or hide tooltip\n if (nodeId && tooltipManager) {\n // Clear edge hover when hovering a node\n if (hoveredEdgeId) {\n hoveredEdgeId = null;\n options?.onEdgeHover?.(null);\n }\n const content = compilation.tooltipDescriptors.get(nodeId);\n if (content) {\n const node = positionedNodes.find((n) => n.id === nodeId);\n if (node && interactionManager) {\n const screen = interactionManager.getTransform().graphToScreen(node.x, node.y);\n tooltipManager.show(content, screen.x, screen.y);\n }\n }\n } else if (!nodeId) {\n // Tooltip hiding handled in onBackgroundHover (edge may show tooltip)\n // If no edge hover happens, tooltip stays hidden\n tooltipManager?.hide();\n }\n },\n onBackgroundHover(graphX, graphY, screenX, screenY) {\n // Edge hit testing: check proximity to edge line segments\n const transform = interactionManager?.getTransform();\n const threshold = 5 / (transform?.k ?? 1); // 5px in screen space\n const edgeId = hitTestEdge(graphX, graphY, threshold);\n\n if (edgeId !== hoveredEdgeId) {\n hoveredEdgeId = edgeId;\n needsRender = true;\n scheduleRender();\n\n if (edgeId) {\n const data = edgeDataById(edgeId);\n options?.onEdgeHover?.(data);\n\n // Show edge tooltip\n if (tooltipManager && data) {\n const fields = Object.entries(data)\n .filter(([key]) => key !== 'source' && key !== 'target')\n .filter(([, value]) => value != null)\n .map(([key, value]) => ({\n label: key,\n value: typeof value === 'number' ? value.toLocaleString() : String(value),\n }));\n\n const [source, target] = edgeId.split('->');\n tooltipManager.show({ title: `${source} → ${target}`, fields }, screenX, screenY);\n }\n } else {\n options?.onEdgeHover?.(null);\n tooltipManager?.hide();\n }\n }\n },\n onSelectionChange(nodeIds) {\n selectedNodeIds = new Set(nodeIds);\n needsRender = true;\n scheduleRender();\n options?.onSelectionChange?.(nodeIds);\n\n // Fire onNodeClick for the most recently added node\n if (nodeIds.length > 0) {\n const lastId = nodeIds[nodeIds.length - 1];\n options?.onNodeClick?.(nodeDataById(lastId));\n }\n },\n onNodeDragStart(nodeId) {\n // Pin at the node's current position to avoid visual snap to origin\n const node = positionedNodes.find((n) => n.id === nodeId);\n const x = node?.x ?? 0;\n const y = node?.y ?? 0;\n simulation?.pinNode(nodeId, x, y);\n canvas?.classList.add('viz-graph-canvas--dragging');\n },\n onNodeDrag(nodeId, x, y) {\n simulation?.dragNode(nodeId, x, y);\n },\n onNodeDragEnd(nodeId) {\n simulation?.unpinNode(nodeId);\n canvas?.classList.remove('viz-graph-canvas--dragging');\n },\n onDoubleClick(nodeId) {\n options?.onNodeDoubleClick?.(nodeDataById(nodeId));\n },\n });\n\n // Wire keyboard navigation\n cleanupKeyboard = attachGraphKeyboardNav({\n canvas,\n getNodes: () => positionedNodes,\n getSelectedIds: () => [...selectedNodeIds],\n getAdjacency: () => adjacencyMap,\n onSelect(nodeId) {\n selectedNodeIds = new Set([nodeId]);\n needsRender = true;\n scheduleRender();\n options?.onNodeClick?.(nodeDataById(nodeId));\n options?.onSelectionChange?.([nodeId]);\n },\n onDeselect() {\n selectedNodeIds.clear();\n needsRender = true;\n scheduleRender();\n options?.onSelectionChange?.([]);\n },\n onZoom(direction) {\n if (!interactionManager || !canvas) return;\n const t = interactionManager.getTransform();\n const { width: cw, height: ch } = getCanvasDimensions();\n const factor = direction === 'in' ? 1.2 : 0.8;\n const newK = t.k * factor;\n const newTransform = t.zoomAt(newK, cw / 2, ch / 2);\n interactionManager.setTransform(newTransform);\n needsRender = true;\n scheduleRender();\n },\n onFitAll() {\n zoomToFit();\n },\n });\n\n // Handle node clicks (from interaction manager selection change wiring above)\n // We catch clicks via the interaction manager's onSelectionChange callback\n }\n\n // ---------------------------------------------------------------------------\n // Public API methods\n // ---------------------------------------------------------------------------\n\n function search(query: string): void {\n if (destroyed) return;\n searchManager.search(query, positionedNodes);\n needsRender = true;\n scheduleRender();\n }\n\n function clearSearch(): void {\n if (destroyed) return;\n searchManager.clearSearch();\n needsRender = true;\n scheduleRender();\n }\n\n function zoomToFit(): void {\n if (destroyed || !interactionManager || positionedNodes.length === 0) return;\n const { width: cw, height: ch } = getCanvasDimensions();\n const fitTransform = ZoomTransform.fitBounds(positionedNodes, cw, ch);\n interactionManager.setTransform(fitTransform);\n needsRender = true;\n scheduleRender();\n }\n\n function zoomToNode(nodeId: string): void {\n if (destroyed || !interactionManager || !canvas) return;\n const node = positionedNodes.find((n) => n.id === nodeId);\n if (!node) return;\n\n const { width: cw, height: ch } = getCanvasDimensions();\n // Zoom to 2x and center on node\n const k = 2;\n const tx = cw / 2 - node.x * k;\n const ty = ch / 2 - node.y * k;\n const newTransform = new ZoomTransform(tx, ty, k);\n interactionManager.setTransform(newTransform);\n needsRender = true;\n scheduleRender();\n }\n\n function selectNode(nodeId: string): void {\n if (destroyed) return;\n selectedNodeIds = new Set([nodeId]);\n needsRender = true;\n scheduleRender();\n options?.onSelectionChange?.([nodeId]);\n }\n\n function getSelectedNodes(): string[] {\n return [...selectedNodeIds];\n }\n\n function doResize(): void {\n if (destroyed || !canvas || !renderer || !wrapper) return;\n const { width, height } = getContainerDimensions();\n const chromeHeight = chromeEl?.getBoundingClientRect().height || 0;\n const canvasHeight = Math.max(height - chromeHeight, 200);\n renderer.resize(width, canvasHeight);\n needsRender = true;\n scheduleRender();\n }\n\n function update(newSpec: GraphSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n\n // Tear down old simulation + interaction\n teardownSubsystems();\n\n // Recompile\n compilation = compile();\n adjacencyMap = buildAdjacencyMap(compilation.edges);\n\n // Update DOM chrome/legend\n renderChrome();\n renderLegend();\n\n // Reinit\n initSimulation();\n initInteraction();\n\n // Reset state\n hoveredNodeId = null;\n hoveredEdgeId = null;\n selectedNodeIds = new Set();\n searchManager.clearSearch();\n }\n\n function updateVisuals(newSpec: GraphSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n\n // Build a position lookup from current positioned nodes\n const posMap = new Map<string, { x: number; y: number }>();\n for (const node of positionedNodes) {\n posMap.set(node.id, { x: node.x, y: node.y });\n }\n\n // Recompile with new spec (encoding, chrome, nodeOverrides, etc.)\n compilation = compile();\n adjacencyMap = buildAdjacencyMap(compilation.edges);\n\n // Transfer positions to new compiled nodes\n positionedNodes = compilation.nodes.map((node) => {\n const pos = posMap.get(node.id) ?? { x: 0, y: 0 };\n return { ...node, x: pos.x, y: pos.y };\n });\n\n // Rebuild positioned edges from existing positions\n positionedEdges = compilation.edges.map((edge) => {\n const src = posMap.get(edge.source) ?? { x: 0, y: 0 };\n const tgt = posMap.get(edge.target) ?? { x: 0, y: 0 };\n return {\n ...edge,\n sourceX: src.x,\n sourceY: src.y,\n targetX: tgt.x,\n targetY: tgt.y,\n };\n });\n\n // Rebuild spatial index with updated visuals\n spatialIndex.rebuild(positionedNodes);\n\n // Update DOM chrome/legend\n renderChrome();\n renderLegend();\n\n // Re-render canvas without restarting simulation\n needsRender = true;\n scheduleRender();\n }\n\n function teardownSubsystems(): void {\n if (animFrameId !== null) {\n cancelAnimationFrame(animFrameId);\n animFrameId = null;\n }\n if (cleanupKeyboard) {\n cleanupKeyboard();\n cleanupKeyboard = null;\n }\n interactionManager?.destroy();\n interactionManager = null;\n simulation?.destroy();\n simulation = null;\n tooltipManager?.destroy();\n tooltipManager = null;\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (gestureTimeout !== null) {\n clearTimeout(gestureTimeout);\n gestureTimeout = null;\n }\n\n teardownSubsystems();\n\n if (disconnectResize) {\n disconnectResize();\n disconnectResize = null;\n }\n\n if (wrapper?.parentNode) {\n wrapper.parentNode.removeChild(wrapper);\n }\n wrapper = null;\n canvas = null;\n chromeEl = null;\n legendEl = null;\n renderer = null;\n\n container.classList.remove('viz-dark');\n }\n\n // ---------------------------------------------------------------------------\n // Initialize\n // ---------------------------------------------------------------------------\n\n try {\n compilation = compile();\n adjacencyMap = buildAdjacencyMap(compilation.edges);\n createDOM();\n initSimulation();\n initInteraction();\n } catch (err) {\n console.error('[viz] Graph mount failed:', err);\n // Return a no-op instance so callers don't crash\n return {\n update() {},\n updateVisuals() {},\n search() {},\n clearSearch() {},\n zoomToFit() {},\n zoomToNode() {},\n selectNode() {},\n getSelectedNodes: () => [],\n resize() {},\n destroy() {},\n };\n }\n\n // Responsive resize\n if (options?.responsive !== false) {\n disconnectResize = observeResize(container, () => {\n doResize();\n });\n }\n\n return {\n update,\n updateVisuals,\n search,\n clearSearch,\n zoomToFit,\n zoomToNode,\n selectNode,\n getSelectedNodes,\n resize: doResize,\n destroy,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Util\n// ---------------------------------------------------------------------------\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","/**\n * Canvas 2D renderer for force-directed graph visualization.\n *\n * Stateless renderer: receives a GraphRenderState each frame and draws it.\n * Handles DPR scaling, viewport culling, LOD labels, dark mode glow effects,\n * and batched drawing for performance at 10k+ nodes.\n *\n * Performance strategy:\n * - Edges batched by (stroke, strokeWidth, dash) key → one stroke() per group\n * - Nodes batched by fill color → one fill() per color group\n * - Node strokes batched by stroke color\n * - Labels and glow skipped during active pan/zoom gestures\n */\n\nimport type { GraphRenderState, PositionedEdge, PositionedNode } from './types';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst LABEL_FONT_MIN = 8;\nconst LABEL_FONT_MAX = 12;\nconst EDGE_ALPHA_DEFAULT = 0.35;\nconst EDGE_ALPHA_CONNECTED = 1.0;\nconst EDGE_ALPHA_DIMMED = 0.05;\nconst SEARCH_NON_MATCH_ALPHA = 0.15;\nconst GLOW_NODE_THRESHOLD = 2000;\nconst GLOW_RADIUS_MULTIPLIER = 1.3;\nconst GLOW_ALPHA = 0.15;\nconst CULL_MARGIN = 50;\nconst TWO_PI = Math.PI * 2;\n\n/** Minimum node radius in screen pixels. Keeps nodes visible when zoomed out. */\nconst MIN_SCREEN_RADIUS = 2.5;\n\n/** Minimum canvas width to render the brand watermark. */\nconst BRAND_MIN_WIDTH = 120;\n\n// ---------------------------------------------------------------------------\n// Helpers (exported for testing)\n// ---------------------------------------------------------------------------\n\n/**\n * Compute label visibility threshold from zoom level.\n * At zoom 0.2 (zoomed out): threshold ~1.0 (only top ~5% visible).\n * At zoom 2.0+: threshold ~0.0 (all visible).\n */\nexport function labelThreshold(zoom: number): number {\n const t = Math.max(0, Math.min(1, (zoom - 0.2) / 1.8));\n return 1 - t;\n}\n\n/** Compute visible rect in graph coordinates from canvas size + transform. */\nexport function visibleRect(\n canvasWidth: number,\n canvasHeight: number,\n transform: { x: number; y: number; k: number },\n margin: number = CULL_MARGIN,\n): { minX: number; minY: number; maxX: number; maxY: number } {\n const { x, y, k } = transform;\n return {\n minX: (-x - margin) / k,\n minY: (-y - margin) / k,\n maxX: (canvasWidth - x + margin) / k,\n maxY: (canvasHeight - y + margin) / k,\n };\n}\n\n/** Check if a node falls within the visible rect. */\nfunction nodeInView(\n node: PositionedNode,\n rect: { minX: number; minY: number; maxX: number; maxY: number },\n): boolean {\n return (\n node.x + node.radius >= rect.minX &&\n node.x - node.radius <= rect.maxX &&\n node.y + node.radius >= rect.minY &&\n node.y - node.radius <= rect.maxY\n );\n}\n\n/** Check if an edge has at least one endpoint in view. */\nfunction edgeInView(\n edge: PositionedEdge,\n rect: { minX: number; minY: number; maxX: number; maxY: number },\n): boolean {\n return (\n (edge.sourceX >= rect.minX &&\n edge.sourceX <= rect.maxX &&\n edge.sourceY >= rect.minY &&\n edge.sourceY <= rect.maxY) ||\n (edge.targetX >= rect.minX &&\n edge.targetX <= rect.maxX &&\n edge.targetY >= rect.minY &&\n edge.targetY <= rect.maxY)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Dash patterns for edge styles\n// ---------------------------------------------------------------------------\n\nconst DASH_PATTERNS: Record<string, number[]> = {\n solid: [],\n dashed: [6, 4],\n dotted: [2, 3],\n};\n\n// ---------------------------------------------------------------------------\n// GraphCanvasRenderer\n// ---------------------------------------------------------------------------\n\nexport class GraphCanvasRenderer {\n private canvas: HTMLCanvasElement;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via this-destructuring\n private ctx: CanvasRenderingContext2D;\n private dpr: number;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via this-destructuring\n private cssWidth = 0;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via this-destructuring\n private cssHeight = 0;\n\n constructor(canvas: HTMLCanvasElement) {\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d')!;\n this.dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n }\n\n /** Update canvas dimensions with DPR scaling. CSS size stays at css values. */\n resize(width: number, height: number): void {\n this.cssWidth = width;\n this.cssHeight = height;\n this.canvas.width = width * this.dpr;\n this.canvas.height = height * this.dpr;\n this.canvas.style.width = `${width}px`;\n this.canvas.style.height = `${height}px`;\n }\n\n /** Clear canvas and render the full graph state. */\n render(state: GraphRenderState): void {\n const { ctx, dpr, cssWidth, cssHeight } = this;\n const {\n nodes,\n edges,\n transform,\n hoveredNodeId,\n hoveredEdgeId,\n selectedNodeIds,\n adjacencyMap,\n theme,\n searchMatches,\n isGesturing,\n } = state;\n\n const hasActiveNode = hoveredNodeId !== null || selectedNodeIds.size > 0;\n const activeNodeIds = new Set<string>();\n if (hoveredNodeId) activeNodeIds.add(hoveredNodeId);\n for (const id of selectedNodeIds) activeNodeIds.add(id);\n\n // Collect all nodes connected to any active node\n const connectedNodeIds = new Set<string>();\n for (const id of activeNodeIds) {\n connectedNodeIds.add(id);\n const neighbors = adjacencyMap.get(id);\n if (neighbors) {\n for (const nid of neighbors) connectedNodeIds.add(nid);\n }\n }\n\n // Viewport culling\n const rect = visibleRect(cssWidth, cssHeight, transform);\n const visibleNodes = nodes.filter((n) => nodeInView(n, rect));\n const visibleEdges = edges.filter((e) => edgeInView(e, rect));\n\n const isDark = theme.isDark;\n const showGlow = isDark && !isGesturing && visibleNodes.length < GLOW_NODE_THRESHOLD;\n const threshold = labelThreshold(transform.k);\n // Minimum radius in graph coordinates so nodes stay visible when zoomed out\n const minRadius = MIN_SCREEN_RADIUS / transform.k;\n\n // -- Clear and apply transform --\n ctx.save();\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, cssWidth, cssHeight);\n\n // Fill background (skip if transparent to let page background show through)\n if (theme.colors.background !== 'transparent') {\n ctx.fillStyle = theme.colors.background;\n ctx.fillRect(0, 0, cssWidth, cssHeight);\n }\n\n ctx.translate(transform.x, transform.y);\n ctx.scale(transform.k, transform.k);\n\n // -- Draw edges (batched by style key) --\n this.drawEdgesBatched(\n ctx,\n visibleEdges,\n hasActiveNode,\n connectedNodeIds,\n isGesturing ? null : searchMatches,\n hoveredEdgeId,\n );\n\n // -- Draw nodes (batched by fill color) --\n this.drawNodesBatched(\n ctx,\n visibleNodes,\n hoveredNodeId,\n selectedNodeIds,\n isGesturing ? null : searchMatches,\n showGlow,\n theme,\n minRadius,\n );\n\n // -- Draw labels (skipped during gestures) --\n if (!isGesturing) {\n this.drawLabels(\n ctx,\n visibleNodes,\n threshold,\n hoveredNodeId,\n selectedNodeIds,\n searchMatches,\n transform.k,\n theme,\n );\n }\n\n ctx.restore();\n\n // Brand watermark in screen coordinates (unaffected by pan/zoom)\n this.drawBrand(ctx, cssWidth, cssHeight, theme);\n }\n\n // -------------------------------------------------------------------------\n // Brand rendering\n // -------------------------------------------------------------------------\n\n private drawBrand(\n ctx: CanvasRenderingContext2D,\n w: number,\n h: number,\n theme: GraphRenderState['theme'],\n ): void {\n if (w < BRAND_MIN_WIDTH) return;\n const { dpr } = this;\n ctx.save();\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n const padding = theme.spacing.padding;\n const x = w - padding;\n const y = h - padding;\n ctx.font = `600 20px ${theme.fonts.family}`;\n ctx.fillStyle = theme.colors.axis;\n ctx.globalAlpha = 0.55;\n ctx.textAlign = 'right';\n ctx.textBaseline = 'alphabetic';\n ctx.fillText('OpenData', x, y);\n ctx.restore();\n }\n\n // -------------------------------------------------------------------------\n // Batched edge drawing\n // -------------------------------------------------------------------------\n\n private drawEdgesBatched(\n ctx: CanvasRenderingContext2D,\n edges: PositionedEdge[],\n hasActiveNode: boolean,\n connectedNodeIds: Set<string>,\n searchMatches: Set<string> | null,\n hoveredEdgeId: string | null,\n ): void {\n // Classify edges by alpha level, then batch by visual style within each level\n const dimmedEdges: PositionedEdge[] = [];\n const defaultEdges: PositionedEdge[] = [];\n const connectedEdges: PositionedEdge[] = [];\n let hoveredEdge: PositionedEdge | null = null;\n\n for (const edge of edges) {\n const edgeId = `${edge.source}->${edge.target}`;\n if (edgeId === hoveredEdgeId) {\n hoveredEdge = edge;\n continue; // Draw hovered edge last, on top\n }\n\n const isConnected =\n hasActiveNode && connectedNodeIds.has(edge.source) && connectedNodeIds.has(edge.target);\n const isDimmed = hasActiveNode && !isConnected;\n\n if (isConnected) {\n connectedEdges.push(edge);\n } else if (isDimmed) {\n dimmedEdges.push(edge);\n } else {\n defaultEdges.push(edge);\n }\n }\n\n // Draw dimmed first, then default, then connected (on top)\n this.drawEdgeGroupBatched(ctx, dimmedEdges, EDGE_ALPHA_DIMMED, searchMatches);\n this.drawEdgeGroupBatched(ctx, defaultEdges, EDGE_ALPHA_DEFAULT, searchMatches);\n this.drawEdgeGroupBatched(ctx, connectedEdges, EDGE_ALPHA_CONNECTED, searchMatches);\n\n // Draw hovered edge on top with highlight\n if (hoveredEdge) {\n const dash = DASH_PATTERNS[hoveredEdge.style] ?? DASH_PATTERNS.solid;\n ctx.setLineDash(dash);\n ctx.strokeStyle = hoveredEdge.stroke;\n ctx.lineWidth = hoveredEdge.strokeWidth * 2;\n ctx.globalAlpha = EDGE_ALPHA_CONNECTED;\n ctx.beginPath();\n ctx.moveTo(hoveredEdge.sourceX, hoveredEdge.sourceY);\n ctx.lineTo(hoveredEdge.targetX, hoveredEdge.targetY);\n ctx.stroke();\n ctx.setLineDash([]);\n ctx.globalAlpha = 1;\n }\n }\n\n /**\n * Draw a group of edges at a given alpha, batched by (stroke, strokeWidth, style).\n * When search is inactive, all edges of the same style are drawn in a single path.\n * When search is active, edges split by search-match status for alpha dimming.\n */\n private drawEdgeGroupBatched(\n ctx: CanvasRenderingContext2D,\n edges: PositionedEdge[],\n alpha: number,\n searchMatches: Set<string> | null,\n ): void {\n if (edges.length === 0) return;\n\n // Group by visual key: stroke + strokeWidth + style\n const groups = new Map<string, PositionedEdge[]>();\n for (const edge of edges) {\n const key = `${edge.stroke}|${edge.strokeWidth}|${edge.style}`;\n let group = groups.get(key);\n if (!group) {\n group = [];\n groups.set(key, group);\n }\n group.push(edge);\n }\n\n for (const [, group] of groups) {\n const sample = group[0];\n const dash = DASH_PATTERNS[sample.style] ?? DASH_PATTERNS.solid;\n ctx.setLineDash(dash);\n ctx.strokeStyle = sample.stroke;\n ctx.lineWidth = sample.strokeWidth;\n\n if (!searchMatches) {\n // Fast path: single batched path for all edges in this group\n ctx.globalAlpha = alpha;\n ctx.beginPath();\n for (const edge of group) {\n ctx.moveTo(edge.sourceX, edge.sourceY);\n ctx.lineTo(edge.targetX, edge.targetY);\n }\n ctx.stroke();\n } else {\n // Search active: split into matched and non-matched batches\n ctx.globalAlpha = alpha;\n ctx.beginPath();\n let hasMatched = false;\n\n const nonMatchPath: PositionedEdge[] = [];\n\n for (const edge of group) {\n const srcMatch = searchMatches.has(edge.source);\n const tgtMatch = searchMatches.has(edge.target);\n if (srcMatch || tgtMatch) {\n ctx.moveTo(edge.sourceX, edge.sourceY);\n ctx.lineTo(edge.targetX, edge.targetY);\n hasMatched = true;\n } else {\n nonMatchPath.push(edge);\n }\n }\n if (hasMatched) ctx.stroke();\n\n // Draw non-matching edges dimmed\n if (nonMatchPath.length > 0) {\n ctx.globalAlpha = SEARCH_NON_MATCH_ALPHA * alpha;\n ctx.beginPath();\n for (const edge of nonMatchPath) {\n ctx.moveTo(edge.sourceX, edge.sourceY);\n ctx.lineTo(edge.targetX, edge.targetY);\n }\n ctx.stroke();\n }\n }\n }\n\n ctx.setLineDash([]);\n ctx.globalAlpha = 1;\n }\n\n // -------------------------------------------------------------------------\n // Batched node drawing\n // -------------------------------------------------------------------------\n\n private drawNodesBatched(\n ctx: CanvasRenderingContext2D,\n nodes: PositionedNode[],\n hoveredNodeId: string | null,\n selectedNodeIds: Set<string>,\n searchMatches: Set<string> | null,\n showGlow: boolean,\n theme: GraphRenderState['theme'],\n minRadius: number,\n ): void {\n // Separate special nodes (hovered/selected) from bulk nodes.\n // Special nodes need individual treatment; bulk nodes get batched by color.\n const bulkNodes: PositionedNode[] = [];\n const specialNodes: PositionedNode[] = [];\n\n for (const node of nodes) {\n if (node.id === hoveredNodeId || selectedNodeIds.has(node.id)) {\n specialNodes.push(node);\n } else {\n bulkNodes.push(node);\n }\n }\n\n // Helper: effective radius clamped to minimum screen size\n const r = (node: PositionedNode) => Math.max(node.radius, minRadius);\n\n // --- Glow pass (dark mode only, before fills) ---\n if (showGlow) {\n this.drawGlowBatched(ctx, bulkNodes, searchMatches, minRadius);\n }\n\n // --- Bulk fill pass: batch by fill color ---\n const fillGroups = new Map<string, PositionedNode[]>();\n for (const node of bulkNodes) {\n let group = fillGroups.get(node.fill);\n if (!group) {\n group = [];\n fillGroups.set(node.fill, group);\n }\n group.push(node);\n }\n\n if (!searchMatches) {\n // Fast path: no search dimming\n ctx.globalAlpha = 1;\n for (const [fill, group] of fillGroups) {\n ctx.fillStyle = fill;\n ctx.beginPath();\n for (const node of group) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.fill();\n }\n } else {\n // Search active: split each color group into matched/dimmed batches\n for (const [fill, group] of fillGroups) {\n ctx.fillStyle = fill;\n\n // Matched nodes\n ctx.globalAlpha = 1;\n ctx.beginPath();\n let hasMatched = false;\n const dimmedNodes: PositionedNode[] = [];\n\n for (const node of group) {\n if (searchMatches.has(node.id)) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n hasMatched = true;\n } else {\n dimmedNodes.push(node);\n }\n }\n if (hasMatched) ctx.fill();\n\n // Dimmed nodes\n if (dimmedNodes.length > 0) {\n ctx.globalAlpha = SEARCH_NON_MATCH_ALPHA;\n ctx.beginPath();\n for (const node of dimmedNodes) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.fill();\n }\n }\n }\n\n // --- Bulk stroke pass: batch by stroke color ---\n const strokeGroups = new Map<string, PositionedNode[]>();\n for (const node of bulkNodes) {\n const key = `${node.stroke}|${node.strokeWidth}`;\n let group = strokeGroups.get(key);\n if (!group) {\n group = [];\n strokeGroups.set(key, group);\n }\n group.push(node);\n }\n\n for (const [key, group] of strokeGroups) {\n const [stroke, widthStr] = key.split('|');\n ctx.strokeStyle = stroke;\n ctx.lineWidth = parseFloat(widthStr);\n\n if (!searchMatches) {\n ctx.globalAlpha = 1;\n ctx.beginPath();\n for (const node of group) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.stroke();\n } else {\n // Split matched/dimmed for strokes too\n ctx.globalAlpha = 1;\n ctx.beginPath();\n let hasMatched = false;\n const dimmedNodes: PositionedNode[] = [];\n\n for (const node of group) {\n if (searchMatches.has(node.id)) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n hasMatched = true;\n } else {\n dimmedNodes.push(node);\n }\n }\n if (hasMatched) ctx.stroke();\n\n if (dimmedNodes.length > 0) {\n ctx.globalAlpha = SEARCH_NON_MATCH_ALPHA;\n ctx.beginPath();\n for (const node of dimmedNodes) {\n const nr = r(node);\n ctx.moveTo(node.x + nr, node.y);\n ctx.arc(node.x, node.y, nr, 0, TWO_PI);\n }\n ctx.stroke();\n }\n }\n }\n\n // --- Special nodes (hovered/selected) drawn individually ---\n for (const node of specialNodes) {\n const isHovered = node.id === hoveredNodeId;\n const isSelected = selectedNodeIds.has(node.id);\n const dimmed = searchMatches !== null && !searchMatches.has(node.id);\n const baseRadius = Math.max(node.radius, minRadius);\n const radius = isHovered ? baseRadius * 1.15 : baseRadius;\n\n ctx.globalAlpha = dimmed ? SEARCH_NON_MATCH_ALPHA : 1;\n\n // Glow for special nodes\n if (showGlow && !dimmed) {\n ctx.beginPath();\n ctx.arc(node.x, node.y, radius * GLOW_RADIUS_MULTIPLIER, 0, TWO_PI);\n ctx.fillStyle = node.fill;\n ctx.globalAlpha = GLOW_ALPHA;\n ctx.fill();\n ctx.globalAlpha = dimmed ? SEARCH_NON_MATCH_ALPHA : 1;\n }\n\n // Fill\n ctx.beginPath();\n ctx.arc(node.x, node.y, radius, 0, TWO_PI);\n ctx.fillStyle = isHovered ? brighten(node.fill) : node.fill;\n ctx.fill();\n\n // Stroke\n ctx.strokeStyle = node.stroke;\n ctx.lineWidth = node.strokeWidth;\n ctx.stroke();\n\n // Selection ring\n if (isSelected) {\n ctx.beginPath();\n ctx.arc(node.x, node.y, radius + 3, 0, TWO_PI);\n ctx.strokeStyle = theme.colors.categorical[0] ?? '#3b82f6';\n ctx.lineWidth = 2;\n ctx.stroke();\n }\n }\n\n ctx.globalAlpha = 1;\n }\n\n /** Batch glow circles by fill color. */\n private drawGlowBatched(\n ctx: CanvasRenderingContext2D,\n nodes: PositionedNode[],\n searchMatches: Set<string> | null,\n minRadius: number,\n ): void {\n const glowGroups = new Map<string, PositionedNode[]>();\n for (const node of nodes) {\n if (searchMatches && !searchMatches.has(node.id)) continue;\n let group = glowGroups.get(node.fill);\n if (!group) {\n group = [];\n glowGroups.set(node.fill, group);\n }\n group.push(node);\n }\n\n ctx.globalAlpha = GLOW_ALPHA;\n for (const [fill, group] of glowGroups) {\n ctx.fillStyle = fill;\n ctx.beginPath();\n for (const node of group) {\n const gr = Math.max(node.radius, minRadius) * GLOW_RADIUS_MULTIPLIER;\n ctx.moveTo(node.x + gr, node.y);\n ctx.arc(node.x, node.y, gr, 0, TWO_PI);\n }\n ctx.fill();\n }\n ctx.globalAlpha = 1;\n }\n\n // -------------------------------------------------------------------------\n // Labels (drawn individually, skipped during gestures)\n // -------------------------------------------------------------------------\n\n private drawLabels(\n ctx: CanvasRenderingContext2D,\n nodes: PositionedNode[],\n threshold: number,\n hoveredNodeId: string | null,\n selectedNodeIds: Set<string>,\n searchMatches: Set<string> | null,\n zoom: number,\n theme: GraphRenderState['theme'],\n ): void {\n // Font size inversely scaled by zoom, clamped to readable range\n const rawSize = 10 / zoom;\n const fontSize = Math.max(LABEL_FONT_MIN, Math.min(LABEL_FONT_MAX, rawSize));\n\n ctx.font = `${fontSize}px ${theme.fonts.family}`;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'top';\n\n for (const node of nodes) {\n if (!node.label) continue;\n\n const isHovered = node.id === hoveredNodeId;\n const isSelected = selectedNodeIds.has(node.id);\n const forced = isHovered || isSelected;\n const dimmed = searchMatches !== null && !searchMatches.has(node.id);\n\n // LOD: skip labels below threshold unless forced\n if (!forced && node.labelPriority < threshold) continue;\n\n ctx.globalAlpha = dimmed ? SEARCH_NON_MATCH_ALPHA : 1;\n\n const labelY = node.y + node.radius + 3;\n\n // Halo for readability: stroke behind text in the background color\n // so labels stay legible over edges and other nodes.\n if (theme.colors.background !== 'transparent') {\n ctx.strokeStyle = theme.colors.background;\n } else {\n // Transparent bg: infer page background from text luminance.\n // Light text = dark page, dark text = light page.\n ctx.strokeStyle = isLightColor(theme.colors.text)\n ? 'rgba(0, 0, 0, 0.7)'\n : 'rgba(255, 255, 255, 0.85)';\n }\n ctx.lineWidth = 3;\n ctx.lineJoin = 'round';\n ctx.miterLimit = 2;\n ctx.strokeText(node.label, node.x, labelY);\n\n ctx.fillStyle = theme.colors.text;\n ctx.fillText(node.label, node.x, labelY);\n }\n\n ctx.globalAlpha = 1;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Color helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Brighten a hex/rgb color by ~20% for hover effect.\n * Quick and dirty approach: parse hex, lighten each channel.\n */\nfunction brighten(color: string): string {\n // Handle rgb(r,g,b) or rgb(r, g, b)\n const rgbMatch = color.match(/^rgb\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/);\n if (rgbMatch) {\n const r = Math.min(255, parseInt(rgbMatch[1], 10) + 40);\n const g = Math.min(255, parseInt(rgbMatch[2], 10) + 40);\n const b = Math.min(255, parseInt(rgbMatch[3], 10) + 40);\n return `rgb(${r},${g},${b})`;\n }\n\n // Handle hex colors (#rgb and #rrggbb)\n const hex = color.replace('#', '');\n const full =\n hex.length === 3\n ? hex\n .split('')\n .map((c) => c + c)\n .join('')\n : hex;\n\n if (full.length === 6) {\n const r = Math.min(255, parseInt(full.slice(0, 2), 16) + 40);\n const g = Math.min(255, parseInt(full.slice(2, 4), 16) + 40);\n const b = Math.min(255, parseInt(full.slice(4, 6), 16) + 40);\n return `rgb(${r},${g},${b})`;\n }\n\n return color;\n}\n\n/**\n * Returns true if a color is perceptually light (luminance > 0.5).\n * Used to pick a contrasting halo color for labels on transparent backgrounds.\n */\nfunction isLightColor(color: string): boolean {\n const hex = color.replace('#', '');\n const full =\n hex.length === 3\n ? hex\n .split('')\n .map((c) => c + c)\n .join('')\n : hex;\n if (full.length !== 6) return false;\n const r = parseInt(full.slice(0, 2), 16) / 255;\n const g = parseInt(full.slice(2, 4), 16) / 255;\n const b = parseInt(full.slice(4, 6), 16) / 255;\n const toLinear = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\n return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b) > 0.5;\n}\n","/**\n * Immutable zoom transform for graph pan/zoom.\n *\n * Provides coordinate conversion between screen space (canvas pixels)\n * and graph space (simulation coordinates). All mutations return new\n * instances rather than modifying in place.\n */\n\nimport type { PositionedNode } from './types';\n\nexport class ZoomTransform {\n constructor(\n readonly x: number,\n readonly y: number,\n readonly k: number,\n ) {}\n\n /** Convert screen coordinates to graph coordinates. */\n screenToGraph(sx: number, sy: number): { x: number; y: number } {\n return {\n x: (sx - this.x) / this.k,\n y: (sy - this.y) / this.k,\n };\n }\n\n /** Convert graph coordinates to screen coordinates. */\n graphToScreen(gx: number, gy: number): { x: number; y: number } {\n return {\n x: gx * this.k + this.x,\n y: gy * this.k + this.y,\n };\n }\n\n /**\n * Zoom to a target scale, keeping the given screen-space pivot\n * point fixed (content under the cursor stays under the cursor).\n */\n zoomAt(targetK: number, pivotX: number, pivotY: number): ZoomTransform {\n // The graph point under the pivot should remain at the same screen position.\n // Before: pivotX = gx * k + x => gx = (pivotX - x) / k\n // After: pivotX = gx * targetK + newX => newX = pivotX - gx * targetK\n const gx = (pivotX - this.x) / this.k;\n const gy = (pivotY - this.y) / this.k;\n return new ZoomTransform(pivotX - gx * targetK, pivotY - gy * targetK, targetK);\n }\n\n /** Pan by a screen-space delta. */\n pan(dx: number, dy: number): ZoomTransform {\n return new ZoomTransform(this.x + dx, this.y + dy, this.k);\n }\n\n /**\n * Compute a transform that fits all nodes within the given canvas\n * dimensions with the specified padding.\n */\n static fitBounds(\n nodes: PositionedNode[],\n canvasW: number,\n canvasH: number,\n padding: number = 40,\n ): ZoomTransform {\n if (nodes.length === 0) {\n return ZoomTransform.identity();\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const n of nodes) {\n const r = n.radius;\n if (n.x - r < minX) minX = n.x - r;\n if (n.y - r < minY) minY = n.y - r;\n if (n.x + r > maxX) maxX = n.x + r;\n if (n.y + r > maxY) maxY = n.y + r;\n }\n\n const graphW = maxX - minX;\n const graphH = maxY - minY;\n\n if (graphW === 0 && graphH === 0) {\n // All nodes at the same point; just center\n return new ZoomTransform(canvasW / 2 - minX, canvasH / 2 - minY, 1);\n }\n\n const availW = canvasW - padding * 2;\n const availH = canvasH - padding * 2;\n const k = Math.min(availW / graphW, availH / graphH);\n\n // Center the graph in the canvas\n const cx = (minX + maxX) / 2;\n const cy = (minY + maxY) / 2;\n const tx = canvasW / 2 - cx * k;\n const ty = canvasH / 2 - cy * k;\n\n return new ZoomTransform(tx, ty, k);\n }\n\n /** Identity transform (no pan, no zoom). */\n static identity(): ZoomTransform {\n return new ZoomTransform(0, 0, 1);\n }\n}\n","/**\n * Graph interaction manager.\n *\n * Handles mouse/touch events on the canvas and translates them into\n * high-level graph interactions: pan, zoom, hover, select, drag nodes.\n * Uses the spatial index for hit testing and ZoomTransform for coordinate\n * conversion.\n */\n\nimport type { SpatialIndex } from './spatial-index';\nimport { ZoomTransform } from './zoom';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst ZOOM_MIN = 0.05;\nconst ZOOM_MAX = 15;\nconst ZOOM_STEP = -0.001;\nconst HIT_DISTANCE = 5;\n\n// ---------------------------------------------------------------------------\n// Callback interface\n// ---------------------------------------------------------------------------\n\nexport interface InteractionCallbacks {\n onTransformChange(transform: ZoomTransform): void;\n onHoverChange(nodeId: string | null): void;\n /** Called during mouse move when no node is hit, with graph-space coordinates for edge hit testing. */\n onBackgroundHover?(graphX: number, graphY: number, screenX: number, screenY: number): void;\n onSelectionChange(nodeIds: string[]): void;\n onNodeDragStart(nodeId: string): void;\n onNodeDrag(nodeId: string, x: number, y: number): void;\n onNodeDragEnd(nodeId: string): void;\n onDoubleClick(nodeId: string): void;\n}\n\n// ---------------------------------------------------------------------------\n// Internal state\n// ---------------------------------------------------------------------------\n\ninterface DragState {\n nodeId: string;\n started: boolean;\n}\n\ninterface PanState {\n startX: number;\n startY: number;\n}\n\n// ---------------------------------------------------------------------------\n// GraphInteractionManager\n// ---------------------------------------------------------------------------\n\nexport class GraphInteractionManager {\n private canvas: HTMLCanvasElement;\n private spatialIndex: SpatialIndex;\n private callbacks: InteractionCallbacks;\n private transform = ZoomTransform.identity();\n\n private dragState: DragState | null = null;\n private panState: PanState | null = null;\n private mousedownNodeId: string | null = null;\n private selectedIds: Set<string> = new Set();\n\n // Touch state\n private lastTouchDist: number | null = null;\n private lastTouchCenter: { x: number; y: number } | null = null;\n\n // Bound handlers for cleanup\n private boundWheel: (e: WheelEvent) => void;\n private boundMouseDown: (e: MouseEvent) => void;\n private boundMouseMove: (e: MouseEvent) => void;\n private boundMouseUp: (e: MouseEvent) => void;\n private boundDblClick: (e: MouseEvent) => void;\n private boundTouchStart: (e: TouchEvent) => void;\n private boundTouchMove: (e: TouchEvent) => void;\n private boundTouchEnd: (e: TouchEvent) => void;\n private boundMouseLeave: (e: MouseEvent) => void;\n\n constructor(\n canvas: HTMLCanvasElement,\n spatialIndex: SpatialIndex,\n callbacks: InteractionCallbacks,\n ) {\n this.canvas = canvas;\n this.spatialIndex = spatialIndex;\n this.callbacks = callbacks;\n\n // Bind handlers\n this.boundWheel = this.onWheel.bind(this);\n this.boundMouseDown = this.onMouseDown.bind(this);\n this.boundMouseMove = this.onMouseMove.bind(this);\n this.boundMouseUp = this.onMouseUp.bind(this);\n this.boundMouseLeave = this.onMouseLeave.bind(this);\n this.boundDblClick = this.onDblClick.bind(this);\n this.boundTouchStart = this.onTouchStart.bind(this);\n this.boundTouchMove = this.onTouchMove.bind(this);\n this.boundTouchEnd = this.onTouchEnd.bind(this);\n\n // Attach event listeners\n canvas.addEventListener('wheel', this.boundWheel, { passive: false });\n canvas.addEventListener('mousedown', this.boundMouseDown);\n canvas.addEventListener('mousemove', this.boundMouseMove);\n canvas.addEventListener('mouseup', this.boundMouseUp);\n canvas.addEventListener('mouseleave', this.boundMouseLeave);\n canvas.addEventListener('dblclick', this.boundDblClick);\n canvas.addEventListener('touchstart', this.boundTouchStart, {\n passive: false,\n });\n canvas.addEventListener('touchmove', this.boundTouchMove, {\n passive: false,\n });\n canvas.addEventListener('touchend', this.boundTouchEnd);\n }\n\n setTransform(transform: ZoomTransform): void {\n this.transform = transform;\n }\n\n getTransform(): ZoomTransform {\n return this.transform;\n }\n\n destroy(): void {\n this.canvas.removeEventListener('wheel', this.boundWheel);\n this.canvas.removeEventListener('mousedown', this.boundMouseDown);\n this.canvas.removeEventListener('mousemove', this.boundMouseMove);\n this.canvas.removeEventListener('mouseup', this.boundMouseUp);\n this.canvas.removeEventListener('mouseleave', this.boundMouseLeave);\n this.canvas.removeEventListener('dblclick', this.boundDblClick);\n this.canvas.removeEventListener('touchstart', this.boundTouchStart);\n this.canvas.removeEventListener('touchmove', this.boundTouchMove);\n this.canvas.removeEventListener('touchend', this.boundTouchEnd);\n }\n\n // -------------------------------------------------------------------------\n // Mouse handlers\n // -------------------------------------------------------------------------\n\n private canvasXY(e: MouseEvent): { x: number; y: number } {\n const rect = this.canvas.getBoundingClientRect();\n return { x: e.clientX - rect.left, y: e.clientY - rect.top };\n }\n\n private hitTest(screenX: number, screenY: number): string | null {\n const graph = this.transform.screenToGraph(screenX, screenY);\n const node = this.spatialIndex.findNearest(graph.x, graph.y, HIT_DISTANCE / this.transform.k);\n return node?.id ?? null;\n }\n\n private onWheel(e: WheelEvent): void {\n e.preventDefault();\n const { x, y } = this.canvasXY(e);\n const factor = e.deltaY * ZOOM_STEP;\n const newK = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, this.transform.k * (1 + factor)));\n this.transform = this.transform.zoomAt(newK, x, y);\n this.callbacks.onTransformChange(this.transform);\n }\n\n private onMouseDown(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n const hitId = this.hitTest(x, y);\n\n if (hitId) {\n // Start potential node drag\n this.dragState = { nodeId: hitId, started: false };\n this.mousedownNodeId = hitId;\n } else {\n // Start pan\n this.panState = { startX: x, startY: y };\n this.mousedownNodeId = null;\n }\n }\n\n private onMouseMove(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n\n if (this.dragState) {\n const graph = this.transform.screenToGraph(x, y);\n if (!this.dragState.started) {\n this.dragState.started = true;\n this.callbacks.onNodeDragStart(this.dragState.nodeId);\n }\n this.callbacks.onNodeDrag(this.dragState.nodeId, graph.x, graph.y);\n return;\n }\n\n if (this.panState) {\n const dx = x - this.panState.startX;\n const dy = y - this.panState.startY;\n this.transform = this.transform.pan(dx, dy);\n this.panState = { startX: x, startY: y };\n this.callbacks.onTransformChange(this.transform);\n return;\n }\n\n // Hover detection\n const hitId = this.hitTest(x, y);\n this.callbacks.onHoverChange(hitId);\n\n // If no node hit, check edges via callback\n if (!hitId) {\n const graph = this.transform.screenToGraph(x, y);\n this.callbacks.onBackgroundHover?.(graph.x, graph.y, x, y);\n }\n\n // Update cursor\n this.canvas.style.cursor = hitId ? 'pointer' : 'default';\n }\n\n private onMouseUp(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n\n if (this.dragState) {\n if (this.dragState.started) {\n this.callbacks.onNodeDragEnd(this.dragState.nodeId);\n } else {\n // Was a click on a node (no drag movement)\n this.handleNodeClick(this.dragState.nodeId, e.shiftKey);\n }\n this.dragState = null;\n return;\n }\n\n if (this.panState) {\n this.panState = null;\n\n // If mouse up is on background (no node), treat as background click\n if (!this.mousedownNodeId) {\n const hitId = this.hitTest(x, y);\n if (!hitId) {\n // Background click: clear selection\n this.selectedIds.clear();\n this.callbacks.onSelectionChange([]);\n }\n }\n return;\n }\n }\n\n private onDblClick(e: MouseEvent): void {\n const { x, y } = this.canvasXY(e);\n const hitId = this.hitTest(x, y);\n if (hitId) {\n this.callbacks.onDoubleClick(hitId);\n }\n }\n\n private onMouseLeave(_e: MouseEvent): void {\n this.callbacks.onHoverChange(null);\n this.canvas.style.cursor = 'default';\n\n // Cancel any in-progress pan\n if (this.panState) {\n this.panState = null;\n }\n }\n\n private handleNodeClick(nodeId: string, shiftKey: boolean): void {\n if (shiftKey) {\n // Toggle node in multi-select\n if (this.selectedIds.has(nodeId)) {\n this.selectedIds.delete(nodeId);\n } else {\n this.selectedIds.add(nodeId);\n }\n } else {\n // Single select\n this.selectedIds.clear();\n this.selectedIds.add(nodeId);\n }\n\n this.callbacks.onSelectionChange([...this.selectedIds]);\n }\n\n // -------------------------------------------------------------------------\n // Touch handlers\n // -------------------------------------------------------------------------\n\n private onTouchStart(e: TouchEvent): void {\n e.preventDefault();\n\n if (e.touches.length === 2) {\n // Pinch-zoom start\n const [t0, t1] = [e.touches[0], e.touches[1]];\n this.lastTouchDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);\n this.lastTouchCenter = {\n x: (t0.clientX + t1.clientX) / 2,\n y: (t0.clientY + t1.clientY) / 2,\n };\n } else if (e.touches.length === 1) {\n const touch = e.touches[0];\n const rect = this.canvas.getBoundingClientRect();\n const x = touch.clientX - rect.left;\n const y = touch.clientY - rect.top;\n\n const hitId = this.hitTest(x, y);\n if (hitId) {\n this.mousedownNodeId = hitId;\n } else {\n this.panState = { startX: x, startY: y };\n this.mousedownNodeId = null;\n }\n }\n }\n\n private onTouchMove(e: TouchEvent): void {\n e.preventDefault();\n\n if (e.touches.length === 2 && this.lastTouchDist !== null) {\n const [t0, t1] = [e.touches[0], e.touches[1]];\n const newDist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);\n const rect = this.canvas.getBoundingClientRect();\n const centerX = (t0.clientX + t1.clientX) / 2 - rect.left;\n const centerY = (t0.clientY + t1.clientY) / 2 - rect.top;\n\n const scale = newDist / this.lastTouchDist;\n const newK = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, this.transform.k * scale));\n this.transform = this.transform.zoomAt(newK, centerX, centerY);\n\n // Pan from center movement\n if (this.lastTouchCenter) {\n const dx = centerX - (this.lastTouchCenter.x - rect.left);\n const dy = centerY - (this.lastTouchCenter.y - rect.top);\n this.transform = this.transform.pan(dx, dy);\n }\n\n this.lastTouchDist = newDist;\n this.lastTouchCenter = {\n x: (t0.clientX + t1.clientX) / 2,\n y: (t0.clientY + t1.clientY) / 2,\n };\n this.callbacks.onTransformChange(this.transform);\n } else if (e.touches.length === 1 && this.panState) {\n const touch = e.touches[0];\n const rect = this.canvas.getBoundingClientRect();\n const x = touch.clientX - rect.left;\n const y = touch.clientY - rect.top;\n\n const dx = x - this.panState.startX;\n const dy = y - this.panState.startY;\n this.transform = this.transform.pan(dx, dy);\n this.panState = { startX: x, startY: y };\n this.callbacks.onTransformChange(this.transform);\n }\n }\n\n private onTouchEnd(e: TouchEvent): void {\n if (e.touches.length === 0) {\n // Tap-select\n if (this.mousedownNodeId && !this.panState) {\n this.handleNodeClick(this.mousedownNodeId, false);\n } else if (!this.mousedownNodeId && this.panState) {\n // Background tap: clear selection\n this.selectedIds.clear();\n this.callbacks.onSelectionChange([]);\n }\n\n this.panState = null;\n this.mousedownNodeId = null;\n this.lastTouchDist = null;\n this.lastTouchCenter = null;\n }\n }\n}\n","/**\n * Keyboard navigation for the graph canvas.\n *\n * Provides accessible keyboard control: Tab to focus, arrow keys to\n * navigate between adjacent nodes (following edges), Enter to select,\n * Escape to clear, +/- to zoom, Home to fit all, / to focus search.\n */\n\nimport type { PositionedNode } from './types';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface KeyboardNavOptions {\n canvas: HTMLCanvasElement;\n getNodes(): PositionedNode[];\n getSelectedIds(): string[];\n getAdjacency(): Map<string, Set<string>>;\n onSelect(nodeId: string): void;\n onDeselect(): void;\n onZoom(direction: 'in' | 'out'): void;\n onFitAll(): void;\n onFocusSearch?(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Attach keyboard navigation to a graph canvas.\n * Returns a cleanup function that removes all listeners.\n */\nexport function attachGraphKeyboardNav(options: KeyboardNavOptions): () => void {\n const {\n canvas,\n getNodes,\n getSelectedIds,\n getAdjacency,\n onSelect,\n onDeselect,\n onZoom,\n onFitAll,\n onFocusSearch,\n } = options;\n\n let focusedNodeId: string | null = null;\n\n // Make canvas focusable\n if (!canvas.hasAttribute('tabindex')) {\n canvas.setAttribute('tabindex', '0');\n }\n\n function findNodeById(id: string): PositionedNode | undefined {\n return getNodes().find((n) => n.id === id);\n }\n\n /**\n * Given a set of neighbor node ids, pick the one that best matches\n * the arrow key direction relative to the current focused node.\n */\n function pickDirectionalNeighbor(\n fromNode: PositionedNode,\n neighborIds: Set<string>,\n direction: 'up' | 'down' | 'left' | 'right',\n ): string | null {\n const nodes = getNodes();\n const candidates = nodes.filter((n) => neighborIds.has(n.id));\n if (candidates.length === 0) return null;\n\n // Score each candidate by how well it matches the desired direction\n let best: PositionedNode | null = null;\n let bestScore = -Infinity;\n\n for (const c of candidates) {\n const dx = c.x - fromNode.x;\n const dy = c.y - fromNode.y;\n let score: number;\n\n switch (direction) {\n case 'right':\n score = dx - Math.abs(dy) * 0.5;\n break;\n case 'left':\n score = -dx - Math.abs(dy) * 0.5;\n break;\n case 'down':\n score = dy - Math.abs(dx) * 0.5;\n break;\n case 'up':\n score = -dy - Math.abs(dx) * 0.5;\n break;\n }\n\n if (score > bestScore) {\n bestScore = score;\n best = c;\n }\n }\n\n return best?.id ?? null;\n }\n\n function onKeyDown(e: KeyboardEvent): void {\n switch (e.key) {\n case 'Tab': {\n // Focus first/selected node\n const selected = getSelectedIds();\n const nodes = getNodes();\n if (nodes.length === 0) return;\n\n if (selected.length > 0) {\n focusedNodeId = selected[0];\n } else if (!focusedNodeId || !findNodeById(focusedNodeId)) {\n focusedNodeId = nodes[0].id;\n }\n\n e.preventDefault();\n break;\n }\n\n case 'ArrowUp':\n case 'ArrowDown':\n case 'ArrowLeft':\n case 'ArrowRight': {\n if (!focusedNodeId) return;\n e.preventDefault();\n\n const focusedNode = findNodeById(focusedNodeId);\n if (!focusedNode) return;\n\n const adjacency = getAdjacency();\n const neighbors = adjacency.get(focusedNodeId);\n if (!neighbors || neighbors.size === 0) return;\n\n const dirMap: Record<string, 'up' | 'down' | 'left' | 'right'> = {\n ArrowUp: 'up',\n ArrowDown: 'down',\n ArrowLeft: 'left',\n ArrowRight: 'right',\n };\n\n const nextId = pickDirectionalNeighbor(focusedNode, neighbors, dirMap[e.key]);\n if (nextId) {\n focusedNodeId = nextId;\n onSelect(nextId);\n }\n break;\n }\n\n case 'Enter': {\n if (focusedNodeId) {\n e.preventDefault();\n const selected = getSelectedIds();\n if (selected.includes(focusedNodeId)) {\n onDeselect();\n } else {\n onSelect(focusedNodeId);\n }\n }\n break;\n }\n\n case 'Escape': {\n e.preventDefault();\n focusedNodeId = null;\n onDeselect();\n break;\n }\n\n case '+':\n case '=': {\n e.preventDefault();\n onZoom('in');\n break;\n }\n\n case '-':\n case '_': {\n e.preventDefault();\n onZoom('out');\n break;\n }\n\n case 'Home': {\n e.preventDefault();\n onFitAll();\n break;\n }\n\n case '/': {\n if (onFocusSearch) {\n e.preventDefault();\n onFocusSearch();\n }\n break;\n }\n }\n }\n\n canvas.addEventListener('keydown', onKeyDown);\n\n // Return cleanup function\n return () => {\n canvas.removeEventListener('keydown', onKeyDown);\n };\n}\n","/**\n * Graph search manager.\n *\n * Provides case-insensitive substring matching against node labels/ids.\n * Returns a Set of matching node ids that the renderer uses to dim\n * non-matching nodes and edges.\n */\n\nexport class GraphSearchManager {\n private matchedIds: Set<string> | null = null;\n\n /**\n * Search for nodes matching the query string.\n * Returns a Set of matching node ids, or an empty set if nothing matches.\n */\n search(query: string, nodes: Array<{ id: string; label?: string }>): Set<string> {\n const q = query.toLowerCase().trim();\n\n if (q === '') {\n this.matchedIds = null;\n return new Set();\n }\n\n const matches = new Set<string>();\n for (const node of nodes) {\n const label = (node.label ?? '').toLowerCase();\n const id = node.id.toLowerCase();\n if (label.includes(q) || id.includes(q)) {\n matches.add(node.id);\n }\n }\n\n this.matchedIds = matches;\n return matches;\n }\n\n /**\n * Clear the current search.\n * Returns null to indicate no active search.\n */\n clearSearch(): Set<string> | null {\n this.matchedIds = null;\n return null;\n }\n\n /** Get the current set of matched ids, or null if no search is active. */\n getMatches(): Set<string> | null {\n return this.matchedIds;\n }\n}\n","/**\n * SimulationManager: spawns a Web Worker for the force simulation,\n * or falls back to synchronous d3-force on the main thread.\n *\n * Synchronous fallback is used when:\n * - Web Workers are unavailable (SSR, test environments)\n * - Node count is below 200 (worker overhead not worth it)\n *\n * The sync path caps at 300 ticks to prevent blocking the main thread.\n */\n\nimport {\n forceCenter,\n forceCollide,\n forceLink,\n forceManyBody,\n forceSimulation,\n forceX,\n forceY,\n type Simulation,\n type SimulationNodeDatum,\n} from 'd3-force';\n\nimport type { SimEdge, SimNode, WorkerOutMessage, WorkerSimulationConfig } from './worker-protocol';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SYNC_THRESHOLD = 200;\nconst SYNC_MAX_TICKS = 300;\n\n// ---------------------------------------------------------------------------\n// Internal node shape for sync simulation\n// ---------------------------------------------------------------------------\n\ninterface SyncNode extends SimulationNodeDatum {\n id: string;\n radius: number;\n community?: string;\n fx?: number | null;\n fy?: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Cluster force (duplicated in simulation-worker.ts for the Web Worker path.\n// Worker can't import from workspace packages, so both copies must stay in sync.)\n// ---------------------------------------------------------------------------\n\nfunction forceCluster(nodes: SyncNode[], strength: number) {\n return (alpha: number) => {\n const cx = new Map<string, number>();\n const cy = new Map<string, number>();\n const count = new Map<string, number>();\n\n for (const node of nodes) {\n if (!node.community) continue;\n const c = node.community;\n cx.set(c, (cx.get(c) ?? 0) + (node.x ?? 0));\n cy.set(c, (cy.get(c) ?? 0) + (node.y ?? 0));\n count.set(c, (count.get(c) ?? 0) + 1);\n }\n\n for (const [c, n] of count) {\n cx.set(c, cx.get(c)! / n);\n cy.set(c, cy.get(c)! / n);\n }\n\n const k = strength * alpha;\n for (const node of nodes) {\n if (!node.community) continue;\n const targetX = cx.get(node.community)!;\n const targetY = cy.get(node.community)!;\n node.vx = (node.vx ?? 0) + (targetX - (node.x ?? 0)) * k;\n node.vy = (node.vy ?? 0) + (targetY - (node.y ?? 0)) * k;\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// SimulationManager\n// ---------------------------------------------------------------------------\n\ntype TickCallback = (positions: Array<{ id: string; x: number; y: number }>, alpha: number) => void;\n\ntype SettledCallback = () => void;\n\nexport class SimulationManager {\n private worker: Worker | null = null;\n private syncSim: Simulation<SyncNode, undefined> | null = null;\n private syncNodes: SyncNode[] = [];\n private syncNodeMap: Map<string, SyncNode> = new Map();\n private tickCb: TickCallback | null = null;\n private settledCb: SettledCallback | null = null;\n private destroyed = false;\n\n // Stored for worker->sync fallback\n private initNodes: SimNode[] = [];\n private initEdges: SimEdge[] = [];\n private initConfig: WorkerSimulationConfig | null = null;\n\n private constructor() {}\n\n /**\n * Create a SimulationManager. Uses Web Worker for large graphs,\n * synchronous fallback for small graphs or when Worker unavailable.\n */\n static create(\n nodes: SimNode[],\n edges: SimEdge[],\n config: WorkerSimulationConfig,\n ): SimulationManager {\n const mgr = new SimulationManager();\n\n const useWorker = typeof Worker !== 'undefined' && nodes.length >= SYNC_THRESHOLD;\n\n if (useWorker) {\n mgr.initWorker(nodes, edges, config);\n } else {\n mgr.initSync(nodes, edges, config);\n }\n\n return mgr;\n }\n\n /** Register a callback for position updates. */\n onTick(cb: TickCallback): void {\n this.tickCb = cb;\n }\n\n /** Register a callback for when the simulation has settled. */\n onSettled(cb: SettledCallback): void {\n this.settledCb = cb;\n }\n\n /** Reheat the simulation. */\n reheat(alpha?: number): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'reheat', alpha });\n } else if (this.syncSim) {\n this.syncSim.alpha(alpha ?? 0.3).restart();\n this.runSyncTicks();\n }\n }\n\n /** Pin a node to fixed x/y coordinates. */\n pinNode(id: string, x: number, y: number): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'pin', nodeId: id, x, y });\n } else {\n const node = this.syncNodeMap.get(id);\n if (node) {\n node.fx = x;\n node.fy = y;\n }\n }\n }\n\n /** Unpin a node (free it from fixed position). */\n unpinNode(id: string): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'unpin', nodeId: id });\n } else {\n const node = this.syncNodeMap.get(id);\n if (node) {\n node.fx = null;\n node.fy = null;\n }\n }\n }\n\n /** Drag a node (pins it and reheats slightly). */\n dragNode(id: string, x: number, y: number): void {\n if (this.destroyed) return;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'drag', nodeId: id, x, y });\n } else {\n const node = this.syncNodeMap.get(id);\n if (node) {\n node.fx = x;\n node.fy = y;\n }\n if (this.syncSim && this.syncSim.alpha() < 0.1) {\n this.syncSim.alpha(0.1).restart();\n this.runSyncTicks();\n }\n }\n }\n\n /** Tear down the simulation and release resources. */\n destroy(): void {\n this.destroyed = true;\n\n if (this.worker) {\n this.worker.postMessage({ type: 'stop' });\n this.worker.terminate();\n this.worker = null;\n }\n\n if (this.syncSim) {\n this.syncSim.stop();\n this.syncSim = null;\n }\n\n this.tickCb = null;\n this.settledCb = null;\n }\n\n // -------------------------------------------------------------------------\n // Worker path\n // -------------------------------------------------------------------------\n\n private initWorker(nodes: SimNode[], edges: SimEdge[], config: WorkerSimulationConfig): void {\n // Store for fallback if worker fails to load\n this.initNodes = nodes;\n this.initEdges = edges;\n this.initConfig = config;\n\n try {\n const workerUrl = new URL('./simulation-worker.ts', import.meta.url);\n this.worker = new Worker(workerUrl, { type: 'module' });\n } catch {\n // Worker construction failed (e.g. SSR or restrictive CSP)\n console.warn('[SimulationManager] Worker creation failed, using sync fallback');\n this.initSync(nodes, edges, config);\n return;\n }\n\n this.worker.onmessage = (event: MessageEvent<WorkerOutMessage>) => {\n if (this.destroyed) return;\n const msg = event.data;\n\n switch (msg.type) {\n case 'positions':\n this.tickCb?.(msg.nodes, msg.alpha);\n break;\n case 'settled':\n this.settledCb?.();\n break;\n case 'error':\n console.error('[SimulationManager] Worker error:', msg.message);\n break;\n }\n };\n\n this.worker.onerror = () => {\n // Worker failed to load (e.g. MIME type error in dev, missing file).\n // Terminate and fall back to synchronous simulation.\n if (this.destroyed) return;\n console.warn('[SimulationManager] Worker failed to load, falling back to sync');\n this.worker?.terminate();\n this.worker = null;\n this.initSync(this.initNodes, this.initEdges, this.initConfig!);\n };\n\n this.worker.postMessage({ type: 'init', nodes, edges, config });\n }\n\n // -------------------------------------------------------------------------\n // Synchronous fallback\n // -------------------------------------------------------------------------\n\n private initSync(nodes: SimNode[], edges: SimEdge[], config: WorkerSimulationConfig): void {\n this.syncNodes = nodes.map((n) => ({\n id: n.id,\n x: n.x,\n y: n.y,\n radius: n.radius,\n community: n.community,\n }));\n\n this.syncNodeMap = new Map(this.syncNodes.map((n) => [n.id, n]));\n\n const linkForce = forceLink(edges.map((e) => ({ ...e })))\n .id((d) => (d as SyncNode).id)\n .distance(config.linkDistance);\n if (config.linkStrength != null) {\n linkForce.strength(config.linkStrength);\n }\n\n const padding = config.collisionPadding ?? 2;\n\n this.syncSim = forceSimulation<SyncNode>(this.syncNodes)\n .force('link', linkForce)\n .force('charge', forceManyBody().strength(config.chargeStrength))\n .force(\n 'collide',\n forceCollide<SyncNode>().radius((d) => d.radius + padding),\n )\n // Weak gravity keeps disconnected nodes from drifting far from center\n .force('gravityX', forceX<SyncNode>(0).strength(0.05))\n .force('gravityY', forceY<SyncNode>(0).strength(0.05))\n .alphaDecay(config.alphaDecay)\n .velocityDecay(config.velocityDecay)\n .stop(); // Don't auto-run; we tick manually\n\n // Center force (default true)\n if (config.centerForce !== false) {\n this.syncSim.force('center', forceCenter(0, 0));\n }\n\n // Add clustering force if configured\n if (config.clustering) {\n const clusterFn = forceCluster(this.syncNodes, config.clustering.strength);\n // d3 calls force functions with (alpha) on each tick\n this.syncSim.force('cluster', clusterFn as unknown as ReturnType<typeof forceCenter>);\n }\n\n // Defer initial delivery: callbacks aren't wired yet at create() time\n this.runSyncTicks(true);\n }\n\n /**\n * Run ticks synchronously and fire callbacks.\n * @param deferred - When true, deliver results via microtask. Used for the\n * initial run where callbacks haven't been registered yet. Subsequent\n * calls (reheat, drag) fire synchronously since callbacks are already set.\n */\n private runSyncTicks(deferred = false): void {\n if (!this.syncSim || this.destroyed) return;\n\n const sim = this.syncSim;\n for (let i = 0; i < SYNC_MAX_TICKS; i++) {\n sim.tick();\n if (sim.alpha() < 0.001) break;\n }\n\n const positions = this.syncNodes.map((n) => ({\n id: n.id,\n x: n.x ?? 0,\n y: n.y ?? 0,\n }));\n const alpha = sim.alpha();\n const settled = alpha < 0.001;\n\n const deliver = () => {\n if (this.destroyed) return;\n this.tickCb?.(positions, alpha);\n if (settled) {\n this.settledCb?.();\n }\n };\n\n if (deferred) {\n // Initial run: callbacks not registered yet, defer to microtask\n queueMicrotask(deliver);\n } else {\n deliver();\n }\n }\n}\n","/**\n * Spatial index for fast node hit-testing.\n *\n * Wraps d3-quadtree to provide findNearest (accounts for node radius)\n * and findInRect queries. Tracks a generation counter to avoid unnecessary\n * rebuilds when positions haven't changed.\n */\n\nimport { type Quadtree, quadtree } from 'd3-quadtree';\nimport type { PositionedNode } from './types';\n\nexport class SpatialIndex {\n private tree: Quadtree<PositionedNode> | null = null;\n private nodes: PositionedNode[] = [];\n private maxRadius = 0;\n private generation = 0;\n\n /** Rebuild the quadtree from the current set of positioned nodes. */\n rebuild(nodes: PositionedNode[]): void {\n this.nodes = nodes;\n this.maxRadius = 0;\n for (const n of nodes) {\n if (n.radius > this.maxRadius) this.maxRadius = n.radius;\n }\n\n this.tree = quadtree<PositionedNode>()\n .x((d) => d.x)\n .y((d) => d.y)\n .addAll(nodes);\n this.generation++;\n }\n\n /** Current generation counter. Increments on each rebuild. */\n getGeneration(): number {\n return this.generation;\n }\n\n /**\n * Find the nearest node to (x, y) within maxDistance.\n * Accounts for node radius: a hit occurs if the point is inside\n * the node circle (distance to center < node.radius), or if the\n * edge-to-edge distance is within maxDistance.\n */\n findNearest(x: number, y: number, maxDistance: number = Infinity): PositionedNode | null {\n if (!this.tree || this.nodes.length === 0) return null;\n\n // The effective search radius for the quadtree needs to include\n // the largest node radius, because we might be \"inside\" a large node\n // whose center is far from our search point.\n const searchRadius = maxDistance + this.maxRadius;\n\n let best: PositionedNode | null = null;\n let bestEffectiveDist = maxDistance + this.maxRadius + 1;\n\n this.tree.visit((node, x0, y0, x1, y1) => {\n // Closest point in this quad to our target\n const closestX = Math.max(x0, Math.min(x, x1));\n const closestY = Math.max(y0, Math.min(y, y1));\n const quadDist = Math.hypot(closestX - x, closestY - y);\n\n // Prune: if the closest edge of this quad is beyond searchRadius, skip\n if (quadDist > searchRadius) return true;\n\n // Check leaf data\n if (!node.length) {\n let current = node;\n do {\n const d = current.data;\n if (d) {\n const dist = Math.hypot(d.x - x, d.y - y);\n // Effective distance: subtract the node's radius.\n // If we're inside the circle, effective distance is 0.\n const effectiveDist = Math.max(0, dist - d.radius);\n if (effectiveDist <= maxDistance && effectiveDist < bestEffectiveDist) {\n bestEffectiveDist = effectiveDist;\n best = d;\n }\n }\n } while ((current = current.next!));\n }\n\n return false;\n });\n\n return best;\n }\n\n /** Find all nodes whose centers fall within the given rectangle. */\n findInRect(x1: number, y1: number, x2: number, y2: number): PositionedNode[] {\n if (!this.tree) return [];\n\n const minX = Math.min(x1, x2);\n const minY = Math.min(y1, y2);\n const maxX = Math.max(x1, x2);\n const maxY = Math.max(y1, y2);\n\n const results: PositionedNode[] = [];\n\n this.tree.visit((node, qx0, qy0, qx1, qy1) => {\n // If quad doesn't overlap the search rect, skip\n if (qx0 > maxX || qx1 < minX || qy0 > maxY || qy1 < minY) {\n return true;\n }\n\n // Check leaf nodes\n if (!node.length) {\n let current = node;\n do {\n const d = current.data;\n if (d && d.x >= minX && d.x <= maxX && d.y >= minY && d.y <= maxY) {\n results.push(d);\n }\n } while ((current = current.next!));\n }\n\n return false;\n });\n\n return results;\n }\n}\n","/**\n * Resize observer: thin wrapper around ResizeObserver with debounce.\n *\n * Watches a container element for size changes and calls back with\n * the new width and height. Debounced at ~60fps (16ms) to avoid\n * excessive re-renders during drag resizes.\n */\n\nconst DEBOUNCE_MS = 16;\n\n/**\n * Observe a container element for size changes.\n *\n * @param container - The element to watch.\n * @param callback - Called with (width, height) when size changes.\n * @returns A cleanup function that disconnects the observer.\n */\nexport function observeResize(\n container: HTMLElement,\n callback: (width: number, height: number) => void,\n): () => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new ResizeObserver((entries) => {\n // Debounce to ~60fps\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n callback(width, height);\n }\n timeoutId = null;\n }, DEBOUNCE_MS);\n });\n\n observer.observe(container);\n\n return () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n observer.disconnect();\n };\n}\n","/**\n * Tooltip manager: creates and positions a floating tooltip element.\n *\n * Shows tooltip content near the mouse/touch position with viewport\n * edge avoidance. Touch support via tap-to-show, tap-outside-to-hide.\n */\n\nimport type { TooltipContent } from '@opendata-ai/openchart-core';\n\nexport interface TooltipManager {\n /** Show the tooltip with content at a given position. */\n show(content: TooltipContent, x: number, y: number): void;\n /** Hide the tooltip. */\n hide(): void;\n /** Remove the tooltip element and clean up event listeners. */\n destroy(): void;\n}\n\nconst TOOLTIP_OFFSET = 12;\n\n/**\n * Create a tooltip manager attached to a container element.\n *\n * The manager creates a floating div positioned relative to the container.\n * Content is rendered as a title line with optional color indicator,\n * followed by a compact list of field-value pairs.\n *\n * @param container - The parent element for the tooltip.\n * @returns TooltipManager with show/hide/destroy methods.\n */\nexport function createTooltipManager(container: HTMLElement): TooltipManager {\n const tooltip = document.createElement('div');\n tooltip.className = 'viz-tooltip';\n tooltip.setAttribute('role', 'tooltip');\n\n container.style.position = container.style.position || 'relative';\n container.appendChild(tooltip);\n\n // Hide on tap-outside for touch devices\n const handleDocumentTouch = (e: Event): void => {\n if (!container.contains(e.target as Node)) {\n hide();\n }\n };\n document.addEventListener('touchstart', handleDocumentTouch);\n\n function show(content: TooltipContent, x: number, y: number): void {\n let html = '';\n\n // Title row: optional color dot + title text\n if (content.title) {\n const titleColor = content.fields.find((f) => f.color)?.color;\n html += '<div class=\"viz-tooltip-header\">';\n if (titleColor) {\n html += `<span class=\"viz-tooltip-dot\" style=\"background:${esc(titleColor)}\"></span>`;\n }\n html += `<span class=\"viz-tooltip-title\">${esc(content.title)}</span>`;\n html += '</div>';\n }\n\n // Field rows\n if (content.fields.length > 0) {\n html += '<div class=\"viz-tooltip-body\">';\n for (const field of content.fields) {\n html += '<div class=\"viz-tooltip-row\">';\n html += `<span class=\"viz-tooltip-label\">${esc(field.label)}</span>`;\n html += `<span class=\"viz-tooltip-value\">${esc(field.value)}</span>`;\n html += '</div>';\n }\n html += '</div>';\n }\n\n tooltip.innerHTML = html;\n tooltip.style.display = 'block';\n\n // Position with viewport edge avoidance\n const containerRect = container.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n\n let left = x + TOOLTIP_OFFSET;\n let top = y + TOOLTIP_OFFSET;\n\n // Flip horizontal if overflowing right\n if (left + tooltipRect.width > containerRect.width) {\n left = x - tooltipRect.width - TOOLTIP_OFFSET;\n }\n // Flip vertical if overflowing bottom\n if (top + tooltipRect.height > containerRect.height) {\n top = y - tooltipRect.height - TOOLTIP_OFFSET;\n }\n\n // Clamp to container bounds\n left = Math.max(0, Math.min(left, containerRect.width - tooltipRect.width));\n top = Math.max(0, Math.min(top, containerRect.height - tooltipRect.height));\n\n tooltip.style.left = `${left}px`;\n tooltip.style.top = `${top}px`;\n }\n\n function hide(): void {\n tooltip.style.display = 'none';\n }\n\n function destroy(): void {\n document.removeEventListener('touchstart', handleDocumentTouch);\n if (tooltip.parentNode) {\n tooltip.parentNode.removeChild(tooltip);\n }\n }\n\n return { show, hide, destroy };\n}\n\nfunction esc(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n","/**\n * Mount API: the main entry point for vanilla JS usage.\n *\n * createChart() takes a container, spec, and options, compiles the chart,\n * renders it as SVG, sets up responsive resizing, tooltip interaction\n * (mouse/touch/keyboard), keyboard navigation between data points,\n * and returns a ChartInstance with update/resize/export/destroy methods.\n */\n\nimport type {\n Annotation,\n AnnotationOffset,\n ChartEventHandlers,\n ChartLayout,\n ChartSpec,\n ChromeKey,\n CompileOptions,\n DarkMode,\n ElementEdit,\n GraphSpec,\n MeasureTextFn,\n RangeAnnotation,\n RefLineAnnotation,\n TextAnnotation,\n ThemeConfig,\n TooltipContent,\n} from '@opendata-ai/openchart-core';\nimport { compileChart } from '@opendata-ai/openchart-engine';\nimport { exportCSV, exportJPG, exportPNG, exportSVG, type JPGExportOptions } from './export';\nimport { observeResize } from './resize-observer';\nimport { renderChartSVG } from './svg-renderer';\nimport { createTooltipManager, type TooltipManager } from './tooltip';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MountOptions extends ChartEventHandlers {\n /** Theme overrides. */\n theme?: ThemeConfig;\n /** Dark mode setting: \"auto\" (system pref), \"force\", or \"off\". */\n darkMode?: DarkMode;\n /** Callback when a data point is clicked. @deprecated Use onMarkClick instead. */\n onDataPointClick?: (data: Record<string, unknown>) => void;\n /** Enable responsive resizing. Defaults to true. */\n responsive?: boolean;\n}\n\nexport interface ExportOptions extends JPGExportOptions {\n // Extensible for future formats (extends JPGExportOptions which extends PNGExportOptions)\n}\n\nexport interface ChartInstance {\n /** Re-compile and re-render with a new spec. */\n update(spec: ChartSpec | GraphSpec): void;\n /** Re-compile at current container dimensions. */\n resize(): void;\n /** Export the chart. */\n export(format: 'svg'): string;\n export(format: 'png', options?: ExportOptions): Promise<Blob>;\n export(format: 'jpg', options?: ExportOptions): Promise<Blob>;\n export(format: 'csv'): string;\n export(format: 'svg' | 'png' | 'jpg' | 'csv', options?: ExportOptions): string | Promise<Blob>;\n /** Remove all DOM elements and disconnect observers. */\n destroy(): void;\n /** The current compiled layout (for hooks / debugging). */\n readonly layout: ChartLayout;\n}\n\n// ---------------------------------------------------------------------------\n// Dark mode resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveDarkMode(mode?: DarkMode): boolean {\n if (mode === 'force') return true;\n if (mode === 'off' || mode === undefined) return false;\n // \"auto\": check system preference\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// measureText via hidden canvas\n// ---------------------------------------------------------------------------\n\nfunction createMeasureText(): MeasureTextFn {\n let canvas: HTMLCanvasElement | null = null;\n let ctx: CanvasRenderingContext2D | null = null;\n\n return (\n text: string,\n fontSize: number,\n fontWeight?: number,\n ): { width: number; height: number } => {\n if (!canvas) {\n canvas = document.createElement('canvas');\n ctx = canvas.getContext('2d');\n }\n if (!ctx) {\n // Fallback: heuristic estimation\n return { width: text.length * fontSize * 0.6, height: fontSize * 1.2 };\n }\n\n const weight = fontWeight ?? 400;\n ctx.font = `${weight} ${fontSize}px Inter, sans-serif`;\n const metrics = ctx.measureText(text);\n return {\n width: metrics.width,\n height: fontSize * 1.2,\n };\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tooltip event wiring\n// ---------------------------------------------------------------------------\n\n/**\n * Wire tooltip events on mark elements inside an SVG.\n * Returns a cleanup function to remove all listeners.\n */\nfunction wireTooltipEvents(\n svg: SVGElement,\n tooltipDescriptors: Map<string, TooltipContent>,\n tooltipManager: TooltipManager,\n): () => void {\n const markElements = svg.querySelectorAll('[data-mark-id]');\n const cleanups: Array<() => void> = [];\n\n for (const el of markElements) {\n const markId = el.getAttribute('data-mark-id');\n if (!markId) continue;\n\n const content = tooltipDescriptors.get(markId);\n if (!content) continue;\n\n // Mouse enter -> show tooltip\n const handleMouseEnter = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n const x = mouseEvent.clientX - svgRect.left;\n const y = mouseEvent.clientY - svgRect.top;\n tooltipManager.show(content, x, y);\n };\n\n // Mouse move -> reposition tooltip\n const handleMouseMove = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n const x = mouseEvent.clientX - svgRect.left;\n const y = mouseEvent.clientY - svgRect.top;\n tooltipManager.show(content, x, y);\n };\n\n // Mouse leave -> hide tooltip\n const handleMouseLeave = () => {\n tooltipManager.hide();\n };\n\n // Touch: tap to show\n const handleTouchStart = (e: Event) => {\n const touchEvent = e as TouchEvent;\n if (touchEvent.touches.length > 0) {\n const touch = touchEvent.touches[0];\n const svgRect = svg.getBoundingClientRect();\n const x = touch.clientX - svgRect.left;\n const y = touch.clientY - svgRect.top;\n tooltipManager.show(content, x, y);\n }\n };\n\n el.addEventListener('mouseenter', handleMouseEnter);\n el.addEventListener('mousemove', handleMouseMove);\n el.addEventListener('mouseleave', handleMouseLeave);\n el.addEventListener('touchstart', handleTouchStart);\n\n cleanups.push(() => {\n el.removeEventListener('mouseenter', handleMouseEnter);\n el.removeEventListener('mousemove', handleMouseMove);\n el.removeEventListener('mouseleave', handleMouseLeave);\n el.removeEventListener('touchstart', handleTouchStart);\n });\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Chart event wiring (click, hover, leave on marks; legend toggle; annotation click)\n// ---------------------------------------------------------------------------\n\n/**\n * Build a map from data-mark-id to { datum, series } so event handlers\n * can look up the data row associated with a clicked/hovered mark element.\n */\nfunction buildMarkDataMap(\n layout: ChartLayout,\n): Map<string, { datum: Record<string, unknown>; series?: string }> {\n const map = new Map<string, { datum: Record<string, unknown>; series?: string }>();\n\n for (let i = 0; i < layout.marks.length; i++) {\n const mark = layout.marks[i];\n switch (mark.type) {\n case 'line':\n map.set(`line-${mark.seriesKey ?? i}`, {\n // For line marks, data is an array. Use the first row as representative.\n datum: mark.data[0] ?? {},\n series: mark.seriesKey,\n });\n break;\n case 'area':\n map.set(`area-${mark.seriesKey ?? i}`, {\n datum: mark.data[0] ?? {},\n series: mark.seriesKey,\n });\n break;\n case 'rect':\n map.set(`rect-${i}`, { datum: mark.data });\n break;\n case 'arc':\n map.set(`arc-${i}`, { datum: mark.data });\n break;\n case 'point':\n map.set(`point-${i}`, { datum: mark.data });\n break;\n }\n }\n\n return map;\n}\n\n/**\n * Wire chart event handlers (onMarkClick, onMarkHover, onMarkLeave) to mark\n * elements, onLegendToggle to legend entries, and onAnnotationClick to annotation\n * elements inside an SVG.\n *\n * Returns a cleanup function to remove all listeners.\n */\nfunction wireChartEvents(\n svg: SVGElement,\n layout: ChartLayout,\n specAnnotations: import('@opendata-ai/openchart-core').Annotation[],\n handlers: ChartEventHandlers,\n): () => void {\n const cleanups: Array<() => void> = [];\n const markDataMap = buildMarkDataMap(layout);\n\n // Wire mark click/hover/leave events\n if (handlers.onMarkClick || handlers.onMarkHover || handlers.onMarkLeave) {\n const markElements = svg.querySelectorAll('[data-mark-id]');\n\n for (const el of markElements) {\n const markId = el.getAttribute('data-mark-id');\n if (!markId) continue;\n\n const markInfo = markDataMap.get(markId);\n if (!markInfo) continue;\n\n const series = markInfo.series ?? el.getAttribute('data-series') ?? undefined;\n\n if (handlers.onMarkClick) {\n const handleClick = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n handlers.onMarkClick!({\n datum: markInfo.datum,\n series,\n position: {\n x: mouseEvent.clientX - svgRect.left,\n y: mouseEvent.clientY - svgRect.top,\n },\n event: mouseEvent,\n });\n };\n el.addEventListener('click', handleClick);\n cleanups.push(() => el.removeEventListener('click', handleClick));\n }\n\n if (handlers.onMarkHover) {\n const handleEnter = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n const svgRect = svg.getBoundingClientRect();\n handlers.onMarkHover!({\n datum: markInfo.datum,\n series,\n position: {\n x: mouseEvent.clientX - svgRect.left,\n y: mouseEvent.clientY - svgRect.top,\n },\n event: mouseEvent,\n });\n };\n el.addEventListener('mouseenter', handleEnter);\n cleanups.push(() => el.removeEventListener('mouseenter', handleEnter));\n }\n\n if (handlers.onMarkLeave) {\n const handleLeave = () => {\n handlers.onMarkLeave!();\n };\n el.addEventListener('mouseleave', handleLeave);\n cleanups.push(() => el.removeEventListener('mouseleave', handleLeave));\n }\n }\n }\n\n // Wire annotation click events\n if (handlers.onAnnotationClick) {\n const annotationElements = svg.querySelectorAll('.viz-annotation');\n\n for (let i = 0; i < annotationElements.length; i++) {\n const el = annotationElements[i];\n const specAnnotation = specAnnotations[i];\n if (!specAnnotation) continue;\n\n const handleClick = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n handlers.onAnnotationClick!(specAnnotation, mouseEvent);\n };\n\n el.addEventListener('click', handleClick);\n cleanups.push(() => el.removeEventListener('click', handleClick));\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared drag handler utility\n// ---------------------------------------------------------------------------\n\ninterface DragConfig {\n element: SVGElement;\n svg: SVGSVGElement;\n onMove: (dx: number, dy: number) => void;\n onEnd: (dx: number, dy: number, moved: boolean) => void;\n setDragging: (dragging: boolean) => void;\n threshold?: number; // default: 3\n}\n\n/**\n * Reusable drag handler for SVG elements.\n * Handles mouse and touch events, viewBox scaling, threshold detection,\n * click suppression after drag, and cursor state.\n *\n * Returns a cleanup function that removes all listeners.\n */\nfunction createDragHandler(config: DragConfig): () => void {\n const { element, svg, onMove, onEnd, setDragging, threshold = 3 } = config;\n const cleanups: Array<() => void> = [];\n\n // Track active document listeners so cleanup can remove them mid-drag\n let activeDocMouseMove: ((e: MouseEvent) => void) | null = null;\n let activeDocMouseUp: ((e: MouseEvent) => void) | null = null;\n let activeDocTouchMove: ((e: TouchEvent) => void) | null = null;\n let activeDocTouchEnd: ((e: TouchEvent) => void) | null = null;\n let activeDocTouchCancel: ((e: TouchEvent) => void) | null = null;\n\n function getScale(): { scaleX: number; scaleY: number } {\n const viewBox = svg.viewBox?.baseVal;\n const svgRect = svg.getBoundingClientRect();\n return {\n scaleX: viewBox?.width && svgRect.width ? viewBox.width / svgRect.width : 1,\n scaleY: viewBox?.height && svgRect.height ? viewBox.height / svgRect.height : 1,\n };\n }\n\n function startDrag(startX: number, startY: number): void {\n setDragging(true);\n const { scaleX, scaleY } = getScale();\n\n element.style.cursor = 'grabbing';\n // Prevent text selection during drag\n svg.style.userSelect = 'none';\n\n const handleMove = (clientX: number, clientY: number) => {\n const dx = (clientX - startX) * scaleX;\n const dy = (clientY - startY) * scaleY;\n onMove(dx, dy);\n };\n\n const cleanupDocListeners = () => {\n if (activeDocMouseMove) {\n document.removeEventListener('mousemove', activeDocMouseMove);\n activeDocMouseMove = null;\n }\n if (activeDocMouseUp) {\n document.removeEventListener('mouseup', activeDocMouseUp);\n activeDocMouseUp = null;\n }\n if (activeDocTouchMove) {\n document.removeEventListener('touchmove', activeDocTouchMove);\n activeDocTouchMove = null;\n }\n if (activeDocTouchEnd) {\n document.removeEventListener('touchend', activeDocTouchEnd);\n activeDocTouchEnd = null;\n }\n if (activeDocTouchCancel) {\n document.removeEventListener('touchcancel', activeDocTouchCancel);\n activeDocTouchCancel = null;\n }\n };\n\n const handleEnd = (clientX: number, clientY: number) => {\n const dx = (clientX - startX) * scaleX;\n const dy = (clientY - startY) * scaleY;\n const moved = Math.abs(dx) > threshold || Math.abs(dy) > threshold;\n\n onEnd(dx, dy, moved);\n\n // Suppress click if drag actually moved\n if (moved) {\n element.addEventListener(\n 'click',\n (clickE) => {\n clickE.stopPropagation();\n },\n { capture: true, once: true },\n );\n }\n\n element.style.cursor = 'grab';\n svg.style.userSelect = '';\n\n cleanupDocListeners();\n setDragging(false);\n };\n\n // Mouse listeners\n const onMouseMove = (moveEvent: MouseEvent) => {\n handleMove(moveEvent.clientX, moveEvent.clientY);\n };\n const onMouseUp = (upEvent: MouseEvent) => {\n handleEnd(upEvent.clientX, upEvent.clientY);\n };\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n activeDocMouseMove = onMouseMove;\n activeDocMouseUp = onMouseUp;\n\n // Touch listeners\n const onTouchMove = (moveEvent: TouchEvent) => {\n if (moveEvent.touches.length > 0) {\n moveEvent.preventDefault();\n handleMove(moveEvent.touches[0].clientX, moveEvent.touches[0].clientY);\n }\n };\n const onTouchEnd = (endEvent: TouchEvent) => {\n const touch = endEvent.changedTouches[0];\n if (touch) {\n handleEnd(touch.clientX, touch.clientY);\n } else {\n handleEnd(startX, startY);\n }\n };\n document.addEventListener('touchmove', onTouchMove, { passive: false });\n document.addEventListener('touchend', onTouchEnd);\n document.addEventListener('touchcancel', onTouchEnd);\n activeDocTouchMove = onTouchMove;\n activeDocTouchEnd = onTouchEnd;\n activeDocTouchCancel = onTouchEnd;\n }\n\n // Mouse down handler\n const handleMouseDown = (e: Event) => {\n const mouseEvent = e as MouseEvent;\n mouseEvent.preventDefault();\n startDrag(mouseEvent.clientX, mouseEvent.clientY);\n };\n\n // Touch start handler\n const handleTouchStart = (e: Event) => {\n const touchEvent = e as TouchEvent;\n if (touchEvent.touches.length === 1) {\n touchEvent.preventDefault();\n startDrag(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);\n }\n };\n\n element.addEventListener('mousedown', handleMouseDown);\n element.addEventListener('touchstart', handleTouchStart, { passive: false });\n cleanups.push(() => {\n element.removeEventListener('mousedown', handleMouseDown);\n element.removeEventListener('touchstart', handleTouchStart);\n });\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n // Clean up any active document listeners (mid-drag unmount)\n if (activeDocMouseMove) {\n document.removeEventListener('mousemove', activeDocMouseMove);\n activeDocMouseMove = null;\n }\n if (activeDocMouseUp) {\n document.removeEventListener('mouseup', activeDocMouseUp);\n activeDocMouseUp = null;\n }\n if (activeDocTouchMove) {\n document.removeEventListener('touchmove', activeDocTouchMove);\n activeDocTouchMove = null;\n }\n if (activeDocTouchEnd) {\n document.removeEventListener('touchend', activeDocTouchEnd);\n activeDocTouchEnd = null;\n }\n if (activeDocTouchCancel) {\n document.removeEventListener('touchcancel', activeDocTouchCancel);\n activeDocTouchCancel = null;\n }\n // Restore user-select in case of mid-drag cleanup\n svg.style.userSelect = '';\n };\n}\n\n// ---------------------------------------------------------------------------\n// Annotation drag editing\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag-to-reposition on text annotation labels.\n * Only activates for text annotations (not range or refline).\n * During drag, applies a CSS transform for real-time visual feedback and\n * counter-adjusts straight connector endpoints so the data-point end stays fixed.\n * On mouseup, fires the callback with the updated offset values.\n *\n * Returns a cleanup function to remove all listeners.\n */\nfunction wireAnnotationDrag(\n svg: SVGElement,\n specAnnotations: Annotation[],\n onAnnotationEdit:\n | ((annotation: TextAnnotation, updatedOffset: AnnotationOffset) => void)\n | undefined,\n onEdit: ((edit: ElementEdit) => void) | undefined,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const annotationElements = svg.querySelectorAll('.viz-annotation-text');\n const cleanups: Array<() => void> = [];\n\n for (const el of annotationElements) {\n const indexStr = el.getAttribute('data-annotation-index');\n if (indexStr === null) continue;\n\n const index = Number(indexStr);\n const specAnnotation = specAnnotations[index];\n if (!specAnnotation || specAnnotation.type !== 'text') continue;\n\n const textAnnotation = specAnnotation as TextAnnotation;\n const annotationG = el as SVGGElement;\n\n // Visual affordance: show grab cursor\n annotationG.style.cursor = 'grab';\n\n // Stash connector info for real-time updates during drag\n const connectorLine = annotationG.querySelector('line.viz-annotation-connector');\n const origX2 = connectorLine ? Number(connectorLine.getAttribute('x2')) : 0;\n const origY2 = connectorLine ? Number(connectorLine.getAttribute('y2')) : 0;\n\n // For curved connectors, stash path/polygon elements to hide during drag\n const curvedPath = annotationG.querySelector('path.viz-annotation-connector');\n const arrowhead = annotationG.querySelector('polygon.viz-annotation-connector');\n const hasCurvedConnector = curvedPath !== null;\n\n const origDx = textAnnotation.offset?.dx ?? 0;\n const origDy = textAnnotation.offset?.dy ?? 0;\n\n const cleanup = createDragHandler({\n element: annotationG,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n // Move the entire annotation group\n annotationG.setAttribute('transform', `translate(${dx}, ${dy})`);\n\n // For straight connectors, counter-adjust the data-point end\n if (connectorLine && !hasCurvedConnector) {\n connectorLine.setAttribute('x2', String(origX2 - dx));\n connectorLine.setAttribute('y2', String(origY2 - dy));\n }\n\n // Hide curved connector elements during drag\n if (hasCurvedConnector) {\n if (curvedPath) curvedPath.setAttribute('display', 'none');\n if (arrowhead) arrowhead.setAttribute('display', 'none');\n }\n },\n onEnd: (dx, dy, moved) => {\n // Clean up visual state\n annotationG.removeAttribute('transform');\n\n // Restore straight connector to original values\n if (connectorLine && !hasCurvedConnector) {\n connectorLine.setAttribute('x2', String(origX2));\n connectorLine.setAttribute('y2', String(origY2));\n }\n\n // Restore curved connector elements\n if (hasCurvedConnector) {\n if (curvedPath) curvedPath.removeAttribute('display');\n if (arrowhead) arrowhead.removeAttribute('display');\n }\n\n if (moved) {\n const newOffset: AnnotationOffset = {\n dx: origDx + dx,\n dy: origDy + dy,\n };\n // Fire legacy callback\n onAnnotationEdit?.(textAnnotation, newOffset);\n // Fire unified edit callback\n onEdit?.({ type: 'annotation', annotation: textAnnotation, offset: newOffset });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Connector endpoint drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on connector endpoint handles for text annotations.\n * Dynamically creates invisible handle circles at connector endpoints\n * so they only exist when editing is active (not in every chart).\n * During drag, updates the handle position and the connector line endpoints.\n * On end, fires onEdit with the accumulated endpoint offset.\n *\n * Shows handles on hover over the parent annotation group.\n * Returns a cleanup function that removes handles and all listeners.\n */\nfunction wireConnectorEndpointDrag(\n svg: SVGElement,\n specAnnotations: Annotation[],\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const SVG_NS = 'http://www.w3.org/2000/svg';\n const cleanups: Array<() => void> = [];\n const annotationGroups = svg.querySelectorAll('.viz-annotation-text');\n\n for (const el of annotationGroups) {\n const annotationG = el as SVGGElement;\n const indexStr = annotationG.getAttribute('data-annotation-index');\n if (indexStr === null) continue;\n\n const index = Number(indexStr);\n const specAnnotation = specAnnotations[index];\n if (!specAnnotation || specAnnotation.type !== 'text') continue;\n\n const textAnnotation = specAnnotation as TextAnnotation;\n\n // Find connector line or curved connector to determine endpoints\n const connectorLine = annotationG.querySelector('line.viz-annotation-connector');\n const curvedPath = annotationG.querySelector('path.viz-annotation-connector');\n if (!connectorLine && !curvedPath) continue;\n\n // Determine connector endpoint positions from the connector element\n let fromX: number, fromY: number, toX: number, toY: number;\n if (connectorLine) {\n fromX = Number(connectorLine.getAttribute('x1'));\n fromY = Number(connectorLine.getAttribute('y1'));\n toX = Number(connectorLine.getAttribute('x2'));\n toY = Number(connectorLine.getAttribute('y2'));\n } else {\n // For curved connectors, get positions from the path data\n // The path starts at M x y, so parse the first coordinates\n const pathD = curvedPath!.getAttribute('d') ?? '';\n const mMatch = pathD.match(/M\\s*([\\d.e+-]+)\\s+([\\d.e+-]+)/);\n fromX = mMatch ? Number(mMatch[1]) : 0;\n fromY = mMatch ? Number(mMatch[2]) : 0;\n // For curved connectors, the arrow polygon has the target\n const arrowhead = annotationG.querySelector('polygon.viz-annotation-connector');\n const points = arrowhead?.getAttribute('points') ?? '';\n const firstPoint = points.split(' ')[0] ?? '0,0';\n const [px, py] = firstPoint.split(',');\n toX = Number(px);\n toY = Number(py);\n }\n\n // Create handles dynamically\n const endpoints: Array<{ name: 'from' | 'to'; cx: number; cy: number }> = [\n { name: 'from', cx: fromX, cy: fromY },\n { name: 'to', cx: toX, cy: toY },\n ];\n\n const createdHandles: SVGCircleElement[] = [];\n\n for (const ep of endpoints) {\n const handleEl = document.createElementNS(SVG_NS, 'circle') as SVGCircleElement;\n handleEl.setAttribute('class', 'viz-connector-handle');\n handleEl.setAttribute('data-endpoint', ep.name);\n handleEl.setAttribute('cx', String(ep.cx));\n handleEl.setAttribute('cy', String(ep.cy));\n handleEl.setAttribute('r', '4');\n handleEl.setAttribute('opacity', '0');\n handleEl.setAttribute('fill', 'currentColor');\n handleEl.setAttribute('stroke', 'currentColor');\n annotationG.appendChild(handleEl);\n createdHandles.push(handleEl);\n\n const origCx = ep.cx;\n const origCy = ep.cy;\n\n // Prevent parent annotation drag from firing\n const stopProp = (e: Event) => {\n e.stopPropagation();\n };\n handleEl.addEventListener('mousedown', stopProp);\n handleEl.addEventListener('touchstart', stopProp);\n cleanups.push(() => {\n handleEl.removeEventListener('mousedown', stopProp);\n handleEl.removeEventListener('touchstart', stopProp);\n });\n\n const cleanup = createDragHandler({\n element: handleEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n handleEl.setAttribute('cx', String(origCx + dx));\n handleEl.setAttribute('cy', String(origCy + dy));\n\n if (connectorLine) {\n if (ep.name === 'from') {\n connectorLine.setAttribute('x1', String(origCx + dx));\n connectorLine.setAttribute('y1', String(origCy + dy));\n } else {\n connectorLine.setAttribute('x2', String(origCx + dx));\n connectorLine.setAttribute('y2', String(origCy + dy));\n }\n }\n },\n onEnd: (dx, dy, moved) => {\n handleEl.setAttribute('cx', String(origCx));\n handleEl.setAttribute('cy', String(origCy));\n\n if (connectorLine) {\n if (ep.name === 'from') {\n connectorLine.setAttribute('x1', String(origCx));\n connectorLine.setAttribute('y1', String(origCy));\n } else {\n connectorLine.setAttribute('x2', String(origCx));\n connectorLine.setAttribute('y2', String(origCy));\n }\n }\n\n if (moved) {\n const existingOffset = textAnnotation.connectorOffset?.[ep.name];\n const origEndDx = existingOffset?.dx ?? 0;\n const origEndDy = existingOffset?.dy ?? 0;\n onEdit({\n type: 'annotation-connector',\n annotation: textAnnotation,\n endpoint: ep.name,\n offset: { dx: origEndDx + dx, dy: origEndDy + dy },\n });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n // Wire hover to show/hide handles\n const showHandles = () => {\n for (const h of createdHandles) {\n h.setAttribute('opacity', '0.6');\n }\n };\n const hideHandles = () => {\n for (const h of createdHandles) {\n h.setAttribute('opacity', '0');\n }\n };\n\n annotationG.addEventListener('mouseenter', showHandles);\n annotationG.addEventListener('mouseleave', hideHandles);\n cleanups.push(() => {\n annotationG.removeEventListener('mouseenter', showHandles);\n annotationG.removeEventListener('mouseleave', hideHandles);\n // Remove dynamically created handles\n for (const h of createdHandles) {\n h.remove();\n }\n });\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Range/refline annotation label drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on range and refline annotation labels.\n * On drag end, fires onEdit with the label offset.\n * Returns a cleanup function.\n */\nfunction wireAnnotationLabelDrag(\n svg: SVGElement,\n specAnnotations: Annotation[],\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const cleanups: Array<() => void> = [];\n\n // Target range and refline annotation labels\n const selectors = [\n '.viz-annotation-range .viz-annotation-label',\n '.viz-annotation-refline .viz-annotation-label',\n ];\n\n for (const selector of selectors) {\n const labels = svg.querySelectorAll(selector);\n\n for (const label of labels) {\n const annotationG = label.closest('.viz-annotation') as SVGGElement | null;\n if (!annotationG) continue;\n\n const indexStr = annotationG.getAttribute('data-annotation-index');\n if (indexStr === null) continue;\n\n const index = Number(indexStr);\n const specAnnotation = specAnnotations[index];\n if (!specAnnotation) continue;\n\n const labelEl = label as SVGTextElement;\n labelEl.style.cursor = 'grab';\n\n const isRange = specAnnotation.type === 'range';\n const existingLabelOffset = isRange\n ? (specAnnotation as RangeAnnotation).labelOffset\n : (specAnnotation as RefLineAnnotation).labelOffset;\n const origLabelDx = existingLabelOffset?.dx ?? 0;\n const origLabelDy = existingLabelOffset?.dy ?? 0;\n\n const cleanup = createDragHandler({\n element: labelEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n if (isRange) {\n onEdit({\n type: 'range-label',\n annotation: specAnnotation as RangeAnnotation,\n labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy },\n });\n } else {\n onEdit({\n type: 'refline-label',\n annotation: specAnnotation as RefLineAnnotation,\n labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy },\n });\n }\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Chrome text drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on chrome text elements (title, subtitle, source, byline, footer).\n * On drag end, fires onEdit with the chrome key, text, and offset.\n * Returns a cleanup function.\n */\nfunction wireChromeDrag(\n svg: SVGElement,\n spec: ChartSpec | GraphSpec,\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const chromeTexts = svg.querySelectorAll('.viz-chrome text[data-chrome-key]');\n const cleanups: Array<() => void> = [];\n\n // Read existing chrome offsets from the spec\n const chromeConfig = 'chrome' in spec ? spec.chrome : undefined;\n\n for (const el of chromeTexts) {\n const textEl = el as SVGTextElement;\n const key = textEl.getAttribute('data-chrome-key') as ChromeKey;\n if (!key) continue;\n\n // Read existing offset for this chrome element\n const chromeEntry = chromeConfig?.[key];\n const existingOffset =\n typeof chromeEntry === 'object' && chromeEntry !== null ? chromeEntry.offset : undefined;\n const origChromeDx = existingOffset?.dx ?? 0;\n const origChromeDy = existingOffset?.dy ?? 0;\n\n textEl.style.cursor = 'grab';\n\n const cleanup = createDragHandler({\n element: textEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (textEl as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (textEl as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n onEdit({\n type: 'chrome',\n key,\n text: textEl.textContent ?? '',\n offset: { dx: origChromeDx + dx, dy: origChromeDy + dy },\n });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Legend drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on the legend group.\n * Click suppression prevents legend toggle from firing after a drag.\n * On drag end, fires onEdit with the legend offset.\n * Returns a cleanup function.\n */\nfunction wireLegendDrag(\n svg: SVGElement,\n spec: ChartSpec | GraphSpec,\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const legendG = svg.querySelector('.viz-legend') as SVGGElement | null;\n if (!legendG) return () => {};\n\n const cleanups: Array<() => void> = [];\n\n // Read existing legend offset from the spec\n const legendConfig = 'legend' in spec ? spec.legend : undefined;\n const origLegendDx = legendConfig?.offset?.dx ?? 0;\n const origLegendDy = legendConfig?.offset?.dy ?? 0;\n\n // Set grab cursor on the legend background, not on entry elements\n legendG.style.cursor = 'grab';\n\n const cleanup = createDragHandler({\n element: legendG,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (legendG as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (legendG as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n onEdit({ type: 'legend', offset: { dx: origLegendDx + dx, dy: origLegendDy + dy } });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Series label drag\n// ---------------------------------------------------------------------------\n\n/**\n * Wire drag on series label elements (.viz-mark-label[data-series]).\n * On drag end, fires onEdit with the series name and offset.\n * Returns a cleanup function.\n */\nfunction wireSeriesLabelDrag(\n svg: SVGElement,\n spec: ChartSpec | GraphSpec,\n onEdit: (edit: ElementEdit) => void,\n setDragging: (dragging: boolean) => void,\n): () => void {\n const labels = svg.querySelectorAll('.viz-mark-label');\n const cleanups: Array<() => void> = [];\n\n // Read existing label offsets from the spec\n const labelsConfig = 'labels' in spec ? spec.labels : undefined;\n\n for (const label of labels) {\n const labelEl = label as SVGTextElement;\n // Check label itself first, then fall back to the parent mark group's data-series\n const series =\n labelEl.getAttribute('data-series') ??\n labelEl.closest('[data-series]')?.getAttribute('data-series');\n if (!series) continue;\n\n // Read existing offset for this series label\n const existingSeriesOffset = labelsConfig?.offsets?.[series];\n const origSeriesDx = existingSeriesOffset?.dx ?? 0;\n const origSeriesDy = existingSeriesOffset?.dy ?? 0;\n\n labelEl.style.cursor = 'grab';\n\n const cleanup = createDragHandler({\n element: labelEl,\n svg: svg as unknown as SVGSVGElement,\n onMove: (dx, dy) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform =\n `translate(${dx}px, ${dy}px)`;\n },\n onEnd: (dx, dy, moved) => {\n (labelEl as SVGElement & ElementCSSInlineStyle).style.transform = '';\n\n if (moved) {\n onEdit({\n type: 'series-label',\n series,\n offset: { dx: origSeriesDx + dx, dy: origSeriesDy + dy },\n });\n }\n },\n setDragging,\n });\n\n cleanups.push(cleanup);\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Legend interactivity\n// ---------------------------------------------------------------------------\n\n/**\n * Wire click handlers on legend entries to toggle series visibility.\n * Fires onEdit with { type: 'legend-toggle', series, hidden } for each toggle,\n * and optionally calls the legacy onLegendToggle callback.\n * Legend entries for hidden series stay visible but dimmed (opacity 0.3).\n * Returns a cleanup function.\n */\nfunction wireLegendInteraction(\n svg: SVGElement,\n _layout: ChartLayout,\n onLegendToggle?: (series: string, visible: boolean) => void,\n onEdit?: (edit: ElementEdit) => void,\n): () => void {\n const legendEntries = svg.querySelectorAll('[data-legend-index]');\n const cleanups: Array<() => void> = [];\n\n // Track which series are hidden\n const hiddenSeries = new Set<string>();\n\n for (const entry of legendEntries) {\n const handleClick = () => {\n const label = entry.getAttribute('data-legend-label');\n if (!label) return;\n\n if (hiddenSeries.has(label)) {\n hiddenSeries.delete(label);\n entry.setAttribute('opacity', '1');\n entry.setAttribute('aria-label', `${label}: visible`);\n onLegendToggle?.(label, true);\n onEdit?.({ type: 'legend-toggle', series: label, hidden: false });\n } else {\n hiddenSeries.add(label);\n entry.setAttribute('opacity', '0.3');\n entry.setAttribute('aria-label', `${label}: hidden`);\n onLegendToggle?.(label, false);\n onEdit?.({ type: 'legend-toggle', series: label, hidden: true });\n }\n\n // Toggle visibility of marks with matching series.\n // Uses the data-series attribute set by the SVG renderer, which works\n // for all mark types (line, area, rect, arc, point).\n const marks = svg.querySelectorAll('.viz-mark');\n for (const mark of marks) {\n const seriesName = mark.getAttribute('data-series');\n if (!seriesName) continue;\n\n if (hiddenSeries.has(seriesName)) {\n (mark as SVGElement).style.display = 'none';\n } else {\n (mark as SVGElement).style.display = '';\n }\n }\n };\n\n entry.addEventListener('click', handleClick);\n cleanups.push(() => entry.removeEventListener('click', handleClick));\n }\n\n return () => {\n for (const cleanup of cleanups) {\n cleanup();\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard navigation\n// ---------------------------------------------------------------------------\n\n/**\n * Wire keyboard navigation on the SVG element.\n * Arrow keys move focus between mark elements. Enter/Space shows tooltip.\n * Escape hides tooltip. Returns a cleanup function.\n */\nfunction wireKeyboardNav(\n svg: SVGElement,\n container: HTMLElement,\n tooltipDescriptors: Map<string, TooltipContent>,\n tooltipManager: TooltipManager,\n layout: ChartLayout,\n): () => void {\n // Make container focusable\n container.setAttribute('tabindex', '0');\n container.setAttribute('aria-roledescription', 'chart');\n container.setAttribute('aria-label', layout.a11y.altText);\n\n // Collect navigable mark elements (those with tooltip content)\n const markElements: SVGElement[] = [];\n const allMarkEls = svg.querySelectorAll('[data-mark-id]');\n for (const el of allMarkEls) {\n const markId = el.getAttribute('data-mark-id');\n if (markId && tooltipDescriptors.has(markId)) {\n markElements.push(el as SVGElement);\n }\n }\n\n let focusIndex = -1;\n\n function highlightMark(index: number): void {\n // Remove previous highlight\n if (focusIndex >= 0 && focusIndex < markElements.length) {\n markElements[focusIndex].classList.remove('viz-mark-focused');\n markElements[focusIndex].removeAttribute('aria-selected');\n }\n\n focusIndex = index;\n\n if (focusIndex >= 0 && focusIndex < markElements.length) {\n const el = markElements[focusIndex];\n el.classList.add('viz-mark-focused');\n el.setAttribute('aria-selected', 'true');\n }\n }\n\n function showTooltipForFocused(): void {\n if (focusIndex < 0 || focusIndex >= markElements.length) return;\n\n const el = markElements[focusIndex];\n const markId = el.getAttribute('data-mark-id');\n if (!markId) return;\n\n const content = tooltipDescriptors.get(markId);\n if (!content) return;\n\n // Position tooltip near the mark element\n const bbox = el.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const x = bbox.left + bbox.width / 2 - containerRect.left;\n const y = bbox.top - containerRect.top;\n tooltipManager.show(content, x, y);\n }\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (markElements.length === 0) return;\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowDown': {\n e.preventDefault();\n const next = focusIndex < markElements.length - 1 ? focusIndex + 1 : 0;\n highlightMark(next);\n showTooltipForFocused();\n break;\n }\n case 'ArrowLeft':\n case 'ArrowUp': {\n e.preventDefault();\n const prev = focusIndex > 0 ? focusIndex - 1 : markElements.length - 1;\n highlightMark(prev);\n showTooltipForFocused();\n break;\n }\n case 'Enter':\n case ' ': {\n e.preventDefault();\n if (focusIndex >= 0) {\n showTooltipForFocused();\n } else if (markElements.length > 0) {\n highlightMark(0);\n showTooltipForFocused();\n }\n break;\n }\n case 'Escape': {\n e.preventDefault();\n tooltipManager.hide();\n highlightMark(-1);\n break;\n }\n }\n };\n\n container.addEventListener('keydown', handleKeyDown);\n\n return () => {\n container.removeEventListener('keydown', handleKeyDown);\n container.removeAttribute('tabindex');\n container.removeAttribute('aria-roledescription');\n container.removeAttribute('aria-label');\n };\n}\n\n// ---------------------------------------------------------------------------\n// Hidden data table for screen readers\n// ---------------------------------------------------------------------------\n\n/**\n * Create a visually-hidden data table from the chart's a11y fallback data.\n * Returns the table element (append to container) and a cleanup function.\n */\nfunction createScreenReaderTable(\n layout: ChartLayout,\n container: HTMLElement,\n): HTMLTableElement | null {\n const data = layout.a11y.dataTableFallback;\n if (!data || data.length === 0) return null;\n\n const table = document.createElement('table');\n table.className = 'viz-sr-only';\n table.setAttribute('role', 'table');\n table.setAttribute('aria-label', `Data table: ${layout.a11y.altText}`);\n\n // First row is headers\n if (data.length > 0) {\n const thead = document.createElement('thead');\n const headerRow = document.createElement('tr');\n const headers = data[0] as unknown[];\n for (const header of headers) {\n const th = document.createElement('th');\n th.textContent = String(header ?? '');\n th.setAttribute('scope', 'col');\n headerRow.appendChild(th);\n }\n thead.appendChild(headerRow);\n table.appendChild(thead);\n }\n\n // Remaining rows are data\n if (data.length > 1) {\n const tbody = document.createElement('tbody');\n for (let i = 1; i < data.length; i++) {\n const tr = document.createElement('tr');\n const cells = data[i] as unknown[];\n for (const cell of cells) {\n const td = document.createElement('td');\n td.textContent = String(cell ?? '');\n tr.appendChild(td);\n }\n tbody.appendChild(tr);\n }\n table.appendChild(tbody);\n }\n\n container.appendChild(table);\n return table;\n}\n\n// ---------------------------------------------------------------------------\n// Main API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a chart instance from a spec and mount it into a container.\n *\n * @param container - The DOM element to render into.\n * @param spec - The visualization spec.\n * @param options - Mount options (theme, darkMode, responsive, etc.).\n * @returns A ChartInstance with update/resize/export/destroy methods.\n */\nexport function createChart(\n container: HTMLElement,\n spec: ChartSpec | GraphSpec,\n options?: MountOptions,\n): ChartInstance {\n let currentSpec: ChartSpec | GraphSpec = spec;\n let currentLayout: ChartLayout;\n let svgElement: SVGElement | null = null;\n let tooltipManager: TooltipManager | null = null;\n let disconnectResize: (() => void) | null = null;\n let cleanupTooltipEvents: (() => void) | null = null;\n let cleanupKeyboardNav: (() => void) | null = null;\n let cleanupLegend: (() => void) | null = null;\n let cleanupChartEvents: (() => void) | null = null;\n let cleanupAnnotationDrag: (() => void) | null = null;\n let cleanupEditDrags: (() => void) | null = null;\n let srTable: HTMLTableElement | null = null;\n let destroyed = false;\n let isDragging = false;\n let pendingRender = false;\n\n const measureText = createMeasureText();\n\n function compile(): ChartLayout {\n const { width, height } = getContainerDimensions();\n const darkMode = resolveDarkMode(options?.darkMode);\n\n const compileOpts: CompileOptions = {\n width,\n height,\n theme: options?.theme,\n darkMode,\n measureText,\n };\n\n return compileChart(currentSpec, compileOpts);\n }\n\n function getContainerDimensions(): { width: number; height: number } {\n const rect = container.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n function render(): void {\n // Defer re-render if a drag is in progress to avoid destroying the dragged element\n if (isDragging) {\n pendingRender = true;\n return;\n }\n\n // Clean up previous render\n if (cleanupTooltipEvents) {\n cleanupTooltipEvents();\n cleanupTooltipEvents = null;\n }\n if (cleanupKeyboardNav) {\n cleanupKeyboardNav();\n cleanupKeyboardNav = null;\n }\n if (cleanupLegend) {\n cleanupLegend();\n cleanupLegend = null;\n }\n if (cleanupChartEvents) {\n cleanupChartEvents();\n cleanupChartEvents = null;\n }\n if (cleanupAnnotationDrag) {\n cleanupAnnotationDrag();\n cleanupAnnotationDrag = null;\n }\n if (cleanupEditDrags) {\n cleanupEditDrags();\n cleanupEditDrags = null;\n }\n if (svgElement?.parentNode) {\n svgElement.parentNode.removeChild(svgElement);\n }\n if (tooltipManager) {\n tooltipManager.destroy();\n }\n if (srTable?.parentNode) {\n srTable.parentNode.removeChild(srTable);\n srTable = null;\n }\n\n currentLayout = compile();\n svgElement = renderChartSVG(currentLayout, container);\n tooltipManager = createTooltipManager(container);\n\n // Wire tooltip events on mark elements\n cleanupTooltipEvents = wireTooltipEvents(\n svgElement,\n currentLayout.tooltipDescriptors,\n tooltipManager,\n );\n\n // Wire keyboard navigation\n cleanupKeyboardNav = wireKeyboardNav(\n svgElement,\n container,\n currentLayout.tooltipDescriptors,\n tooltipManager,\n currentLayout,\n );\n\n // Wire legend interactivity\n cleanupLegend = wireLegendInteraction(\n svgElement,\n currentLayout,\n options?.onLegendToggle,\n options?.onEdit,\n );\n\n // Wire chart event handlers (mark click/hover/leave, annotation click)\n if (\n options?.onMarkClick ||\n options?.onMarkHover ||\n options?.onMarkLeave ||\n options?.onAnnotationClick\n ) {\n const specAnnotations: import('@opendata-ai/openchart-core').Annotation[] =\n 'annotations' in currentSpec && Array.isArray(currentSpec.annotations)\n ? currentSpec.annotations\n : [];\n cleanupChartEvents = wireChartEvents(svgElement, currentLayout, specAnnotations, options);\n }\n\n // Shared setDragging callback for all drag handlers\n const setDragging = (dragging: boolean) => {\n isDragging = dragging;\n if (!dragging && pendingRender) {\n pendingRender = false;\n render();\n }\n };\n\n // Shared annotation list for drag handlers (computed once)\n const dragAnnotations: Annotation[] =\n 'annotations' in currentSpec && Array.isArray(currentSpec.annotations)\n ? currentSpec.annotations\n : [];\n\n // Wire annotation drag editing (activates when onAnnotationEdit or onEdit is provided)\n if (options?.onAnnotationEdit || options?.onEdit) {\n cleanupAnnotationDrag = wireAnnotationDrag(\n svgElement,\n dragAnnotations,\n options?.onAnnotationEdit,\n options?.onEdit,\n setDragging,\n );\n }\n\n // Wire all edit drag handlers when onEdit is provided\n if (options?.onEdit) {\n const editCleanups: Array<() => void> = [];\n\n // Connector endpoint drag\n editCleanups.push(\n wireConnectorEndpointDrag(svgElement, dragAnnotations, options.onEdit, setDragging),\n );\n\n // Range/refline annotation label drag\n editCleanups.push(\n wireAnnotationLabelDrag(svgElement, dragAnnotations, options.onEdit, setDragging),\n );\n\n // Chrome text drag\n editCleanups.push(wireChromeDrag(svgElement, currentSpec, options.onEdit, setDragging));\n\n // Legend drag\n editCleanups.push(wireLegendDrag(svgElement, currentSpec, options.onEdit, setDragging));\n\n // Series label drag\n editCleanups.push(wireSeriesLabelDrag(svgElement, currentSpec, options.onEdit, setDragging));\n\n cleanupEditDrags = () => {\n for (const cleanup of editCleanups) {\n cleanup();\n }\n };\n }\n\n // Create hidden data table for screen readers\n srTable = createScreenReaderTable(currentLayout, container);\n\n // Apply container classes for CSS variable scoping and dark mode\n container.classList.add('viz-root');\n const isDark = resolveDarkMode(options?.darkMode);\n if (isDark) {\n container.classList.add('viz-dark');\n } else {\n container.classList.remove('viz-dark');\n }\n }\n\n function update(newSpec: ChartSpec | GraphSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n render();\n }\n\n function resize(): void {\n if (destroyed) return;\n render();\n }\n\n function doExport(format: 'svg'): string;\n function doExport(format: 'png', exportOptions?: ExportOptions): Promise<Blob>;\n function doExport(format: 'jpg', exportOptions?: ExportOptions): Promise<Blob>;\n function doExport(format: 'csv'): string;\n function doExport(\n format: 'svg' | 'png' | 'jpg' | 'csv',\n exportOptions?: ExportOptions,\n ): string | Promise<Blob> {\n if (!svgElement) {\n throw new Error('Chart is not rendered yet');\n }\n\n switch (format) {\n case 'svg':\n return exportSVG(svgElement);\n case 'png':\n return exportPNG(svgElement, exportOptions);\n case 'jpg':\n return exportJPG(svgElement, exportOptions);\n case 'csv':\n return exportCSV(\n 'data' in currentSpec && Array.isArray(currentSpec.data) ? currentSpec.data : [],\n );\n default:\n throw new Error(`Unsupported export format: ${format}`);\n }\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (cleanupTooltipEvents) {\n cleanupTooltipEvents();\n cleanupTooltipEvents = null;\n }\n if (cleanupKeyboardNav) {\n cleanupKeyboardNav();\n cleanupKeyboardNav = null;\n }\n if (cleanupLegend) {\n cleanupLegend();\n cleanupLegend = null;\n }\n if (cleanupChartEvents) {\n cleanupChartEvents();\n cleanupChartEvents = null;\n }\n if (cleanupAnnotationDrag) {\n cleanupAnnotationDrag();\n cleanupAnnotationDrag = null;\n }\n if (cleanupEditDrags) {\n cleanupEditDrags();\n cleanupEditDrags = null;\n }\n if (disconnectResize) {\n disconnectResize();\n disconnectResize = null;\n }\n if (tooltipManager) {\n tooltipManager.destroy();\n tooltipManager = null;\n }\n if (svgElement?.parentNode) {\n svgElement.parentNode.removeChild(svgElement);\n svgElement = null;\n }\n if (srTable?.parentNode) {\n srTable.parentNode.removeChild(srTable);\n srTable = null;\n }\n container.classList.remove('viz-dark');\n container.classList.remove('viz-root');\n }\n\n // Initial render\n render();\n\n // Set up responsive resize\n if (options?.responsive !== false) {\n disconnectResize = observeResize(container, () => {\n resize();\n });\n }\n\n return {\n update,\n resize,\n export: doExport,\n destroy,\n get layout() {\n return currentLayout;\n },\n };\n}\n","/**\n * SVG renderer: converts a ChartLayout into SVG DOM elements.\n *\n * Creates an <svg> element with viewBox matching layout dimensions,\n * renders chrome (title/subtitle/source), axes, marks, annotations,\n * and legend. All styling via inline SVG attributes from layout data.\n *\n * Mark rendering dispatches per mark type with dedicated renderers\n * for line, area, rect, arc, and point marks.\n */\n\nimport type {\n ArcMark,\n AreaMark,\n AxisLayout,\n ChartLayout,\n LegendLayout,\n LineMark,\n Mark,\n Point,\n PointMark,\n RectMark,\n ResolvedAnnotation,\n ResolvedChromeElement,\n TextStyle,\n} from '@opendata-ai/openchart-core';\nimport { estimateTextWidth } from '@opendata-ai/openchart-core';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\n/**\n * Compute the vertical extent of x-axis labels below the chart area.\n * Accounts for rotated tick labels which need more vertical space.\n */\nfunction computeXAxisExtent(layout: ChartLayout): number {\n const xAxis = layout.axes.x;\n if (!xAxis) return 0;\n\n if (xAxis.tickAngle && Math.abs(xAxis.tickAngle) > 10) {\n // Rotated labels: estimate height from the longest tick label.\n const fontSize = xAxis.tickLabelStyle.fontSize;\n const fontWeight = xAxis.tickLabelStyle.fontWeight;\n const angleRad = Math.abs(xAxis.tickAngle) * (Math.PI / 180);\n let maxLabelWidth = 40;\n for (const tick of xAxis.ticks) {\n const w = estimateTextWidth(tick.label, fontSize, fontWeight);\n if (w > maxLabelWidth) maxLabelWidth = w;\n }\n const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);\n return xAxis.label ? rotatedHeight + 20 : rotatedHeight;\n }\n\n return xAxis.label ? 48 : 26;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createSVGElement(tag: string): SVGElement {\n return document.createElementNS(SVG_NS, tag);\n}\n\nfunction setAttrs(el: SVGElement, attrs: Record<string, string | number>): void {\n for (const [key, value] of Object.entries(attrs)) {\n el.setAttribute(key, String(value));\n }\n}\n\nfunction applyTextStyle(el: SVGElement, style: TextStyle): void {\n setAttrs(el, {\n 'font-family': style.fontFamily,\n 'font-size': style.fontSize,\n 'font-weight': style.fontWeight,\n });\n // Use inline style for fill so it takes priority over CSS class defaults\n // (e.g. .viz-title { fill: var(--viz-text) } which would override attributes)\n (el as SVGElement & ElementCSSInlineStyle).style.setProperty('fill', style.fill);\n if (style.textAnchor) {\n el.setAttribute('text-anchor', style.textAnchor);\n }\n if (style.dominantBaseline) {\n el.setAttribute('dominant-baseline', style.dominantBaseline);\n }\n if (style.fontVariant) {\n el.setAttribute('font-variant', style.fontVariant);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Chrome rendering\n// ---------------------------------------------------------------------------\n\nfunction renderChromeElement(\n parent: SVGElement,\n element: ResolvedChromeElement,\n className: string,\n chromeKey: string,\n): void {\n const text = createSVGElement('text');\n setAttrs(text, { x: element.x, y: element.y });\n applyTextStyle(text, element.style);\n text.setAttribute('class', className);\n text.setAttribute('data-chrome-key', chromeKey);\n text.textContent = element.text;\n parent.appendChild(text);\n}\n\nfunction renderChrome(parent: SVGElement, layout: ChartLayout): void {\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-chrome');\n\n const { chrome } = layout;\n\n // Top chrome: render at their stored y positions (already absolute)\n if (chrome.title) {\n renderChromeElement(g, chrome.title, 'viz-title', 'title');\n }\n if (chrome.subtitle) {\n renderChromeElement(g, chrome.subtitle, 'viz-subtitle', 'subtitle');\n }\n\n // Bottom chrome starts below x-axis labels/title, not at chart area bottom.\n // Accounts for rotated tick labels which need more vertical space.\n const xAxisExtent = computeXAxisExtent(layout);\n const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;\n if (chrome.source) {\n renderChromeElement(\n g,\n { ...chrome.source, y: bottomOffset + chrome.source.y },\n 'viz-source',\n 'source',\n );\n }\n if (chrome.byline) {\n renderChromeElement(\n g,\n { ...chrome.byline, y: bottomOffset + chrome.byline.y },\n 'viz-byline',\n 'byline',\n );\n }\n if (chrome.footer) {\n renderChromeElement(\n g,\n { ...chrome.footer, y: bottomOffset + chrome.footer.y },\n 'viz-footer',\n 'footer',\n );\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Axis rendering\n// ---------------------------------------------------------------------------\n\nfunction renderAxis(\n parent: SVGElement,\n axis: AxisLayout,\n orientation: 'x' | 'y',\n layout: ChartLayout,\n): void {\n const g = createSVGElement('g');\n g.setAttribute('class', `viz-axis viz-axis-${orientation}`);\n\n const { area } = layout;\n\n // Only draw axis line for x-axis (bottom baseline).\n // Horizontal gridlines already guide y-values, so the vertical y-axis line is redundant.\n if (orientation === 'x') {\n const line = createSVGElement('line');\n line.setAttribute('class', 'viz-axis-line');\n setAttrs(line, {\n x1: axis.start.x,\n y1: axis.start.y,\n x2: axis.end.x,\n y2: axis.end.y,\n stroke: layout.theme.colors.axis,\n 'stroke-width': 1,\n });\n g.appendChild(line);\n }\n\n // Ticks and labels\n // Tick positions are absolute pixel coordinates from D3 scales whose range\n // was set to [chartArea.x, chartArea.x + chartArea.width] (and similarly for y).\n // Don't add area.x/area.y again or you'll double-offset everything.\n for (const tick of axis.ticks) {\n if (orientation === 'x') {\n // Label (no tick marks -- gridlines provide sufficient reference)\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-axis-tick');\n\n if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {\n // Rotated labels: anchor at the rotation pivot point\n const labelX = tick.position;\n const labelY = area.y + area.height + 6;\n setAttrs(label, {\n x: labelX,\n y: labelY,\n 'text-anchor': axis.tickAngle < 0 ? 'end' : 'start',\n 'dominant-baseline': 'central',\n transform: `rotate(${axis.tickAngle}, ${labelX}, ${labelY})`,\n });\n } else {\n setAttrs(label, {\n x: tick.position,\n y: area.y + area.height + 14,\n 'text-anchor': 'middle',\n });\n }\n\n applyTextStyle(label, axis.tickLabelStyle);\n label.textContent = tick.label;\n g.appendChild(label);\n } else {\n // Label (no tick marks -- gridlines provide sufficient reference)\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-axis-tick');\n setAttrs(label, {\n x: area.x - 6,\n y: tick.position,\n 'text-anchor': 'end',\n 'dominant-baseline': 'central',\n });\n applyTextStyle(label, axis.tickLabelStyle);\n label.textContent = tick.label;\n g.appendChild(label);\n }\n }\n\n // Gridlines (positions are also absolute from the scales)\n for (const gridline of axis.gridlines) {\n const gl = createSVGElement('line');\n gl.setAttribute('class', 'viz-gridline');\n if (orientation === 'y') {\n setAttrs(gl, {\n x1: area.x,\n y1: gridline.position,\n x2: area.x + area.width,\n y2: gridline.position,\n stroke: layout.theme.colors.gridline,\n 'stroke-width': 1,\n 'stroke-opacity': 0.35,\n });\n } else {\n setAttrs(gl, {\n x1: gridline.position,\n y1: area.y,\n x2: gridline.position,\n y2: area.y + area.height,\n stroke: layout.theme.colors.gridline,\n 'stroke-width': 1,\n 'stroke-opacity': 0.35,\n });\n }\n g.appendChild(gl);\n }\n\n // Axis label\n if (axis.label && axis.labelStyle) {\n const axisLabel = createSVGElement('text');\n axisLabel.setAttribute('class', 'viz-axis-title');\n applyTextStyle(axisLabel, axis.labelStyle);\n axisLabel.textContent = axis.label;\n\n if (orientation === 'x') {\n // Position axis title below tick labels. For rotated labels, compute\n // the vertical extent of the rotated ticks and place the title below.\n let titleY = area.y + area.height + 35;\n if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {\n const angleRad = Math.abs(axis.tickAngle) * (Math.PI / 180);\n let maxLabelWidth = 40;\n for (const tick of axis.ticks) {\n const w = estimateTextWidth(\n tick.label,\n axis.tickLabelStyle.fontSize,\n axis.tickLabelStyle.fontWeight,\n );\n if (w > maxLabelWidth) maxLabelWidth = w;\n }\n const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);\n titleY = area.y + area.height + rotatedHeight + 14;\n }\n setAttrs(axisLabel, {\n x: area.x + area.width / 2,\n y: titleY,\n 'text-anchor': 'middle',\n });\n } else {\n // Rotated y-axis label\n setAttrs(axisLabel, {\n x: area.x - 45,\n y: area.y + area.height / 2,\n 'text-anchor': 'middle',\n transform: `rotate(-90, ${area.x - 45}, ${area.y + area.height / 2})`,\n });\n }\n g.appendChild(axisLabel);\n }\n\n parent.appendChild(g);\n}\n\nfunction renderAxes(parent: SVGElement, layout: ChartLayout): void {\n if (layout.axes.x) {\n renderAxis(parent, layout.axes.x, 'x', layout);\n }\n if (layout.axes.y) {\n renderAxis(parent, layout.axes.y, 'y', layout);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Mark rendering (dispatch per mark type)\n// ---------------------------------------------------------------------------\n\ntype MarkRenderer<T extends Mark> = (mark: T, index: number) => SVGElement;\n\nconst markRenderers: Record<string, MarkRenderer<Mark>> = {};\n\n/**\n * Register a mark renderer for a specific mark type.\n * Built-in renderers are registered below for all chart types.\n */\nexport function registerMarkRenderer<T extends Mark>(\n type: T['type'],\n renderer: MarkRenderer<T>,\n): void {\n markRenderers[type] = renderer as MarkRenderer<Mark>;\n}\n\nfunction renderLineMark(mark: LineMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `line-${mark.seriesKey ?? index}`);\n g.setAttribute('class', 'viz-mark viz-mark-line');\n\n if (mark.points.length > 1) {\n const path = createSVGElement('path');\n // Use the pre-computed D3 curve path when available (smooth monotone),\n // otherwise fall back to straight M/L segments.\n const d =\n mark.path ?? mark.points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' ');\n setAttrs(path, {\n d,\n fill: 'none',\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth,\n });\n if (mark.strokeDasharray) {\n path.setAttribute('stroke-dasharray', mark.strokeDasharray);\n }\n if (mark.opacity != null) {\n path.setAttribute('opacity', String(mark.opacity));\n }\n g.appendChild(path);\n }\n\n // Render end-of-line label if present and visible\n if (mark.label?.visible) {\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-mark-label');\n if (mark.seriesKey) {\n label.setAttribute('data-series', mark.seriesKey);\n }\n setAttrs(label, { x: mark.label.x, y: mark.label.y });\n applyTextStyle(label, mark.label.style);\n label.textContent = mark.label.text;\n g.appendChild(label);\n\n // Render connector line if label was offset from anchor\n if (mark.label.connector) {\n const connector = createSVGElement('line');\n connector.setAttribute('class', 'viz-mark-connector');\n setAttrs(connector, {\n x1: mark.label.connector.from.x,\n y1: mark.label.connector.from.y,\n x2: mark.label.connector.to.x,\n y2: mark.label.connector.to.y,\n stroke: mark.label.connector.stroke,\n 'stroke-width': 1,\n 'stroke-opacity': 0.5,\n });\n g.appendChild(connector);\n }\n }\n\n return g;\n}\n\nfunction renderAreaMark(mark: AreaMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `area-${mark.seriesKey ?? index}`);\n g.setAttribute('class', 'viz-mark viz-mark-area');\n\n if (mark.path) {\n // Area fill: the full closed shape (top line + baseline), no stroke\n const fill = createSVGElement('path');\n setAttrs(fill, {\n d: mark.path,\n fill: mark.fill,\n 'fill-opacity': mark.fillOpacity,\n stroke: 'none',\n });\n g.appendChild(fill);\n\n // Top-line stroke: only along the data points, not the baseline\n if (mark.stroke && mark.topPath) {\n const strokePath = createSVGElement('path');\n setAttrs(strokePath, {\n d: mark.topPath,\n fill: 'none',\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth ?? 1,\n });\n g.appendChild(strokePath);\n }\n }\n\n return g;\n}\n\nfunction renderRectMark(mark: RectMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `rect-${index}`);\n g.setAttribute('class', 'viz-mark viz-mark-rect');\n\n const rect = createSVGElement('rect');\n setAttrs(rect, {\n x: mark.x,\n y: mark.y,\n width: mark.width,\n height: mark.height,\n fill: mark.fill,\n });\n if (mark.stroke) {\n rect.setAttribute('stroke', mark.stroke);\n }\n if (mark.strokeWidth) {\n rect.setAttribute('stroke-width', String(mark.strokeWidth));\n }\n if (mark.cornerRadius) {\n setAttrs(rect, { rx: mark.cornerRadius, ry: mark.cornerRadius });\n }\n g.appendChild(rect);\n\n // Render value label if present and visible\n if (mark.label?.visible) {\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-mark-label');\n setAttrs(label, { x: mark.label.x, y: mark.label.y });\n applyTextStyle(label, mark.label.style);\n label.textContent = mark.label.text;\n g.appendChild(label);\n }\n\n return g;\n}\n\nfunction renderArcMark(mark: ArcMark, index: number): SVGElement {\n const g = createSVGElement('g');\n g.setAttribute('data-mark-id', `arc-${index}`);\n g.setAttribute('class', 'viz-mark viz-mark-arc');\n g.setAttribute('transform', `translate(${mark.center.x},${mark.center.y})`);\n\n const path = createSVGElement('path');\n setAttrs(path, {\n d: mark.path,\n fill: mark.fill,\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth,\n });\n g.appendChild(path);\n\n // Render label if present and visible\n if (mark.label?.visible) {\n const label = createSVGElement('text');\n label.setAttribute('class', 'viz-mark-label');\n // Label position is in absolute coords, but we're in a translated group,\n // so subtract the center offset\n setAttrs(label, {\n x: mark.label.x - mark.center.x,\n y: mark.label.y - mark.center.y,\n });\n applyTextStyle(label, mark.label.style);\n label.textContent = mark.label.text;\n g.appendChild(label);\n }\n\n return g;\n}\n\nfunction renderPointMark(mark: PointMark, index: number): SVGElement {\n const circle = createSVGElement('circle');\n circle.setAttribute('data-mark-id', `point-${index}`);\n circle.setAttribute('class', 'viz-mark viz-mark-point');\n setAttrs(circle, {\n cx: mark.cx,\n cy: mark.cy,\n r: mark.r,\n fill: mark.fill,\n stroke: mark.stroke,\n 'stroke-width': mark.strokeWidth,\n });\n if (mark.fillOpacity !== undefined) {\n circle.setAttribute('fill-opacity', String(mark.fillOpacity));\n }\n return circle;\n}\n\n// Register built-in renderers\nregisterMarkRenderer('line', renderLineMark as MarkRenderer<Mark>);\nregisterMarkRenderer('area', renderAreaMark as MarkRenderer<Mark>);\nregisterMarkRenderer('rect', renderRectMark as MarkRenderer<Mark>);\nregisterMarkRenderer('arc', renderArcMark as MarkRenderer<Mark>);\nregisterMarkRenderer('point', renderPointMark as MarkRenderer<Mark>);\n\n/** Extract series name from a mark for legend toggle matching. */\nfunction getMarkSeries(mark: Mark): string | undefined {\n // Line and area marks have an explicit seriesKey\n if (mark.type === 'line' || mark.type === 'area') {\n return mark.seriesKey;\n }\n // For arc marks, the category name is the first part of the aria label (before ':')\n if (mark.type === 'arc') {\n return mark.aria.label.split(':')[0]?.trim();\n }\n // For rect/point, the aria label may be \"category: value\" or \"category, group: value\".\n // The series name is the category part (before the colon).\n if (mark.aria?.label) {\n const beforeColon = mark.aria.label.split(':')[0]?.trim();\n if (beforeColon) return beforeColon;\n }\n return undefined;\n}\n\nfunction renderMarks(parent: SVGElement, layout: ChartLayout): void {\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-marks');\n\n for (let i = 0; i < layout.marks.length; i++) {\n const mark = layout.marks[i];\n const renderer = markRenderers[mark.type];\n if (renderer) {\n const el = renderer(mark, i);\n // Add ARIA label if present\n if (mark.aria?.label) {\n el.setAttribute('aria-label', mark.aria.label);\n }\n // Add data-series attribute for legend toggle matching\n const series = getMarkSeries(mark);\n if (series) {\n el.setAttribute('data-series', series);\n }\n g.appendChild(el);\n }\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Annotation rendering\n// ---------------------------------------------------------------------------\n\nfunction renderAnnotations(parent: SVGElement, layout: ChartLayout): void {\n if (layout.annotations.length === 0) return;\n\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-annotations');\n\n // Annotations are already sorted by zIndex from the engine, so render in order\n for (let i = 0; i < layout.annotations.length; i++) {\n renderAnnotation(g, layout.annotations[i], i);\n }\n\n parent.appendChild(g);\n}\n\n/**\n * Render a curved arrow connector from a label to a data point.\n * Uses a cubic bezier that sweeps outward then curves toward the\n * target, with a triangular arrowhead at the tip.\n */\nfunction renderCurvedArrow(parent: SVGElement, from: Point, to: Point, stroke: string): void {\n // Pad above the target so the arrow doesn't sit right on the element.\n const pad = 6;\n const tipY = to.y - pad;\n\n const dy = tipY - from.y;\n const dist = Math.sqrt((to.x - from.x) ** 2 + dy ** 2) || 1;\n\n // Arrowhead geometry\n const arrowLen = 8;\n const arrowWidth = 4;\n\n // cp2 directly above target so arrow arrives pointing straight down.\n const bulge = Math.max(dist * 0.4, 35);\n const cp1x = from.x + bulge;\n const cp1y = from.y + dy * 0.35;\n const cp2x = to.x;\n const cp2y = tipY - Math.abs(dy) * 0.25;\n\n // Tangent at the tip (from cp2 → tip), used for arrowhead direction.\n const tx = to.x - cp2x;\n const ty = tipY - cp2y;\n const tLen = Math.sqrt(tx * tx + ty * ty) || 1;\n const ux = tx / tLen;\n const uy = ty / tLen;\n\n // End the curve path at the arrowhead BASE so the stroke doesn't\n // poke through the filled triangle.\n const baseX = to.x - ux * arrowLen;\n const baseY = tipY - uy * arrowLen;\n\n const path = createSVGElement('path');\n path.setAttribute('class', 'viz-annotation-connector');\n setAttrs(path, {\n d: `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${baseX} ${baseY}`,\n fill: 'none',\n stroke,\n 'stroke-width': 1.5,\n });\n parent.appendChild(path);\n\n // Arrowhead triangle: perpendicular to tangent direction.\n const px = -uy;\n const py = ux;\n\n const arrow = createSVGElement('polygon');\n arrow.setAttribute('class', 'viz-annotation-connector');\n setAttrs(arrow, {\n points: [\n `${to.x},${tipY}`,\n `${baseX + px * arrowWidth},${baseY + py * arrowWidth}`,\n `${baseX - px * arrowWidth},${baseY - py * arrowWidth}`,\n ].join(' '),\n fill: stroke,\n });\n parent.appendChild(arrow);\n}\n\nfunction renderAnnotation(parent: SVGElement, annotation: ResolvedAnnotation, index: number): void {\n const g = createSVGElement('g');\n g.setAttribute('class', `viz-annotation viz-annotation-${annotation.type}`);\n g.setAttribute('data-annotation-index', String(index));\n\n // Range rect\n if (annotation.rect) {\n const rect = createSVGElement('rect');\n rect.setAttribute('class', 'viz-annotation-range');\n setAttrs(rect, {\n x: annotation.rect.x,\n y: annotation.rect.y,\n width: annotation.rect.width,\n height: annotation.rect.height,\n });\n if (annotation.fill) rect.setAttribute('fill', annotation.fill);\n if (annotation.opacity !== undefined) {\n rect.setAttribute('fill-opacity', String(annotation.opacity));\n }\n g.appendChild(rect);\n }\n\n // Reference line\n if (annotation.line) {\n const line = createSVGElement('line');\n line.setAttribute('class', 'viz-annotation-line');\n setAttrs(line, {\n x1: annotation.line.start.x,\n y1: annotation.line.start.y,\n x2: annotation.line.end.x,\n y2: annotation.line.end.y,\n 'stroke-width': annotation.strokeWidth ?? 1,\n });\n if (annotation.stroke) line.setAttribute('stroke', annotation.stroke);\n if (annotation.strokeDasharray) {\n line.setAttribute('stroke-dasharray', annotation.strokeDasharray);\n }\n g.appendChild(line);\n }\n\n // Label with optional connector line\n if (annotation.label?.visible) {\n // Render connector first (behind the label text)\n if (annotation.label.connector) {\n const c = annotation.label.connector;\n if (c.style === 'caret') {\n // Small directional chevron centered in the gap between the label\n // text and the data mark, pointing toward the data.\n const pointsDown = c.to.y > c.from.y;\n const caretSize = 4;\n // c.from.y is near the text baseline, not the visual bottom.\n // Estimate the text bottom from the label's line count and font size.\n const labelLines = annotation.label.text.split('\\n');\n const labelFontSize = annotation.label.style.fontSize ?? 12;\n const labelLineHeight = labelFontSize * (annotation.label.style.lineHeight ?? 1.3);\n const textBottom =\n annotation.label.y + (labelLines.length - 1) * labelLineHeight + labelFontSize * 0.25;\n const textTop = annotation.label.y - labelFontSize;\n // Center caret in the gap between text edge and data point\n const gapEdge = pointsDown ? textBottom : textTop;\n const midY = (gapEdge + c.to.y) / 2;\n const tipX = c.to.x;\n const tipY = pointsDown ? midY + caretSize / 2 : midY - caretSize / 2;\n const baseY = pointsDown ? tipY - caretSize : tipY + caretSize;\n const path = createSVGElement('path');\n path.setAttribute('class', 'viz-annotation-connector');\n setAttrs(path, {\n d: `M${tipX - caretSize},${baseY} L${tipX},${tipY} L${tipX + caretSize},${baseY}`,\n fill: 'none',\n stroke: c.stroke,\n 'stroke-width': 1.5,\n 'stroke-opacity': 0.4,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n });\n g.appendChild(path);\n } else if (c.style === 'curve') {\n renderCurvedArrow(g, c.from, c.to, c.stroke);\n } else {\n const connector = createSVGElement('line');\n connector.setAttribute('class', 'viz-annotation-connector');\n setAttrs(connector, {\n x1: c.from.x,\n y1: c.from.y,\n x2: c.to.x,\n y2: c.to.y,\n stroke: c.stroke,\n 'stroke-width': 1,\n 'stroke-opacity': 0.5,\n });\n g.appendChild(connector);\n }\n }\n\n const text = createSVGElement('text');\n text.setAttribute('class', 'viz-annotation-label');\n setAttrs(text, { x: annotation.label.x, y: annotation.label.y });\n applyTextStyle(text, annotation.label.style);\n\n const lines = annotation.label.text.split('\\n');\n const fontSize = annotation.label.style.fontSize ?? 12;\n const lineHeight = fontSize * (annotation.label.style.lineHeight ?? 1.3);\n const isMultiLine = lines.length > 1;\n\n // Multi-line text uses center alignment for a cleaner look\n if (isMultiLine) {\n text.setAttribute('text-anchor', 'middle');\n for (let i = 0; i < lines.length; i++) {\n const tspan = createSVGElement('tspan');\n setAttrs(tspan, { x: annotation.label.x, dy: i === 0 ? 0 : lineHeight });\n tspan.textContent = lines[i];\n text.appendChild(tspan);\n }\n } else {\n text.textContent = annotation.label.text;\n }\n\n // Render background rect behind text if specified\n if (annotation.label.background) {\n const charWidth = fontSize * 0.55;\n const maxLineWidth = Math.max(...lines.map((l) => l.length)) * charWidth;\n const totalHeight = lines.length * lineHeight;\n const pad = 3;\n const bgX = isMultiLine\n ? annotation.label.x - maxLineWidth / 2 - pad\n : annotation.label.x - pad;\n const bgRect = createSVGElement('rect');\n bgRect.setAttribute('class', 'viz-annotation-bg');\n setAttrs(bgRect, {\n x: bgX,\n y: annotation.label.y - fontSize + (lineHeight - fontSize) / 2 - pad,\n width: maxLineWidth + pad * 2,\n height: totalHeight + pad * 2,\n fill: annotation.label.background,\n rx: 2,\n });\n g.appendChild(bgRect);\n }\n\n g.appendChild(text);\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Legend rendering\n// ---------------------------------------------------------------------------\n\nfunction renderLegend(parent: SVGElement, legend: LegendLayout): void {\n if (legend.entries.length === 0) return;\n\n const g = createSVGElement('g');\n g.setAttribute('class', 'viz-legend');\n g.setAttribute('role', 'list');\n g.setAttribute('aria-label', 'Chart legend');\n\n const isHorizontal = legend.position === 'top' || legend.position === 'bottom';\n let offsetX = legend.bounds.x;\n let offsetY = legend.bounds.y;\n\n for (let i = 0; i < legend.entries.length; i++) {\n const entry = legend.entries[i];\n const entryG = createSVGElement('g');\n entryG.setAttribute('class', 'viz-legend-entry');\n entryG.setAttribute('role', 'listitem');\n entryG.setAttribute('data-legend-index', String(i));\n entryG.setAttribute('data-legend-label', entry.label);\n entryG.setAttribute(\n 'aria-label',\n `${entry.label}: ${entry.active !== false ? 'visible' : 'hidden'}`,\n );\n entryG.setAttribute('style', 'cursor: pointer');\n\n // Apply dimming for inactive entries\n if (entry.active === false) {\n entryG.setAttribute('opacity', '0.3');\n }\n\n // Swatch\n if (entry.shape === 'circle') {\n const circle = createSVGElement('circle');\n setAttrs(circle, {\n cx: offsetX + legend.swatchSize / 2,\n cy: offsetY + legend.swatchSize / 2,\n r: legend.swatchSize / 2,\n fill: entry.color,\n });\n entryG.appendChild(circle);\n } else if (entry.shape === 'line') {\n // Line swatch: a short line segment with a dot in the middle\n const line = createSVGElement('line');\n setAttrs(line, {\n x1: offsetX,\n y1: offsetY + legend.swatchSize / 2,\n x2: offsetX + legend.swatchSize,\n y2: offsetY + legend.swatchSize / 2,\n stroke: entry.color,\n 'stroke-width': 2,\n });\n entryG.appendChild(line);\n // Small dot at center\n const dot = createSVGElement('circle');\n setAttrs(dot, {\n cx: offsetX + legend.swatchSize / 2,\n cy: offsetY + legend.swatchSize / 2,\n r: 2.5,\n fill: entry.color,\n });\n entryG.appendChild(dot);\n } else {\n const rect = createSVGElement('rect');\n setAttrs(rect, {\n x: offsetX,\n y: offsetY,\n width: legend.swatchSize,\n height: legend.swatchSize,\n fill: entry.color,\n rx: 2,\n });\n entryG.appendChild(rect);\n }\n\n // Label\n const label = createSVGElement('text');\n setAttrs(label, {\n x: offsetX + legend.swatchSize + legend.swatchGap,\n y: offsetY + legend.swatchSize / 2,\n 'dominant-baseline': 'central',\n });\n applyTextStyle(label, legend.labelStyle);\n label.textContent = entry.label;\n entryG.appendChild(label);\n\n g.appendChild(entryG);\n\n // Advance position for next entry\n if (isHorizontal) {\n const labelWidth = estimateTextWidth(\n entry.label,\n legend.labelStyle.fontSize,\n legend.labelStyle.fontWeight,\n );\n const entryWidth = legend.swatchSize + legend.swatchGap + labelWidth + legend.entryGap;\n offsetX += entryWidth;\n // Wrap to next line if exceeding bounds\n if (offsetX > legend.bounds.x + legend.bounds.width && i < legend.entries.length - 1) {\n offsetX = legend.bounds.x;\n offsetY += legend.swatchSize + 6;\n }\n } else {\n offsetY += legend.swatchSize + legend.entryGap;\n }\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Brand rendering\n// ---------------------------------------------------------------------------\n\nconst BRAND_FONT_SIZE = 20;\nconst BRAND_MIN_WIDTH = 120;\nconst BRAND_URL = 'https://tryopendata.ai';\nconst XLINK_NS = 'http://www.w3.org/1999/xlink';\n\n/**\n * Render the \"OpenData\" brand as a footer-row element, right-aligned on the\n * same baseline as the first bottom chrome text (source/byline/footer).\n * Uses the same font size as chrome source text so it blends in as a subtle\n * footer item rather than occupying independent visual space.\n */\nfunction renderBrand(parent: SVGElement, layout: ChartLayout): void {\n if (layout.dimensions.width < BRAND_MIN_WIDTH) return;\n\n const { width } = layout.dimensions;\n const padding = layout.theme.spacing.padding;\n const rightEdge = width - padding;\n const fill = layout.theme.colors.axis;\n\n // Vertically align with the first bottom chrome element.\n const { chrome } = layout;\n const xAxisExtent = computeXAxisExtent(layout);\n const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;\n const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;\n const chromeY = firstBottom\n ? bottomOffset + firstBottom.y\n : bottomOffset + layout.theme.spacing.chartToFooter;\n\n const a = createSVGElement('a');\n a.setAttribute('href', BRAND_URL);\n a.setAttributeNS(XLINK_NS, 'xlink:href', BRAND_URL);\n a.setAttribute('target', '_blank');\n a.setAttribute('rel', 'noopener');\n a.setAttribute('class', 'viz-chrome-ref');\n\n // \"Open\" in normal weight, \"Data\" in semibold, rendered as a single\n // right-aligned text element with two tspans.\n const text = createSVGElement('text');\n setAttrs(text, {\n x: rightEdge,\n y: chromeY,\n 'font-family': layout.theme.fonts.family,\n 'font-size': BRAND_FONT_SIZE,\n 'text-anchor': 'end',\n 'dominant-baseline': 'hanging',\n 'fill-opacity': 0.55,\n });\n (text as SVGElement & ElementCSSInlineStyle).style.setProperty('fill', fill);\n\n const openSpan = createSVGElement('tspan');\n openSpan.setAttribute('font-weight', '500');\n openSpan.textContent = 'Open';\n text.appendChild(openSpan);\n\n const dataSpan = createSVGElement('tspan');\n dataSpan.setAttribute('font-weight', '600');\n dataSpan.textContent = 'Data';\n text.appendChild(dataSpan);\n\n a.appendChild(text);\n parent.appendChild(a);\n}\n\n// ---------------------------------------------------------------------------\n// Main render function\n// ---------------------------------------------------------------------------\n\n/**\n * Render a compiled ChartLayout into an SVG element and append it to a container.\n *\n * @param layout - Compiled ChartLayout from compileChart().\n * @param container - DOM element to mount the SVG into.\n * @returns The created SVG element.\n */\nexport function renderChartSVG(layout: ChartLayout, container: HTMLElement): SVGElement {\n const { width, height } = layout.dimensions;\n\n const svg = createSVGElement('svg') as SVGSVGElement;\n setAttrs(svg, {\n viewBox: `0 0 ${width} ${height}`,\n xmlns: SVG_NS,\n });\n svg.setAttribute('role', layout.a11y.role);\n svg.setAttribute('aria-label', layout.a11y.altText);\n svg.setAttribute('class', 'viz-chart');\n\n // Background\n const bg = createSVGElement('rect');\n setAttrs(bg, {\n x: 0,\n y: 0,\n width,\n height,\n fill: layout.theme.colors.background,\n });\n svg.appendChild(bg);\n\n // Clip path to prevent marks (especially area fills) from overflowing\n // into the chrome region (title/subtitle). Extends full width so\n // end-of-line labels aren't clipped, but constrains vertically.\n const clipId = `viz-clip-${Math.random().toString(36).slice(2, 8)}`;\n const defs = createSVGElement('defs');\n const clipPath = createSVGElement('clipPath');\n clipPath.setAttribute('id', clipId);\n const clipRect = createSVGElement('rect');\n setAttrs(clipRect, {\n x: 0,\n y: layout.area.y,\n width,\n height: layout.area.height + 2,\n });\n clipPath.appendChild(clipRect);\n defs.appendChild(clipPath);\n svg.appendChild(defs);\n\n // Render layers in order (back to front)\n // Axes render outside clip (labels extend beyond chart area)\n renderAxes(svg, layout);\n\n // Marks are clipped to chart area so area fills don't cover chrome\n const clippedGroup = createSVGElement('g');\n clippedGroup.setAttribute('clip-path', `url(#${clipId})`);\n renderMarks(clippedGroup, layout);\n svg.appendChild(clippedGroup);\n\n renderAnnotations(svg, layout);\n renderLegend(svg, layout.legend);\n\n // Chrome renders on top so titles are never obscured by chart elements\n renderChrome(svg, layout);\n\n // Brand renders as a footer item, right-aligned on the source/footer row\n renderBrand(svg, layout);\n\n container.appendChild(svg);\n return svg;\n}\n","/**\n * Table cell renderers: produce DOM elements for each cell type.\n *\n * Each renderer takes a resolved TableCell and returns an HTMLElement\n * (typically a <td>) with appropriate content, styling, and classes.\n */\n\nimport type {\n BarTableCell,\n CategoryTableCell,\n FlagTableCell,\n HeatmapTableCell,\n ImageTableCell,\n SparklineData,\n SparklineTableCell,\n TableCell,\n TextTableCell,\n} from '@opendata-ai/openchart-core';\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\n/** Apply common cell styles (background, color, font weight, font variant). */\nfunction applyCellStyle(td: HTMLTableCellElement, cell: TableCell): void {\n if (cell.style.backgroundColor) {\n td.style.background = cell.style.backgroundColor;\n }\n if (cell.style.color) {\n td.style.color = cell.style.color;\n }\n if (cell.style.fontWeight) {\n td.style.fontWeight = String(cell.style.fontWeight);\n }\n if (cell.style.fontVariant) {\n td.style.fontVariant = cell.style.fontVariant;\n }\n if (cell.aria) {\n td.setAttribute('aria-label', cell.aria);\n }\n}\n\n/**\n * Convert a 2-char ISO 3166-1 alpha-2 country code to a flag emoji.\n * Each letter maps to a Regional Indicator Symbol (offset 0x1F1A5 from ASCII).\n */\nfunction countryToEmoji(code: string): string {\n return [...code.toUpperCase()]\n .map((c) => String.fromCodePoint(c.charCodeAt(0) + 0x1f1a5))\n .join('');\n}\n\n/**\n * Lookup map for common country names by ISO 3166-1 alpha-2 code.\n * Falls back to the raw code for unrecognized values.\n */\nconst COUNTRY_NAMES: Record<string, string> = {\n US: 'United States',\n CN: 'China',\n IN: 'India',\n ID: 'Indonesia',\n BR: 'Brazil',\n PK: 'Pakistan',\n NG: 'Nigeria',\n BD: 'Bangladesh',\n RU: 'Russia',\n MX: 'Mexico',\n JP: 'Japan',\n DE: 'Germany',\n GB: 'United Kingdom',\n FR: 'France',\n IT: 'Italy',\n CA: 'Canada',\n AU: 'Australia',\n KR: 'South Korea',\n ES: 'Spain',\n AR: 'Argentina',\n CO: 'Colombia',\n ZA: 'South Africa',\n TR: 'Turkey',\n SA: 'Saudi Arabia',\n UA: 'Ukraine',\n PL: 'Poland',\n NL: 'Netherlands',\n SE: 'Sweden',\n NO: 'Norway',\n DK: 'Denmark',\n FI: 'Finland',\n CH: 'Switzerland',\n AT: 'Austria',\n BE: 'Belgium',\n PT: 'Portugal',\n IE: 'Ireland',\n NZ: 'New Zealand',\n SG: 'Singapore',\n IL: 'Israel',\n AE: 'United Arab Emirates',\n EG: 'Egypt',\n TH: 'Thailand',\n VN: 'Vietnam',\n PH: 'Philippines',\n MY: 'Malaysia',\n CL: 'Chile',\n PE: 'Peru',\n CZ: 'Czech Republic',\n GR: 'Greece',\n HU: 'Hungary',\n RO: 'Romania',\n ET: 'Ethiopia',\n};\n\n/** Get a human-readable name for a country code. */\nfunction getCountryName(code: string): string {\n return COUNTRY_NAMES[code.toUpperCase()] || code.toUpperCase();\n}\n\n/**\n * Describe a sparkline trend for screen readers.\n * Compares first and last values to determine direction.\n */\nfunction describeSparklineTrend(data: SparklineData): string {\n if (data.type === 'line' && data.points.length >= 2) {\n const first = data.points[0].y;\n const last = data.points[data.points.length - 1].y;\n const count = data.points.length;\n if (last > first) return `Sparkline with ${count} points, trending upward`;\n if (last < first) return `Sparkline with ${count} points, trending downward`;\n return `Sparkline with ${count} points, roughly flat`;\n }\n if ((data.type === 'bar' || data.type === 'column') && data.bars.length > 0) {\n return `${data.type === 'column' ? 'Column' : 'Bar'} sparkline with ${data.bars.length} values`;\n }\n return 'Sparkline';\n}\n\n// ---------------------------------------------------------------------------\n// Cell renderers\n// ---------------------------------------------------------------------------\n\n/** Render a plain text cell. */\nexport function renderTextCell(cell: TextTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.textContent = cell.formattedValue;\n applyCellStyle(td, cell);\n return td;\n}\n\n/** Render a heatmap-colored cell. */\nexport function renderHeatmapCell(cell: HeatmapTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.textContent = cell.formattedValue;\n applyCellStyle(td, cell);\n return td;\n}\n\n/** Render a category-colored cell. */\nexport function renderCategoryCell(cell: CategoryTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.textContent = cell.formattedValue;\n applyCellStyle(td, cell);\n return td;\n}\n\n/** Render a cell with an inline bar visualization. */\nexport function renderBarCell(cell: BarTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n td.className = 'viz-table-bar';\n applyCellStyle(td, cell);\n\n const fill = document.createElement('div');\n fill.className = 'viz-table-bar-fill';\n fill.style.width = `${Math.round(cell.barWidth * 100)}%`;\n fill.style.left = `${Math.round(cell.barOffset * 100)}%`;\n fill.style.background = cell.barColor;\n td.appendChild(fill);\n\n const valueSpan = document.createElement('span');\n valueSpan.className = 'viz-table-bar-value';\n valueSpan.textContent = cell.formattedValue;\n td.appendChild(valueSpan);\n\n return td;\n}\n\n/**\n * Format a sparkline endpoint value for display.\n * Keeps it compact: no decimals for values >= 100, one decimal otherwise.\n */\nfunction formatSparklineValue(v: number): string {\n if (Math.abs(v) >= 1000) {\n return v.toLocaleString('en-US', { maximumFractionDigits: 0 });\n }\n if (Math.abs(v) >= 100) {\n return v.toFixed(0);\n }\n return v.toFixed(1);\n}\n\n/** Render a cell with an inline sparkline SVG. */\nexport function renderSparklineCell(cell: SparklineTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n applyCellStyle(td, cell);\n\n const sparklineData = cell.sparklineData;\n if (!sparklineData || sparklineData.count === 0) {\n td.textContent = cell.formattedValue || '';\n return td;\n }\n\n // Add aria-label describing the trend for screen readers\n const trendDescription = describeSparklineTrend(sparklineData);\n if (!td.getAttribute('aria-label')) {\n td.setAttribute('aria-label', trendDescription);\n }\n\n const wrapper = document.createElement('span');\n wrapper.className = 'viz-table-sparkline';\n\n const svgNS = 'http://www.w3.org/2000/svg';\n\n if (sparklineData.type === 'line') {\n // Infrographic-style sparkline: SVG polyline fills cell width via percentage x-coords.\n // Dots are HTML elements (not SVG circles) to avoid aspect-ratio distortion.\n // Labels are HTML below the SVG.\n const svgH = 28;\n const padY = 4;\n const lineH = svgH - padY * 2;\n\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('aria-hidden', 'true');\n svg.setAttribute('xmlns', svgNS);\n svg.style.height = `${svgH}px`;\n\n // Compute y positions in pixel space (no viewBox scaling)\n const yPositions = sparklineData.points.map((p) => padY + (1 - p.y) * lineH);\n\n // SVG polyline doesn't support % in points attribute, so use a viewBox\n // with a wide aspect ratio and map x to 0-1000 range. preserveAspectRatio=\"none\"\n // stretches the x-axis to fill the cell width. Y values stay in pixel space\n // since viewBox height matches the SVG height. Only the polyline is in the SVG;\n // dots are HTML elements to avoid circle distortion from the non-uniform scaling.\n const viewW = 1000;\n svg.setAttribute('viewBox', `0 0 ${viewW} ${svgH}`);\n svg.setAttribute('preserveAspectRatio', 'none');\n\n const ptsScaled = sparklineData.points.map((p, i) => ({\n x: p.x * viewW,\n y: yPositions[i],\n }));\n const scaledPointsStr = ptsScaled.map((p) => `${p.x},${p.y}`).join(' ');\n\n const polyline = document.createElementNS(svgNS, 'polyline');\n polyline.setAttribute('points', scaledPointsStr);\n polyline.setAttribute('fill', 'none');\n polyline.setAttribute('stroke', sparklineData.color);\n polyline.setAttribute('stroke-width', '1.5');\n polyline.setAttribute('stroke-linejoin', 'round');\n polyline.setAttribute('vector-effect', 'non-scaling-stroke');\n svg.appendChild(polyline);\n\n wrapper.appendChild(svg);\n\n // HTML dots at start and end (positioned absolutely over the SVG)\n const firstY = yPositions[0];\n const lastY = yPositions[yPositions.length - 1];\n const dotSize = 5;\n\n const startDot = document.createElement('span');\n startDot.className = 'viz-table-sparkline-dot';\n startDot.style.left = '0';\n startDot.style.top = `${firstY - dotSize / 2}px`;\n startDot.style.background = sparklineData.color;\n wrapper.appendChild(startDot);\n\n const endDot = document.createElement('span');\n endDot.className = 'viz-table-sparkline-dot';\n endDot.style.right = '0';\n endDot.style.top = `${lastY - dotSize / 2}px`;\n endDot.style.background = sparklineData.color;\n wrapper.appendChild(endDot);\n\n // HTML labels below the SVG, positioned at left and right edges\n const labelsRow = document.createElement('span');\n labelsRow.className = 'viz-table-sparkline-labels';\n labelsRow.style.color = sparklineData.color;\n\n const startLabel = document.createElement('span');\n startLabel.textContent = formatSparklineValue(sparklineData.startValue);\n labelsRow.appendChild(startLabel);\n\n const endLabel = document.createElement('span');\n endLabel.textContent = formatSparklineValue(sparklineData.endValue);\n labelsRow.appendChild(endLabel);\n\n wrapper.appendChild(labelsRow);\n } else if (sparklineData.type === 'column') {\n // Vertical bars at proportional heights\n const width = 80;\n const height = 20;\n const padding = 2;\n const innerW = width - padding * 2;\n const innerH = height - padding * 2;\n\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('width', String(width));\n svg.setAttribute('height', String(height));\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n svg.setAttribute('aria-hidden', 'true');\n\n const barCount = sparklineData.bars.length;\n if (barCount > 0) {\n const gap = 1;\n const barW = Math.max(1, (innerW - gap * (barCount - 1)) / barCount);\n\n for (let i = 0; i < barCount; i++) {\n const barH = Math.max(1, sparklineData.bars[i] * innerH);\n const x = padding + i * (barW + gap);\n const y = padding + innerH - barH;\n\n const rect = document.createElementNS(svgNS, 'rect');\n rect.setAttribute('x', String(x));\n rect.setAttribute('y', String(y));\n rect.setAttribute('width', String(barW));\n rect.setAttribute('height', String(barH));\n rect.setAttribute('rx', '1.5');\n rect.setAttribute('fill', sparklineData.color);\n svg.appendChild(rect);\n }\n }\n\n wrapper.appendChild(svg);\n } else {\n // 'bar' type: horizontal bars at proportional widths\n const width = 80;\n const height = 20;\n const padding = 2;\n const innerW = width - padding * 2;\n const innerH = height - padding * 2;\n\n const svg = document.createElementNS(svgNS, 'svg');\n svg.setAttribute('width', String(width));\n svg.setAttribute('height', String(height));\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n svg.setAttribute('aria-hidden', 'true');\n\n const barCount = sparklineData.bars.length;\n if (barCount > 0) {\n const gap = 1;\n const barH = Math.max(1, (innerH - gap * (barCount - 1)) / barCount);\n\n for (let i = 0; i < barCount; i++) {\n const barW = Math.max(1, sparklineData.bars[i] * innerW);\n const x = padding;\n const y = padding + i * (barH + gap);\n\n const rect = document.createElementNS(svgNS, 'rect');\n rect.setAttribute('x', String(x));\n rect.setAttribute('y', String(y));\n rect.setAttribute('width', String(barW));\n rect.setAttribute('height', String(barH));\n rect.setAttribute('rx', '1.5');\n rect.setAttribute('fill', sparklineData.color);\n svg.appendChild(rect);\n }\n }\n\n wrapper.appendChild(svg);\n }\n\n td.appendChild(wrapper);\n\n return td;\n}\n\n/** Render a cell with an image. */\nexport function renderImageCell(cell: ImageTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n applyCellStyle(td, cell);\n\n const wrapper = document.createElement('span');\n wrapper.className = `viz-table-image${cell.rounded ? ' viz-table-image-rounded' : ''}`;\n\n const img = document.createElement('img');\n img.src = cell.src;\n img.alt = cell.formattedValue || '';\n img.width = cell.imageWidth;\n img.height = cell.imageHeight;\n img.loading = 'lazy';\n\n wrapper.appendChild(img);\n td.appendChild(wrapper);\n\n return td;\n}\n\n/** Render a cell with a country flag emoji. */\nexport function renderFlagCell(cell: FlagTableCell): HTMLTableCellElement {\n const td = document.createElement('td');\n applyCellStyle(td, cell);\n\n const span = document.createElement('span');\n span.className = 'viz-table-flag';\n span.setAttribute('role', 'img');\n\n if (cell.countryCode && cell.countryCode.length === 2) {\n const countryName = getCountryName(cell.countryCode);\n span.textContent = countryToEmoji(cell.countryCode);\n span.setAttribute('aria-label', `Flag: ${countryName}`);\n } else {\n span.textContent = cell.formattedValue;\n span.setAttribute('aria-label', cell.formattedValue);\n }\n\n td.appendChild(span);\n\n return td;\n}\n\n// ---------------------------------------------------------------------------\n// Dispatcher\n// ---------------------------------------------------------------------------\n\n/** Render any table cell by dispatching on its cellType. */\nexport function renderCell(cell: TableCell): HTMLTableCellElement {\n switch (cell.cellType) {\n case 'text':\n return renderTextCell(cell);\n case 'heatmap':\n return renderHeatmapCell(cell);\n case 'category':\n return renderCategoryCell(cell);\n case 'bar':\n return renderBarCell(cell);\n case 'sparkline':\n return renderSparklineCell(cell);\n case 'image':\n return renderImageCell(cell);\n case 'flag':\n return renderFlagCell(cell);\n default:\n // Exhaustive check fallback\n return renderTextCell(cell as TextTableCell);\n }\n}\n","/**\n * Table keyboard navigation: arrow-key cell navigation, Enter to sort,\n * Escape to clear search, and aria-activedescendant management.\n *\n * Designed to be wired up by table-mount.ts after render. Returns a\n * cleanup function to remove listeners on re-render or destroy.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface KeyboardNavOptions {\n /** The wrapper element containing the whole table UI. */\n wrapper: HTMLElement;\n /** Callback to trigger sort on a column. */\n onSort: (columnKey: string) => void;\n /** Callback to clear search and return focus to the table body. */\n onClearSearch: () => void;\n /** Callback to announce text to screen readers via the live region. */\n onAnnounce: (message: string) => void;\n}\n\ninterface CellPosition {\n row: number;\n col: number;\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\n/**\n * Attach keyboard navigation to a rendered table.\n *\n * @returns A cleanup function that removes all event listeners.\n */\nexport function attachKeyboardNav(options: KeyboardNavOptions): () => void {\n const { wrapper, onSort, onClearSearch, onAnnounce } = options;\n\n let focusedCell: CellPosition = { row: -1, col: 0 };\n\n const table = wrapper.querySelector('table');\n if (!table) return () => {};\n\n const tbody = table.querySelector('tbody');\n const thead = table.querySelector('thead');\n if (!tbody || !thead) return () => {};\n\n // Make tbody focusable\n tbody.setAttribute('tabindex', '0');\n\n function getRows(): HTMLTableRowElement[] {\n if (!tbody) return [];\n return Array.from(tbody.querySelectorAll('tr'));\n }\n\n function getHeaderCells(): HTMLTableCellElement[] {\n if (!thead) return [];\n const headerRow = thead.querySelector('tr');\n if (!headerRow) return [];\n return Array.from(headerRow.querySelectorAll('th'));\n }\n\n function getCellsInRow(tr: HTMLTableRowElement): HTMLTableCellElement[] {\n return Array.from(tr.querySelectorAll('td'));\n }\n\n function getColCount(): number {\n const rows = getRows();\n if (rows.length === 0) return getHeaderCells().length;\n return getCellsInRow(rows[0]).length;\n }\n\n function clearFocusHighlight(): void {\n const prev = wrapper.querySelector('.viz-table-cell-focus');\n if (prev) {\n prev.classList.remove('viz-table-cell-focus');\n prev.removeAttribute('id');\n }\n }\n\n function setFocusedCell(row: number, col: number): void {\n clearFocusHighlight();\n const rows = getRows();\n const colCount = getColCount();\n\n // Clamp values\n if (rows.length === 0) return;\n row = Math.max(0, Math.min(row, rows.length - 1));\n col = Math.max(0, Math.min(col, colCount - 1));\n\n focusedCell = { row, col };\n\n // Highlight the cell\n const tr = rows[row];\n if (!tr) return;\n const cells = getCellsInRow(tr);\n const cell = cells[col];\n if (!cell) return;\n\n const cellId = `viz-cell-${row}-${col}`;\n cell.id = cellId;\n cell.classList.add('viz-table-cell-focus');\n cell.setAttribute('data-row', String(row));\n cell.setAttribute('data-col', String(col));\n\n // Set aria-activedescendant on tbody\n if (tbody) {\n tbody.setAttribute('aria-activedescendant', cellId);\n }\n\n // Scroll cell into view if needed\n cell.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n\n function handleTbodyFocus(): void {\n // When tbody receives focus, highlight the first cell (or restore last)\n if (focusedCell.row < 0) {\n setFocusedCell(0, 0);\n } else {\n setFocusedCell(focusedCell.row, focusedCell.col);\n }\n }\n\n function handleTbodyKeydown(e: KeyboardEvent): void {\n const rows = getRows();\n if (rows.length === 0) return;\n\n const colCount = getColCount();\n const { row, col } = focusedCell;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (row < rows.length - 1) {\n setFocusedCell(row + 1, col);\n }\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (row > 0) {\n setFocusedCell(row - 1, col);\n } else {\n // Move focus to the header\n focusHeaderCell(col);\n }\n break;\n case 'ArrowRight':\n e.preventDefault();\n if (col < colCount - 1) {\n setFocusedCell(row, col + 1);\n }\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (col > 0) {\n setFocusedCell(row, col - 1);\n }\n break;\n case 'Home':\n e.preventDefault();\n setFocusedCell(row, 0);\n break;\n case 'End':\n e.preventDefault();\n setFocusedCell(row, colCount - 1);\n break;\n }\n }\n\n // Header cell keyboard handling\n function focusHeaderCell(col: number): void {\n const headers = getHeaderCells();\n if (col >= 0 && col < headers.length) {\n clearFocusHighlight();\n headers[col].focus();\n }\n }\n\n function handleHeaderKeydown(e: KeyboardEvent): void {\n const th = e.currentTarget as HTMLTableCellElement;\n const headers = getHeaderCells();\n const colIndex = headers.indexOf(th);\n if (colIndex < 0) return;\n\n switch (e.key) {\n case 'ArrowRight':\n e.preventDefault();\n if (colIndex < headers.length - 1) {\n headers[colIndex + 1].focus();\n }\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (colIndex > 0) {\n headers[colIndex - 1].focus();\n }\n break;\n case 'ArrowDown':\n e.preventDefault();\n // Move focus to first body row at this column\n if (tbody) {\n tbody.focus();\n setFocusedCell(0, colIndex);\n }\n break;\n case 'Enter':\n case ' ': {\n e.preventDefault();\n const sortColumn = th.getAttribute('data-column');\n const sortBtn = th.querySelector('[data-sort-column]');\n if (sortColumn && sortBtn) {\n onSort(sortColumn);\n }\n break;\n }\n }\n }\n\n // Search escape handling\n const searchInput = wrapper.querySelector('.viz-table-search input') as HTMLInputElement | null;\n\n function handleSearchKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClearSearch();\n // Return focus to tbody\n if (tbody) {\n tbody.focus();\n onAnnounce('Search cleared');\n }\n }\n }\n\n // Wire up event listeners\n tbody.addEventListener('focus', handleTbodyFocus);\n tbody.addEventListener('keydown', handleTbodyKeydown as EventListener);\n\n // Make header cells focusable and wire keyboard\n const headerCells = getHeaderCells();\n for (const th of headerCells) {\n th.setAttribute('tabindex', '0');\n th.addEventListener('keydown', handleHeaderKeydown as EventListener);\n }\n\n if (searchInput) {\n searchInput.addEventListener('keydown', handleSearchKeydown as EventListener);\n }\n\n // Cleanup\n return () => {\n tbody.removeEventListener('focus', handleTbodyFocus);\n tbody.removeEventListener('keydown', handleTbodyKeydown as EventListener);\n\n for (const th of headerCells) {\n th.removeEventListener('keydown', handleHeaderKeydown as EventListener);\n }\n\n if (searchInput) {\n searchInput.removeEventListener('keydown', handleSearchKeydown as EventListener);\n }\n\n clearFocusHighlight();\n };\n}\n","/**\n * Table mount API: the main entry point for vanilla JS table usage.\n *\n * createTable() takes a container, spec, and options, compiles the table,\n * renders it as HTML, sets up responsive resizing, sort/search/pagination\n * interactivity, and returns a TableInstance with update/resize/export/destroy.\n *\n * Supports both controlled and uncontrolled modes:\n * - Uncontrolled (default): manages sort/search/page internally\n * - Controlled: reads state from externalState, fires onStateChange\n */\n\nimport type {\n CompileTableOptions,\n DarkMode,\n SortState,\n TableLayout,\n TableSpec,\n ThemeConfig,\n} from '@opendata-ai/openchart-core';\nimport { getBreakpoint } from '@opendata-ai/openchart-core';\nimport { compileTable } from '@opendata-ai/openchart-engine';\nimport { observeResize } from './resize-observer';\nimport { attachKeyboardNav } from './table-keyboard';\nimport { renderTable } from './table-renderer';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TableState {\n sort: SortState | null;\n search: string;\n page: number;\n}\n\nexport interface TableMountOptions {\n theme?: ThemeConfig;\n darkMode?: DarkMode;\n responsive?: boolean;\n onRowClick?: (row: Record<string, unknown>) => void;\n onStateChange?: (state: TableState) => void;\n externalState?: { sort?: SortState | null; search?: string; page?: number };\n}\n\nexport interface TableInstance {\n update(spec: TableSpec): void;\n resize(): void;\n export(format: 'csv'): string;\n getState(): TableState;\n setState(partial: Partial<TableState>): void;\n destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Dark mode resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveDarkMode(mode?: DarkMode): boolean {\n if (mode === 'force') return true;\n if (mode === 'off' || mode === undefined) return false;\n if (typeof window !== 'undefined' && window.matchMedia) {\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// CSV export\n// ---------------------------------------------------------------------------\n\nfunction csvEscape(value: string): string {\n if (value.includes(',') || value.includes('\"') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\n// Sort cycling\n// ---------------------------------------------------------------------------\n\n/**\n * Cycle sort state: clicking same column cycles none -> asc -> desc -> none.\n * Clicking a different column resets to asc.\n */\nfunction cycleSort(current: SortState | null, column: string): SortState | null {\n if (!current || current.column !== column) {\n return { column, direction: 'asc' };\n }\n if (current.direction === 'asc') {\n return { column, direction: 'desc' };\n }\n // desc -> none\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Main API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a table instance from a spec and mount it into a container.\n *\n * @param container - The DOM element to render into.\n * @param spec - The table spec.\n * @param options - Mount options.\n * @returns A TableInstance with update/resize/export/destroy methods.\n */\nexport function createTable(\n container: HTMLElement,\n spec: TableSpec,\n options?: TableMountOptions,\n): TableInstance {\n let currentSpec = spec;\n let currentLayout: TableLayout;\n let wrapperElement: HTMLElement | null = null;\n let disconnectResize: (() => void) | null = null;\n let cleanupKeyboard: (() => void) | null = null;\n let destroyed = false;\n\n // Internal state (used in uncontrolled mode)\n const internalState: TableState = {\n sort: null,\n search: '',\n page: 0,\n };\n\n // Debounce timers\n let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n let resizeDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const isControlled = options?.externalState !== undefined;\n\n function getState(): TableState {\n if (isControlled && options?.externalState) {\n return {\n sort: options.externalState.sort ?? null,\n search: options.externalState.search ?? '',\n page: options.externalState.page ?? 0,\n };\n }\n return { ...internalState };\n }\n\n function updateState(partial: Partial<TableState>): void {\n if (isControlled) {\n // In controlled mode, notify parent\n const current = getState();\n const next: TableState = {\n sort: partial.sort !== undefined ? partial.sort : current.sort,\n search: partial.search !== undefined ? partial.search : current.search,\n page: partial.page !== undefined ? partial.page : current.page,\n };\n options?.onStateChange?.(next);\n } else {\n // In uncontrolled mode, update internal state\n if (partial.sort !== undefined) internalState.sort = partial.sort;\n if (partial.search !== undefined) internalState.search = partial.search;\n if (partial.page !== undefined) internalState.page = partial.page;\n options?.onStateChange?.({ ...internalState });\n }\n }\n\n function compile(): TableLayout {\n const state = getState();\n const darkMode = resolveDarkMode(options?.darkMode);\n const { width } = getContainerDimensions();\n\n const compileOpts: CompileTableOptions = {\n width,\n height: 600,\n theme: options?.theme,\n darkMode,\n sort: state.sort ?? undefined,\n search: state.search || undefined,\n page: state.page,\n };\n\n return compileTable(currentSpec, compileOpts);\n }\n\n function getContainerDimensions(): { width: number; height: number } {\n const rect = container.getBoundingClientRect();\n return {\n width: Math.max(rect.width || 600, 100),\n height: Math.max(rect.height || 400, 100),\n };\n }\n\n /**\n * Announce a message to screen readers via the live region.\n */\n function announce(message: string): void {\n if (!wrapperElement) return;\n const liveRegion = wrapperElement.querySelector('.viz-table-live-region');\n if (liveRegion) {\n liveRegion.textContent = message;\n }\n }\n\n /**\n * Apply responsive breakpoint class based on container width.\n * Only auto-adds compact mode at small sizes. Never removes compact\n * if the spec explicitly requests it (layout.compact === true).\n */\n function applyBreakpointClass(): void {\n if (!wrapperElement) return;\n const { width } = getContainerDimensions();\n const bp = getBreakpoint(width);\n\n if (bp === 'compact' || bp === 'medium') {\n wrapperElement.classList.add('viz-table--compact');\n } else if (!currentLayout?.compact) {\n // Only remove compact if the spec didn't explicitly request it\n wrapperElement.classList.remove('viz-table--compact');\n }\n }\n\n function render(): void {\n if (destroyed) return;\n\n try {\n // Clean up previous keyboard nav\n if (cleanupKeyboard) {\n cleanupKeyboard();\n cleanupKeyboard = null;\n }\n\n // Clean up previous render\n if (wrapperElement?.parentNode) {\n wrapperElement.parentNode.removeChild(wrapperElement);\n wrapperElement = null;\n }\n\n currentLayout = compile();\n wrapperElement = renderTable(currentLayout, container);\n\n // Apply dark mode class\n const isDark = resolveDarkMode(options?.darkMode);\n if (isDark) {\n container.classList.add('viz-dark');\n } else {\n container.classList.remove('viz-dark');\n }\n\n // Apply responsive breakpoint\n applyBreakpointClass();\n\n // Add clickable class if onRowClick is provided\n if (options?.onRowClick) {\n wrapperElement.classList.add('viz-table--clickable');\n }\n\n // Wire up event handlers\n wireEvents();\n\n // Wire up keyboard navigation\n if (wrapperElement) {\n cleanupKeyboard = attachKeyboardNav({\n wrapper: wrapperElement,\n onSort: (columnKey: string) => {\n const state = getState();\n const newSort = cycleSort(state.sort, columnKey);\n updateState({ sort: newSort, page: 0 });\n\n // Announce sort change\n if (newSort) {\n const dir = newSort.direction === 'asc' ? 'ascending' : 'descending';\n announce(`Sorted by ${columnKey} ${dir}`);\n } else {\n announce('Sort cleared');\n }\n\n if (!isControlled) {\n rerender();\n }\n },\n onClearSearch: () => {\n updateState({ search: '', page: 0 });\n if (!isControlled) {\n rerender();\n }\n },\n onAnnounce: announce,\n });\n }\n } catch (err) {\n console.error('[viz] Table render failed:', err);\n }\n }\n\n function wireEvents(): void {\n if (!wrapperElement) return;\n\n // Sort click handlers on thead buttons\n const sortBtns = wrapperElement.querySelectorAll('[data-sort-column]');\n for (const btn of sortBtns) {\n btn.addEventListener('click', handleSortClick);\n }\n\n // Search input\n const searchInput = wrapperElement.querySelector(\n '.viz-table-search input',\n ) as HTMLInputElement | null;\n if (searchInput) {\n searchInput.addEventListener('input', handleSearchInput);\n }\n\n // Pagination buttons\n const pageButtons = wrapperElement.querySelectorAll('[data-page-action]');\n for (const btn of pageButtons) {\n btn.addEventListener('click', handlePageClick);\n }\n\n // Row click\n if (options?.onRowClick) {\n const rows = wrapperElement.querySelectorAll('tbody tr');\n for (const row of rows) {\n row.addEventListener('click', handleRowClick);\n }\n }\n }\n\n function handleSortClick(e: Event): void {\n const btn = e.currentTarget as HTMLElement;\n const column = btn.getAttribute('data-sort-column');\n if (!column) return;\n\n const state = getState();\n const newSort = cycleSort(state.sort, column);\n\n updateState({ sort: newSort, page: 0 });\n\n // Announce sort change for screen readers\n if (newSort) {\n const dir = newSort.direction === 'asc' ? 'ascending' : 'descending';\n announce(`Sorted by ${column} ${dir}`);\n } else {\n announce('Sort cleared');\n }\n\n if (!isControlled) {\n rerender();\n }\n }\n\n function handleSearchInput(e: Event): void {\n const input = e.target as HTMLInputElement;\n const query = input.value;\n\n if (searchDebounceTimer !== null) {\n clearTimeout(searchDebounceTimer);\n }\n\n searchDebounceTimer = setTimeout(() => {\n searchDebounceTimer = null;\n updateState({ search: query, page: 0 });\n\n if (!isControlled) {\n rerender();\n // Announce search result count\n const rowCount = currentLayout?.rows?.length ?? 0;\n if (query) {\n announce(`${rowCount} result${rowCount !== 1 ? 's' : ''} found`);\n }\n }\n }, 200);\n }\n\n function handlePageClick(e: Event): void {\n const btn = e.currentTarget as HTMLElement;\n const action = btn.getAttribute('data-page-action');\n const state = getState();\n\n if (action === 'prev' && state.page > 0) {\n updateState({ page: state.page - 1 });\n } else if (action === 'next') {\n updateState({ page: state.page + 1 });\n }\n\n if (!isControlled) {\n rerender();\n }\n }\n\n function handleRowClick(e: Event): void {\n const tr = e.currentTarget as HTMLElement;\n const rowId = tr.getAttribute('data-row-id');\n if (!rowId || !currentLayout) return;\n\n const row = currentLayout.rows.find((r) => r.id === rowId);\n if (row) {\n options?.onRowClick?.(row.data);\n }\n }\n\n /**\n * Re-render the table, preserving search input focus across the DOM rebuild.\n */\n function rerender(): void {\n if (destroyed) return;\n\n // Capture current search input state before re-render\n const searchInput = wrapperElement?.querySelector(\n '.viz-table-search input',\n ) as HTMLInputElement | null;\n const hadFocus = searchInput && document.activeElement === searchInput;\n const selectionStart = searchInput?.selectionStart ?? 0;\n const selectionEnd = searchInput?.selectionEnd ?? 0;\n\n render();\n\n // Restore search focus after re-render\n if (hadFocus) {\n const newInput = wrapperElement?.querySelector(\n '.viz-table-search input',\n ) as HTMLInputElement | null;\n if (newInput) {\n newInput.focus();\n newInput.setSelectionRange(selectionStart, selectionEnd);\n }\n }\n }\n\n function update(newSpec: TableSpec): void {\n if (destroyed) return;\n currentSpec = newSpec;\n render();\n }\n\n function resize(): void {\n if (destroyed) return;\n render();\n }\n\n function doExport(format: 'csv'): string {\n if (format !== 'csv') {\n throw new Error(`Unsupported export format: ${format}`);\n }\n\n // Export all filtered/sorted data (not just current page)\n // Re-compile without pagination\n const state = getState();\n const darkMode = resolveDarkMode(options?.darkMode);\n const { width } = getContainerDimensions();\n\n const fullLayout = compileTable(currentSpec, {\n width,\n height: 600,\n theme: options?.theme,\n darkMode,\n sort: state.sort ?? undefined,\n search: state.search || undefined,\n // No page/pageSize: get all rows\n });\n\n const headers = fullLayout.columns.map((c) => c.label);\n const csvRows = [headers.map(csvEscape).join(',')];\n\n for (const row of fullLayout.rows) {\n const values = row.cells.map((cell) => csvEscape(cell.formattedValue));\n csvRows.push(values.join(','));\n }\n\n return csvRows.join('\\n');\n }\n\n function setState(partial: Partial<TableState>): void {\n if (destroyed) return;\n\n if (partial.sort !== undefined) internalState.sort = partial.sort;\n if (partial.search !== undefined) internalState.search = partial.search;\n if (partial.page !== undefined) internalState.page = partial.page;\n\n render();\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (cleanupKeyboard) {\n cleanupKeyboard();\n cleanupKeyboard = null;\n }\n if (searchDebounceTimer !== null) {\n clearTimeout(searchDebounceTimer);\n searchDebounceTimer = null;\n }\n if (resizeDebounceTimer !== null) {\n clearTimeout(resizeDebounceTimer);\n resizeDebounceTimer = null;\n }\n if (disconnectResize) {\n disconnectResize();\n disconnectResize = null;\n }\n if (wrapperElement?.parentNode) {\n wrapperElement.parentNode.removeChild(wrapperElement);\n wrapperElement = null;\n }\n container.classList.remove('viz-dark');\n }\n\n // Initial render\n render();\n\n // Set up responsive resize with breakpoint detection\n if (options?.responsive !== false) {\n disconnectResize = observeResize(container, () => {\n if (resizeDebounceTimer !== null) {\n clearTimeout(resizeDebounceTimer);\n }\n resizeDebounceTimer = setTimeout(() => {\n resizeDebounceTimer = null;\n // Update breakpoint class without full re-render when possible\n applyBreakpointClass();\n resize();\n }, 100);\n });\n }\n\n return {\n update,\n resize,\n export: doExport,\n getState,\n setState,\n destroy,\n };\n}\n","/**\n * Table renderer: produces semantic HTML from a TableLayout.\n *\n * renderTable() creates the full DOM structure: chrome, search bar,\n * scrollable table with sticky column support, pagination, and footer.\n * The returned element replaces or appends to the given container.\n */\n\nimport type { ResolvedColumn, TableLayout, TableRow } from '@opendata-ai/openchart-core';\nimport { renderCell } from './renderers/table-cells';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BRAND_URL = 'https://tryopendata.ai';\nconst BRAND_FONT_SIZE = 20;\n\n// ---------------------------------------------------------------------------\n// Chrome rendering\n// ---------------------------------------------------------------------------\n\n/** Create chrome (title/subtitle or source/footer) block. */\nfunction renderChromeBlock(\n layout: TableLayout,\n position: 'header' | 'footer',\n): HTMLDivElement | null {\n const chrome = layout.chrome;\n\n if (position === 'header') {\n if (!chrome.title && !chrome.subtitle) return null;\n\n const div = document.createElement('div');\n div.className = 'viz-chrome';\n\n if (chrome.title) {\n const h = document.createElement('div');\n h.className = 'viz-table-title';\n h.textContent = chrome.title.text;\n div.appendChild(h);\n }\n if (chrome.subtitle) {\n const sub = document.createElement('div');\n sub.className = 'viz-table-subtitle';\n sub.textContent = chrome.subtitle.text;\n div.appendChild(sub);\n }\n\n return div;\n }\n\n // Footer position\n if (!chrome.source && !chrome.footer) return null;\n\n const div = document.createElement('div');\n div.className = 'viz-chrome viz-chrome-footer';\n\n if (chrome.source) {\n const src = document.createElement('div');\n src.className = 'viz-table-source';\n src.textContent = chrome.source.text;\n div.appendChild(src);\n }\n if (chrome.footer) {\n const foot = document.createElement('div');\n foot.className = 'viz-table-footer-text';\n foot.textContent = chrome.footer.text;\n div.appendChild(foot);\n }\n\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Table head\n// ---------------------------------------------------------------------------\n\nfunction renderThead(\n columns: ResolvedColumn[],\n sort: TableLayout['sort'],\n): HTMLTableSectionElement {\n const thead = document.createElement('thead');\n const tr = document.createElement('tr');\n tr.setAttribute('role', 'row');\n\n for (const col of columns) {\n const th = document.createElement('th');\n th.setAttribute('scope', 'col');\n th.setAttribute('role', 'columnheader');\n th.style.textAlign = col.align;\n th.style.width = `${col.width}px`;\n\n // Sort state: use 'ascending'/'descending' per WAI-ARIA spec\n let ariaSortValue: string = 'none';\n if (sort && sort.column === col.key) {\n ariaSortValue = sort.direction === 'asc' ? 'ascending' : 'descending';\n }\n th.setAttribute('aria-sort', ariaSortValue);\n th.setAttribute('data-column', col.key);\n\n // Label\n const labelSpan = document.createTextNode(col.label);\n th.appendChild(labelSpan);\n\n // Sort button\n if (col.sortable) {\n const btn = document.createElement('button');\n btn.className = 'viz-table-sort-btn';\n btn.setAttribute('aria-label', `Sort by ${col.label}`);\n btn.setAttribute('data-sort-column', col.key);\n btn.type = 'button';\n th.appendChild(btn);\n }\n\n tr.appendChild(th);\n }\n\n thead.appendChild(tr);\n return thead;\n}\n\n// ---------------------------------------------------------------------------\n// Table body\n// ---------------------------------------------------------------------------\n\nfunction renderTbody(rows: TableRow[], columns: ResolvedColumn[]): HTMLTableSectionElement {\n const tbody = document.createElement('tbody');\n\n for (let r = 0; r < rows.length; r++) {\n const row = rows[r];\n const tr = document.createElement('tr');\n tr.setAttribute('role', 'row');\n tr.setAttribute('data-row-id', row.id);\n\n for (let c = 0; c < columns.length; c++) {\n const cell = row.cells[c];\n if (!cell) continue;\n\n const td = renderCell(cell);\n td.setAttribute('role', 'gridcell');\n td.style.textAlign = columns[c].align;\n tr.appendChild(td);\n }\n\n tbody.appendChild(tr);\n }\n\n return tbody;\n}\n\n// ---------------------------------------------------------------------------\n// Search bar\n// ---------------------------------------------------------------------------\n\nfunction renderSearchBar(layout: TableLayout): HTMLDivElement | null {\n if (!layout.search.enabled) return null;\n\n const div = document.createElement('div');\n div.className = 'viz-table-search';\n\n const input = document.createElement('input');\n input.type = 'search';\n input.placeholder = layout.search.placeholder;\n input.setAttribute('aria-label', 'Search table');\n input.value = layout.search.query;\n\n div.appendChild(input);\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Pagination\n// ---------------------------------------------------------------------------\n\nfunction renderPagination(layout: TableLayout): HTMLDivElement | null {\n if (!layout.pagination) return null;\n\n const { page, pageSize, totalRows, totalPages } = layout.pagination;\n\n const div = document.createElement('div');\n div.className = 'viz-table-pagination';\n\n const info = document.createElement('span');\n info.className = 'viz-table-pagination-info';\n\n if (totalRows === 0) {\n info.textContent = 'No results';\n } else {\n const start = page * pageSize + 1;\n const end = Math.min((page + 1) * pageSize, totalRows);\n info.textContent = `Showing ${start}-${end} of ${totalRows}`;\n }\n\n div.appendChild(info);\n\n const btnGroup = document.createElement('span');\n btnGroup.className = 'viz-table-pagination-btns';\n\n const prevBtn = document.createElement('button');\n prevBtn.setAttribute('aria-label', 'Previous page');\n prevBtn.setAttribute('data-page-action', 'prev');\n prevBtn.textContent = 'Prev';\n prevBtn.disabled = page <= 0;\n btnGroup.appendChild(prevBtn);\n\n const nextBtn = document.createElement('button');\n nextBtn.setAttribute('aria-label', 'Next page');\n nextBtn.setAttribute('data-page-action', 'next');\n nextBtn.textContent = 'Next';\n nextBtn.disabled = page >= totalPages - 1;\n btnGroup.appendChild(nextBtn);\n\n div.appendChild(btnGroup);\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Empty state\n// ---------------------------------------------------------------------------\n\nfunction renderEmptyState(message: string): HTMLDivElement {\n const div = document.createElement('div');\n div.className = 'viz-table-empty';\n div.setAttribute('aria-live', 'polite');\n div.textContent = message;\n return div;\n}\n\n// ---------------------------------------------------------------------------\n// Main render\n// ---------------------------------------------------------------------------\n\n/**\n * Render a TableLayout into a full DOM structure.\n *\n * @param layout - The compiled table layout.\n * @param container - The container element to render into.\n * @returns The wrapper element that was created.\n */\nexport function renderTable(layout: TableLayout, container: HTMLElement): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'viz-table-wrapper';\n\n // Apply theme colors as CSS custom properties so table CSS picks them up.\n // Without this, dark-background themes show invisible text since the\n // CSS defaults (--viz-text etc.) are light-mode values.\n const { theme, chrome } = layout;\n if (theme) {\n const s = wrapper.style;\n s.setProperty('--viz-bg', theme.colors.background);\n s.setProperty('--viz-text', theme.colors.text);\n s.setProperty('--viz-text-secondary', theme.colors.axis ?? theme.colors.text);\n s.setProperty('--viz-text-muted', theme.colors.axis ?? theme.colors.text);\n s.setProperty('--viz-gridline', theme.colors.gridline);\n s.setProperty('--viz-border', theme.colors.gridline);\n s.setProperty('--viz-font-family', theme.fonts.family);\n s.fontFamily = theme.fonts.family;\n }\n\n // Set computed chrome CSS custom properties so chrome elements pick up\n // theme-resolved values via CSS fallbacks (e.g. --viz-title-computed-size).\n {\n const s = wrapper.style;\n if (chrome.title) {\n s.setProperty('--viz-title-computed-size', `${chrome.title.style.fontSize}px`);\n s.setProperty('--viz-title-computed-weight', String(chrome.title.style.fontWeight));\n s.setProperty('--viz-title-computed-color', chrome.title.style.fill);\n }\n if (chrome.subtitle) {\n s.setProperty('--viz-subtitle-computed-size', `${chrome.subtitle.style.fontSize}px`);\n s.setProperty('--viz-subtitle-computed-weight', String(chrome.subtitle.style.fontWeight));\n s.setProperty('--viz-subtitle-computed-color', chrome.subtitle.style.fill);\n }\n if (chrome.source) {\n s.setProperty('--viz-source-computed-size', `${chrome.source.style.fontSize}px`);\n s.setProperty('--viz-source-computed-color', chrome.source.style.fill);\n }\n if (chrome.footer) {\n s.setProperty('--viz-footer-computed-size', `${chrome.footer.style.fontSize}px`);\n s.setProperty('--viz-footer-computed-color', chrome.footer.style.fill);\n }\n }\n\n // Apply class modifiers\n if (layout.compact) {\n wrapper.classList.add('viz-table--compact');\n }\n\n // Header chrome\n const headerChrome = renderChromeBlock(layout, 'header');\n if (headerChrome) {\n wrapper.appendChild(headerChrome);\n }\n\n // Search bar\n const searchBar = renderSearchBar(layout);\n if (searchBar) {\n wrapper.appendChild(searchBar);\n }\n\n // Handle empty data\n if (layout.rows.length === 0) {\n const message = layout.search.query ? 'No results found' : 'No data';\n wrapper.appendChild(renderEmptyState(message));\n } else {\n // Scroll container\n const scroll = document.createElement('div');\n scroll.className = 'viz-table-scroll';\n\n // Table\n const table = document.createElement('table');\n table.setAttribute('role', 'grid');\n table.setAttribute('aria-label', layout.a11y.caption);\n\n if (layout.stickyFirstColumn) {\n table.classList.add('viz-table--sticky');\n }\n\n // Caption (screen reader only)\n const caption = document.createElement('caption');\n caption.className = 'viz-sr-only';\n caption.textContent = layout.a11y.summary;\n table.appendChild(caption);\n\n // Thead\n table.appendChild(renderThead(layout.columns, layout.sort));\n\n // Tbody\n table.appendChild(renderTbody(layout.rows, layout.columns));\n\n scroll.appendChild(table);\n wrapper.appendChild(scroll);\n }\n\n // Pagination\n const pagination = renderPagination(layout);\n if (pagination) {\n wrapper.appendChild(pagination);\n }\n\n // Footer chrome\n const footerChrome = renderChromeBlock(layout, 'footer');\n if (footerChrome) {\n wrapper.appendChild(footerChrome);\n }\n\n // Live region for screen reader announcements (sort changes, search results)\n const liveRegion = document.createElement('div');\n liveRegion.className = 'viz-table-live-region viz-sr-only';\n liveRegion.setAttribute('aria-live', 'polite');\n liveRegion.setAttribute('aria-atomic', 'true');\n liveRegion.setAttribute('role', 'status');\n wrapper.appendChild(liveRegion);\n\n // Brand watermark\n const brandColor = theme ? theme.colors.axis : '#999999';\n const brand = document.createElement('div');\n brand.className = 'viz-table-ref';\n brand.style.cssText = 'text-align: right; padding: 4px 8px;';\n const brandLink = document.createElement('a');\n brandLink.href = BRAND_URL;\n brandLink.target = '_blank';\n brandLink.rel = 'noopener';\n brandLink.style.cssText = `font-size: ${BRAND_FONT_SIZE}px; font-weight: 600; color: ${brandColor}; opacity: 0.55; text-decoration: none; font-family: ${theme ? theme.fonts.family : 'sans-serif'};`;\n brandLink.textContent = 'OpenData';\n brand.appendChild(brandLink);\n wrapper.appendChild(brand);\n\n container.appendChild(wrapper);\n return wrapper;\n}\n"],"mappings":"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","names":["workerUrl","renderChrome","renderLegend","BRAND_MIN_WIDTH","resolveDarkMode","SVG_NS","cleanup","BRAND_URL","BRAND_FONT_SIZE","div","resolveDarkMode","csvEscape"]}
|