@0xbuff/throughline 0.0.0-stage → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["PAD"],"sources":["../../src/layout/edges.ts","../../src/layout/ranks.ts","../../src/layout/value.ts","../../src/layout.ts","../../src/react/caption.tsx","../../src/seek.ts","../../src/react/controls.tsx","../../src/react/inspector.tsx","../../src/react/node-card.tsx","../../src/react/scene.tsx","../../src/react/use-player.ts","../../src/sonify.ts","../../src/react/use-sound.ts","../../src/react/player.tsx"],"sourcesContent":["import type { Relation } from \"../model.js\";\nimport type { Box, Edge, Pt } from \"./types.js\";\n\nfunction bypass(a: Box, b: Box, blockers: Box[], horizontal: boolean): Edge {\n\tif (horizontal) {\n\t\tconst y =\n\t\t\tMath.max(a.y + a.h, b.y + b.h, ...blockers.map((box) => box.y + box.h)) +\n\t\t\t16;\n\t\tconst from: Pt = [a.x + a.w / 2, a.y + a.h];\n\t\tconst to: Pt = [b.x + b.w / 2, b.y + b.h];\n\t\treturn {\n\t\t\tfrom,\n\t\t\tto,\n\t\t\td: `M${from[0]} ${from[1]}V${y}H${to[0]}V${to[1]}`,\n\t\t\tlabel: [(from[0] + to[0]) / 2, y],\n\t\t\tangle: -Math.PI / 2,\n\t\t};\n\t}\n\tconst x = Math.min(a.x, b.x, ...blockers.map((box) => box.x)) - 10;\n\tconst from: Pt = [a.x, a.y + a.h / 2];\n\tconst to: Pt = [b.x, b.y + b.h / 2];\n\treturn {\n\t\tfrom,\n\t\tto,\n\t\td: `M${from[0]} ${from[1]}H${x}V${to[1]}H${to[0]}`,\n\t\tlabel: [x, (from[1] + to[1]) / 2],\n\t\tangle: 0,\n\t};\n}\n\n/** Attach to the facing sides, preserving direction in horizontal and stacked scenes. */\nexport function routeEdges(\n\trelations: Relation[],\n\tnodes: Record<string, Box>,\n): Record<string, Edge> {\n\tconst edges: Record<string, Edge> = {};\n\tfor (const relation of relations) {\n\t\tconst a = nodes[relation.from];\n\t\tconst b = nodes[relation.to];\n\t\tif (!a || !b) continue;\n\t\tconst dx = b.x + b.w / 2 - a.x - a.w / 2;\n\t\tconst dy = b.y + b.h / 2 - a.y - a.h / 2;\n\t\tconst horizontal = Math.abs(dx) > Math.abs(dy);\n\t\tconst blockers = Object.values(nodes).filter(\n\t\t\t(box) =>\n\t\t\t\tbox !== a &&\n\t\t\t\tbox !== b &&\n\t\t\t\t(horizontal\n\t\t\t\t\t? box.x > Math.min(a.x, b.x) &&\n\t\t\t\t\t\tbox.x < Math.max(a.x, b.x) &&\n\t\t\t\t\t\tbox.y < Math.max(a.y + a.h, b.y + b.h) &&\n\t\t\t\t\t\tbox.y + box.h > Math.min(a.y, b.y)\n\t\t\t\t\t: box.y > Math.min(a.y, b.y) &&\n\t\t\t\t\t\tbox.y < Math.max(a.y, b.y) &&\n\t\t\t\t\t\tbox.x < Math.max(a.x + a.w, b.x + b.w) &&\n\t\t\t\t\t\tbox.x + box.w > Math.min(a.x, b.x)),\n\t\t);\n\t\tif (blockers.length) {\n\t\t\tedges[relation.id] = bypass(a, b, blockers, horizontal);\n\t\t\tcontinue;\n\t\t}\n\t\tlet from: Pt, to: Pt, c1: Pt, c2: Pt;\n\t\tif (relation.from === relation.to) {\n\t\t\tfrom = [a.x + a.w * 0.65, a.y + a.h];\n\t\t\tto = [a.x + a.w * 0.35, a.y + a.h];\n\t\t\tc1 = [from[0] + 12, from[1] + 24];\n\t\t\tc2 = [to[0] - 12, to[1] + 24];\n\t\t} else if (Math.abs(dx) > Math.abs(dy)) {\n\t\t\tconst direction = Math.sign(dx);\n\t\t\tfrom = [direction > 0 ? a.x + a.w : a.x, a.y + a.h / 2];\n\t\t\tto = [direction > 0 ? b.x : b.x + b.w, b.y + b.h / 2];\n\t\t\tconst bend = Math.max(14, Math.abs(to[0] - from[0]) / 2);\n\t\t\tc1 = [from[0] + direction * bend, from[1]];\n\t\t\tc2 = [to[0] - direction * bend, to[1]];\n\t\t} else {\n\t\t\tconst direction = Math.sign(dy) || 1;\n\t\t\tfrom = [a.x + a.w / 2, direction > 0 ? a.y + a.h : a.y];\n\t\t\tto = [b.x + b.w / 2, direction > 0 ? b.y : b.y + b.h];\n\t\t\tconst bend = Math.max(14, Math.abs(to[1] - from[1]) / 2);\n\t\t\tc1 = [from[0], from[1] + direction * bend];\n\t\t\tc2 = [to[0], to[1] - direction * bend];\n\t\t}\n\t\tedges[relation.id] = {\n\t\t\td: `M${from[0]} ${from[1]}C${c1[0]} ${c1[1]} ${c2[0]} ${c2[1]} ${to[0]} ${to[1]}`,\n\t\t\tfrom,\n\t\t\tto,\n\t\t\tlabel: [\n\t\t\t\t(from[0] + 3 * c1[0] + 3 * c2[0] + to[0]) / 8,\n\t\t\t\t(from[1] + 3 * c1[1] + 3 * c2[1] + to[1]) / 8,\n\t\t\t],\n\t\t\tangle: Math.atan2(to[1] - c2[1], to[0] - c2[0]),\n\t\t};\n\t}\n\treturn edges;\n}\n","import type { Entity, Relation } from \"../model.js\";\n\nexport function assignRanks(\n\tentities: Entity[],\n\trelations: Relation[],\n): Map<string, number> {\n\tconst out = new Map<string, string[]>(\n\t\tentities.map((entity) => [entity.id, []]),\n\t);\n\tconst indegree = new Map<string, number>(\n\t\tentities.map((entity) => [entity.id, 0]),\n\t);\n\tconst connected = new Set<string>();\n\tfor (const relation of relations) {\n\t\tout.get(relation.from)?.push(relation.to);\n\t\tindegree.set(relation.to, indegree.get(relation.to)! + 1);\n\t\tconnected.add(relation.from);\n\t\tconnected.add(relation.to);\n\t}\n\tconst colour = new Map<string, 1 | 2>();\n\tconst back = new Set<string>();\n\tconst visit = (id: string) => {\n\t\tcolour.set(id, 1);\n\t\tfor (const to of out.get(id)!) {\n\t\t\tconst state = colour.get(to);\n\t\t\tif (state === 1) back.add(`${id}>${to}`);\n\t\t\telse if (!state) visit(to);\n\t\t}\n\t\tcolour.set(id, 2);\n\t};\n\tconst order = [\n\t\t...entities.filter((entity) => indegree.get(entity.id) === 0),\n\t\t...entities,\n\t];\n\tfor (const entity of order) if (!colour.has(entity.id)) visit(entity.id);\n\n\tconst preds = new Map<string, string[]>(\n\t\tentities.map((entity) => [entity.id, []]),\n\t);\n\tfor (const relation of relations)\n\t\tif (\n\t\t\trelation.from !== relation.to &&\n\t\t\t!back.has(`${relation.from}>${relation.to}`)\n\t\t)\n\t\t\tpreds.get(relation.to)?.push(relation.from);\n\tconst ranks = new Map<string, number>();\n\tconst rank = (id: string): number => {\n\t\tconst known = ranks.get(id);\n\t\tif (known !== undefined) return known;\n\t\tranks.set(id, 0);\n\t\tconst value = Math.max(-1, ...(preds.get(id) ?? []).map(rank)) + 1;\n\t\tranks.set(id, value);\n\t\treturn value;\n\t};\n\tfor (const entity of entities) if (connected.has(entity.id)) rank(entity.id);\n\tconst perLane = new Map<string, number>();\n\tfor (const entity of entities) {\n\t\tif (connected.has(entity.id)) continue;\n\t\tconst lane = entity.lane ?? \"State\";\n\t\tconst next = perLane.get(lane) ?? 0;\n\t\tperLane.set(lane, next + 1);\n\t\tranks.set(entity.id, next);\n\t}\n\treturn ranks;\n}\n","import type { Entity, Value } from \"../model.js\";\nimport type { Path } from \"../path.js\";\nimport { effectiveView, format } from \"./measure.js\";\n\nexport type ValueCell = {\n\tpath: Path;\n\ttext: string;\n\tlabel?: string;\n\tx: number;\n\ty: number;\n\tw: number;\n\th: number;\n};\nexport type ValueLayout = {\n\tw: number;\n\th: number;\n\tcells: ValueCell[];\n\tnote?: string;\n};\n\nconst PAD = 10;\nconst TOP = 32;\nconst gap = 4;\nconst limit = (n: number, min: number, max: number): number =>\n\tMath.max(min, Math.min(max, n));\n\n/** Keep the focused position in a bounded window without hiding truncation. */\nfunction windowStart(length: number, count: number, focus: number): number {\n\treturn limit(focus - count + 1, 0, Math.max(0, length - count));\n}\n\nfunction focusIndex(key: Path[number] | undefined, fallback = 0): number {\n\treturn typeof key === \"number\" && Number.isFinite(key) ? key : fallback;\n}\n\nfunction grid(\n\titems: { text: string; path: Path; label?: string }[],\n\tcolumns: number,\n\tcellW: number,\n\tnote?: string,\n): ValueLayout {\n\tconst labeled = items.some((item) => item.label !== undefined);\n\tconst stride = labeled ? 46 : 32;\n\treturn {\n\t\tw: columns * (cellW + gap) - gap + PAD * 2,\n\t\th:\n\t\t\tTOP +\n\t\t\tMath.max(1, Math.ceil(items.length / columns)) * stride +\n\t\t\t(note ? 24 : 6),\n\t\tnote,\n\t\tcells: items.map((item, i) => ({\n\t\t\t...item,\n\t\t\tx: PAD + (i % columns) * (cellW + gap),\n\t\t\ty: TOP + Math.floor(i / columns) * stride,\n\t\t\tw: cellW,\n\t\t\th: 28,\n\t\t})),\n\t};\n}\n\n/** Shared by measurement and SVG rendering: the two cannot disagree on clipping. */\nexport function presentValue(\n\tentity: Entity,\n\tmaxWidth: number,\n\tfocus: Path[] = [],\n): ValueLayout {\n\tconst { value } = entity;\n\tconst view = effectiveView(entity.view, value);\n\tconst width = Math.max(80, maxWidth);\n\tlet result: ValueLayout;\n\tif (view === \"matrix\") {\n\t\tconst rows = value as Value[][];\n\t\tconst colCount = rows.reduce(\n\t\t\t(maximum, row) => Math.max(maximum, row.length),\n\t\t\t0,\n\t\t);\n\t\tconst cols = Math.max(\n\t\t\t1,\n\t\t\tMath.min(6, colCount, Math.floor((width - 16) / 38)),\n\t\t);\n\t\tconst firstRow = windowStart(rows.length, 6, focusIndex(focus[0]?.[0]));\n\t\tconst firstCol = windowStart(colCount, cols, focusIndex(focus[0]?.[1]));\n\t\tconst visibleRows = rows.slice(firstRow, firstRow + 6);\n\t\tconst items = visibleRows.flatMap((row, r) =>\n\t\t\tArray.from({ length: cols }, (_, c) => ({\n\t\t\t\ttext: row[firstCol + c] === undefined ? \"·\" : format(row[firstCol + c]),\n\t\t\t\tpath: [firstRow + r, firstCol + c],\n\t\t\t})),\n\t\t);\n\t\tconst note =\n\t\t\trows.length > 6 || colCount > cols\n\t\t\t\t? `Rows ${firstRow + 1}–${firstRow + visibleRows.length}/${rows.length} · cols ${firstCol + 1}–${firstCol + cols}/${colCount}`\n\t\t\t\t: undefined;\n\t\tresult = grid(\n\t\t\titems,\n\t\t\tcols,\n\t\t\tMath.min(56, (width - 20 - (cols - 1) * gap) / cols),\n\t\t\tnote,\n\t\t);\n\t} else if (view === \"array\" || view === \"stack\" || view === \"bits\") {\n\t\tconst bits =\n\t\t\tview === \"bits\"\n\t\t\t\t? (value as number)\n\t\t\t\t\t\t.toString(2)\n\t\t\t\t\t\t.padStart(\n\t\t\t\t\t\t\tMath.max(\n\t\t\t\t\t\t\t\t8,\n\t\t\t\t\t\t\t\tMath.ceil((value as number).toString(2).length / 4) * 4,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\"0\",\n\t\t\t\t\t\t)\n\t\t\t\t: \"\";\n\t\tconst items: Value[] = bits ? [...bits] : (value as Value[]);\n\t\tconst count = view === \"stack\" ? 6 : view === \"bits\" ? items.length : 12;\n\t\tconst focused = focusIndex(\n\t\t\tfocus[0]?.[0],\n\t\t\tview === \"stack\" ? items.length - 1 : 0,\n\t\t);\n\t\tconst start = windowStart(items.length, count, focused);\n\t\tconst shown = items.slice(start, start + count);\n\t\tconst cellW =\n\t\t\tview === \"bits\"\n\t\t\t\t? 28\n\t\t\t\t: Math.min(\n\t\t\t\t\t\t86,\n\t\t\t\t\t\tMath.max(\n\t\t\t\t\t\t\t34,\n\t\t\t\t\t\t\t...shown.map((item) => format(item).length * 7.5 + 14),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\tconst cols =\n\t\t\tview === \"stack\"\n\t\t\t\t? 1\n\t\t\t\t: Math.max(\n\t\t\t\t\t\t1,\n\t\t\t\t\t\tMath.min(\n\t\t\t\t\t\t\tshown.length || 1,\n\t\t\t\t\t\t\tMath.floor((width - 16) / (cellW + gap)),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\tconst note =\n\t\t\titems.length > count\n\t\t\t\t? `Items ${start + 1}–${start + shown.length} of ${items.length}`\n\t\t\t\t: undefined;\n\t\tresult = grid(\n\t\t\tshown.map((item, i) => ({\n\t\t\t\ttext: format(item),\n\t\t\t\tpath: [bits ? bits.length - 1 - i : start + i],\n\t\t\t\tlabel: String(bits ? bits.length - 1 - i : start + i),\n\t\t\t})),\n\t\t\tcols,\n\t\t\tMath.min(cellW, width - 20),\n\t\t\tnote,\n\t\t);\n\t} else if (view === \"record\") {\n\t\tconst entries = Object.entries(value as Record<string, Value>);\n\t\tconst focused = entries.findIndex(([key]) => key === focus[0]?.[0]);\n\t\tconst start = windowStart(entries.length, 6, focused);\n\t\tconst shown = entries.slice(start, start + 6);\n\t\tconst note =\n\t\t\tentries.length > 6\n\t\t\t\t? `Fields ${start + 1}–${start + shown.length} of ${entries.length}`\n\t\t\t\t: undefined;\n\t\tconst cellW = Math.min(\n\t\t\twidth - 20,\n\t\t\tMath.max(\n\t\t\t\t100,\n\t\t\t\t...shown.map(\n\t\t\t\t\t([key, item]) => (key.length + format(item).length + 2) * 7.5 + 16,\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t\tresult = grid(\n\t\t\tshown.map(([key, item]) => ({\n\t\t\t\ttext: `${key}: ${format(item)}`,\n\t\t\t\tpath: [key],\n\t\t\t})),\n\t\t\t1,\n\t\t\tcellW,\n\t\t\tnote,\n\t\t);\n\t} else {\n\t\tconst text = format(value);\n\t\tresult = grid(\n\t\t\t[{ text, path: [] }],\n\t\t\t1,\n\t\t\tMath.min(width - 20, Math.max(60, text.length * 8 + 16)),\n\t\t);\n\t}\n\tresult.w = Math.min(\n\t\twidth,\n\t\tMath.max(result.w, Math.min(240, entity.label.length * 7 + 20)),\n\t);\n\tif (!result.cells.length)\n\t\tresult.cells.push({\n\t\t\tpath: [],\n\t\t\ttext: \"empty\",\n\t\t\tx: PAD,\n\t\t\ty: TOP,\n\t\t\tw: result.w - 20,\n\t\t\th: 28,\n\t\t});\n\treturn result;\n}\n","import { routeEdges } from \"./layout/edges.js\";\nimport { assignRanks } from \"./layout/ranks.js\";\nimport type { Box, Layout } from \"./layout/types.js\";\nimport { presentValue } from \"./layout/value.js\";\nimport type { Entity, GroupLayout, Relation, Trace } from \"./model.js\";\nimport { parseTarget } from \"./path.js\";\n\nexport { effectiveView, format } from \"./layout/measure.js\";\nexport type { Box, Edge, Layout, Pt } from \"./layout/types.js\";\n\nconst PAD = 12;\nconst GAP = 28;\nconst laneOf = (entity: Entity): string => entity.lane ?? \"State\";\n\ntype Group = Layout[\"groups\"][number] & { nodes: Record<string, Box> };\n\nfunction arrangeGroup(\n\tname: string,\n\tmembers: Entity[],\n\trelations: Relation[],\n\tsizes: Map<string, Box>,\n\tarrangement: GroupLayout,\n\tmaxWidth: number,\n): Group {\n\tconst ids = new Set(members.map((entity) => entity.id));\n\tconst ranks = assignRanks(\n\t\tmembers,\n\t\trelations.filter(\n\t\t\t(relation) => ids.has(relation.from) && ids.has(relation.to),\n\t\t),\n\t);\n\tconst rows: Entity[][] = [];\n\tfor (const member of members) {\n\t\tconst rank =\n\t\t\tarrangement === \"tree\"\n\t\t\t\t? (ranks.get(member.id) ?? 0)\n\t\t\t\t: arrangement === \"column\"\n\t\t\t\t\t? rows.length\n\t\t\t\t\t: 0;\n\t\trows[rank] ??= [];\n\t\trows[rank].push(member);\n\t}\n\tconst nodes: Record<string, Box> = {};\n\tlet y = 32,\n\t\tw = Math.min(maxWidth, Math.max(100, name.length * 7 + 24));\n\tfor (const row of rows) {\n\t\tif (!row) continue;\n\t\tlet x = PAD,\n\t\t\trowHeight = 0;\n\t\tfor (const entity of row) {\n\t\t\tconst size = sizes.get(entity.id)!;\n\t\t\tif (x > PAD && x + size.w + PAD > maxWidth) {\n\t\t\t\ty += rowHeight + GAP;\n\t\t\t\tx = PAD;\n\t\t\t\trowHeight = 0;\n\t\t\t}\n\t\t\tnodes[entity.id] = { ...size, x, y };\n\t\t\trowHeight = Math.max(rowHeight, size.h);\n\t\t\tw = Math.max(w, x + size.w + PAD);\n\t\t\tx += size.w + GAP;\n\t\t}\n\t\ty += rowHeight + GAP;\n\t}\n\t// Center each tree layer after its widest layer has set the panel width.\n\tif (arrangement === \"tree\") {\n\t\tconst levels = new Set(Object.values(nodes).map((box) => box.y));\n\t\tfor (const level of levels) {\n\t\t\tconst boxes = Object.values(nodes).filter((box) => box.y === level);\n\t\t\tconst right = Math.max(...boxes.map((box) => box.x + box.w));\n\t\t\tconst offset = (w - PAD - right) / 2;\n\t\t\tfor (const box of boxes) box.x += offset;\n\t\t}\n\t}\n\treturn { name, x: 0, y: 0, w, h: Math.max(64, y), nodes };\n}\n\n/** History-sized boxes and responsive group packing; no per-step layout or shrinking text. */\nexport function composeTrace(trace: Trace, availableWidth = 720): Layout {\n\tconst width = Math.max(\n\t\t160,\n\t\tNumber.isFinite(availableWidth) ? Math.floor(availableWidth) : 720,\n\t);\n\tconst entities = trace.frames[0]?.state.entities ?? [];\n\tconst relations = trace.frames[0]?.state.relations ?? [];\n\tconst maxWidth = width - PAD * 2;\n\tconst nodeWidth = Math.min(260, maxWidth - PAD * 2);\n\tconst sizes = new Map<string, Box>();\n\tfor (const frame of trace.frames) {\n\t\tconst targets = frame.state.active.map(parseTarget);\n\t\tfor (const entity of frame.state.entities) {\n\t\t\tconst focus = targets\n\t\t\t\t.filter((target) => target?.id === entity.id)\n\t\t\t\t.map((target) => target!.path);\n\t\t\tconst value = presentValue(entity, nodeWidth, focus);\n\t\t\tconst old = sizes.get(entity.id);\n\t\t\tsizes.set(entity.id, {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: Math.max(old?.w ?? 0, value.w),\n\t\t\t\th: Math.max(old?.h ?? 0, value.h),\n\t\t\t});\n\t\t}\n\t}\n\tconst names = [...new Set(entities.map(laneOf))];\n\tconst groups = names.map((name) =>\n\t\tarrangeGroup(\n\t\t\tname,\n\t\t\tentities.filter((entity) => laneOf(entity) === name),\n\t\t\trelations,\n\t\t\tsizes,\n\t\t\ttrace.groups?.find((group) => group.name === name)?.layout ?? \"row\",\n\t\t\tmaxWidth,\n\t\t),\n\t);\n\tconst nodes: Record<string, Box> = {};\n\tlet x = PAD,\n\t\ty = PAD,\n\t\trowHeight = 0;\n\tfor (const group of groups) {\n\t\tif (x > PAD && x + group.w + PAD > width) {\n\t\t\tx = PAD;\n\t\t\ty += rowHeight + GAP;\n\t\t\trowHeight = 0;\n\t\t}\n\t\tgroup.x = x;\n\t\tgroup.y = y;\n\t\tfor (const [id, box] of Object.entries(group.nodes))\n\t\t\tnodes[id] = { ...box, x: box.x + x, y: box.y + y };\n\t\tx += group.w + GAP;\n\t\trowHeight = Math.max(rowHeight, group.h);\n\t}\n\tfor (const rowY of new Set(groups.map((group) => group.y))) {\n\t\tconst row = groups.filter((group) => group.y === rowY);\n\t\tconst right = Math.max(...row.map((group) => group.x + group.w));\n\t\tconst offset = Math.max(0, (width - PAD - right) / 2);\n\t\tfor (const group of row) {\n\t\t\tgroup.x += offset;\n\t\t\tfor (const id of Object.keys(group.nodes)) nodes[id].x += offset;\n\t\t}\n\t}\n\tconst height = Math.max(100, y + rowHeight + PAD);\n\treturn {\n\t\twidth,\n\t\theight,\n\t\tviewBox: `0 0 ${width} ${height}`,\n\t\tgroups: groups.map(({ nodes: _, ...group }) => group),\n\t\tnodes,\n\t\tedges: routeEdges(relations, nodes),\n\t};\n}\n","import type * as React from \"react\";\nimport { format } from \"../layout/measure.js\";\nimport type { Frame } from \"../model.js\";\n\ninterface PlayerCaptionProps {\n\tframe: Frame;\n\tid: string;\n}\nexport function PlayerCaption({\n\tframe,\n\tid,\n}: PlayerCaptionProps): React.JSX.Element {\n\treturn (\n\t\t<div className=\"thl-caption\" id={id}>\n\t\t\t<span className=\"thl-step\">\n\t\t\t\t{String(frame.index + 1).padStart(2, \"0\")}\n\t\t\t</span>\n\t\t\t<div>\n\t\t\t\t<p>\n\t\t\t\t\t<strong>{frame.label}</strong>\n\t\t\t\t\t{frame.state.notes.length > 0 && (\n\t\t\t\t\t\t<span>{frame.state.notes.join(\" \")}</span>\n\t\t\t\t\t)}\n\t\t\t\t</p>\n\t\t\t\t{frame.state.transfers.length > 0 && (\n\t\t\t\t\t<ul className=\"thl-transfers\" aria-label=\"Transfers\">\n\t\t\t\t\t\t{frame.state.transfers.map((transfer, index) => {\n\t\t\t\t\t\t\tconst link = frame.state.relations.find(\n\t\t\t\t\t\t\t\t(relation) => relation.id === transfer.relation,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: transfers are stateless entries; identical sends may repeat within one frame.\n\t\t\t\t\t\t\t\t<li key={`${transfer.relation}:${index}`}>\n\t\t\t\t\t\t\t\t\t{link ? `${link.from} → ${link.to}` : transfer.relation}\n\t\t\t\t\t\t\t\t\t{transfer.value !== undefined && (\n\t\t\t\t\t\t\t\t\t\t<code>{format(transfer.value)}</code>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</ul>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * Player seek model: pure state transitions, no DOM, no timers. The React\n * binding (usePlayerInTimeline) drives these from an interval; tests drive\n * them directly.\n */\n\nexport const SPEEDS = [0.5, 1, 2] as const;\nexport type Speed = (typeof SPEEDS)[number];\n\nexport type SeekState = {\n\t/** Current frame index, 0..max. */\n\tstep: number;\n\tmax: number;\n\tplaying: boolean;\n\tspeed: Speed;\n};\n\nexport type SeekAction =\n\t| { type: \"reset\"; max: number }\n\t| { type: \"tick\" }\n\t| { type: \"toggle\" }\n\t| { type: \"pause\" }\n\t| { type: \"step\"; to: number }\n\t| { type: \"speed\"; to: Speed };\n\nexport const initialSeek = (max: number): SeekState => ({\n\tstep: 0,\n\tmax,\n\tplaying: false,\n\tspeed: 1,\n});\n\nconst clamp = (n: number, max: number) => Math.max(0, Math.min(max, n));\n\nexport function seekReducer(state: SeekState, action: SeekAction): SeekState {\n\tswitch (action.type) {\n\t\tcase \"reset\":\n\t\t\treturn initialSeek(action.max);\n\t\tcase \"tick\":\n\t\t\tif (!state.playing) return state;\n\t\t\tif (state.step >= state.max) return { ...state, playing: false };\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\tstep: state.step + 1,\n\t\t\t\tplaying: state.step + 1 < state.max,\n\t\t\t};\n\t\tcase \"toggle\":\n\t\t\tif (state.playing) return { ...state, playing: false };\n\t\t\tif (state.step >= state.max) return { ...state, step: 0, playing: true };\n\t\t\treturn { ...state, playing: true };\n\t\tcase \"pause\":\n\t\t\treturn { ...state, playing: false };\n\t\tcase \"step\":\n\t\t\treturn { ...state, step: clamp(action.to, state.max), playing: false };\n\t\tcase \"speed\":\n\t\t\treturn { ...state, speed: action.to };\n\t}\n}\n\n/** Milliseconds per frame at the given speed; `base` is the 1x cadence. */\nexport const stepDelayMs = (base: number, speed: Speed): number =>\n\tMath.round(base / speed);\n","import type * as React from \"react\";\nimport {\n\ttype SeekAction,\n\ttype SeekState,\n\tSPEEDS,\n\ttype Speed,\n} from \"../seek.js\";\n\ninterface PlayerControlsProps {\n\tstate: SeekState;\n\tdispatch: React.Dispatch<SeekAction>;\n\tsound?: { enabled: boolean; toggle: () => void };\n}\nexport function PlayerControls({\n\tstate,\n\tdispatch,\n\tsound,\n}: PlayerControlsProps): React.JSX.Element {\n\treturn (\n\t\t<div className=\"thl-controls\">\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\taria-label=\"Step back\"\n\t\t\t\tdisabled={state.step === 0}\n\t\t\t\tonClick={() => dispatch({ type: \"step\", to: state.step - 1 })}\n\t\t\t>\n\t\t\t\t←\n\t\t\t</button>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"thl-play\"\n\t\t\t\taria-label={state.playing ? \"Pause\" : \"Play\"}\n\t\t\t\tdisabled={state.max < 1}\n\t\t\t\tonClick={() => dispatch({ type: \"toggle\" })}\n\t\t\t>\n\t\t\t\t{state.playing\n\t\t\t\t\t? \"Pause\"\n\t\t\t\t\t: state.step === state.max && state.max > 0\n\t\t\t\t\t\t? \"Replay\"\n\t\t\t\t\t\t: \"Play\"}\n\t\t\t</button>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\taria-label=\"Step forward\"\n\t\t\t\tdisabled={state.step === state.max}\n\t\t\t\tonClick={() => dispatch({ type: \"step\", to: state.step + 1 })}\n\t\t\t>\n\t\t\t\t→\n\t\t\t</button>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tclassName=\"thl-timeline\"\n\t\t\t\taria-label=\"Timeline\"\n\t\t\t\taria-valuetext={`Step ${state.step + 1} of ${state.max + 1}`}\n\t\t\t\tmin={0}\n\t\t\t\tmax={state.max}\n\t\t\t\tvalue={state.step}\n\t\t\t\tdisabled={state.max < 1}\n\t\t\t\tonChange={(event) =>\n\t\t\t\t\tdispatch({ type: \"step\", to: Number(event.currentTarget.value) })\n\t\t\t\t}\n\t\t\t/>\n\t\t\t<span className=\"thl-count\">\n\t\t\t\t{state.step + 1} / {state.max + 1}\n\t\t\t</span>\n\t\t\t<select\n\t\t\t\taria-label=\"Playback speed\"\n\t\t\t\tvalue={state.speed}\n\t\t\t\tonChange={(event) =>\n\t\t\t\t\tdispatch({\n\t\t\t\t\t\ttype: \"speed\",\n\t\t\t\t\t\tto: Number(event.currentTarget.value) as Speed,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t>\n\t\t\t\t{SPEEDS.map((speed) => (\n\t\t\t\t\t<option key={speed} value={speed}>\n\t\t\t\t\t\t{speed}×\n\t\t\t\t\t</option>\n\t\t\t\t))}\n\t\t\t</select>\n\t\t\t{sound && (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\taria-label=\"Sonify trace\"\n\t\t\t\t\taria-pressed={sound.enabled}\n\t\t\t\t\tonClick={sound.toggle}\n\t\t\t\t>\n\t\t\t\t\tSound\n\t\t\t\t</button>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","import type * as React from \"react\";\nimport type { Frame } from \"../model.js\";\nimport { visibleScene } from \"../scene.js\";\n\ninterface StateInspectorProps {\n\tframe: Frame;\n}\nexport function StateInspector({\n\tframe,\n}: StateInspectorProps): React.JSX.Element {\n\treturn (\n\t\t<details className=\"thl-inspector\">\n\t\t\t<summary>Inspect state</summary>\n\t\t\t<dl>\n\t\t\t\t{visibleScene(frame.state).entities.map((entity) => (\n\t\t\t\t\t<div key={entity.id}>\n\t\t\t\t\t\t<dt>\n\t\t\t\t\t\t\t{entity.label} <span>({entity.id})</span>\n\t\t\t\t\t\t</dt>\n\t\t\t\t\t\t<dd>\n\t\t\t\t\t\t\t<pre>{JSON.stringify(entity.value)}</pre>\n\t\t\t\t\t\t</dd>\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t</dl>\n\t\t</details>\n\t);\n}\n","import { cn } from \"cn\";\nimport type * as React from \"react\";\nimport { effectiveView, format } from \"../layout/measure.js\";\nimport type { Box } from \"../layout/types.js\";\nimport { presentValue } from \"../layout/value.js\";\nimport type { Entity, Value } from \"../model.js\";\nimport { getAt, overlaps, type Path } from \"../path.js\";\n\nexport const truncate = (text: string, length: number): string =>\n\ttext.length > length ? `${text.slice(0, Math.max(1, length - 1))}…` : text;\n\ninterface NodeCardProps {\n\tentity: Entity;\n\tprevious?: Value;\n\tbox: Box;\n\tfocus: Path[];\n\tchanged: Path[];\n}\n\nexport function NodeCard({\n\tentity,\n\tprevious,\n\tbox,\n\tfocus,\n\tchanged,\n}: NodeCardProps): React.JSX.Element {\n\tconst value = presentValue(entity, box.w, focus);\n\treturn (\n\t\t<g transform={`translate(${box.x},${box.y})`} data-node={entity.id}>\n\t\t\t<title>{`${entity.label}: ${format(entity.value)}${focus.length ? \" (focus)\" : \"\"}`}</title>\n\t\t\t<rect\n\t\t\t\twidth={box.w}\n\t\t\t\theight={box.h}\n\t\t\t\trx={8}\n\t\t\t\tclassName={cn(\"thl-node\", { \"thl-focused\": focus.length > 0 })}\n\t\t\t/>\n\t\t\t<text x={10} y={19} className=\"thl-node-label\">\n\t\t\t\t{truncate(entity.label, Math.floor((box.w - 20) / 7))}\n\t\t\t</text>\n\t\t\t{value.cells.map((cell) => {\n\t\t\t\tconst isChanged =\n\t\t\t\t\tchanged.some((path) => overlaps(path, cell.path)) &&\n\t\t\t\t\t(effectiveView(entity.view, entity.value) === \"bits\"\n\t\t\t\t\t\t? typeof previous !== \"number\" ||\n\t\t\t\t\t\t\tString(Math.floor(previous / 2 ** Number(cell.path[0])) % 2) !==\n\t\t\t\t\t\t\t\tcell.text\n\t\t\t\t\t\t: JSON.stringify(getAt(previous, cell.path)) !==\n\t\t\t\t\t\t\tJSON.stringify(getAt(entity.value, cell.path)));\n\t\t\t\tconst isFocused = focus.some((path) => overlaps(path, cell.path));\n\t\t\t\treturn (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={JSON.stringify(cell.path)}\n\t\t\t\t\t\ttransform={`translate(${cell.x},${cell.y})`}\n\t\t\t\t\t\tclassName={cn(\"thl-cell\", {\n\t\t\t\t\t\t\t\"thl-changed\": isChanged,\n\t\t\t\t\t\t\t\"thl-focused\": isFocused,\n\t\t\t\t\t\t})}\n\t\t\t\t\t>\n\t\t\t\t\t\t<title>{cell.text}</title>\n\t\t\t\t\t\t<rect width={cell.w} height={cell.h} rx={4} />\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx={cell.w / 2}\n\t\t\t\t\t\t\ty={cell.h / 2}\n\t\t\t\t\t\t\tdominantBaseline=\"central\"\n\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{truncate(cell.text, Math.floor((cell.w - 10) / 7.8))}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t\t{cell.label !== undefined && (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={cell.w / 2}\n\t\t\t\t\t\t\t\ty={cell.h + 13}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tclassName=\"thl-index\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{cell.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</g>\n\t\t\t\t);\n\t\t\t})}\n\t\t\t{value.note && (\n\t\t\t\t<text x={10} y={box.h - 10} className=\"thl-window\">\n\t\t\t\t\t{truncate(value.note, Math.floor((box.w - 20) / 6.2))}\n\t\t\t\t\t<title>{value.note}</title>\n\t\t\t\t</text>\n\t\t\t)}\n\t\t</g>\n\t);\n}\n","import { cn } from \"cn\";\nimport type * as React from \"react\";\nimport type { Edge, Layout } from \"../layout/types.js\";\nimport type { Frame, Relation } from \"../model.js\";\nimport { parseTarget } from \"../path.js\";\nimport { visibleScene } from \"../scene.js\";\nimport { NodeCard, truncate } from \"./node-card.js\";\n\ninterface SceneLinkProps {\n\trelation: Relation;\n\tedge: Edge;\n\tframe: Frame;\n}\nfunction SceneLink({\n\trelation,\n\tedge,\n\tframe,\n}: SceneLinkProps): React.JSX.Element {\n\tconst transfer = frame.state.transfers.find(\n\t\t(item) => item.relation === relation.id,\n\t);\n\tconst label = relation.label;\n\treturn (\n\t\t<g className={cn(\"thl-link\", { \"thl-transferred\": !!transfer })}>\n\t\t\t<title>{`${relation.from} → ${relation.to}${relation.label ? `: ${relation.label}` : \"\"}`}</title>\n\t\t\t<path d={edge.d} className=\"thl-edge\" />\n\t\t\t{transfer && (\n\t\t\t\t<path\n\t\t\t\t\tkey={frame.index}\n\t\t\t\t\td={edge.d}\n\t\t\t\t\tpathLength={1}\n\t\t\t\t\tclassName=\"thl-flow\"\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<polygon\n\t\t\t\tpoints=\"0,0 -7,-3.5 -7,3.5\"\n\t\t\t\ttransform={`translate(${edge.to[0]},${edge.to[1]}) rotate(${(edge.angle * 180) / Math.PI})`}\n\t\t\t/>\n\t\t\t{label && (\n\t\t\t\t<text\n\t\t\t\t\tx={edge.label[0]}\n\t\t\t\t\ty={edge.label[1] - 8}\n\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\tclassName=\"thl-edge-label\"\n\t\t\t\t>\n\t\t\t\t\t{truncate(label, 20)}\n\t\t\t\t\t<title>{label}</title>\n\t\t\t\t</text>\n\t\t\t)}\n\t\t</g>\n\t);\n}\n\ninterface TraceSceneProps {\n\tframe: Frame;\n\tprevious?: Frame;\n\tlayout: Layout;\n}\nexport function TraceScene({\n\tframe,\n\tprevious,\n\tlayout,\n}: TraceSceneProps): React.JSX.Element {\n\tconst scene = visibleScene(frame.state);\n\tconst before = new Map(\n\t\tprevious?.state.entities.map((entity) => [entity.id, entity.value]),\n\t);\n\tconst targets = frame.state.active.map(parseTarget);\n\tconst changes = frame.state.changed.map(parseTarget);\n\treturn (\n\t\t<>\n\t\t\t{layout.groups\n\t\t\t\t.filter((group) =>\n\t\t\t\t\tscene.entities.some(\n\t\t\t\t\t\t(entity) => (entity.lane ?? \"State\") === group.name,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.map((group) => (\n\t\t\t\t\t<g key={group.name}>\n\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\tx={group.x}\n\t\t\t\t\t\t\ty={group.y}\n\t\t\t\t\t\t\twidth={group.w}\n\t\t\t\t\t\t\theight={group.h}\n\t\t\t\t\t\t\trx={12}\n\t\t\t\t\t\t\tclassName=\"thl-group\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<text x={group.x + 12} y={group.y + 20} className=\"thl-group-label\">\n\t\t\t\t\t\t\t{truncate(group.name, Math.floor((group.w - 24) / 7))}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t</g>\n\t\t\t\t))}\n\t\t\t{scene.relations.map((relation) => (\n\t\t\t\t<SceneLink\n\t\t\t\t\tkey={relation.id}\n\t\t\t\t\trelation={relation}\n\t\t\t\t\tedge={layout.edges[relation.id]}\n\t\t\t\t\tframe={frame}\n\t\t\t\t/>\n\t\t\t))}\n\t\t\t{scene.entities.map((entity) => (\n\t\t\t\t<NodeCard\n\t\t\t\t\tkey={entity.id}\n\t\t\t\t\tentity={entity}\n\t\t\t\t\tprevious={before.get(entity.id)}\n\t\t\t\t\tbox={layout.nodes[entity.id]}\n\t\t\t\t\tfocus={targets.flatMap((target) =>\n\t\t\t\t\t\ttarget?.id === entity.id ? [target.path] : [],\n\t\t\t\t\t)}\n\t\t\t\t\tchanged={changes.flatMap((target) =>\n\t\t\t\t\t\ttarget?.id === entity.id ? [target.path] : [],\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t))}\n\t\t\t{!scene.entities.length && (\n\t\t\t\t<text\n\t\t\t\t\tx={layout.width / 2}\n\t\t\t\t\ty={50}\n\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\tclassName=\"thl-empty\"\n\t\t\t\t>\n\t\t\t\t\tNo visible state at this step.\n\t\t\t\t</text>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","import type * as React from \"react\";\nimport { useEffect, useReducer, useState } from \"react\";\nimport type { Trace } from \"../model.js\";\nimport {\n\tinitialSeek,\n\ttype SeekAction,\n\ttype SeekState,\n\tseekReducer,\n\tstepDelayMs,\n} from \"../seek.js\";\n\nexport function usePlayer(trace: Trace): {\n\tstate: SeekState;\n\tdispatch: React.Dispatch<SeekAction>;\n\tonKeyDown: React.KeyboardEventHandler;\n} {\n\tconst [identity, setIdentity] = useState(trace);\n\tconst [state, dispatch] = useReducer(\n\t\tseekReducer,\n\t\tMath.max(0, trace.frames.length - 1),\n\t\tinitialSeek,\n\t);\n\t// Reset during render, before any child can read a stale frame index.\n\tif (identity !== trace) {\n\t\tsetIdentity(trace);\n\t\tdispatch({ type: \"reset\", max: Math.max(0, trace.frames.length - 1) });\n\t}\n\tuseEffect(() => {\n\t\tif (!state.playing) return;\n\t\tconst timer = setInterval(\n\t\t\t() => dispatch({ type: \"tick\" }),\n\t\t\tstepDelayMs(1500, state.speed),\n\t\t);\n\t\treturn () => clearInterval(timer);\n\t}, [state.playing, state.speed]);\n\tconst onKeyDown: React.KeyboardEventHandler = (event) => {\n\t\tif (event.target !== event.currentTarget) return;\n\t\tif ([\"ArrowRight\", \"ArrowLeft\", \"Home\", \"End\"].includes(event.key)) {\n\t\t\tevent.preventDefault();\n\t\t\tconst to =\n\t\t\t\tevent.key === \"Home\"\n\t\t\t\t\t? 0\n\t\t\t\t\t: event.key === \"End\"\n\t\t\t\t\t\t? state.max\n\t\t\t\t\t\t: state.step + (event.key === \"ArrowRight\" ? 1 : -1);\n\t\t\tdispatch({ type: \"step\", to });\n\t\t} else if (event.key === \" \" && state.max > 0) {\n\t\t\tevent.preventDefault();\n\t\t\tdispatch({ type: \"toggle\" });\n\t\t}\n\t};\n\treturn { state, dispatch, onKeyDown };\n}\n\nexport function useSceneWidth(\n\tref: React.RefObject<HTMLDivElement | null>,\n): number {\n\tconst [width, setWidth] = useState(720);\n\tuseEffect(() => {\n\t\tconst element = ref.current;\n\t\tif (!element) return;\n\t\tconst observer = new ResizeObserver(([entry]) => {\n\t\t\tif (entry.contentRect.width > 0)\n\t\t\t\tsetWidth(Math.floor(entry.contentRect.width));\n\t\t});\n\t\tobserver.observe(element);\n\t\treturn () => observer.disconnect();\n\t}, [ref]);\n\treturn width;\n}\n","/** Optional stage feedback. Visual focus is not evidence of cost or contention. */\nexport const SONIFY_MIN_HZ = 261.63;\nexport const SONIFY_MAX_HZ = 523.25;\nexport const SONIFY_STANDARD_MS = 180;\nexport type SonificationEvent = {\n\tstep: number;\n\ttotal: number;\n\tfrequency: number;\n\tdurationMs: number;\n};\nexport type SonificationHandler = (event: SonificationEvent) => void;\nexport type SonificationOptions = {\n\tenabled?: boolean;\n\tonTone?: SonificationHandler;\n};\n\nexport function frequencyForStep(step: number, total: number): number {\n\tif (\n\t\t!Number.isFinite(step) ||\n\t\t!Number.isFinite(total) ||\n\t\ttotal <= 1 ||\n\t\tstep <= 0\n\t)\n\t\treturn SONIFY_MIN_HZ;\n\tif (step >= total - 1) return SONIFY_MAX_HZ;\n\treturn (\n\t\tMath.round(\n\t\t\tSONIFY_MIN_HZ *\n\t\t\t\t(SONIFY_MAX_HZ / SONIFY_MIN_HZ) ** (step / (total - 1)) *\n\t\t\t\t100,\n\t\t) / 100\n\t);\n}\n\nexport function sonificationEvent(\n\tstep: number,\n\ttotal: number,\n): SonificationEvent {\n\treturn {\n\t\tstep,\n\t\ttotal,\n\t\tfrequency: frequencyForStep(step, total),\n\t\tdurationMs: SONIFY_STANDARD_MS,\n\t};\n}\n","import { useEffect, useRef } from \"react\";\nimport { type SonificationOptions, sonificationEvent } from \"../sonify.js\";\n\n/** One short voice at a time; seeking or disabling sound stops the previous voice. */\nexport function useSound(\n\tstep: number,\n\ttotal: number,\n\tenabled: boolean,\n\toptions?: SonificationOptions,\n): void {\n\tconst context = useRef<AudioContext | null>(null);\n\tconst handler = options?.onTone;\n\tuseEffect(\n\t\t() => () => {\n\t\t\tconst audio = context.current;\n\t\t\tcontext.current = null;\n\t\t\tvoid audio?.close().catch(() => {});\n\t\t},\n\t\t[],\n\t);\n\tuseEffect(() => {\n\t\tif (!enabled || total === 0) return;\n\t\tconst event = sonificationEvent(step, total);\n\t\ttry {\n\t\t\tif (handler) {\n\t\t\t\thandler(event);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (typeof AudioContext === \"undefined\") return;\n\t\t\tcontext.current ??= new AudioContext();\n\t\t\tconst audio = context.current;\n\t\t\tvoid audio.resume().catch(() => {});\n\t\t\tconst oscillator = audio.createOscillator();\n\t\t\tconst gain = audio.createGain();\n\t\t\tconst now = audio.currentTime;\n\t\t\toscillator.frequency.value = event.frequency;\n\t\t\tgain.gain.setValueAtTime(0.0001, now);\n\t\t\tgain.gain.exponentialRampToValueAtTime(0.1, now + 0.02);\n\t\t\tgain.gain.exponentialRampToValueAtTime(\n\t\t\t\t0.0001,\n\t\t\t\tnow + event.durationMs / 1000,\n\t\t\t);\n\t\t\toscillator.connect(gain);\n\t\t\tgain.connect(audio.destination);\n\t\t\toscillator.onended = () => {\n\t\t\t\toscillator.disconnect();\n\t\t\t\tgain.disconnect();\n\t\t\t};\n\t\t\toscillator.start();\n\t\t\toscillator.stop(now + event.durationMs / 1000 + 0.02);\n\t\t\treturn () => {\n\t\t\t\toscillator.stop();\n\t\t\t};\n\t\t} catch {\n\t\t\t// Audio is optional; unavailable hardware or a consumer callback cannot stop playback.\n\t\t}\n\t}, [step, total, enabled, handler]);\n}\n","import { cn } from \"cn\";\nimport type * as React from \"react\";\nimport { useId, useMemo, useRef, useState } from \"react\";\nimport { describeFrame } from \"../describe.js\";\nimport { composeTrace } from \"../layout.js\";\nimport type { Trace } from \"../model.js\";\nimport type { SonificationOptions } from \"../sonify.js\";\nimport { PlayerCaption } from \"./caption.js\";\nimport { PlayerControls } from \"./controls.js\";\nimport { StateInspector } from \"./inspector.js\";\nimport { TraceScene } from \"./scene.js\";\nimport { usePlayer, useSceneWidth } from \"./use-player.js\";\nimport { useSound } from \"./use-sound.js\";\n\nexport interface ThroughlinePlayerProps {\n\ttrace: Trace;\n\tclassName?: string;\n\ttheme?: \"light\" | \"dark\" | \"auto\";\n\t/** Omit to remove audio controls entirely. */\n\tsonification?: SonificationOptions;\n}\n\nexport function ThroughlinePlayer({\n\ttrace,\n\tclassName,\n\ttheme = \"auto\",\n\tsonification,\n}: ThroughlinePlayerProps): React.JSX.Element {\n\tconst ref = useRef<HTMLDivElement>(null);\n\tconst width = useSceneWidth(ref);\n\tconst { state, dispatch, onKeyDown } = usePlayer(trace);\n\tconst [sound, setSound] = useState(sonification?.enabled ?? false);\n\tconst layout = useMemo(() => composeTrace(trace, width), [trace, width]);\n\tconst captionId = useId();\n\tconst frame = trace.frames[state.step] ?? trace.frames[0];\n\tuseSound(\n\t\tstate.step,\n\t\ttrace.frames.length,\n\t\t!!sonification && sound,\n\t\tsonification,\n\t);\n\tconst summary = frame\n\t\t? describeFrame(frame, trace.frames.length)\n\t\t: \"No steps to display.\";\n\treturn (\n\t\t// biome-ignore lint/a11y/useSemanticElements: one focusable group owns scene stepping without changing native control keys.\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={cn(\"thl\", className)}\n\t\t\tdata-theme={theme}\n\t\t\trole=\"group\"\n\t\t\taria-label=\"Visualization player\"\n\t\t\t// biome-ignore lint/a11y/noNoninteractiveTabindex: the group exposes documented Arrow/Home/End/Space shortcuts.\n\t\t\ttabIndex={0}\n\t\t\tonKeyDown={onKeyDown}\n\t\t>\n\t\t\t<p role=\"status\" className=\"thl-sr-only\">\n\t\t\t\t{summary}\n\t\t\t</p>\n\t\t\t{frame ? (\n\t\t\t\t<>\n\t\t\t\t\t<svg\n\t\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\t\theight={layout.height}\n\t\t\t\t\t\tviewBox={layout.viewBox}\n\t\t\t\t\t\trole=\"img\"\n\t\t\t\t\t\taria-label={summary}\n\t\t\t\t\t\taria-describedby={captionId}\n\t\t\t\t\t\tclassName=\"thl-scene\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<TraceScene\n\t\t\t\t\t\t\tframe={frame}\n\t\t\t\t\t\t\tprevious={trace.frames[state.step - 1]}\n\t\t\t\t\t\t\tlayout={layout}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t\t<PlayerCaption frame={frame} id={captionId} />\n\t\t\t\t\t<PlayerControls\n\t\t\t\t\t\tstate={state}\n\t\t\t\t\t\tdispatch={dispatch}\n\t\t\t\t\t\tsound={\n\t\t\t\t\t\t\tsonification\n\t\t\t\t\t\t\t\t? { enabled: sound, toggle: () => setSound(!sound) }\n\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t<StateInspector frame={frame} />\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<p className=\"thl-empty\">No steps to display.</p>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;AAGA,SAAS,OAAO,GAAQ,GAAQ,UAAiB,YAA2B;CAC3E,IAAI,YAAY;EACf,MAAM,IACL,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,SAAS,KAAK,QAAQ,IAAI,IAAI,IAAI,CAAC,CAAC,IACtE;EACD,MAAM,OAAW,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC;EAC1C,MAAM,KAAS,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC;EACxC,OAAO;GACN;GACA;GACA,GAAG,IAAI,KAAK,GAAG,GAAG,KAAK,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG;GAC9C,OAAO,EAAE,KAAK,KAAK,GAAG,MAAM,GAAG,CAAC;GAChC,OAAO,CAAC,KAAK,KAAK;EACnB;CACD;CACA,MAAM,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI;CAChE,MAAM,OAAW,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;CACpC,MAAM,KAAS,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;CAClC,OAAO;EACN;EACA;EACA,GAAG,IAAI,KAAK,GAAG,GAAG,KAAK,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG;EAC9C,OAAO,CAAC,IAAI,KAAK,KAAK,GAAG,MAAM,CAAC;EAChC,OAAO;CACR;AACD;;AAGA,SAAgB,WACf,WACA,OACuB;CACvB,MAAM,QAA8B,CAAC;CACrC,KAAK,MAAM,YAAY,WAAW;EACjC,MAAM,IAAI,MAAM,SAAS;EACzB,MAAM,IAAI,MAAM,SAAS;EACzB,IAAI,CAAC,KAAK,CAAC,GAAG;EACd,MAAM,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI;EACvC,MAAM,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI;EACvC,MAAM,aAAa,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE;EAC7C,MAAM,WAAW,OAAO,OAAO,KAAK,CAAC,CAAC,QACpC,QACA,QAAQ,KACR,QAAQ,MACP,aACE,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KAC1B,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KACzB,IAAI,IAAI,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,KACrC,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,IAChC,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KAC1B,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,KACzB,IAAI,IAAI,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,KACrC,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,CAAC,EACrC;EACA,IAAI,SAAS,QAAQ;GACpB,MAAM,SAAS,MAAM,OAAO,GAAG,GAAG,UAAU,UAAU;GACtD;EACD;EACA,IAAI,MAAU,IAAQ,IAAQ;EAC9B,IAAI,SAAS,SAAS,SAAS,IAAI;GAClC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,KAAM,EAAE,IAAI,EAAE,CAAC;GACnC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,KAAM,EAAE,IAAI,EAAE,CAAC;GACjC,KAAK,CAAC,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE;GAChC,KAAK,CAAC,GAAG,KAAK,IAAI,GAAG,KAAK,EAAE;EAC7B,OAAO,IAAI,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,GAAG;GACvC,MAAM,YAAY,KAAK,KAAK,EAAE;GAC9B,OAAO,CAAC,YAAY,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;GACtD,KAAK,CAAC,YAAY,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;GACpD,MAAM,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,EAAE,IAAI,CAAC;GACvD,KAAK,CAAC,KAAK,KAAK,YAAY,MAAM,KAAK,EAAE;GACzC,KAAK,CAAC,GAAG,KAAK,YAAY,MAAM,GAAG,EAAE;EACtC,OAAO;GACN,MAAM,YAAY,KAAK,KAAK,EAAE,KAAK;GACnC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,YAAY,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;GACtD,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,YAAY,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;GACpD,MAAM,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,EAAE,IAAI,CAAC;GACvD,KAAK,CAAC,KAAK,IAAI,KAAK,KAAK,YAAY,IAAI;GACzC,KAAK,CAAC,GAAG,IAAI,GAAG,KAAK,YAAY,IAAI;EACtC;EACA,MAAM,SAAS,MAAM;GACpB,GAAG,IAAI,KAAK,GAAG,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;GAC7E;GACA;GACA,OAAO,EACL,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG,MAAM,IAC3C,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG,MAAM,CAC7C;GACA,OAAO,KAAK,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,EAAE;EAC/C;CACD;CACA,OAAO;AACR;;AC5FA,SAAgB,YACf,UACA,WACsB;CACtB,MAAM,MAAM,IAAI,IACf,SAAS,KAAK,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CACzC;CACA,MAAM,WAAW,IAAI,IACpB,SAAS,KAAK,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC,CACxC;CACA,MAAM,4BAAY,IAAI,IAAY;CAClC,KAAK,MAAM,YAAY,WAAW;EACjC,IAAI,IAAI,SAAS,IAAI,CAAC,EAAE,KAAK,SAAS,EAAE;EACxC,SAAS,IAAI,SAAS,IAAI,SAAS,IAAI,SAAS,EAAE,IAAK,CAAC;EACxD,UAAU,IAAI,SAAS,IAAI;EAC3B,UAAU,IAAI,SAAS,EAAE;CAC1B;CACA,MAAM,yBAAS,IAAI,IAAmB;CACtC,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,SAAS,OAAe;EAC7B,OAAO,IAAI,IAAI,CAAC;EAChB,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,GAAI;GAC9B,MAAM,QAAQ,OAAO,IAAI,EAAE;GAC3B,IAAI,UAAU,GAAG,KAAK,IAAI,GAAG,GAAG,GAAG,IAAI;QAClC,IAAI,CAAC,OAAO,MAAM,EAAE;EAC1B;EACA,OAAO,IAAI,IAAI,CAAC;CACjB;CACA,MAAM,QAAQ,CACb,GAAG,SAAS,QAAQ,WAAW,SAAS,IAAI,OAAO,EAAE,MAAM,CAAC,GAC5D,GAAG,QACJ;CACA,KAAK,MAAM,UAAU,OAAO,IAAI,CAAC,OAAO,IAAI,OAAO,EAAE,GAAG,MAAM,OAAO,EAAE;CAEvE,MAAM,QAAQ,IAAI,IACjB,SAAS,KAAK,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CACzC;CACA,KAAK,MAAM,YAAY,WACtB,IACC,SAAS,SAAS,SAAS,MAC3B,CAAC,KAAK,IAAI,GAAG,SAAS,KAAK,GAAG,SAAS,IAAI,GAE3C,MAAM,IAAI,SAAS,EAAE,CAAC,EAAE,KAAK,SAAS,IAAI;CAC5C,MAAM,wBAAQ,IAAI,IAAoB;CACtC,MAAM,QAAQ,OAAuB;EACpC,MAAM,QAAQ,MAAM,IAAI,EAAE;EAC1B,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,MAAM,IAAI,IAAI,CAAC;EACf,MAAM,QAAQ,KAAK,IAAI,IAAI,IAAI,MAAM,IAAI,EAAE,KAAK,CAAC,EAAA,CAAG,IAAI,IAAI,CAAC,IAAI;EACjE,MAAM,IAAI,IAAI,KAAK;EACnB,OAAO;CACR;CACA,KAAK,MAAM,UAAU,UAAU,IAAI,UAAU,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE;CAC3E,MAAM,0BAAU,IAAI,IAAoB;CACxC,KAAK,MAAM,UAAU,UAAU;EAC9B,IAAI,UAAU,IAAI,OAAO,EAAE,GAAG;EAC9B,MAAM,OAAO,OAAO,QAAQ;EAC5B,MAAM,OAAO,QAAQ,IAAI,IAAI,KAAK;EAClC,QAAQ,IAAI,MAAM,OAAO,CAAC;EAC1B,MAAM,IAAI,OAAO,IAAI,IAAI;CAC1B;CACA,OAAO;AACR;;AC5CA,MAAMA,QAAM;AACZ,MAAM,MAAM;AACZ,MAAM,MAAM;AACZ,MAAM,SAAS,GAAW,KAAa,QACtC,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;;AAG/B,SAAS,YAAY,QAAgB,OAAe,OAAuB;CAC1E,OAAO,MAAM,QAAQ,QAAQ,GAAG,GAAG,KAAK,IAAI,GAAG,SAAS,KAAK,CAAC;AAC/D;AAEA,SAAS,WAAW,KAA+B,WAAW,GAAW;CACxE,OAAO,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,IAAI,MAAM;AAChE;AAEA,SAAS,KACR,OACA,SACA,OACA,MACc;CAEd,MAAM,SADU,MAAM,MAAM,SAAS,KAAK,UAAU,KAAA,CAC/B,IAAI,KAAK;CAC9B,OAAO;EACN,GAAG,WAAW,QAAQ,OAAO,MAAM;EACnC,GACC,MACA,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,OAAO,CAAC,IAAI,UAChD,OAAO,KAAK;EACd;EACA,OAAO,MAAM,KAAK,MAAM,OAAO;GAC9B,GAAG;GACH,GAAGA,QAAO,IAAI,WAAY,QAAQ;GAClC,GAAG,MAAM,KAAK,MAAM,IAAI,OAAO,IAAI;GACnC,GAAG;GACH,GAAG;EACJ,EAAE;CACH;AACD;;AAGA,SAAgB,aACf,QACA,UACA,QAAgB,CAAC,GACH;CACd,MAAM,EAAE,UAAU;CAClB,MAAM,OAAO,cAAc,OAAO,MAAM,KAAK;CAC7C,MAAM,QAAQ,KAAK,IAAI,IAAI,QAAQ;CACnC,IAAI;CACJ,IAAI,SAAS,UAAU;EACtB,MAAM,OAAO;EACb,MAAM,WAAW,KAAK,QACpB,SAAS,QAAQ,KAAK,IAAI,SAAS,IAAI,MAAM,GAC9C,CACD;EACA,MAAM,OAAO,KAAK,IACjB,GACA,KAAK,IAAI,GAAG,UAAU,KAAK,OAAO,QAAQ,MAAM,EAAE,CAAC,CACpD;EACA,MAAM,WAAW,YAAY,KAAK,QAAQ,GAAG,WAAW,MAAM,EAAE,GAAG,EAAE,CAAC;EACtE,MAAM,WAAW,YAAY,UAAU,MAAM,WAAW,MAAM,EAAE,GAAG,EAAE,CAAC;EACtE,MAAM,cAAc,KAAK,MAAM,UAAU,WAAW,CAAC;EACrD,MAAM,QAAQ,YAAY,SAAS,KAAK,MACvC,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO;GACvC,MAAM,IAAI,WAAW,OAAO,KAAA,IAAY,MAAM,OAAO,IAAI,WAAW,EAAE;GACtE,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC;EAClC,EAAE,CACH;EACA,MAAM,OACL,KAAK,SAAS,KAAK,WAAW,OAC3B,QAAQ,WAAW,EAAE,GAAG,WAAW,YAAY,OAAO,GAAG,KAAK,OAAO,UAAU,WAAW,EAAE,GAAG,WAAW,KAAK,GAAG,aAClH,KAAA;EACJ,SAAS,KACR,OACA,MACA,KAAK,IAAI,KAAK,QAAQ,MAAM,OAAO,KAAK,OAAO,IAAI,GACnD,IACD;CACD,OAAO,IAAI,SAAS,WAAW,SAAS,WAAW,SAAS,QAAQ;EACnE,MAAM,OACL,SAAS,SACL,MACA,SAAS,CAAC,CAAC,CACX,SACA,KAAK,IACJ,GACA,KAAK,KAAM,MAAiB,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CACvD,GACA,GACD,IACA;EACJ,MAAM,QAAiB,OAAO,CAAC,GAAG,IAAI,IAAK;EAC3C,MAAM,QAAQ,SAAS,UAAU,IAAI,SAAS,SAAS,MAAM,SAAS;EACtE,MAAM,UAAU,WACf,MAAM,EAAE,GAAG,IACX,SAAS,UAAU,MAAM,SAAS,IAAI,CACvC;EACA,MAAM,QAAQ,YAAY,MAAM,QAAQ,OAAO,OAAO;EACtD,MAAM,QAAQ,MAAM,MAAM,OAAO,QAAQ,KAAK;EAC9C,MAAM,QACL,SAAS,SACN,KACA,KAAK,IACL,IACA,KAAK,IACJ,IACA,GAAG,MAAM,KAAK,SAAS,OAAO,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,CACtD,CACD;EACH,MAAM,OACL,SAAS,UACN,IACA,KAAK,IACL,GACA,KAAK,IACJ,MAAM,UAAU,GAChB,KAAK,OAAO,QAAQ,OAAO,QAAQ,IAAI,CACxC,CACD;EACH,MAAM,OACL,MAAM,SAAS,QACZ,SAAS,QAAQ,EAAE,GAAG,QAAQ,MAAM,OAAO,MAAM,MAAM,WACvD,KAAA;EACJ,SAAS,KACR,MAAM,KAAK,MAAM,OAAO;GACvB,MAAM,OAAO,IAAI;GACjB,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,QAAQ,CAAC;GAC7C,OAAO,OAAO,OAAO,KAAK,SAAS,IAAI,IAAI,QAAQ,CAAC;EACrD,EAAE,GACF,MACA,KAAK,IAAI,OAAO,QAAQ,EAAE,GAC1B,IACD;CACD,OAAO,IAAI,SAAS,UAAU;EAC7B,MAAM,UAAU,OAAO,QAAQ,KAA8B;EAC7D,MAAM,UAAU,QAAQ,WAAW,CAAC,SAAS,QAAQ,MAAM,EAAE,GAAG,EAAE;EAClE,MAAM,QAAQ,YAAY,QAAQ,QAAQ,GAAG,OAAO;EACpD,MAAM,QAAQ,QAAQ,MAAM,OAAO,QAAQ,CAAC;EAC5C,MAAM,OACL,QAAQ,SAAS,IACd,UAAU,QAAQ,EAAE,GAAG,QAAQ,MAAM,OAAO,MAAM,QAAQ,WAC1D,KAAA;EACJ,MAAM,QAAQ,KAAK,IAClB,QAAQ,IACR,KAAK,IACJ,KACA,GAAG,MAAM,KACP,CAAC,KAAK,WAAW,IAAI,SAAS,OAAO,IAAI,CAAC,CAAC,SAAS,KAAK,MAAM,EACjE,CACD,CACD;EACA,SAAS,KACR,MAAM,KAAK,CAAC,KAAK,WAAW;GAC3B,MAAM,GAAG,IAAI,IAAI,OAAO,IAAI;GAC5B,MAAM,CAAC,GAAG;EACX,EAAE,GACF,GACA,OACA,IACD;CACD,OAAO;EACN,MAAM,OAAO,OAAO,KAAK;EACzB,SAAS,KACR,CAAC;GAAE;GAAM,MAAM,CAAC;EAAE,CAAC,GACnB,GACA,KAAK,IAAI,QAAQ,IAAI,KAAK,IAAI,IAAI,KAAK,SAAS,IAAI,EAAE,CAAC,CACxD;CACD;CACA,OAAO,IAAI,KAAK,IACf,OACA,KAAK,IAAI,OAAO,GAAG,KAAK,IAAI,KAAK,OAAO,MAAM,SAAS,IAAI,EAAE,CAAC,CAC/D;CACA,IAAI,CAAC,OAAO,MAAM,QACjB,OAAO,MAAM,KAAK;EACjB,MAAM,CAAC;EACP,MAAM;EACN,GAAGA;EACH,GAAG;EACH,GAAG,OAAO,IAAI;EACd,GAAG;CACJ,CAAC;CACF,OAAO;AACR;;ACjMA,MAAM,MAAM;AACZ,MAAM,MAAM;AACZ,MAAM,UAAU,WAA2B,OAAO,QAAQ;AAI1D,SAAS,aACR,MACA,SACA,WACA,OACA,aACA,UACQ;CACR,MAAM,MAAM,IAAI,IAAI,QAAQ,KAAK,WAAW,OAAO,EAAE,CAAC;CACtD,MAAM,QAAQ,YACb,SACA,UAAU,QACR,aAAa,IAAI,IAAI,SAAS,IAAI,KAAK,IAAI,IAAI,SAAS,EAAE,CAC5D,CACD;CACA,MAAM,OAAmB,CAAC;CAC1B,KAAK,MAAM,UAAU,SAAS;EAC7B,MAAM,OACL,gBAAgB,SACZ,MAAM,IAAI,OAAO,EAAE,KAAK,IACzB,gBAAgB,WACf,KAAK,SACL;EACL,KAAK,UAAU,CAAC;EAChB,KAAK,KAAK,CAAC,KAAK,MAAM;CACvB;CACA,MAAM,QAA6B,CAAC;CACpC,IAAI,IAAI,IACP,IAAI,KAAK,IAAI,UAAU,KAAK,IAAI,KAAK,KAAK,SAAS,IAAI,EAAE,CAAC;CAC3D,KAAK,MAAM,OAAO,MAAM;EACvB,IAAI,CAAC,KAAK;EACV,IAAI,IAAI,KACP,YAAY;EACb,KAAK,MAAM,UAAU,KAAK;GACzB,MAAM,OAAO,MAAM,IAAI,OAAO,EAAE;GAChC,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,MAAM,UAAU;IAC3C,KAAK,YAAY;IACjB,IAAI;IACJ,YAAY;GACb;GACA,MAAM,OAAO,MAAM;IAAE,GAAG;IAAM;IAAG;GAAE;GACnC,YAAY,KAAK,IAAI,WAAW,KAAK,CAAC;GACtC,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG;GAChC,KAAK,KAAK,IAAI;EACf;EACA,KAAK,YAAY;CAClB;CAEA,IAAI,gBAAgB,QAAQ;EAC3B,MAAM,SAAS,IAAI,IAAI,OAAO,OAAO,KAAK,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC;EAC/D,KAAK,MAAM,SAAS,QAAQ;GAC3B,MAAM,QAAQ,OAAO,OAAO,KAAK,CAAC,CAAC,QAAQ,QAAQ,IAAI,MAAM,KAAK;GAClE,MAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,KAAK,QAAQ,IAAI,IAAI,IAAI,CAAC,CAAC;GAC3D,MAAM,UAAU,IAAI,MAAM,SAAS;GACnC,KAAK,MAAM,OAAO,OAAO,IAAI,KAAK;EACnC;CACD;CACA,OAAO;EAAE;EAAM,GAAG;EAAG,GAAG;EAAG;EAAG,GAAG,KAAK,IAAI,IAAI,CAAC;EAAG;CAAM;AACzD;;AAGA,SAAgB,aAAa,OAAc,iBAAiB,KAAa;CACxE,MAAM,QAAQ,KAAK,IAClB,KACA,OAAO,SAAS,cAAc,IAAI,KAAK,MAAM,cAAc,IAAI,GAChE;CACA,MAAM,WAAW,MAAM,OAAO,EAAE,EAAE,MAAM,YAAY,CAAC;CACrD,MAAM,YAAY,MAAM,OAAO,EAAE,EAAE,MAAM,aAAa,CAAC;CACvD,MAAM,WAAW,QAAQ;CACzB,MAAM,YAAY,KAAK,IAAI,KAAK,WAAW,EAAO;CAClD,MAAM,wBAAQ,IAAI,IAAiB;CACnC,KAAK,MAAM,SAAS,MAAM,QAAQ;EACjC,MAAM,UAAU,MAAM,MAAM,OAAO,IAAI,WAAW;EAClD,KAAK,MAAM,UAAU,MAAM,MAAM,UAAU;GAI1C,MAAM,QAAQ,aAAa,QAAQ,WAHrB,QACZ,QAAQ,WAAW,QAAQ,OAAO,OAAO,EAAE,CAAC,CAC5C,KAAK,WAAW,OAAQ,IACoB,CAAK;GACnD,MAAM,MAAM,MAAM,IAAI,OAAO,EAAE;GAC/B,MAAM,IAAI,OAAO,IAAI;IACpB,GAAG;IACH,GAAG;IACH,GAAG,KAAK,IAAI,KAAK,KAAK,GAAG,MAAM,CAAC;IAChC,GAAG,KAAK,IAAI,KAAK,KAAK,GAAG,MAAM,CAAC;GACjC,CAAC;EACF;CACD;CAEA,MAAM,SAAS,CADA,GAAG,IAAI,IAAI,SAAS,IAAI,MAAM,CAAC,CAC3B,CAAC,CAAC,KAAK,SACzB,aACC,MACA,SAAS,QAAQ,WAAW,OAAO,MAAM,MAAM,IAAI,GACnD,WACA,OACA,MAAM,QAAQ,MAAM,UAAU,MAAM,SAAS,IAAI,CAAC,EAAE,UAAU,OAC9D,QACD,CACD;CACA,MAAM,QAA6B,CAAC;CACpC,IAAI,IAAI,KACP,IAAI,KACJ,YAAY;CACb,KAAK,MAAM,SAAS,QAAQ;EAC3B,IAAI,IAAI,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO;GACzC,IAAI;GACJ,KAAK,YAAY;GACjB,YAAY;EACb;EACA,MAAM,IAAI;EACV,MAAM,IAAI;EACV,KAAK,MAAM,CAAC,IAAI,QAAQ,OAAO,QAAQ,MAAM,KAAK,GACjD,MAAM,MAAM;GAAE,GAAG;GAAK,GAAG,IAAI,IAAI;GAAG,GAAG,IAAI,IAAI;EAAE;EAClD,KAAK,MAAM,IAAI;EACf,YAAY,KAAK,IAAI,WAAW,MAAM,CAAC;CACxC;CACA,KAAK,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC,GAAG;EAC3D,MAAM,MAAM,OAAO,QAAQ,UAAU,MAAM,MAAM,IAAI;EACrD,MAAM,QAAQ,KAAK,IAAI,GAAG,IAAI,KAAK,UAAU,MAAM,IAAI,MAAM,CAAC,CAAC;EAC/D,MAAM,SAAS,KAAK,IAAI,IAAI,QAAQ,MAAM,SAAS,CAAC;EACpD,KAAK,MAAM,SAAS,KAAK;GACxB,MAAM,KAAK;GACX,KAAK,MAAM,MAAM,OAAO,KAAK,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK;EAC3D;CACD;CACA,MAAM,SAAS,KAAK,IAAI,KAAK,IAAI,YAAY,GAAG;CAChD,OAAO;EACN;EACA;EACA,SAAS,OAAO,MAAM,GAAG;EACzB,QAAQ,OAAO,KAAK,EAAE,OAAO,GAAG,GAAG,YAAY,KAAK;EACpD;EACA,OAAO,WAAW,WAAW,KAAK;CACnC;AACD;;AC7IA,SAAgB,cAAc,EAC7B,OACA,MACyC;CACzC,OACC,qBAAC,OAAD;EAAK,WAAU;EAAkB;EAAjC,UAAA,CACC,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;EACnC,CAAA,GACN,qBAAC,OAAD,EAAA,UAAA,CACC,qBAAC,KAAD,EAAA,UAAA,CACC,oBAAC,UAAD,EAAA,UAAS,MAAM,MAAc,CAAA,GAC5B,MAAM,MAAM,MAAM,SAAS,KAC3B,oBAAC,QAAD,EAAA,UAAO,MAAM,MAAM,MAAM,KAAK,GAAG,EAAQ,CAAA,CAExC,EAAA,CAAA,GACF,MAAM,MAAM,UAAU,SAAS,KAC/B,oBAAC,MAAD;GAAI,WAAU;GAAgB,cAAW;GACvC,UAAA,MAAM,MAAM,UAAU,KAAK,UAAU,UAAU;IAC/C,MAAM,OAAO,MAAM,MAAM,UAAU,MACjC,aAAa,SAAS,OAAO,SAAS,QACxC;IACA,OAEC,qBAAC,MAAD,EAAA,UAAA,CACE,OAAO,GAAG,KAAK,KAAK,KAAK,KAAK,OAAO,SAAS,UAC9C,SAAS,UAAU,KAAA,KACnB,oBAAC,QAAD,EAAA,UAAO,OAAO,SAAS,KAAK,EAAQ,CAAA,CAElC,EAAA,GALK,GAAG,SAAS,SAAS,GAAG,OAK7B;GAEN,CAAC;EACE,CAAA,CAED,EAAA,CAAA,CACD;;AAEP;;;;;;;ACvCA,MAAa,SAAS;CAAC;CAAK;CAAG;AAAC;AAmBhC,MAAa,eAAe,SAA4B;CACvD,MAAM;CACN;CACA,SAAS;CACT,OAAO;AACR;AAEA,MAAM,SAAS,GAAW,QAAgB,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,CAAC,CAAC;AAEtE,SAAgB,YAAY,OAAkB,QAA+B;CAC5E,QAAQ,OAAO,MAAf;EACC,KAAK,SACJ,OAAO,YAAY,OAAO,GAAG;EAC9B,KAAK;GACJ,IAAI,CAAC,MAAM,SAAS,OAAO;GAC3B,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO;IAAE,GAAG;IAAO,SAAS;GAAM;GAC/D,OAAO;IACN,GAAG;IACH,MAAM,MAAM,OAAO;IACnB,SAAS,MAAM,OAAO,IAAI,MAAM;GACjC;EACD,KAAK;GACJ,IAAI,MAAM,SAAS,OAAO;IAAE,GAAG;IAAO,SAAS;GAAM;GACrD,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO;IAAE,GAAG;IAAO,MAAM;IAAG,SAAS;GAAK;GACvE,OAAO;IAAE,GAAG;IAAO,SAAS;GAAK;EAClC,KAAK,SACJ,OAAO;GAAE,GAAG;GAAO,SAAS;EAAM;EACnC,KAAK,QACJ,OAAO;GAAE,GAAG;GAAO,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG;GAAG,SAAS;EAAM;EACtE,KAAK,SACJ,OAAO;GAAE,GAAG;GAAO,OAAO,OAAO;EAAG;CACtC;AACD;;AAGA,MAAa,eAAe,MAAc,UACzC,KAAK,MAAM,OAAO,KAAK;;AChDxB,SAAgB,eAAe,EAC9B,OACA,UACA,SAC0C;CAC1C,OACC,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACC,oBAAC,UAAD;IACC,MAAK;IACL,cAAW;IACX,UAAU,MAAM,SAAS;IACzB,eAAe,SAAS;KAAE,MAAM;KAAQ,IAAI,MAAM,OAAO;IAAE,CAAC;IAC5D,UAAA;GAEO,CAAA;GACR,oBAAC,UAAD;IACC,MAAK;IACL,WAAU;IACV,cAAY,MAAM,UAAU,UAAU;IACtC,UAAU,MAAM,MAAM;IACtB,eAAe,SAAS,EAAE,MAAM,SAAS,CAAC;IAEzC,UAAA,MAAM,UACJ,UACA,MAAM,SAAS,MAAM,OAAO,MAAM,MAAM,IACvC,WACA;GACG,CAAA;GACR,oBAAC,UAAD;IACC,MAAK;IACL,cAAW;IACX,UAAU,MAAM,SAAS,MAAM;IAC/B,eAAe,SAAS;KAAE,MAAM;KAAQ,IAAI,MAAM,OAAO;IAAE,CAAC;IAC5D,UAAA;GAEO,CAAA;GACR,oBAAC,SAAD;IACC,MAAK;IACL,WAAU;IACV,cAAW;IACX,kBAAgB,QAAQ,MAAM,OAAO,EAAE,MAAM,MAAM,MAAM;IACzD,KAAK;IACL,KAAK,MAAM;IACX,OAAO,MAAM;IACb,UAAU,MAAM,MAAM;IACtB,WAAW,UACV,SAAS;KAAE,MAAM;KAAQ,IAAI,OAAO,MAAM,cAAc,KAAK;IAAE,CAAC;GAEjE,CAAA;GACD,qBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA;KACE,MAAM,OAAO;KAAE;KAAI,MAAM,MAAM;IAC3B;;GACN,oBAAC,UAAD;IACC,cAAW;IACX,OAAO,MAAM;IACb,WAAW,UACV,SAAS;KACR,MAAM;KACN,IAAI,OAAO,MAAM,cAAc,KAAK;IACrC,CAAC;IAGD,UAAA,OAAO,KAAK,UACZ,qBAAC,UAAD;KAAoB,OAAO;KAA3B,UAAA,CACE,OAAM,GACA;IAFK,GAAA,KAEL,CACR;GACM,CAAA;GACP,SACA,oBAAC,UAAD;IACC,MAAK;IACL,cAAW;IACX,gBAAc,MAAM;IACpB,SAAS,MAAM;IACf,UAAA;GAEO,CAAA;EAEL;;AAEP;;ACtFA,SAAgB,eAAe,EAC9B,SAC0C;CAC1C,OACC,qBAAC,WAAD;EAAS,WAAU;EAAnB,UAAA,CACC,oBAAC,WAAD,EAAA,UAAS,gBAAsB,CAAA,GAC/B,oBAAC,MAAD,EAAA,UACE,aAAa,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,WACxC,qBAAC,OAAD,EAAA,UAAA,CACC,qBAAC,MAAD,EAAA,UAAA;GACE,OAAO;GAAM;GAAC,qBAAC,QAAD,EAAA,UAAA;IAAM;IAAE,OAAO;IAAG;GAAO,EAAA,CAAA;EACrC,EAAA,CAAA,GACJ,oBAAC,MAAD,EAAA,UACC,oBAAC,OAAD,EAAA,UAAM,KAAK,UAAU,OAAO,KAAK,EAAO,CAAA,EACrC,CAAA,CACA,EAAA,GAPK,OAAO,EAOZ,CACL,EACE,CAAA,CACI;;AAEX;;ACnBA,MAAa,YAAY,MAAc,WACtC,KAAK,SAAS,SAAS,GAAG,KAAK,MAAM,GAAG,KAAK,IAAI,GAAG,SAAS,CAAC,CAAC,EAAE,KAAK;AAUvE,SAAgB,SAAS,EACxB,QACA,UACA,KACA,OACA,WACoC;CACpC,MAAM,QAAQ,aAAa,QAAQ,IAAI,GAAG,KAAK;CAC/C,OACC,qBAAC,KAAD;EAAG,WAAW,aAAa,IAAI,EAAE,GAAG,IAAI,EAAE;EAAI,aAAW,OAAO;EAAhE,UAAA;GACC,oBAAC,SAAD,EAAA,UAAQ,GAAG,OAAO,MAAM,IAAI,OAAO,OAAO,KAAK,IAAI,MAAM,SAAS,aAAa,KAAY,CAAA;GAC3F,oBAAC,QAAD;IACC,OAAO,IAAI;IACX,QAAQ,IAAI;IACZ,IAAI;IACJ,WAAW,GAAG,YAAY,EAAE,eAAe,MAAM,SAAS,EAAE,CAAC;GAC7D,CAAA;GACD,oBAAC,QAAD;IAAM,GAAG;IAAI,GAAG;IAAI,WAAU;IAC5B,UAAA,SAAS,OAAO,OAAO,KAAK,OAAO,IAAI,IAAI,MAAM,CAAC,CAAC;GAC/C,CAAA;GACL,MAAM,MAAM,KAAK,SAAS;IAC1B,MAAM,YACL,QAAQ,MAAM,SAAS,SAAS,MAAM,KAAK,IAAI,CAAC,MAC/C,cAAc,OAAO,MAAM,OAAO,KAAK,MAAM,SAC3C,OAAO,aAAa,YACrB,OAAO,KAAK,MAAM,WAAW,KAAK,OAAO,KAAK,KAAK,EAAE,CAAC,IAAI,CAAC,MAC1D,KAAK,OACL,KAAK,UAAU,MAAM,UAAU,KAAK,IAAI,CAAC,MAC1C,KAAK,UAAU,MAAM,OAAO,OAAO,KAAK,IAAI,CAAC;IAChD,MAAM,YAAY,MAAM,MAAM,SAAS,SAAS,MAAM,KAAK,IAAI,CAAC;IAChE,OACC,qBAAC,KAAD;KAEC,WAAW,aAAa,KAAK,EAAE,GAAG,KAAK,EAAE;KACzC,WAAW,GAAG,YAAY;MACzB,eAAe;MACf,eAAe;KAChB,CAAC;KANF,UAAA;MAQC,oBAAC,SAAD,EAAA,UAAQ,KAAK,KAAY,CAAA;MACzB,oBAAC,QAAD;OAAM,OAAO,KAAK;OAAG,QAAQ,KAAK;OAAG,IAAI;MAAI,CAAA;MAC7C,oBAAC,QAAD;OACC,GAAG,KAAK,IAAI;OACZ,GAAG,KAAK,IAAI;OACZ,kBAAiB;OACjB,YAAW;OAEV,UAAA,SAAS,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,MAAM,GAAG,CAAC;MAC/C,CAAA;MACL,KAAK,UAAU,KAAA,KACf,oBAAC,QAAD;OACC,GAAG,KAAK,IAAI;OACZ,GAAG,KAAK,IAAI;OACZ,YAAW;OACX,WAAU;OAET,UAAA,KAAK;MACD,CAAA;KAEL;IA3BG,GAAA,KAAK,UAAU,KAAK,IAAI,CA2B3B;GAEL,CAAC;GACA,MAAM,QACN,qBAAC,QAAD;IAAM,GAAG;IAAI,GAAG,IAAI,IAAI;IAAI,WAAU;IAAtC,UAAA,CACE,SAAS,MAAM,MAAM,KAAK,OAAO,IAAI,IAAI,MAAM,GAAG,CAAC,GACpD,oBAAC,SAAD,EAAA,UAAQ,MAAM,KAAY,CAAA,CACrB;;EAEL;;AAEL;;AC5EA,SAAS,UAAU,EAClB,UACA,MACA,SACqC;CACrC,MAAM,WAAW,MAAM,MAAM,UAAU,MACrC,SAAS,KAAK,aAAa,SAAS,EACtC;CACA,MAAM,QAAQ,SAAS;CACvB,OACC,qBAAC,KAAD;EAAG,WAAW,GAAG,YAAY,EAAE,mBAAmB,CAAC,CAAC,SAAS,CAAC;EAA9D,UAAA;GACC,oBAAC,SAAD,EAAA,UAAQ,GAAG,SAAS,KAAK,KAAK,SAAS,KAAK,SAAS,QAAQ,KAAK,SAAS,UAAU,KAAY,CAAA;GACjG,oBAAC,QAAD;IAAM,GAAG,KAAK;IAAG,WAAU;GAAY,CAAA;GACtC,YACA,oBAAC,QAAD;IAEC,GAAG,KAAK;IACR,YAAY;IACZ,WAAU;GACV,GAJK,MAAM,KAIX;GAEF,oBAAC,WAAD;IACC,QAAO;IACP,WAAW,aAAa,KAAK,GAAG,GAAG,GAAG,KAAK,GAAG,GAAG,WAAY,KAAK,QAAQ,MAAO,KAAK,GAAG;GACzF,CAAA;GACA,SACA,qBAAC,QAAD;IACC,GAAG,KAAK,MAAM;IACd,GAAG,KAAK,MAAM,KAAK;IACnB,YAAW;IACX,WAAU;IAJX,UAAA,CAME,SAAS,OAAO,EAAE,GACnB,oBAAC,SAAD,EAAA,UAAQ,MAAa,CAAA,CAChB;;EAEL;;AAEL;AAOA,SAAgB,WAAW,EAC1B,OACA,UACA,UACsC;CACtC,MAAM,QAAQ,aAAa,MAAM,KAAK;CACtC,MAAM,SAAS,IAAI,IAClB,UAAU,MAAM,SAAS,KAAK,WAAW,CAAC,OAAO,IAAI,OAAO,KAAK,CAAC,CACnE;CACA,MAAM,UAAU,MAAM,MAAM,OAAO,IAAI,WAAW;CAClD,MAAM,UAAU,MAAM,MAAM,QAAQ,IAAI,WAAW;CACnD,OACC,qBAAA,UAAA,EAAA,UAAA;EACE,OAAO,OACN,QAAQ,UACR,MAAM,SAAS,MACb,YAAY,OAAO,QAAQ,aAAa,MAAM,IAChD,CACD,CAAC,CACA,KAAK,UACL,qBAAC,KAAD,EAAA,UAAA,CACC,oBAAC,QAAD;GACC,GAAG,MAAM;GACT,GAAG,MAAM;GACT,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,IAAI;GACJ,WAAU;EACV,CAAA,GACD,oBAAC,QAAD;GAAM,GAAG,MAAM,IAAI;GAAI,GAAG,MAAM,IAAI;GAAI,WAAU;GAChD,UAAA,SAAS,MAAM,MAAM,KAAK,OAAO,MAAM,IAAI,MAAM,CAAC,CAAC;EAC/C,CAAA,CACJ,EAAA,GAZK,MAAM,IAYX,CACH;EACD,MAAM,UAAU,KAAK,aACrB,oBAAC,WAAD;GAEW;GACV,MAAM,OAAO,MAAM,SAAS;GACrB;EACP,GAJK,SAAS,EAId,CACD;EACA,MAAM,SAAS,KAAK,WACpB,oBAAC,UAAD;GAES;GACR,UAAU,OAAO,IAAI,OAAO,EAAE;GAC9B,KAAK,OAAO,MAAM,OAAO;GACzB,OAAO,QAAQ,SAAS,WACvB,QAAQ,OAAO,OAAO,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAC7C;GACA,SAAS,QAAQ,SAAS,WACzB,QAAQ,OAAO,OAAO,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAC7C;EACA,GAVK,OAAO,EAUZ,CACD;EACA,CAAC,MAAM,SAAS,UAChB,oBAAC,QAAD;GACC,GAAG,OAAO,QAAQ;GAClB,GAAG;GACH,YAAW;GACX,WAAU;GACV,UAAA;EAEK,CAAA;CAEN,EAAA,CAAA;AAEJ;;ACnHA,SAAgB,UAAU,OAIxB;CACD,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAC9C,MAAM,CAAC,OAAO,YAAY,WACzB,aACA,KAAK,IAAI,GAAG,MAAM,OAAO,SAAS,CAAC,GACnC,WACD;CAEA,IAAI,aAAa,OAAO;EACvB,YAAY,KAAK;EACjB,SAAS;GAAE,MAAM;GAAS,KAAK,KAAK,IAAI,GAAG,MAAM,OAAO,SAAS,CAAC;EAAE,CAAC;CACtE;CACA,gBAAgB;EACf,IAAI,CAAC,MAAM,SAAS;EACpB,MAAM,QAAQ,kBACP,SAAS,EAAE,MAAM,OAAO,CAAC,GAC/B,YAAY,MAAM,MAAM,KAAK,CAC9B;EACA,aAAa,cAAc,KAAK;CACjC,GAAG,CAAC,MAAM,SAAS,MAAM,KAAK,CAAC;CAC/B,MAAM,aAAyC,UAAU;EACxD,IAAI,MAAM,WAAW,MAAM,eAAe;EAC1C,IAAI;GAAC;GAAc;GAAa;GAAQ;EAAK,CAAC,CAAC,SAAS,MAAM,GAAG,GAAG;GACnE,MAAM,eAAe;GACrB,MAAM,KACL,MAAM,QAAQ,SACX,IACA,MAAM,QAAQ,QACb,MAAM,MACN,MAAM,QAAQ,MAAM,QAAQ,eAAe,IAAI;GACpD,SAAS;IAAE,MAAM;IAAQ;GAAG,CAAC;EAC9B,OAAO,IAAI,MAAM,QAAQ,OAAO,MAAM,MAAM,GAAG;GAC9C,MAAM,eAAe;GACrB,SAAS,EAAE,MAAM,SAAS,CAAC;EAC5B;CACD;CACA,OAAO;EAAE;EAAO;EAAU;CAAU;AACrC;AAEA,SAAgB,cACf,KACS;CACT,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,gBAAgB;EACf,MAAM,UAAU,IAAI;EACpB,IAAI,CAAC,SAAS;EACd,MAAM,WAAW,IAAI,gBAAgB,CAAC,WAAW;GAChD,IAAI,MAAM,YAAY,QAAQ,GAC7B,SAAS,KAAK,MAAM,MAAM,YAAY,KAAK,CAAC;EAC9C,CAAC;EACD,SAAS,QAAQ,OAAO;EACxB,aAAa,SAAS,WAAW;CAClC,GAAG,CAAC,GAAG,CAAC;CACR,OAAO;AACR;;;ACpEA,MAAa,gBAAgB;AAC7B,MAAa,gBAAgB;AAc7B,SAAgB,iBAAiB,MAAc,OAAuB;CACrE,IACC,CAAC,OAAO,SAAS,IAAI,KACrB,CAAC,OAAO,SAAS,KAAK,KACtB,SAAS,KACT,QAAQ,GAER,OAAO;CACR,IAAI,QAAQ,QAAQ,GAAG,OAAO;CAC9B,OACC,KAAK,MACJ,iBACE,gBAAgB,mBAAmB,QAAQ,QAAQ,MACpD,GACF,IAAI;AAEN;AAEA,SAAgB,kBACf,MACA,OACoB;CACpB,OAAO;EACN;EACA;EACA,WAAW,iBAAiB,MAAM,KAAK;EACvC,YAAA;CACD;AACD;;;ACxCA,SAAgB,SACf,MACA,OACA,SACA,SACO;CACP,MAAM,UAAU,OAA4B,IAAI;CAChD,MAAM,UAAU,SAAS;CACzB,sBACa;EACX,MAAM,QAAQ,QAAQ;EACtB,QAAQ,UAAU;EAClB,OAAY,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;CACnC,GACA,CAAC,CACF;CACA,gBAAgB;EACf,IAAI,CAAC,WAAW,UAAU,GAAG;EAC7B,MAAM,QAAQ,kBAAkB,MAAM,KAAK;EAC3C,IAAI;GACH,IAAI,SAAS;IACZ,QAAQ,KAAK;IACb;GACD;GACA,IAAI,OAAO,iBAAiB,aAAa;GACzC,QAAQ,YAAY,IAAI,aAAa;GACrC,MAAM,QAAQ,QAAQ;GACtB,MAAW,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;GAClC,MAAM,aAAa,MAAM,iBAAiB;GAC1C,MAAM,OAAO,MAAM,WAAW;GAC9B,MAAM,MAAM,MAAM;GAClB,WAAW,UAAU,QAAQ,MAAM;GACnC,KAAK,KAAK,eAAe,MAAQ,GAAG;GACpC,KAAK,KAAK,6BAA6B,IAAK,MAAM,GAAI;GACtD,KAAK,KAAK,6BACT,MACA,MAAM,MAAM,aAAa,GAC1B;GACA,WAAW,QAAQ,IAAI;GACvB,KAAK,QAAQ,MAAM,WAAW;GAC9B,WAAW,gBAAgB;IAC1B,WAAW,WAAW;IACtB,KAAK,WAAW;GACjB;GACA,WAAW,MAAM;GACjB,WAAW,KAAK,MAAM,MAAM,aAAa,MAAO,GAAI;GACpD,aAAa;IACZ,WAAW,KAAK;GACjB;EACD,QAAQ,CAER;CACD,GAAG;EAAC;EAAM;EAAO;EAAS;CAAO,CAAC;AACnC;;ACnCA,SAAgB,kBAAkB,EACjC,OACA,WACA,QAAQ,QACR,gBAC6C;CAC7C,MAAM,MAAM,OAAuB,IAAI;CACvC,MAAM,QAAQ,cAAc,GAAG;CAC/B,MAAM,EAAE,OAAO,UAAU,cAAc,UAAU,KAAK;CACtD,MAAM,CAAC,OAAO,YAAY,SAAS,cAAc,WAAW,KAAK;CACjE,MAAM,SAAS,cAAc,aAAa,OAAO,KAAK,GAAG,CAAC,OAAO,KAAK,CAAC;CACvE,MAAM,YAAY,MAAM;CACxB,MAAM,QAAQ,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO;CACvD,SACC,MAAM,MACN,MAAM,OAAO,QACb,CAAC,CAAC,gBAAgB,OAClB,YACD;CACA,MAAM,UAAU,QACb,cAAc,OAAO,MAAM,OAAO,MAAM,IACxC;CACH,OAEC,qBAAC,OAAD;EACM;EACL,WAAW,GAAG,OAAO,SAAS;EAC9B,cAAY;EACZ,MAAK;EACL,cAAW;EAEX,UAAU;EACC;EARZ,UAAA,CAUC,oBAAC,KAAD;GAAG,MAAK;GAAS,WAAU;GACzB,UAAA;EACC,CAAA,GACF,QACA,qBAAA,UAAA,EAAA,UAAA;GACC,oBAAC,OAAD;IACC,OAAM;IACN,QAAQ,OAAO;IACf,SAAS,OAAO;IAChB,MAAK;IACL,cAAY;IACZ,oBAAkB;IAClB,WAAU;IAEV,UAAA,oBAAC,YAAD;KACQ;KACP,UAAU,MAAM,OAAO,MAAM,OAAO;KAC5B;IACR,CAAA;GACG,CAAA;GACL,oBAAC,eAAD;IAAsB;IAAO,IAAI;GAAY,CAAA;GAC7C,oBAAC,gBAAD;IACQ;IACG;IACV,OACC,eACG;KAAE,SAAS;KAAO,cAAc,SAAS,CAAC,KAAK;IAAE,IACjD,KAAA;GAEJ,CAAA;GACD,oBAAC,gBAAD,EAAuB,MAAQ,CAAA;EAC9B,EAAA,CAAA,IAEF,oBAAC,KAAD;GAAG,WAAU;GAAY,UAAA;EAAuB,CAAA,CAE7C;;AAEP"}
package/package.json CHANGED
@@ -1,6 +1,96 @@
1
1
  {
2
2
  "name": "@0xbuff/throughline",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.2",
4
+ "description": "Readable state over time, authored in Markdown. A deterministic trace compiler with an optional React player.",
5
+ "private": false,
6
+ "license": "Apache-2.0",
7
+ "type": "module",
8
+ "engines": {
9
+ "node": ">=24"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/0xbuffdev/throughline.git"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/0xbuffdev/throughline/issues"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./react": {
24
+ "types": "./dist/react/index.d.ts",
25
+ "default": "./dist/react/index.js"
26
+ },
27
+ "./examples": {
28
+ "types": "./dist/examples.d.ts",
29
+ "default": "./dist/examples.js"
30
+ },
31
+ "./styles/throughline.css": "./src/styles/throughline.css"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "src/styles",
36
+ "README.md",
37
+ "LICENSE",
38
+ "COPYRIGHT.md",
39
+ "CHANGELOG.md"
40
+ ],
41
+ "sideEffects": [
42
+ "**/*.css"
43
+ ],
44
+ "dependencies": {
45
+ "cn": "^0.4.0"
46
+ },
47
+ "peerDependencies": {
48
+ "react": "^19.2.7",
49
+ "react-dom": "^19.2.7"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "react": {
53
+ "optional": true
54
+ },
55
+ "react-dom": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "devDependencies": {
60
+ "@playwright/test": "^1.61.0",
61
+ "@types/node": "^24.0.0",
62
+ "@types/react": "^19.2.14",
63
+ "@types/react-dom": "^19.2.3",
64
+ "@vitest/browser-playwright": "^4.1.11",
65
+ "react": "^19.2.7",
66
+ "react-dom": "^19.2.7",
67
+ "tsdown": "^0.23.5",
68
+ "typescript": "^5.9.3",
69
+ "vite": "^8.0.3",
70
+ "vitest": "^4.1.8",
71
+ "vitest-browser-react": "^2.3.0"
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ },
76
+ "homepage": "https://docs.0xbuff.com/docs/throughline",
77
+ "keywords": [
78
+ "state-traces",
79
+ "visualization",
80
+ "markdown",
81
+ "typescript",
82
+ "react",
83
+ "developer-tools",
84
+ "education"
85
+ ],
86
+ "scripts": {
87
+ "build": "tsdown && node scripts/verify-dist.mjs",
88
+ "typecheck": "tsc --noEmit",
89
+ "test:run": "vitest run",
90
+ "test:browser:run": "vitest run --config vitest.browser.config.ts",
91
+ "audit:pack": "node scripts/audit-pack.mjs",
92
+ "audit:source": "node scripts/audit-source.mjs",
93
+ "audit:browser": "node scripts/audit-browser.mjs",
94
+ "audit:licenses": "node scripts/audit-licenses.mjs"
95
+ }
6
96
  }
@@ -0,0 +1,77 @@
1
+ /* Self-contained tokens: consumers may override --thl-* on the player. */
2
+ .thl {
3
+ box-sizing: border-box;
4
+ --thl-bg: light-dark(#fcfcfa, #141b19);
5
+ --thl-panel: light-dark(#f3f4f0, #19231f);
6
+ --thl-surface: light-dark(#ffffff, #202c26);
7
+ --thl-text: light-dark(#202b28, #ecf2eb);
8
+ --thl-muted: light-dark(#596761, #a5b5a9);
9
+ --thl-line: light-dark(#d5dcd5, #3d4e43);
10
+ --thl-focus: light-dark(#036c62, #75e5b7);
11
+ --thl-focus-bg: light-dark(#e0f4ed, #183d31);
12
+ --thl-change: light-dark(#8b4b08, #ffce83);
13
+ --thl-change-bg: light-dark(#fff0cf, #45371f);
14
+ color-scheme: light dark;
15
+ container-type: inline-size;
16
+ background: var(--thl-bg);
17
+ color: var(--thl-text);
18
+ border: 1px solid var(--thl-line);
19
+ border-radius: 16px;
20
+ font: 14px/1.5 ui-sans-serif, system-ui, sans-serif;
21
+ width: 100%;
22
+ min-width: 0;
23
+ overflow: hidden;
24
+ }
25
+ .thl[data-theme="dark"] { color-scheme: dark; }
26
+ .thl[data-theme="light"] { color-scheme: light; }
27
+ .thl *, .thl *::before, .thl *::after { box-sizing: border-box; }
28
+ .thl :where(p, dl, dd, pre) { margin: 0; }
29
+ .thl:focus-visible, .thl :focus-visible { outline: 2px solid var(--thl-focus); outline-offset: -3px; }
30
+ .thl-scene { display: block; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
31
+ .thl-group { fill: var(--thl-panel); stroke: var(--thl-line); stroke-width: 0.75; }
32
+ .thl-group-label { fill: var(--thl-muted); font: 600 12px ui-sans-serif, system-ui, sans-serif; letter-spacing: 0.3px; }
33
+ .thl-node { fill: var(--thl-surface); stroke: var(--thl-line); }
34
+ .thl-node.thl-focused { stroke: var(--thl-focus); stroke-width: 1.5; }
35
+ .thl-node-label { fill: var(--thl-muted); font: 500 12px ui-sans-serif, system-ui, sans-serif; }
36
+ .thl-cell rect { fill: var(--thl-panel); stroke: transparent; }
37
+ .thl-cell text { fill: var(--thl-text); font-size: 13px; font-weight: 600; }
38
+ .thl-cell.thl-focused rect { fill: var(--thl-focus-bg); stroke: var(--thl-focus); }
39
+ .thl-cell.thl-changed rect { fill: var(--thl-change-bg); stroke: var(--thl-change); }
40
+ .thl-cell.thl-changed text { fill: var(--thl-change); }
41
+ .thl-cell .thl-index, .thl-window { fill: var(--thl-muted); font-size: 11px; font-weight: 400; }
42
+ .thl-edge, .thl-flow { fill: none; stroke: var(--thl-muted); stroke-width: 1.3; }
43
+ .thl-link polygon { fill: var(--thl-muted); }
44
+ .thl-edge-label { fill: var(--thl-muted); stroke: var(--thl-bg); stroke-width: 4px; paint-order: stroke; font-size: 11px; }
45
+ .thl-transferred .thl-edge { stroke: var(--thl-focus); }
46
+ .thl-transferred polygon, .thl-transferred text { fill: var(--thl-focus); }
47
+ .thl-flow { stroke: var(--thl-focus); stroke-width: 2.5; stroke-dasharray: 1; animation: thl-draw 350ms ease-out both; }
48
+ .thl-caption { display: flex; gap: 12px; padding: 14px 16px; border-top: 1px solid var(--thl-line); }
49
+ .thl-step { color: var(--thl-focus); font: 600 12px/22px ui-monospace, monospace; }
50
+ .thl-caption p { min-width: 0; }
51
+ .thl-caption strong { display: block; font-weight: 600; }
52
+ .thl-caption p > span { display: block; color: var(--thl-muted); font-size: 13px; }
53
+ .thl-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 12px; border-top: 1px solid var(--thl-line); }
54
+ .thl-controls button, .thl-controls select { appearance: auto; font: inherit; margin: 0; height: 44px; min-width: 40px; padding: 0 10px; color: var(--thl-text); background: transparent; border: 1px solid transparent; border-radius: 7px; cursor: pointer; }
55
+ .thl-controls button:hover:not(:disabled), .thl-controls select:hover { background: var(--thl-panel); border-color: var(--thl-line); }
56
+ .thl-controls .thl-play { background: var(--thl-focus-bg); color: var(--thl-focus); min-width: 66px; font-weight: 600; }
57
+ .thl-controls button:disabled { opacity: 0.4; cursor: default; }
58
+ .thl-timeline { flex: 1; min-width: 40px; width: 80px; height: 44px; margin: 0 6px; accent-color: var(--thl-focus); cursor: pointer; }
59
+ .thl-count { white-space: nowrap; color: var(--thl-muted); font: 12px ui-monospace, monospace; font-variant-numeric: tabular-nums; }
60
+ .thl-inspector { border-top: 1px solid var(--thl-line); font-size: 12px; }
61
+ .thl-inspector summary { padding: 8px 16px; color: var(--thl-muted); cursor: pointer; }
62
+ .thl-inspector dl { padding: 0 16px 12px; }
63
+ .thl-inspector dl > div + div { margin-top: 10px; }
64
+ .thl-inspector dt { font-weight: 600; }
65
+ .thl-inspector dt span { color: var(--thl-muted); font-weight: 400; }
66
+ .thl-inspector pre { overflow: auto; max-height: 240px; font: 12px/1.6 ui-monospace, monospace; white-space: pre-wrap; overflow-wrap: anywhere; }
67
+ .thl-empty { padding: 20px; fill: var(--thl-muted); color: var(--thl-muted); }
68
+ .thl-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
69
+ @container (max-width: 480px) {
70
+ .thl-timeline { order: 1; flex-basis: 100%; width: 100%; margin: 0; height: 32px; }
71
+ .thl-count { margin-left: auto; }
72
+ }
73
+ @keyframes thl-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
74
+ @media (prefers-reduced-motion: reduce) { .thl-flow { animation: none; } }
75
+ .thl-caption > div { min-width: 0; }
76
+ .thl-transfers { display: flex; flex-wrap: wrap; gap: 6px 12px; padding: 0; margin: 6px 0 0; list-style: none; color: var(--thl-muted); font-size: 12px; }
77
+ .thl-transfers code { margin-left: 6px; color: var(--thl-focus); overflow-wrap: anywhere; font: inherit; font-family: ui-monospace, monospace; }