@hex-core/components 1.9.0 → 1.10.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.
Files changed (146) hide show
  1. package/dist/_tsup-dts-rollup.d.ts +575 -18
  2. package/dist/accordion.js.map +1 -1
  3. package/dist/alert-dialog.js.map +1 -1
  4. package/dist/alert.js.map +1 -1
  5. package/dist/arc.js.map +1 -1
  6. package/dist/attachment.js.map +1 -1
  7. package/dist/audio-player.js.map +1 -1
  8. package/dist/audio-waveform.js.map +1 -1
  9. package/dist/auth-forgot-password.js.map +1 -1
  10. package/dist/auth-reset-password.js.map +1 -1
  11. package/dist/auth-sign-in-split.js.map +1 -1
  12. package/dist/auth-sign-up-card.js.map +1 -1
  13. package/dist/auth-verify-email.js.map +1 -1
  14. package/dist/auth-verify-otp.js.map +1 -1
  15. package/dist/avatar.js.map +1 -1
  16. package/dist/badge.js.map +1 -1
  17. package/dist/branch.d.ts +2 -0
  18. package/dist/branch.js +136 -0
  19. package/dist/branch.js.map +1 -0
  20. package/dist/breadcrumb.js.map +1 -1
  21. package/dist/button.js.map +1 -1
  22. package/dist/calendar.js.map +1 -1
  23. package/dist/canvas.js.map +1 -1
  24. package/dist/card.js.map +1 -1
  25. package/dist/chain-of-thought.d.ts +3 -0
  26. package/dist/chain-of-thought.js +119 -0
  27. package/dist/chain-of-thought.js.map +1 -0
  28. package/dist/checkbox.js.map +1 -1
  29. package/dist/chord.js.map +1 -1
  30. package/dist/citation.js.map +1 -1
  31. package/dist/cloze.js.map +1 -1
  32. package/dist/cluster.js.map +1 -1
  33. package/dist/code-block-copy.js.map +1 -1
  34. package/dist/code-block.js.map +1 -1
  35. package/dist/color-picker.js.map +1 -1
  36. package/dist/combobox.js.map +1 -1
  37. package/dist/command.js.map +1 -1
  38. package/dist/compare-table.js.map +1 -1
  39. package/dist/composer.js.map +1 -1
  40. package/dist/container.js.map +1 -1
  41. package/dist/context-menu.js.map +1 -1
  42. package/dist/conversation.d.ts +3 -0
  43. package/dist/conversation.js +358 -0
  44. package/dist/conversation.js.map +1 -0
  45. package/dist/data-table.js.map +1 -1
  46. package/dist/date-picker.js.map +1 -1
  47. package/dist/deck.js.map +1 -1
  48. package/dist/dendrogram.js.map +1 -1
  49. package/dist/diagram.js.map +1 -1
  50. package/dist/dialog.js.map +1 -1
  51. package/dist/drawer.js.map +1 -1
  52. package/dist/dropdown-menu.js.map +1 -1
  53. package/dist/dropzone.js.map +1 -1
  54. package/dist/empty.js.map +1 -1
  55. package/dist/error-state.js.map +1 -1
  56. package/dist/file-tree.js.map +1 -1
  57. package/dist/flashcard.js.map +1 -1
  58. package/dist/flowchart.js.map +1 -1
  59. package/dist/form.js.map +1 -1
  60. package/dist/funnel.js.map +1 -1
  61. package/dist/gantt.js.map +1 -1
  62. package/dist/grid.js.map +1 -1
  63. package/dist/hover-card.js.map +1 -1
  64. package/dist/image-occlusion.js.map +1 -1
  65. package/dist/index.d.ts +21 -0
  66. package/dist/index.js +1011 -13
  67. package/dist/index.js.map +1 -1
  68. package/dist/inline-citation.d.ts +2 -0
  69. package/dist/inline-citation.js +108 -0
  70. package/dist/inline-citation.js.map +1 -0
  71. package/dist/input-otp.js.map +1 -1
  72. package/dist/input.js.map +1 -1
  73. package/dist/label.js.map +1 -1
  74. package/dist/loading-indicator.js.map +1 -1
  75. package/dist/loading.js.map +1 -1
  76. package/dist/markdown.d.ts +1 -0
  77. package/dist/markdown.js +784 -4
  78. package/dist/markdown.js.map +1 -1
  79. package/dist/matrix.js.map +1 -1
  80. package/dist/menubar.js.map +1 -1
  81. package/dist/message-actions.js.map +1 -1
  82. package/dist/message-list.js.map +1 -1
  83. package/dist/message.js.map +1 -1
  84. package/dist/mind-map.js.map +1 -1
  85. package/dist/multi-combobox.js.map +1 -1
  86. package/dist/navigation-menu.js.map +1 -1
  87. package/dist/org-chart.js.map +1 -1
  88. package/dist/pagination.js.map +1 -1
  89. package/dist/plan.d.ts +3 -0
  90. package/dist/plan.js +183 -0
  91. package/dist/plan.js.map +1 -0
  92. package/dist/popover.js.map +1 -1
  93. package/dist/progress.js.map +1 -1
  94. package/dist/pyramid.js.map +1 -1
  95. package/dist/quiz.js.map +1 -1
  96. package/dist/radio-group.js.map +1 -1
  97. package/dist/reasoning.js.map +1 -1
  98. package/dist/resizable.js.map +1 -1
  99. package/dist/sankey.js.map +1 -1
  100. package/dist/schemas.d.ts +8 -0
  101. package/dist/schemas.js +774 -17
  102. package/dist/schemas.js.map +1 -1
  103. package/dist/scroll-area.js.map +1 -1
  104. package/dist/select.js.map +1 -1
  105. package/dist/separator.js.map +1 -1
  106. package/dist/sequence.js.map +1 -1
  107. package/dist/sheet.js.map +1 -1
  108. package/dist/shimmer.d.ts +2 -0
  109. package/dist/shimmer.js +39 -0
  110. package/dist/shimmer.js.map +1 -0
  111. package/dist/sidebar.js.map +1 -1
  112. package/dist/skeleton.js.map +1 -1
  113. package/dist/slider.js.map +1 -1
  114. package/dist/sources.d.ts +3 -0
  115. package/dist/sources.js +164 -0
  116. package/dist/sources.js.map +1 -0
  117. package/dist/spaced-repetition.js.map +1 -1
  118. package/dist/spacer.js.map +1 -1
  119. package/dist/speech-recognition.js.map +1 -1
  120. package/dist/stack.js.map +1 -1
  121. package/dist/stepper.js.map +1 -1
  122. package/dist/suggestion.js.map +1 -1
  123. package/dist/sunburst.js.map +1 -1
  124. package/dist/switch.js.map +1 -1
  125. package/dist/table.js.map +1 -1
  126. package/dist/tabs.js.map +1 -1
  127. package/dist/tag.js.map +1 -1
  128. package/dist/task.d.ts +3 -0
  129. package/dist/task.js +189 -0
  130. package/dist/task.js.map +1 -0
  131. package/dist/terminal.js +11 -0
  132. package/dist/terminal.js.map +1 -1
  133. package/dist/textarea.js.map +1 -1
  134. package/dist/time-axis.js.map +1 -1
  135. package/dist/time-picker.js.map +1 -1
  136. package/dist/timeline.js.map +1 -1
  137. package/dist/toggle-group.js.map +1 -1
  138. package/dist/toggle.js.map +1 -1
  139. package/dist/tool-call.js +5 -6
  140. package/dist/tool-call.js.map +1 -1
  141. package/dist/toolbar.js.map +1 -1
  142. package/dist/tooltip.js.map +1 -1
  143. package/dist/tree-map.js.map +1 -1
  144. package/dist/tree.js.map +1 -1
  145. package/dist/venn.js.map +1 -1
  146. package/package.json +8 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/time-axis/time-axis.tsx"],"names":[],"mappings":"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","file":"time-axis.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Events plotted along a horizontal time axis. Pure SVG; no heavy peer\n * dependency. Distinct from the existing `<Timeline>` component in\n * `components/timeline` — that one is an event-list with vertical\n * status markers; this one is a CHART with a real time axis where\n * spacing reflects elapsed time.\n *\n * Use TimeAxis when the *gap between events* is the message (release\n * cadence, incident frequency, sparse-then-dense patterns). Use\n * Timeline when the order of events is the message but the absolute\n * dates are secondary.\n *\n * @example\n * <TimeAxis\n * events={[\n * { id: \"v1\", label: \"v1.0\", date: \"2025-01-15\" },\n * { id: \"v2\", label: \"v2.0\", date: \"2025-04-20\" },\n * { id: \"v3\", label: \"v3.0\", date: \"2025-09-10\" },\n * ]}\n * />\n */\nexport type TimeAxisEvent = {\n\tid: string;\n\tlabel: string;\n\t/** Accepts Date, ISO string, or epoch ms. */\n\tdate: Date | string | number;\n\t/** Optional category — events with the same `category` share a row band. */\n\tcategory?: string;\n};\n\n// `start` and `end` collide with SVG's `<animate start=...>` / `<set end=...>`\n// animation attributes (their value type is string|number, not Date). Omit\n// both from the inherited SVG attributes so consumers can pass Date values\n// freely without a type collision.\nexport interface TimeAxisProps\n\textends Omit<React.SVGAttributes<SVGSVGElement>, \"children\" | \"start\" | \"end\"> {\n\t/** Events to plot. Order doesn't matter — positions come from `date`. */\n\tevents: TimeAxisEvent[];\n\t/** Optional explicit axis start. Auto-derived from `events` if omitted. */\n\tstart?: Date | string | number;\n\t/** Optional explicit axis end. Auto-derived from `events` if omitted. */\n\tend?: Date | string | number;\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 200. */\n\theight?: number;\n\t/** Number of axis ticks to show. Default 6. */\n\ttickCount?: number;\n\t/** Fired when an event is clicked. */\n\tonEventClick?: (event: TimeAxisEvent) => void;\n}\n\ninterface LaidOutEvent {\n\tevent: TimeAxisEvent;\n\tx: number;\n\ty: number;\n\trowIndex: number;\n}\n\ninterface AxisTick {\n\tx: number;\n\tlabel: string;\n}\n\nfunction TimeAxis({\n\tevents,\n\tstart,\n\tend,\n\twidth = 720,\n\theight = 200,\n\ttickCount = 6,\n\tonEventClick,\n\tclassName,\n\t...rest\n}: TimeAxisProps) {\n\tconst laidOut = React.useMemo(\n\t\t() => layout(events, start, end, width, height, tickCount),\n\t\t[events, start, end, width, height, tickCount],\n\t);\n\n\tconst desc =\n\t\tevents.length === 0\n\t\t\t? \"Empty time axis\"\n\t\t\t: `Time axis with ${events.length} event${events.length === 1 ? \"\" : \"s\"}, range ${laidOut.startLabel} to ${laidOut.endLabel}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-time-axis\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Time axis</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t{/* Baseline + ticks */}\n\t\t\t<g data-hex-time-axis-axis>\n\t\t\t\t<line\n\t\t\t\t\tx1={laidOut.axisLeft}\n\t\t\t\t\tx2={laidOut.axisRight}\n\t\t\t\t\ty1={laidOut.axisY}\n\t\t\t\t\ty2={laidOut.axisY}\n\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t/>\n\t\t\t\t{laidOut.ticks.map((tick, i) => (\n\t\t\t\t\t<g key={`tick-${i}`} data-hex-time-axis-tick>\n\t\t\t\t\t\t<line\n\t\t\t\t\t\t\tx1={tick.x}\n\t\t\t\t\t\t\tx2={tick.x}\n\t\t\t\t\t\t\ty1={laidOut.axisY - 4}\n\t\t\t\t\t\t\ty2={laidOut.axisY + 4}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx={tick.x}\n\t\t\t\t\t\t\ty={laidOut.axisY + 18}\n\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{tick.label}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t</g>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-time-axis-events>\n\t\t\t\t{laidOut.events.map((e) => {\n\t\t\t\t\tconst interactive = Boolean(onEventClick);\n\t\t\t\t\tconst handleActivate = () => onEventClick?.(e.event);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={e.event.id}\n\t\t\t\t\t\t\tdata-hex-time-axis-event\n\t\t\t\t\t\t\tdata-row={e.rowIndex}\n\t\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\t\taria-label={interactive ? `${e.event.label} on ${formatDate(toDate(e.event.date))}` : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={interactive ? (k) => activateOnKey(k, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Connector from event marker to baseline */}\n\t\t\t\t\t\t\t<line\n\t\t\t\t\t\t\t\tx1={e.x}\n\t\t\t\t\t\t\t\tx2={e.x}\n\t\t\t\t\t\t\t\ty1={e.y}\n\t\t\t\t\t\t\t\ty2={laidOut.axisY}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t\tstrokeOpacity={0.65}\n\t\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={e.x}\n\t\t\t\t\t\t\t\tcy={e.y}\n\t\t\t\t\t\t\t\tr={5}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={e.x + 8}\n\t\t\t\t\t\t\t\ty={e.y + 4}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ paintOrder: \"stroke\", pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={3}\n\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{e.event.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\tevents: TimeAxisEvent[],\n\tstart: Date | string | number | undefined,\n\tend: Date | string | number | undefined,\n\twidth: number,\n\theight: number,\n\ttickCount: number,\n): {\n\tevents: LaidOutEvent[];\n\tticks: AxisTick[];\n\taxisY: number;\n\taxisLeft: number;\n\taxisRight: number;\n\tstartLabel: string;\n\tendLabel: string;\n} {\n\tconst margin = 32;\n\tconst axisLeft = margin;\n\tconst axisRight = width - margin;\n\tconst axisY = height - 40;\n\tconst usableWidth = axisRight - axisLeft;\n\n\t// Drop events whose date doesn't parse — `new Date(\"garbage\")` returns\n\t// NaN, which would propagate through `Math.min/max` and produce NaN\n\t// x-coordinates that crash some SVG renderers and noise up dev\n\t// consoles. Skip silently; downstream components can still inspect\n\t// the unrendered ids if they want to surface validation errors.\n\tconst validEvents = events.filter((e) => Number.isFinite(toDate(e.date).getTime()));\n\n\tif (validEvents.length === 0) {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tticks: [],\n\t\t\taxisY,\n\t\t\taxisLeft,\n\t\t\taxisRight,\n\t\t\tstartLabel: \"—\",\n\t\t\tendLabel: \"—\",\n\t\t};\n\t}\n\n\tconst dates = validEvents.map((e) => toDate(e.date).getTime());\n\tconst explicitMin = start != null ? toDate(start).getTime() : NaN;\n\tconst explicitMax = end != null ? toDate(end).getTime() : NaN;\n\tconst minTs = Number.isFinite(explicitMin) ? explicitMin : Math.min(...dates);\n\tconst maxTs = Number.isFinite(explicitMax) ? explicitMax : Math.max(...dates);\n\tconst span = Math.max(1, maxTs - minTs);\n\n\tconst tToX = (t: number) => axisLeft + ((t - minTs) / span) * usableWidth;\n\n\t// Distribute events across stacked rows so labels don't overlap when\n\t// events cluster. A new event takes the topmost row whose previously-\n\t// placed event is at least MIN_GAP px to the left.\n\t//\n\t// Tiebreaker on id: Array.prototype.sort isn't stable across all\n\t// engines for events with identical timestamps. Sorting by id second\n\t// produces deterministic rows on every engine, so visual diffing\n\t// across builds stays meaningful.\n\tconst MIN_GAP_PX = 48;\n\tconst ROW_HEIGHT_PX = 22;\n\t// Cap rows so events never spill below the baseline. The first row\n\t// sits at y=24; remaining rows go in 22-px steps; clamp at axisY-10.\n\tconst maxRows = Math.max(1, Math.floor((axisY - 10 - 24) / ROW_HEIGHT_PX) + 1);\n\tconst rowsLastX: number[] = [];\n\tconst positioned = validEvents\n\t\t.map((event) => ({ event, t: toDate(event.date).getTime() }))\n\t\t.sort((a, b) => (a.t - b.t) || a.event.id.localeCompare(b.event.id))\n\t\t.map(({ event, t }) => {\n\t\t\tconst x = tToX(t);\n\t\t\tlet rowIndex = rowsLastX.findIndex((lastX) => x - lastX >= MIN_GAP_PX);\n\t\t\tif (rowIndex === -1) {\n\t\t\t\tif (rowsLastX.length < maxRows) {\n\t\t\t\t\trowIndex = rowsLastX.length;\n\t\t\t\t\trowsLastX.push(x);\n\t\t\t\t} else {\n\t\t\t\t\t// Degraded fallback — recycle the last row instead of\n\t\t\t\t\t// pushing the event below the baseline.\n\t\t\t\t\trowIndex = maxRows - 1;\n\t\t\t\t\trowsLastX[rowIndex] = x;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trowsLastX[rowIndex] = x;\n\t\t\t}\n\t\t\treturn { event, x, y: 24 + rowIndex * ROW_HEIGHT_PX, rowIndex };\n\t\t});\n\n\t// Generate ticks then dedupe consecutive identical labels — at year-scale\n\t// or month-scale spans the formatter often emits the same string twice\n\t// (e.g. five ticks all in 2025 render as \"2025\" / \"2025\" / ...). Blanking\n\t// the duplicate keeps the gridline tick but drops the noisy repeat.\n\tconst rawTicks: AxisTick[] = [];\n\tfor (let i = 0; i < tickCount; i++) {\n\t\tconst t = minTs + (i / Math.max(1, tickCount - 1)) * span;\n\t\trawTicks.push({ x: tToX(t), label: formatTick(new Date(t), span) });\n\t}\n\tconst ticks: AxisTick[] = rawTicks.map((t, i) => ({\n\t\tx: t.x,\n\t\tlabel: i > 0 && rawTicks[i - 1]?.label === t.label ? \"\" : t.label,\n\t}));\n\n\treturn {\n\t\tevents: positioned,\n\t\tticks,\n\t\taxisY,\n\t\taxisLeft,\n\t\taxisRight,\n\t\tstartLabel: formatDate(new Date(minTs)),\n\t\tendLabel: formatDate(new Date(maxTs)),\n\t};\n}\n\nfunction toDate(v: Date | string | number): Date {\n\tif (v instanceof Date) return v;\n\tif (typeof v === \"number\") return new Date(v);\n\treturn new Date(v);\n}\n\nfunction formatDate(d: Date): string {\n\tif (Number.isNaN(d.getTime())) return \"—\";\n\treturn d.toISOString().slice(0, 10);\n}\n\nfunction formatTick(d: Date, spanMs: number): string {\n\tif (Number.isNaN(d.getTime())) return \"—\";\n\tconst ONE_DAY = 24 * 60 * 60 * 1000;\n\tif (spanMs <= 7 * ONE_DAY) {\n\t\t// short range — show day\n\t\treturn d.toISOString().slice(0, 10);\n\t}\n\tif (spanMs <= 90 * ONE_DAY) {\n\t\t// up to ~3 months — show month-day so adjacent ticks stay distinct\n\t\treturn d.toISOString().slice(5, 10);\n\t}\n\tif (spanMs <= 730 * ONE_DAY) {\n\t\t// up to ~2 years — show year-month\n\t\treturn d.toISOString().slice(0, 7);\n\t}\n\t// long range — year only\n\treturn String(d.getUTCFullYear());\n}\n\nfunction activateOnKey(e: React.KeyboardEvent, fn: () => void): void {\n\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\te.preventDefault();\n\t\tfn();\n\t}\n}\n\nexport { TimeAxis };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/time-axis/time-axis.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;AC2DA,SAAS,QAAA,CAAS;AAAA,EACjB,MAAA;AAAA,EACA,KAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA,GAAQ,GAAA;AAAA,EACR,MAAA,GAAS,GAAA;AAAA,EACT,SAAA,GAAY,CAAA;AAAA,EACZ,YAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAkB;AACjB,EAAA,MAAM,OAAA,GAAgB,KAAA,CAAA,OAAA;AAAA,IACrB,MAAM,MAAA,CAAO,MAAA,EAAQ,OAAO,GAAA,EAAK,KAAA,EAAO,QAAQ,SAAS,CAAA;AAAA,IACzD,CAAC,MAAA,EAAQ,KAAA,EAAO,GAAA,EAAK,KAAA,EAAO,QAAQ,SAAS;AAAA,GAC9C;AAEA,EAAA,MAAM,OACL,MAAA,CAAO,MAAA,KAAW,IACf,iBAAA,GACA,CAAA,eAAA,EAAkB,OAAO,MAAM,CAAA,MAAA,EAAS,OAAO,MAAA,KAAW,CAAA,GAAI,KAAK,GAAG,CAAA,QAAA,EAAW,QAAQ,UAAU,CAAA,IAAA,EAAO,QAAQ,QAAQ,CAAA,CAAA;AAE9H,EAAA,uBACC,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAG,IAAA;AAAA,MACJ,oBAAA,EAAkB,IAAA;AAAA,MAClB,IAAA,EAAK,KAAA;AAAA,MACL,KAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA,EAAS,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,MAC/B,SAAA,EAAW,EAAA,CAAG,OAAA,EAAS,SAAS,CAAA;AAAA,MAEhC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,WAAM,QAAA,EAAA,WAAA,EAAS,CAAA;AAAA,wBAChB,GAAA,CAAC,UAAM,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,wBAEZ,IAAA,CAAC,GAAA,EAAA,EAAE,yBAAA,EAAuB,IAAA,EACzB,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACA,IAAI,OAAA,CAAQ,QAAA;AAAA,cACZ,IAAI,OAAA,CAAQ,SAAA;AAAA,cACZ,IAAI,OAAA,CAAQ,KAAA;AAAA,cACZ,IAAI,OAAA,CAAQ,KAAA;AAAA,cACZ,MAAA,EAAO,8BAAA;AAAA,cACP,WAAA,EAAa;AAAA;AAAA,WACd;AAAA,UACC,OAAA,CAAQ,MAAM,GAAA,CAAI,CAAC,MAAM,CAAA,qBACzB,IAAA,CAAC,GAAA,EAAA,EAAoB,yBAAA,EAAuB,IAAA,EAC3C,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACA,IAAI,IAAA,CAAK,CAAA;AAAA,gBACT,IAAI,IAAA,CAAK,CAAA;AAAA,gBACT,EAAA,EAAI,QAAQ,KAAA,GAAQ,CAAA;AAAA,gBACpB,EAAA,EAAI,QAAQ,KAAA,GAAQ,CAAA;AAAA,gBACpB,MAAA,EAAO,8BAAA;AAAA,gBACP,WAAA,EAAa;AAAA;AAAA,aACd;AAAA,4BACA,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACA,GAAG,IAAA,CAAK,CAAA;AAAA,gBACR,CAAA,EAAG,QAAQ,KAAA,GAAQ,EAAA;AAAA,gBACnB,UAAA,EAAW,QAAA;AAAA,gBACX,QAAA,EAAU,EAAA;AAAA,gBACV,IAAA,EAAK,8BAAA;AAAA,gBAEJ,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA;AACP,WAAA,EAAA,EAjBO,CAAA,KAAA,EAAQ,CAAC,CAAA,CAkBjB,CACA;AAAA,SAAA,EACF,CAAA;AAAA,wBACA,GAAA,CAAC,OAAE,2BAAA,EAAyB,IAAA,EAC1B,kBAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM;AAC1B,UAAA,MAAM,WAAA,GAAc,QAAQ,YAAY,CAAA;AACxC,UAAA,MAAM,cAAA,GAAiB,MAAM,YAAA,GAAe,CAAA,CAAE,KAAK,CAAA;AACnD,UAAA,uBACC,IAAA;AAAA,YAAC,GAAA;AAAA,YAAA;AAAA,cAEA,0BAAA,EAAwB,IAAA;AAAA,cACxB,YAAU,CAAA,CAAE,QAAA;AAAA,cACZ,IAAA,EAAM,cAAc,QAAA,GAAW,MAAA;AAAA,cAC/B,QAAA,EAAU,cAAc,CAAA,GAAI,MAAA;AAAA,cAC5B,YAAA,EAAY,WAAA,GAAc,CAAA,EAAG,CAAA,CAAE,MAAM,KAAK,CAAA,IAAA,EAAO,UAAA,CAAW,MAAA,CAAO,CAAA,CAAE,KAAA,CAAM,IAAI,CAAC,CAAC,CAAA,CAAA,GAAK,MAAA;AAAA,cACtF,KAAA,EAAO,WAAA,GAAc,EAAE,MAAA,EAAQ,WAAU,GAAI,MAAA;AAAA,cAC7C,OAAA,EAAS,cAAc,cAAA,GAAiB,MAAA;AAAA,cACxC,WAAW,WAAA,GAAc,CAAC,MAAM,aAAA,CAAc,CAAA,EAAG,cAAc,CAAA,GAAI,MAAA;AAAA,cAGnE,QAAA,EAAA;AAAA,gCAAA,GAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACA,IAAI,CAAA,CAAE,CAAA;AAAA,oBACN,IAAI,CAAA,CAAE,CAAA;AAAA,oBACN,IAAI,CAAA,CAAE,CAAA;AAAA,oBACN,IAAI,OAAA,CAAQ,KAAA;AAAA,oBACZ,MAAA,EAAO,8BAAA;AAAA,oBACP,aAAA,EAAe,IAAA;AAAA,oBACf,WAAA,EAAa;AAAA;AAAA,iBACd;AAAA,gCACA,GAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACA,IAAI,CAAA,CAAE,CAAA;AAAA,oBACN,IAAI,CAAA,CAAE,CAAA;AAAA,oBACN,CAAA,EAAG,CAAA;AAAA,oBACH,IAAA,EAAK,qBAAA;AAAA,oBACL,MAAA,EAAO,wBAAA;AAAA,oBACP,WAAA,EAAa;AAAA;AAAA,iBACd;AAAA,gCACA,GAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACA,CAAA,EAAG,EAAE,CAAA,GAAI,CAAA;AAAA,oBACT,CAAA,EAAG,EAAE,CAAA,GAAI,CAAA;AAAA,oBACT,QAAA,EAAU,EAAA;AAAA,oBACV,IAAA,EAAK,wBAAA;AAAA,oBACL,KAAA,EAAO,EAAE,UAAA,EAAY,QAAA,EAAU,eAAe,MAAA,EAAO;AAAA,oBACrD,MAAA,EAAO,wBAAA;AAAA,oBACP,WAAA,EAAa,CAAA;AAAA,oBACb,cAAA,EAAe,OAAA;AAAA,oBAEd,YAAE,KAAA,CAAM;AAAA;AAAA;AACV;AAAA,aAAA;AAAA,YAvCK,EAAE,KAAA,CAAM;AAAA,WAwCd;AAAA,QAEF,CAAC,CAAA,EACF;AAAA;AAAA;AAAA,GACD;AAEF;AAEA,SAAS,OACR,MAAA,EACA,KAAA,EACA,GAAA,EACA,KAAA,EACA,QACA,SAAA,EASC;AACD,EAAA,MAAM,MAAA,GAAS,EAAA;AACf,EAAA,MAAM,QAAA,GAAW,MAAA;AACjB,EAAA,MAAM,YAAY,KAAA,GAAQ,MAAA;AAC1B,EAAA,MAAM,QAAQ,MAAA,GAAS,EAAA;AACvB,EAAA,MAAM,cAAc,SAAA,GAAY,QAAA;AAOhC,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,CAAA,CAAE,IAAI,CAAA,CAAE,OAAA,EAAS,CAAC,CAAA;AAElF,EAAA,IAAI,WAAA,CAAY,WAAW,CAAA,EAAG;AAC7B,IAAA,OAAO;AAAA,MACN,QAAQ,EAAC;AAAA,MACT,OAAO,EAAC;AAAA,MACR,KAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAA;AAAA,MACA,UAAA,EAAY,QAAA;AAAA,MACZ,QAAA,EAAU;AAAA,KACX;AAAA,EACD;AAEA,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,OAAO,CAAA,CAAE,IAAI,CAAA,CAAE,OAAA,EAAS,CAAA;AAC7D,EAAA,MAAM,cAAc,KAAA,IAAS,IAAA,GAAO,OAAO,KAAK,CAAA,CAAE,SAAQ,GAAI,GAAA;AAC9D,EAAA,MAAM,cAAc,GAAA,IAAO,IAAA,GAAO,OAAO,GAAG,CAAA,CAAE,SAAQ,GAAI,GAAA;AAC1D,EAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,CAAS,WAAW,IAAI,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,GAAG,KAAK,CAAA;AAC5E,EAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,CAAS,WAAW,IAAI,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,GAAG,KAAK,CAAA;AAC5E,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,KAAK,CAAA;AAEtC,EAAA,MAAM,OAAO,CAAC,CAAA,KAAc,QAAA,GAAA,CAAa,CAAA,GAAI,SAAS,IAAA,GAAQ,WAAA;AAU9D,EAAA,MAAM,UAAA,GAAa,EAAA;AACnB,EAAA,MAAM,aAAA,GAAgB,EAAA;AAGtB,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAA,CAAO,KAAA,GAAQ,EAAA,GAAK,EAAA,IAAM,aAAa,CAAA,GAAI,CAAC,CAAA;AAC7E,EAAA,MAAM,YAAsB,EAAC;AAC7B,EAAA,MAAM,aAAa,WAAA,CACjB,GAAA,CAAI,CAAC,KAAA,MAAW,EAAE,KAAA,EAAO,CAAA,EAAG,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA,CAAE,OAAA,IAAU,CAAE,CAAA,CAC3D,KAAK,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,CAAE,IAAI,CAAA,CAAE,CAAA,IAAM,CAAA,CAAE,KAAA,CAAM,GAAG,aAAA,CAAc,CAAA,CAAE,KAAA,CAAM,EAAE,CAAC,CAAA,CAClE,GAAA,CAAI,CAAC,EAAE,KAAA,EAAO,GAAE,KAAM;AACtB,IAAA,MAAM,CAAA,GAAI,KAAK,CAAC,CAAA;AAChB,IAAA,IAAI,WAAW,SAAA,CAAU,SAAA,CAAU,CAAC,KAAA,KAAU,CAAA,GAAI,SAAS,UAAU,CAAA;AACrE,IAAA,IAAI,aAAa,EAAA,EAAI;AACpB,MAAA,IAAI,SAAA,CAAU,SAAS,OAAA,EAAS;AAC/B,QAAA,QAAA,GAAW,SAAA,CAAU,MAAA;AACrB,QAAA,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,MACjB,CAAA,MAAO;AAGN,QAAA,QAAA,GAAW,OAAA,GAAU,CAAA;AACrB,QAAA,SAAA,CAAU,QAAQ,CAAA,GAAI,CAAA;AAAA,MACvB;AAAA,IACD,CAAA,MAAO;AACN,MAAA,SAAA,CAAU,QAAQ,CAAA,GAAI,CAAA;AAAA,IACvB;AACA,IAAA,OAAO,EAAE,KAAA,EAAO,CAAA,EAAG,GAAG,EAAA,GAAK,QAAA,GAAW,eAAe,QAAA,EAAS;AAAA,EAC/D,CAAC,CAAA;AAMF,EAAA,MAAM,WAAuB,EAAC;AAC9B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,SAAA,EAAW,CAAA,EAAA,EAAK;AACnC,IAAA,MAAM,CAAA,GAAI,QAAS,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,SAAA,GAAY,CAAC,CAAA,GAAK,IAAA;AACrD,IAAA,QAAA,CAAS,IAAA,CAAK,EAAE,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,EAAG,KAAA,EAAO,UAAA,CAAW,IAAI,IAAA,CAAK,CAAC,CAAA,EAAG,IAAI,GAAG,CAAA;AAAA,EACnE;AACA,EAAA,MAAM,KAAA,GAAoB,QAAA,CAAS,GAAA,CAAI,CAAC,GAAG,CAAA,MAAO;AAAA,IACjD,GAAG,CAAA,CAAE,CAAA;AAAA,IACL,KAAA,EAAO,CAAA,GAAI,CAAA,IAAK,QAAA,CAAS,CAAA,GAAI,CAAC,CAAA,EAAG,KAAA,KAAU,CAAA,CAAE,KAAA,GAAQ,EAAA,GAAK,CAAA,CAAE;AAAA,GAC7D,CAAE,CAAA;AAEF,EAAA,OAAO;AAAA,IACN,MAAA,EAAQ,UAAA;AAAA,IACR,KAAA;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA,EAAY,UAAA,CAAW,IAAI,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,IACtC,QAAA,EAAU,UAAA,CAAW,IAAI,IAAA,CAAK,KAAK,CAAC;AAAA,GACrC;AACD;AAEA,SAAS,OAAO,CAAA,EAAiC;AAChD,EAAA,IAAI,CAAA,YAAa,MAAM,OAAO,CAAA;AAC9B,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,IAAI,KAAK,CAAC,CAAA;AAC5C,EAAA,OAAO,IAAI,KAAK,CAAC,CAAA;AAClB;AAEA,SAAS,WAAW,CAAA,EAAiB;AACpC,EAAA,IAAI,OAAO,KAAA,CAAM,CAAA,CAAE,OAAA,EAAS,GAAG,OAAO,QAAA;AACtC,EAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AACnC;AAEA,SAAS,UAAA,CAAW,GAAS,MAAA,EAAwB;AACpD,EAAA,IAAI,OAAO,KAAA,CAAM,CAAA,CAAE,OAAA,EAAS,GAAG,OAAO,QAAA;AACtC,EAAA,MAAM,OAAA,GAAU,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAC/B,EAAA,IAAI,MAAA,IAAU,IAAI,OAAA,EAAS;AAE1B,IAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,EACnC;AACA,EAAA,IAAI,MAAA,IAAU,KAAK,OAAA,EAAS;AAE3B,IAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,EACnC;AACA,EAAA,IAAI,MAAA,IAAU,MAAM,OAAA,EAAS;AAE5B,IAAA,OAAO,CAAA,CAAE,WAAA,EAAY,CAAE,KAAA,CAAM,GAAG,CAAC,CAAA;AAAA,EAClC;AAEA,EAAA,OAAO,MAAA,CAAO,CAAA,CAAE,cAAA,EAAgB,CAAA;AACjC;AAEA,SAAS,aAAA,CAAc,GAAwB,EAAA,EAAsB;AACpE,EAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACvC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,EAAA,EAAG;AAAA,EACJ;AACD","file":"time-axis.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Events plotted along a horizontal time axis. Pure SVG; no heavy peer\n * dependency. Distinct from the existing `<Timeline>` component in\n * `components/timeline` — that one is an event-list with vertical\n * status markers; this one is a CHART with a real time axis where\n * spacing reflects elapsed time.\n *\n * Use TimeAxis when the *gap between events* is the message (release\n * cadence, incident frequency, sparse-then-dense patterns). Use\n * Timeline when the order of events is the message but the absolute\n * dates are secondary.\n *\n * @example\n * <TimeAxis\n * events={[\n * { id: \"v1\", label: \"v1.0\", date: \"2025-01-15\" },\n * { id: \"v2\", label: \"v2.0\", date: \"2025-04-20\" },\n * { id: \"v3\", label: \"v3.0\", date: \"2025-09-10\" },\n * ]}\n * />\n */\nexport type TimeAxisEvent = {\n\tid: string;\n\tlabel: string;\n\t/** Accepts Date, ISO string, or epoch ms. */\n\tdate: Date | string | number;\n\t/** Optional category — events with the same `category` share a row band. */\n\tcategory?: string;\n};\n\n// `start` and `end` collide with SVG's `<animate start=...>` / `<set end=...>`\n// animation attributes (their value type is string|number, not Date). Omit\n// both from the inherited SVG attributes so consumers can pass Date values\n// freely without a type collision.\nexport interface TimeAxisProps\n\textends Omit<React.SVGAttributes<SVGSVGElement>, \"children\" | \"start\" | \"end\"> {\n\t/** Events to plot. Order doesn't matter — positions come from `date`. */\n\tevents: TimeAxisEvent[];\n\t/** Optional explicit axis start. Auto-derived from `events` if omitted. */\n\tstart?: Date | string | number;\n\t/** Optional explicit axis end. Auto-derived from `events` if omitted. */\n\tend?: Date | string | number;\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 200. */\n\theight?: number;\n\t/** Number of axis ticks to show. Default 6. */\n\ttickCount?: number;\n\t/** Fired when an event is clicked. */\n\tonEventClick?: (event: TimeAxisEvent) => void;\n}\n\ninterface LaidOutEvent {\n\tevent: TimeAxisEvent;\n\tx: number;\n\ty: number;\n\trowIndex: number;\n}\n\ninterface AxisTick {\n\tx: number;\n\tlabel: string;\n}\n\nfunction TimeAxis({\n\tevents,\n\tstart,\n\tend,\n\twidth = 720,\n\theight = 200,\n\ttickCount = 6,\n\tonEventClick,\n\tclassName,\n\t...rest\n}: TimeAxisProps) {\n\tconst laidOut = React.useMemo(\n\t\t() => layout(events, start, end, width, height, tickCount),\n\t\t[events, start, end, width, height, tickCount],\n\t);\n\n\tconst desc =\n\t\tevents.length === 0\n\t\t\t? \"Empty time axis\"\n\t\t\t: `Time axis with ${events.length} event${events.length === 1 ? \"\" : \"s\"}, range ${laidOut.startLabel} to ${laidOut.endLabel}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-time-axis\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Time axis</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t{/* Baseline + ticks */}\n\t\t\t<g data-hex-time-axis-axis>\n\t\t\t\t<line\n\t\t\t\t\tx1={laidOut.axisLeft}\n\t\t\t\t\tx2={laidOut.axisRight}\n\t\t\t\t\ty1={laidOut.axisY}\n\t\t\t\t\ty2={laidOut.axisY}\n\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t/>\n\t\t\t\t{laidOut.ticks.map((tick, i) => (\n\t\t\t\t\t<g key={`tick-${i}`} data-hex-time-axis-tick>\n\t\t\t\t\t\t<line\n\t\t\t\t\t\t\tx1={tick.x}\n\t\t\t\t\t\t\tx2={tick.x}\n\t\t\t\t\t\t\ty1={laidOut.axisY - 4}\n\t\t\t\t\t\t\ty2={laidOut.axisY + 4}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx={tick.x}\n\t\t\t\t\t\t\ty={laidOut.axisY + 18}\n\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{tick.label}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t</g>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-time-axis-events>\n\t\t\t\t{laidOut.events.map((e) => {\n\t\t\t\t\tconst interactive = Boolean(onEventClick);\n\t\t\t\t\tconst handleActivate = () => onEventClick?.(e.event);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={e.event.id}\n\t\t\t\t\t\t\tdata-hex-time-axis-event\n\t\t\t\t\t\t\tdata-row={e.rowIndex}\n\t\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\t\taria-label={interactive ? `${e.event.label} on ${formatDate(toDate(e.event.date))}` : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={interactive ? (k) => activateOnKey(k, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Connector from event marker to baseline */}\n\t\t\t\t\t\t\t<line\n\t\t\t\t\t\t\t\tx1={e.x}\n\t\t\t\t\t\t\t\tx2={e.x}\n\t\t\t\t\t\t\t\ty1={e.y}\n\t\t\t\t\t\t\t\ty2={laidOut.axisY}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t\tstrokeOpacity={0.65}\n\t\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={e.x}\n\t\t\t\t\t\t\t\tcy={e.y}\n\t\t\t\t\t\t\t\tr={5}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={e.x + 8}\n\t\t\t\t\t\t\t\ty={e.y + 4}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ paintOrder: \"stroke\", pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={3}\n\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{e.event.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\tevents: TimeAxisEvent[],\n\tstart: Date | string | number | undefined,\n\tend: Date | string | number | undefined,\n\twidth: number,\n\theight: number,\n\ttickCount: number,\n): {\n\tevents: LaidOutEvent[];\n\tticks: AxisTick[];\n\taxisY: number;\n\taxisLeft: number;\n\taxisRight: number;\n\tstartLabel: string;\n\tendLabel: string;\n} {\n\tconst margin = 32;\n\tconst axisLeft = margin;\n\tconst axisRight = width - margin;\n\tconst axisY = height - 40;\n\tconst usableWidth = axisRight - axisLeft;\n\n\t// Drop events whose date doesn't parse — `new Date(\"garbage\")` returns\n\t// NaN, which would propagate through `Math.min/max` and produce NaN\n\t// x-coordinates that crash some SVG renderers and noise up dev\n\t// consoles. Skip silently; downstream components can still inspect\n\t// the unrendered ids if they want to surface validation errors.\n\tconst validEvents = events.filter((e) => Number.isFinite(toDate(e.date).getTime()));\n\n\tif (validEvents.length === 0) {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tticks: [],\n\t\t\taxisY,\n\t\t\taxisLeft,\n\t\t\taxisRight,\n\t\t\tstartLabel: \"—\",\n\t\t\tendLabel: \"—\",\n\t\t};\n\t}\n\n\tconst dates = validEvents.map((e) => toDate(e.date).getTime());\n\tconst explicitMin = start != null ? toDate(start).getTime() : NaN;\n\tconst explicitMax = end != null ? toDate(end).getTime() : NaN;\n\tconst minTs = Number.isFinite(explicitMin) ? explicitMin : Math.min(...dates);\n\tconst maxTs = Number.isFinite(explicitMax) ? explicitMax : Math.max(...dates);\n\tconst span = Math.max(1, maxTs - minTs);\n\n\tconst tToX = (t: number) => axisLeft + ((t - minTs) / span) * usableWidth;\n\n\t// Distribute events across stacked rows so labels don't overlap when\n\t// events cluster. A new event takes the topmost row whose previously-\n\t// placed event is at least MIN_GAP px to the left.\n\t//\n\t// Tiebreaker on id: Array.prototype.sort isn't stable across all\n\t// engines for events with identical timestamps. Sorting by id second\n\t// produces deterministic rows on every engine, so visual diffing\n\t// across builds stays meaningful.\n\tconst MIN_GAP_PX = 48;\n\tconst ROW_HEIGHT_PX = 22;\n\t// Cap rows so events never spill below the baseline. The first row\n\t// sits at y=24; remaining rows go in 22-px steps; clamp at axisY-10.\n\tconst maxRows = Math.max(1, Math.floor((axisY - 10 - 24) / ROW_HEIGHT_PX) + 1);\n\tconst rowsLastX: number[] = [];\n\tconst positioned = validEvents\n\t\t.map((event) => ({ event, t: toDate(event.date).getTime() }))\n\t\t.sort((a, b) => (a.t - b.t) || a.event.id.localeCompare(b.event.id))\n\t\t.map(({ event, t }) => {\n\t\t\tconst x = tToX(t);\n\t\t\tlet rowIndex = rowsLastX.findIndex((lastX) => x - lastX >= MIN_GAP_PX);\n\t\t\tif (rowIndex === -1) {\n\t\t\t\tif (rowsLastX.length < maxRows) {\n\t\t\t\t\trowIndex = rowsLastX.length;\n\t\t\t\t\trowsLastX.push(x);\n\t\t\t\t} else {\n\t\t\t\t\t// Degraded fallback — recycle the last row instead of\n\t\t\t\t\t// pushing the event below the baseline.\n\t\t\t\t\trowIndex = maxRows - 1;\n\t\t\t\t\trowsLastX[rowIndex] = x;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trowsLastX[rowIndex] = x;\n\t\t\t}\n\t\t\treturn { event, x, y: 24 + rowIndex * ROW_HEIGHT_PX, rowIndex };\n\t\t});\n\n\t// Generate ticks then dedupe consecutive identical labels — at year-scale\n\t// or month-scale spans the formatter often emits the same string twice\n\t// (e.g. five ticks all in 2025 render as \"2025\" / \"2025\" / ...). Blanking\n\t// the duplicate keeps the gridline tick but drops the noisy repeat.\n\tconst rawTicks: AxisTick[] = [];\n\tfor (let i = 0; i < tickCount; i++) {\n\t\tconst t = minTs + (i / Math.max(1, tickCount - 1)) * span;\n\t\trawTicks.push({ x: tToX(t), label: formatTick(new Date(t), span) });\n\t}\n\tconst ticks: AxisTick[] = rawTicks.map((t, i) => ({\n\t\tx: t.x,\n\t\tlabel: i > 0 && rawTicks[i - 1]?.label === t.label ? \"\" : t.label,\n\t}));\n\n\treturn {\n\t\tevents: positioned,\n\t\tticks,\n\t\taxisY,\n\t\taxisLeft,\n\t\taxisRight,\n\t\tstartLabel: formatDate(new Date(minTs)),\n\t\tendLabel: formatDate(new Date(maxTs)),\n\t};\n}\n\nfunction toDate(v: Date | string | number): Date {\n\tif (v instanceof Date) return v;\n\tif (typeof v === \"number\") return new Date(v);\n\treturn new Date(v);\n}\n\nfunction formatDate(d: Date): string {\n\tif (Number.isNaN(d.getTime())) return \"—\";\n\treturn d.toISOString().slice(0, 10);\n}\n\nfunction formatTick(d: Date, spanMs: number): string {\n\tif (Number.isNaN(d.getTime())) return \"—\";\n\tconst ONE_DAY = 24 * 60 * 60 * 1000;\n\tif (spanMs <= 7 * ONE_DAY) {\n\t\t// short range — show day\n\t\treturn d.toISOString().slice(0, 10);\n\t}\n\tif (spanMs <= 90 * ONE_DAY) {\n\t\t// up to ~3 months — show month-day so adjacent ticks stay distinct\n\t\treturn d.toISOString().slice(5, 10);\n\t}\n\tif (spanMs <= 730 * ONE_DAY) {\n\t\t// up to ~2 years — show year-month\n\t\treturn d.toISOString().slice(0, 7);\n\t}\n\t// long range — year only\n\treturn String(d.getUTCFullYear());\n}\n\nfunction activateOnKey(e: React.KeyboardEvent, fn: () => void): void {\n\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\te.preventDefault();\n\t\tfn();\n\t}\n}\n\nexport { TimeAxis };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/components/time-picker/time-picker.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;AC6BA,IAAM,UAAA,GAAmB,KAAA,CAAA,UAAA;AAAA,EACxB,CACC;AAAA,IACC,KAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAA;AAAA,IACA,GAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA,EAAc,SAAA;AAAA,IACd,GAAG;AAAA,KAEJ,GAAA,KACI;AACJ,IAAA,uBACC,GAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACA,GAAA;AAAA,QACA,IAAA,EAAK,MAAA;AAAA,QACL,OAAO,KAAA,IAAS,EAAA;AAAA,QAChB,IAAA;AAAA,QACA,GAAA;AAAA,QACA,GAAA;AAAA,QACA,QAAA;AAAA,QACA,YAAA,EAAY,SAAA;AAAA,QACZ,UAAU,CAAC,CAAA,KAAM,QAAA,GAAW,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,QAC1C,SAAA,EAAW,EAAA;AAAA,UACV,gQAAA;AAAA,UACA,qGAAA;AAAA,UACA,kDAAA;AAAA,UACA,kKAAA;AAAA,UACA;AAAA,SACD;AAAA,QACC,GAAG;AAAA;AAAA,KACL;AAAA,EAEF;AACD;AACA,UAAA,CAAW,WAAA,GAAc,YAAA","file":"time-picker.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\ninterface TimePickerProps\n\textends Omit<\n\t\tReact.InputHTMLAttributes<HTMLInputElement>,\n\t\t\"type\" | \"value\" | \"onChange\" | \"size\"\n\t> {\n\t/** Controlled time value as `\"HH:MM\"` or `\"HH:MM:SS\"` (24-hour). */\n\tvalue?: string;\n\t/** Fired with the new `\"HH:MM\"` (or `\"HH:MM:SS\"`) string when the user picks a time. */\n\tonChange?: (value: string) => void;\n\t/**\n\t * Step in seconds — `60` shows HH:MM only (default), `1` shows HH:MM:SS.\n\t * Smaller values change the spinner increment in supported browsers.\n\t */\n\tstep?: number;\n\t/** Earliest selectable time as `\"HH:MM\"`. */\n\tmin?: string;\n\t/** Latest selectable time as `\"HH:MM\"`. */\n\tmax?: string;\n\t/** Disable the input. */\n\tdisabled?: boolean;\n\t/** Accessible label for the trigger (required when no adjacent visible <label>). */\n\t\"aria-label\"?: string;\n}\n\n/**\n * Time input — styled wrapper around the native `<input type=\"time\">` so the\n * browser handles 12/24-hour locale, keyboard arrow stepping, and screen-reader\n * announcement. Value is a `\"HH:MM\"` (or `\"HH:MM:SS\"` when `step={1}`) string.\n *\n * For free-form composite hour/minute fields with explicit AM/PM segments,\n * compose Input + Select yourself — the native input covers ~95% of TimePicker\n * use cases with full a11y, including keyboard-driven hour/minute spinning.\n * @returns A token-styled native time input.\n */\nconst TimePicker = React.forwardRef<HTMLInputElement, TimePickerProps>(\n\t(\n\t\t{\n\t\t\tvalue,\n\t\t\tonChange,\n\t\t\tstep,\n\t\t\tmin,\n\t\t\tmax,\n\t\t\tdisabled,\n\t\t\tclassName,\n\t\t\t\"aria-label\": ariaLabel,\n\t\t\t...rest\n\t\t},\n\t\tref,\n\t) => {\n\t\treturn (\n\t\t\t<input\n\t\t\t\tref={ref}\n\t\t\t\ttype=\"time\"\n\t\t\t\tvalue={value ?? \"\"}\n\t\t\t\tstep={step}\n\t\t\t\tmin={min}\n\t\t\t\tmax={max}\n\t\t\t\tdisabled={disabled}\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\tonChange={(e) => onChange?.(e.target.value)}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"inline-flex h-[var(--control-height-md,2.5rem)] w-[160px] items-center rounded-md border border-input bg-background px-[var(--space-3,0.75rem)] py-[var(--space-2,0.5rem)] text-sm font-normal transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\t\"[&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-60 hover:[&::-webkit-calendar-picker-indicator]:opacity-100\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\t{...rest}\n\t\t\t/>\n\t\t);\n\t},\n);\nTimePicker.displayName = \"TimePicker\";\n\nexport { TimePicker };\nexport type { TimePickerProps };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/time-picker/time-picker.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;AC6BA,IAAM,UAAA,GAAmB,KAAA,CAAA,UAAA;AAAA,EACxB,CACC;AAAA,IACC,KAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAA;AAAA,IACA,GAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA,EAAc,SAAA;AAAA,IACd,GAAG;AAAA,KAEJ,GAAA,KACI;AACJ,IAAA,uBACC,GAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACA,GAAA;AAAA,QACA,IAAA,EAAK,MAAA;AAAA,QACL,OAAO,KAAA,IAAS,EAAA;AAAA,QAChB,IAAA;AAAA,QACA,GAAA;AAAA,QACA,GAAA;AAAA,QACA,QAAA;AAAA,QACA,YAAA,EAAY,SAAA;AAAA,QACZ,UAAU,CAAC,CAAA,KAAM,QAAA,GAAW,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,QAC1C,SAAA,EAAW,EAAA;AAAA,UACV,gQAAA;AAAA,UACA,qGAAA;AAAA,UACA,kDAAA;AAAA,UACA,kKAAA;AAAA,UACA;AAAA,SACD;AAAA,QACC,GAAG;AAAA;AAAA,KACL;AAAA,EAEF;AACD;AACA,UAAA,CAAW,WAAA,GAAc,YAAA","file":"time-picker.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\ninterface TimePickerProps\n\textends Omit<\n\t\tReact.InputHTMLAttributes<HTMLInputElement>,\n\t\t\"type\" | \"value\" | \"onChange\" | \"size\"\n\t> {\n\t/** Controlled time value as `\"HH:MM\"` or `\"HH:MM:SS\"` (24-hour). */\n\tvalue?: string;\n\t/** Fired with the new `\"HH:MM\"` (or `\"HH:MM:SS\"`) string when the user picks a time. */\n\tonChange?: (value: string) => void;\n\t/**\n\t * Step in seconds — `60` shows HH:MM only (default), `1` shows HH:MM:SS.\n\t * Smaller values change the spinner increment in supported browsers.\n\t */\n\tstep?: number;\n\t/** Earliest selectable time as `\"HH:MM\"`. */\n\tmin?: string;\n\t/** Latest selectable time as `\"HH:MM\"`. */\n\tmax?: string;\n\t/** Disable the input. */\n\tdisabled?: boolean;\n\t/** Accessible label for the trigger (required when no adjacent visible <label>). */\n\t\"aria-label\"?: string;\n}\n\n/**\n * Time input — styled wrapper around the native `<input type=\"time\">` so the\n * browser handles 12/24-hour locale, keyboard arrow stepping, and screen-reader\n * announcement. Value is a `\"HH:MM\"` (or `\"HH:MM:SS\"` when `step={1}`) string.\n *\n * For free-form composite hour/minute fields with explicit AM/PM segments,\n * compose Input + Select yourself — the native input covers ~95% of TimePicker\n * use cases with full a11y, including keyboard-driven hour/minute spinning.\n * @returns A token-styled native time input.\n */\nconst TimePicker = React.forwardRef<HTMLInputElement, TimePickerProps>(\n\t(\n\t\t{\n\t\t\tvalue,\n\t\t\tonChange,\n\t\t\tstep,\n\t\t\tmin,\n\t\t\tmax,\n\t\t\tdisabled,\n\t\t\tclassName,\n\t\t\t\"aria-label\": ariaLabel,\n\t\t\t...rest\n\t\t},\n\t\tref,\n\t) => {\n\t\treturn (\n\t\t\t<input\n\t\t\t\tref={ref}\n\t\t\t\ttype=\"time\"\n\t\t\t\tvalue={value ?? \"\"}\n\t\t\t\tstep={step}\n\t\t\t\tmin={min}\n\t\t\t\tmax={max}\n\t\t\t\tdisabled={disabled}\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\tonChange={(e) => onChange?.(e.target.value)}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"inline-flex h-[var(--control-height-md,2.5rem)] w-[160px] items-center rounded-md border border-input bg-background px-[var(--space-3,0.75rem)] py-[var(--space-2,0.5rem)] text-sm font-normal transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\t\t\t\"[&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-60 hover:[&::-webkit-calendar-picker-indicator]:opacity-100\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\t{...rest}\n\t\t\t/>\n\t\t);\n\t},\n);\nTimePicker.displayName = \"TimePicker\";\n\nexport { TimePicker };\nexport type { TimePickerProps };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/components/timeline/timeline.tsx"],"names":[],"mappings":"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","file":"timeline.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","import { cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\ntype TimelineStatus = \"default\" | \"success\" | \"warning\" | \"error\" | \"info\";\n\ninterface TimelineEvent {\n\t/** Stable unique id used as the React key. */\n\tid: string;\n\t/** Headline for the event. */\n\ttitle: string;\n\t/** Optional timestamp/metadata (e.g. \"2 hours ago\", \"2026-04-27 14:30\"). */\n\ttimestamp?: React.ReactNode;\n\t/** Optional secondary text/body. */\n\tdescription?: React.ReactNode;\n\t/** Optional icon override for the indicator. Replaces the default dot. */\n\ticon?: React.ReactNode;\n\t/** Color variant for the indicator. */\n\tstatus?: TimelineStatus;\n}\n\n/*\n * `warning` uses a token-with-fallback so themes can override `--color-warning`\n * without touching component source. The OKLCH default lands at a desaturated\n * amber that holds AA contrast on both light and dark backgrounds.\n */\nconst indicator = cva(\n\t\"relative z-10 inline-flex shrink-0 items-center justify-center rounded-full border-2 transition-colors duration-[var(--duration-normal,200ms)] ease-out\",\n\t{\n\t\tvariants: {\n\t\t\tstatus: {\n\t\t\t\tdefault: \"bg-background border-input text-muted-foreground\",\n\t\t\t\tsuccess: \"bg-background border-primary text-primary\",\n\t\t\t\twarning:\n\t\t\t\t\t\"bg-background border-[var(--color-warning,oklch(0.78_0.16_82))] text-[var(--color-warning,oklch(0.78_0.16_82))]\",\n\t\t\t\terror: \"bg-destructive border-destructive text-destructive-foreground\",\n\t\t\t\tinfo: \"bg-background border-ring text-ring\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tsm: \"h-5 w-5 text-[10px]\",\n\t\t\t\tmd: \"h-7 w-7 text-xs\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { status: \"default\", size: \"md\" },\n\t},\n);\n\ninterface TimelineProps\n\textends Omit<React.HTMLAttributes<HTMLOListElement>, \"aria-label\"> {\n\t/** Ordered list of chronological events. */\n\tevents: TimelineEvent[];\n\t/** Indicator size — `\"md\"` by default. */\n\tsize?: \"sm\" | \"md\";\n\t/** Required accessible name for the ordered list. */\n\t\"aria-label\": string;\n}\n\n/** Solid dot rendered inside the indicator when no per-event icon is supplied. */\nfunction DefaultDot() {\n\treturn <span className=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />;\n}\n\n/**\n * Vertical chronological event feed (activity log, audit trail, release notes).\n * Pure semantic HTML — `<ol>` of `<li>`. Events expose an optional icon, a\n * status color, a timestamp, and a description; status is purely visual, no\n * aria-current is set since events are historical, not navigational.\n *\n * For Gantt-style project timelines, build a custom layout — Timeline is for\n * event feeds, not scheduling.\n * @returns An accessible vertical event list.\n */\nfunction Timeline({\n\tevents,\n\tsize = \"md\",\n\t\"aria-label\": ariaLabel,\n\tclassName,\n\t...rest\n}: TimelineProps) {\n\treturn (\n\t\t<ol\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\"flex flex-col list-none p-0 m-0\", className)}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{events.map((event, index) => {\n\t\t\t\tconst isLast = index === events.length - 1;\n\t\t\t\tconst status = event.status ?? \"default\";\n\t\t\t\treturn (\n\t\t\t\t\t<li key={event.id} className=\"relative flex gap-[var(--space-3,0.75rem)]\">\n\t\t\t\t\t\t<div className=\"flex flex-col items-center\">\n\t\t\t\t\t\t\t<span className={indicator({ status, size })}>\n\t\t\t\t\t\t\t\t{event.icon ?? <DefaultDot />}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{!isLast ? (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\tclassName=\"w-px flex-1 bg-input min-h-[var(--space-6,1.5rem)]\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex flex-col gap-[var(--space-1,0.25rem)] min-w-0\",\n\t\t\t\t\t\t\t\t!isLast && \"pb-[var(--space-6,1.5rem)]\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"flex flex-wrap items-baseline gap-[var(--space-2,0.5rem)]\">\n\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium text-foreground\">\n\t\t\t\t\t\t\t\t\t{event.title}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{event.timestamp ? (\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{event.timestamp}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{event.description ? (\n\t\t\t\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{event.description}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t);\n\t\t\t})}\n\t\t</ol>\n\t);\n}\nTimeline.displayName = \"Timeline\";\n\nexport { Timeline };\nexport type { TimelineEvent, TimelineProps, TimelineStatus };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/timeline/timeline.tsx"],"names":[],"mappings":"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","file":"timeline.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","import { cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\ntype TimelineStatus = \"default\" | \"success\" | \"warning\" | \"error\" | \"info\";\n\ninterface TimelineEvent {\n\t/** Stable unique id used as the React key. */\n\tid: string;\n\t/** Headline for the event. */\n\ttitle: string;\n\t/** Optional timestamp/metadata (e.g. \"2 hours ago\", \"2026-04-27 14:30\"). */\n\ttimestamp?: React.ReactNode;\n\t/** Optional secondary text/body. */\n\tdescription?: React.ReactNode;\n\t/** Optional icon override for the indicator. Replaces the default dot. */\n\ticon?: React.ReactNode;\n\t/** Color variant for the indicator. */\n\tstatus?: TimelineStatus;\n}\n\n/*\n * `warning` uses a token-with-fallback so themes can override `--color-warning`\n * without touching component source. The OKLCH default lands at a desaturated\n * amber that holds AA contrast on both light and dark backgrounds.\n */\nconst indicator = cva(\n\t\"relative z-10 inline-flex shrink-0 items-center justify-center rounded-full border-2 transition-colors duration-[var(--duration-normal,200ms)] ease-out\",\n\t{\n\t\tvariants: {\n\t\t\tstatus: {\n\t\t\t\tdefault: \"bg-background border-input text-muted-foreground\",\n\t\t\t\tsuccess: \"bg-background border-primary text-primary\",\n\t\t\t\twarning:\n\t\t\t\t\t\"bg-background border-[var(--color-warning,oklch(0.78_0.16_82))] text-[var(--color-warning,oklch(0.78_0.16_82))]\",\n\t\t\t\terror: \"bg-destructive border-destructive text-destructive-foreground\",\n\t\t\t\tinfo: \"bg-background border-ring text-ring\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tsm: \"h-5 w-5 text-[10px]\",\n\t\t\t\tmd: \"h-7 w-7 text-xs\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { status: \"default\", size: \"md\" },\n\t},\n);\n\ninterface TimelineProps\n\textends Omit<React.HTMLAttributes<HTMLOListElement>, \"aria-label\"> {\n\t/** Ordered list of chronological events. */\n\tevents: TimelineEvent[];\n\t/** Indicator size — `\"md\"` by default. */\n\tsize?: \"sm\" | \"md\";\n\t/** Required accessible name for the ordered list. */\n\t\"aria-label\": string;\n}\n\n/** Solid dot rendered inside the indicator when no per-event icon is supplied. */\nfunction DefaultDot() {\n\treturn <span className=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />;\n}\n\n/**\n * Vertical chronological event feed (activity log, audit trail, release notes).\n * Pure semantic HTML — `<ol>` of `<li>`. Events expose an optional icon, a\n * status color, a timestamp, and a description; status is purely visual, no\n * aria-current is set since events are historical, not navigational.\n *\n * For Gantt-style project timelines, build a custom layout — Timeline is for\n * event feeds, not scheduling.\n * @returns An accessible vertical event list.\n */\nfunction Timeline({\n\tevents,\n\tsize = \"md\",\n\t\"aria-label\": ariaLabel,\n\tclassName,\n\t...rest\n}: TimelineProps) {\n\treturn (\n\t\t<ol\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\"flex flex-col list-none p-0 m-0\", className)}\n\t\t\t{...rest}\n\t\t>\n\t\t\t{events.map((event, index) => {\n\t\t\t\tconst isLast = index === events.length - 1;\n\t\t\t\tconst status = event.status ?? \"default\";\n\t\t\t\treturn (\n\t\t\t\t\t<li key={event.id} className=\"relative flex gap-[var(--space-3,0.75rem)]\">\n\t\t\t\t\t\t<div className=\"flex flex-col items-center\">\n\t\t\t\t\t\t\t<span className={indicator({ status, size })}>\n\t\t\t\t\t\t\t\t{event.icon ?? <DefaultDot />}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{!isLast ? (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\tclassName=\"w-px flex-1 bg-input min-h-[var(--space-6,1.5rem)]\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex flex-col gap-[var(--space-1,0.25rem)] min-w-0\",\n\t\t\t\t\t\t\t\t!isLast && \"pb-[var(--space-6,1.5rem)]\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"flex flex-wrap items-baseline gap-[var(--space-2,0.5rem)]\">\n\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium text-foreground\">\n\t\t\t\t\t\t\t\t\t{event.title}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{event.timestamp ? (\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{event.timestamp}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{event.description ? (\n\t\t\t\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{event.description}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t);\n\t\t\t})}\n\t\t</ol>\n\t);\n}\nTimeline.displayName = \"Timeline\";\n\nexport { Timeline };\nexport type { TimelineEvent, TimelineProps, TimelineStatus };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/toggle/toggle.tsx","../src/primitives/toggle-group/toggle-group.tsx"],"names":["React","jsx"],"mappings":";;;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,cAAA,GAAiB,GAAA;AAAA,EACtB;AAAA,IACC,wEAAA;AAAA,IACA,iEAAA;AAAA,IACA,4CAAA;AAAA,IACA,qGAAA;AAAA,IACA,kDAAA;AAAA,IACA,kEAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,gBAAA;AAAA,QACT,OAAA,EACC;AAAA,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACL,OAAA,EAAS,yGAAA;AAAA,QACT,EAAA,EAAI,sFAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACL,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA,EAAW,MAAM,SAAA;AAAU;AAEzD,CAAA;AAMA,IAAM,MAAA,GAAeA,MAAA,CAAA,UAAA,CAGnB,CAAC,EAAE,SAAA,EAAW,SAAS,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,qBAC1C,GAAA;AAAA,EAAiB,eAAA,CAAA,IAAA;AAAA,EAAhB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,GAAG,cAAA,CAAe,EAAE,SAAS,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,IACzD,GAAG;AAAA;AACL,CACA,CAAA;AACD,MAAA,CAAO,WAAA,GAAc,QAAA;ACtCrB,IAAM,qBAA2B,MAAA,CAAA,aAAA,CAAuC;AAAA,EACvE,IAAA,EAAM,SAAA;AAAA,EACN,OAAA,EAAS;AACV,CAAC,CAAA;AAGD,IAAM,WAAA,GAAoB,MAAA,CAAA,UAAA,CAIxB,CAAC,EAAE,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACpDC,GAAAA;AAAA,EAAsB,oBAAA,CAAA,IAAA;AAAA,EAArB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA,CAAG,wCAAA,EAA0C,SAAS,CAAA;AAAA,IAChE,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAAA,GAAAA,CAAC,kBAAA,CAAmB,QAAA,EAAnB,EAA4B,OAAO,EAAE,OAAA,EAAS,IAAA,EAAK,EAAI,QAAA,EAAS;AAAA;AAClE,CACA;AACD,WAAA,CAAY,WAAA,GAAc,aAAA;AAG1B,IAAM,eAAA,GAAwB,MAAA,CAAA,UAAA,CAI5B,CAAC,EAAE,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AAC5D,EAAA,MAAM,OAAA,GAAgB,kBAAW,kBAAkB,CAAA;AACnD,EAAA,uBACCA,GAAAA;AAAA,IAAsB,oBAAA,CAAA,IAAA;AAAA,IAArB;AAAA,MACA,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,cAAA,CAAe;AAAA,UACd,OAAA,EAAS,WAAW,OAAA,CAAQ,OAAA;AAAA,UAC5B,IAAA,EAAM,QAAQ,OAAA,CAAQ;AAAA,SACtB,CAAA;AAAA,QACD;AAAA,OACD;AAAA,MACC,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACF;AAEF,CAAC;AACD,eAAA,CAAgB,WAAA,GAAc,iBAAA","file":"toggle-group.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst toggleVariants = cva(\n\t[\n\t\t\"inline-flex items-center justify-center rounded-md text-sm font-medium\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\"hover:bg-muted hover:text-muted-foreground\",\n\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\",\n\t\t\"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-transparent\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-input bg-transparent shadow-sm inset-ring-1 inset-ring-foreground/[0.06] hover:bg-accent hover:text-accent-foreground\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-[var(--control-height-md,2.5rem)] px-[var(--space-3,0.75rem)] min-w-[var(--control-height-md,2.5rem)]\",\n\t\t\t\tsm: \"h-[var(--control-height-sm,2.25rem)] px-2.5 min-w-[var(--control-height-sm,2.25rem)]\",\n\t\t\t\tlg: \"h-[var(--control-height-lg,2.75rem)] px-5 min-w-[var(--control-height-lg,2.75rem)]\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\", size: \"default\" },\n\t},\n);\n\n/**\n * A two-state button. Stays pressed when toggled on.\n * @returns A styled Radix Toggle root\n */\nconst Toggle = React.forwardRef<\n\tReact.ComponentRef<typeof TogglePrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>\n>(({ className, variant, size, ...props }, ref) => (\n\t<TogglePrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(toggleVariants({ variant, size, className }))}\n\t\t{...props}\n\t/>\n));\nToggle.displayName = \"Toggle\";\n\nexport { Toggle, toggleVariants };\n","\"use client\";\n\nimport * as ToggleGroupPrimitive from \"@radix-ui/react-toggle-group\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport { toggleVariants } from \"../toggle/toggle.js\";\n\ntype ToggleGroupContextValue = VariantProps<typeof toggleVariants>;\n\nconst ToggleGroupContext = React.createContext<ToggleGroupContextValue>({\n\tsize: \"default\",\n\tvariant: \"default\",\n});\n\n/** Root container for a set of toggles (single-select or multi-select). */\nconst ToggleGroup = React.forwardRef<\n\tReact.ComponentRef<typeof ToggleGroupPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &\n\t\tVariantProps<typeof toggleVariants>\n>(({ className, variant, size, children, ...props }, ref) => (\n\t<ToggleGroupPrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(\"flex items-center justify-center gap-1\", className)}\n\t\t{...props}\n\t>\n\t\t<ToggleGroupContext.Provider value={{ variant, size }}>{children}</ToggleGroupContext.Provider>\n\t</ToggleGroupPrimitive.Root>\n));\nToggleGroup.displayName = \"ToggleGroup\";\n\n/** A single toggle option within a ToggleGroup. Inherits variant/size from context. */\nconst ToggleGroupItem = React.forwardRef<\n\tReact.ComponentRef<typeof ToggleGroupPrimitive.Item>,\n\tReact.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &\n\t\tVariantProps<typeof toggleVariants>\n>(({ className, children, variant, size, ...props }, ref) => {\n\tconst context = React.useContext(ToggleGroupContext);\n\treturn (\n\t\t<ToggleGroupPrimitive.Item\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\ttoggleVariants({\n\t\t\t\t\tvariant: variant ?? context.variant,\n\t\t\t\t\tsize: size ?? context.size,\n\t\t\t\t}),\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</ToggleGroupPrimitive.Item>\n\t);\n});\nToggleGroupItem.displayName = \"ToggleGroupItem\";\n\nexport { ToggleGroup, ToggleGroupItem };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/toggle/toggle.tsx","../src/primitives/toggle-group/toggle-group.tsx"],"names":["React","jsx"],"mappings":";;;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,cAAA,GAAiB,GAAA;AAAA,EACtB;AAAA,IACC,wEAAA;AAAA,IACA,iEAAA;AAAA,IACA,4CAAA;AAAA,IACA,qGAAA;AAAA,IACA,kDAAA;AAAA,IACA,kEAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,gBAAA;AAAA,QACT,OAAA,EACC;AAAA,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACL,OAAA,EAAS,yGAAA;AAAA,QACT,EAAA,EAAI,sFAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACL,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA,EAAW,MAAM,SAAA;AAAU;AAEzD,CAAA;AAMA,IAAM,MAAA,GAAeA,MAAA,CAAA,UAAA,CAGnB,CAAC,EAAE,SAAA,EAAW,SAAS,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,qBAC1C,GAAA;AAAA,EAAiB,eAAA,CAAA,IAAA;AAAA,EAAhB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,GAAG,cAAA,CAAe,EAAE,SAAS,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,IACzD,GAAG;AAAA;AACL,CACA,CAAA;AACD,MAAA,CAAO,WAAA,GAAc,QAAA;ACtCrB,IAAM,qBAA2B,MAAA,CAAA,aAAA,CAAuC;AAAA,EACvE,IAAA,EAAM,SAAA;AAAA,EACN,OAAA,EAAS;AACV,CAAC,CAAA;AAGD,IAAM,WAAA,GAAoB,MAAA,CAAA,UAAA,CAIxB,CAAC,EAAE,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACpDC,GAAAA;AAAA,EAAsB,oBAAA,CAAA,IAAA;AAAA,EAArB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA,CAAG,wCAAA,EAA0C,SAAS,CAAA;AAAA,IAChE,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAAA,GAAAA,CAAC,kBAAA,CAAmB,QAAA,EAAnB,EAA4B,OAAO,EAAE,OAAA,EAAS,IAAA,EAAK,EAAI,QAAA,EAAS;AAAA;AAClE,CACA;AACD,WAAA,CAAY,WAAA,GAAc,aAAA;AAG1B,IAAM,eAAA,GAAwB,MAAA,CAAA,UAAA,CAI5B,CAAC,EAAE,SAAA,EAAW,QAAA,EAAU,OAAA,EAAS,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AAC5D,EAAA,MAAM,OAAA,GAAgB,kBAAW,kBAAkB,CAAA;AACnD,EAAA,uBACCA,GAAAA;AAAA,IAAsB,oBAAA,CAAA,IAAA;AAAA,IAArB;AAAA,MACA,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,cAAA,CAAe;AAAA,UACd,OAAA,EAAS,WAAW,OAAA,CAAQ,OAAA;AAAA,UAC5B,IAAA,EAAM,QAAQ,OAAA,CAAQ;AAAA,SACtB,CAAA;AAAA,QACD;AAAA,OACD;AAAA,MACC,GAAG,KAAA;AAAA,MAEH;AAAA;AAAA,GACF;AAEF,CAAC;AACD,eAAA,CAAgB,WAAA,GAAc,iBAAA","file":"toggle-group.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst toggleVariants = cva(\n\t[\n\t\t\"inline-flex items-center justify-center rounded-md text-sm font-medium\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\"hover:bg-muted hover:text-muted-foreground\",\n\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\",\n\t\t\"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-transparent\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-input bg-transparent shadow-sm inset-ring-1 inset-ring-foreground/[0.06] hover:bg-accent hover:text-accent-foreground\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-[var(--control-height-md,2.5rem)] px-[var(--space-3,0.75rem)] min-w-[var(--control-height-md,2.5rem)]\",\n\t\t\t\tsm: \"h-[var(--control-height-sm,2.25rem)] px-2.5 min-w-[var(--control-height-sm,2.25rem)]\",\n\t\t\t\tlg: \"h-[var(--control-height-lg,2.75rem)] px-5 min-w-[var(--control-height-lg,2.75rem)]\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\", size: \"default\" },\n\t},\n);\n\n/**\n * A two-state button. Stays pressed when toggled on.\n * @returns A styled Radix Toggle root\n */\nconst Toggle = React.forwardRef<\n\tReact.ComponentRef<typeof TogglePrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>\n>(({ className, variant, size, ...props }, ref) => (\n\t<TogglePrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(toggleVariants({ variant, size, className }))}\n\t\t{...props}\n\t/>\n));\nToggle.displayName = \"Toggle\";\n\nexport { Toggle, toggleVariants };\n","\"use client\";\n\nimport * as ToggleGroupPrimitive from \"@radix-ui/react-toggle-group\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport { toggleVariants } from \"../toggle/toggle.js\";\n\ntype ToggleGroupContextValue = VariantProps<typeof toggleVariants>;\n\nconst ToggleGroupContext = React.createContext<ToggleGroupContextValue>({\n\tsize: \"default\",\n\tvariant: \"default\",\n});\n\n/** Root container for a set of toggles (single-select or multi-select). */\nconst ToggleGroup = React.forwardRef<\n\tReact.ComponentRef<typeof ToggleGroupPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &\n\t\tVariantProps<typeof toggleVariants>\n>(({ className, variant, size, children, ...props }, ref) => (\n\t<ToggleGroupPrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(\"flex items-center justify-center gap-1\", className)}\n\t\t{...props}\n\t>\n\t\t<ToggleGroupContext.Provider value={{ variant, size }}>{children}</ToggleGroupContext.Provider>\n\t</ToggleGroupPrimitive.Root>\n));\nToggleGroup.displayName = \"ToggleGroup\";\n\n/** A single toggle option within a ToggleGroup. Inherits variant/size from context. */\nconst ToggleGroupItem = React.forwardRef<\n\tReact.ComponentRef<typeof ToggleGroupPrimitive.Item>,\n\tReact.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &\n\t\tVariantProps<typeof toggleVariants>\n>(({ className, children, variant, size, ...props }, ref) => {\n\tconst context = React.useContext(ToggleGroupContext);\n\treturn (\n\t\t<ToggleGroupPrimitive.Item\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\ttoggleVariants({\n\t\t\t\t\tvariant: variant ?? context.variant,\n\t\t\t\t\tsize: size ?? context.size,\n\t\t\t\t}),\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</ToggleGroupPrimitive.Item>\n\t);\n});\nToggleGroupItem.displayName = \"ToggleGroupItem\";\n\nexport { ToggleGroup, ToggleGroupItem };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/toggle/toggle.tsx"],"names":[],"mappings":";;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,cAAA,GAAiB,GAAA;AAAA,EACtB;AAAA,IACC,wEAAA;AAAA,IACA,iEAAA;AAAA,IACA,4CAAA;AAAA,IACA,qGAAA;AAAA,IACA,kDAAA;AAAA,IACA,kEAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,gBAAA;AAAA,QACT,OAAA,EACC;AAAA,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACL,OAAA,EAAS,yGAAA;AAAA,QACT,EAAA,EAAI,sFAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACL,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA,EAAW,MAAM,SAAA;AAAU;AAEzD;AAMA,IAAM,MAAA,GAAe,KAAA,CAAA,UAAA,CAGnB,CAAC,EAAE,SAAA,EAAW,SAAS,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,qBAC1C,GAAA;AAAA,EAAiB,eAAA,CAAA,IAAA;AAAA,EAAhB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,GAAG,cAAA,CAAe,EAAE,SAAS,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,IACzD,GAAG;AAAA;AACL,CACA;AACD,MAAA,CAAO,WAAA,GAAc,QAAA","file":"toggle.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst toggleVariants = cva(\n\t[\n\t\t\"inline-flex items-center justify-center rounded-md text-sm font-medium\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\"hover:bg-muted hover:text-muted-foreground\",\n\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\",\n\t\t\"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-transparent\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-input bg-transparent shadow-sm inset-ring-1 inset-ring-foreground/[0.06] hover:bg-accent hover:text-accent-foreground\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-[var(--control-height-md,2.5rem)] px-[var(--space-3,0.75rem)] min-w-[var(--control-height-md,2.5rem)]\",\n\t\t\t\tsm: \"h-[var(--control-height-sm,2.25rem)] px-2.5 min-w-[var(--control-height-sm,2.25rem)]\",\n\t\t\t\tlg: \"h-[var(--control-height-lg,2.75rem)] px-5 min-w-[var(--control-height-lg,2.75rem)]\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\", size: \"default\" },\n\t},\n);\n\n/**\n * A two-state button. Stays pressed when toggled on.\n * @returns A styled Radix Toggle root\n */\nconst Toggle = React.forwardRef<\n\tReact.ComponentRef<typeof TogglePrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>\n>(({ className, variant, size, ...props }, ref) => (\n\t<TogglePrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(toggleVariants({ variant, size, className }))}\n\t\t{...props}\n\t/>\n));\nToggle.displayName = \"Toggle\";\n\nexport { Toggle, toggleVariants };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/toggle/toggle.tsx"],"names":[],"mappings":";;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,cAAA,GAAiB,GAAA;AAAA,EACtB;AAAA,IACC,wEAAA;AAAA,IACA,iEAAA;AAAA,IACA,4CAAA;AAAA,IACA,qGAAA;AAAA,IACA,kDAAA;AAAA,IACA,kEAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,gBAAA;AAAA,QACT,OAAA,EACC;AAAA,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACL,OAAA,EAAS,yGAAA;AAAA,QACT,EAAA,EAAI,sFAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACL,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA,EAAW,MAAM,SAAA;AAAU;AAEzD;AAMA,IAAM,MAAA,GAAe,KAAA,CAAA,UAAA,CAGnB,CAAC,EAAE,SAAA,EAAW,SAAS,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,qBAC1C,GAAA;AAAA,EAAiB,eAAA,CAAA,IAAA;AAAA,EAAhB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,GAAG,cAAA,CAAe,EAAE,SAAS,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,IACzD,GAAG;AAAA;AACL,CACA;AACD,MAAA,CAAO,WAAA,GAAc,QAAA","file":"toggle.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst toggleVariants = cva(\n\t[\n\t\t\"inline-flex items-center justify-center rounded-md text-sm font-medium\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\"hover:bg-muted hover:text-muted-foreground\",\n\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\t\"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\",\n\t\t\"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-transparent\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-input bg-transparent shadow-sm inset-ring-1 inset-ring-foreground/[0.06] hover:bg-accent hover:text-accent-foreground\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-[var(--control-height-md,2.5rem)] px-[var(--space-3,0.75rem)] min-w-[var(--control-height-md,2.5rem)]\",\n\t\t\t\tsm: \"h-[var(--control-height-sm,2.25rem)] px-2.5 min-w-[var(--control-height-sm,2.25rem)]\",\n\t\t\t\tlg: \"h-[var(--control-height-lg,2.75rem)] px-5 min-w-[var(--control-height-lg,2.75rem)]\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\", size: \"default\" },\n\t},\n);\n\n/**\n * A two-state button. Stays pressed when toggled on.\n * @returns A styled Radix Toggle root\n */\nconst Toggle = React.forwardRef<\n\tReact.ComponentRef<typeof TogglePrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>\n>(({ className, variant, size, ...props }, ref) => (\n\t<TogglePrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(toggleVariants({ variant, size, className }))}\n\t\t{...props}\n\t/>\n));\nToggle.displayName = \"Toggle\";\n\nexport { Toggle, toggleVariants };\n"]}
package/dist/tool-call.js CHANGED
@@ -15,12 +15,11 @@ var STATE_LABEL = {
15
15
  };
16
16
  var STATE_CLASSES = {
17
17
  pending: "bg-muted text-muted-foreground",
18
- // Solid primary bg + primary-foreground text in dark mode the
19
- // previously tinted `bg-primary/15` made the badge fg/bg too close in
20
- // hue at 10px to clear WCAG AA (was 4.41:1, fails the 4.5 floor).
21
- // Light mode keeps the soft tint where contrast holds; dark mode uses
22
- // the solid surface for guaranteed ≥7:1 contrast.
23
- running: "bg-primary/15 text-primary dark:bg-primary dark:text-primary-foreground animate-pulse",
18
+ // `text-foreground` (12:1) survives axe's mid-pulse capture; the
19
+ // previous `text-primary` (`#7081a8` on dark `--muted`, 4.28:1) was
20
+ // intermittently flagged below AA 4.5:1 at the 10px badge size when
21
+ // `animate-pulse` caught the scan during a low-opacity frame.
22
+ running: "bg-muted text-foreground font-medium animate-pulse",
24
23
  result: "bg-accent/30 text-accent-foreground",
25
24
  error: "bg-destructive/15 text-destructive"
26
25
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/ai/tool-call/tool-call.tsx"],"names":[],"mappings":"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","file":"tool-call.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport type { ToolCallState } from \"../types.js\";\n\nconst STATE_LABEL: Record<ToolCallState, string> = {\n\tpending: \"Pending\",\n\trunning: \"Running\",\n\tresult: \"Done\",\n\terror: \"Error\",\n};\n\nconst STATE_CLASSES: Record<ToolCallState, string> = {\n\tpending: \"bg-muted text-muted-foreground\",\n\t// Solid primary bg + primary-foreground text in dark mode — the\n\t// previously tinted `bg-primary/15` made the badge fg/bg too close in\n\t// hue at 10px to clear WCAG AA (was 4.41:1, fails the 4.5 floor).\n\t// Light mode keeps the soft tint where contrast holds; dark mode uses\n\t// the solid surface for guaranteed ≥7:1 contrast.\n\trunning: \"bg-primary/15 text-primary dark:bg-primary dark:text-primary-foreground animate-pulse\",\n\tresult: \"bg-accent/30 text-accent-foreground\",\n\terror: \"bg-destructive/15 text-destructive\",\n};\n\n/**\n * Collapsible card displaying a tool / function invocation. Header shows the\n * tool name and lifecycle state badge; body reveals the JSON-stringified\n * arguments and result on expand.\n *\n * Display-only — the component does not run the tool. Wire it up in the\n * consumer (AI SDK `tool-*` parts → ToolCall props, LangChain\n * `AIMessage.tool_calls` → ToolCall props).\n *\n * @example\n * <ToolCall\n * name=\"searchDocs\"\n * state=\"result\"\n * args={{ query: \"auth\" }}\n * result={{ hits: 12 }}\n * />\n */\nexport interface ToolCallProps {\n\tname: string;\n\tstate: ToolCallState;\n\targs?: unknown;\n\tresult?: unknown;\n\tdefaultOpen?: boolean;\n\tclassName?: string;\n}\n\n/**\n * Renders a tool-invocation card with collapsible details.\n * @param props - tool name, state, optional args/result\n * @returns A Collapsible wrapping a header + JSON body\n */\nfunction ToolCall({\n\tname,\n\tstate,\n\targs,\n\tresult,\n\tdefaultOpen = false,\n\tclassName,\n}: ToolCallProps) {\n\treturn (\n\t\t<CollapsiblePrimitive.Root\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-hidden rounded-md border bg-card text-card-foreground\",\n\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\"data-[state=open]:shadow-sm\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<CollapsiblePrimitive.Trigger\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm\",\n\t\t\t\t\t\"hover:bg-muted/40\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<span className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t\t<ToolGlyph />\n\t\t\t\t\t<span className=\"truncate font-mono text-xs text-foreground\">{name}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium\",\n\t\t\t\t\t\t\tSTATE_CLASSES[state],\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{STATE_LABEL[state]}\n\t\t\t\t\t</span>\n\t\t\t\t</span>\n\t\t\t\t<Chevron />\n\t\t\t</CollapsiblePrimitive.Trigger>\n\t\t\t<CollapsiblePrimitive.Content className=\"overflow-hidden border-t bg-muted/20 px-3 py-2 text-xs\">\n\t\t\t\t{args !== undefined ? <CodeSection label=\"Arguments\" value={args} /> : null}\n\t\t\t\t{result !== undefined ? <CodeSection label=\"Result\" value={result} /> : null}\n\t\t\t\t{args === undefined && result === undefined ? (\n\t\t\t\t\t<p className=\"text-muted-foreground\">No arguments or result yet.</p>\n\t\t\t\t) : null}\n\t\t\t</CollapsiblePrimitive.Content>\n\t\t</CollapsiblePrimitive.Root>\n\t);\n}\n\nfunction CodeSection({ label, value }: { label: string; value: unknown }) {\n\tconst text = typeof value === \"string\" ? value : safeStringify(value);\n\treturn (\n\t\t<div className=\"space-y-1 py-1\">\n\t\t\t<div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">{label}</div>\n\t\t\t<pre className=\"overflow-x-auto rounded bg-background/60 p-2 font-mono text-[11px] leading-snug\">\n\t\t\t\t{text}\n\t\t\t</pre>\n\t\t</div>\n\t);\n}\n\nfunction safeStringify(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value, null, 2);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction ToolGlyph() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"14\"\n\t\t\theight=\"14\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0 text-muted-foreground\"\n\t\t>\n\t\t\t<path d=\"M11.5 1.5l3 3-2.5 2.5-3-3 2.5-2.5z\" />\n\t\t\t<path d=\"M9 4l-7 7v3h3l7-7\" />\n\t\t</svg>\n\t);\n}\n\nfunction Chevron() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"14\"\n\t\t\theight=\"14\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180\"\n\t\t>\n\t\t\t<path d=\"M4 6l4 4 4-4\" />\n\t\t</svg>\n\t);\n}\n\nexport { ToolCall };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/ai/tool-call/tool-call.tsx"],"names":[],"mappings":"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","file":"tool-call.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport type { ToolCallState } from \"../types.js\";\n\nconst STATE_LABEL: Record<ToolCallState, string> = {\n\tpending: \"Pending\",\n\trunning: \"Running\",\n\tresult: \"Done\",\n\terror: \"Error\",\n};\n\nconst STATE_CLASSES: Record<ToolCallState, string> = {\n\tpending: \"bg-muted text-muted-foreground\",\n\t// `text-foreground` (12:1) survives axe's mid-pulse capture; the\n\t// previous `text-primary` (`#7081a8` on dark `--muted`, 4.28:1) was\n\t// intermittently flagged below AA 4.5:1 at the 10px badge size when\n\t// `animate-pulse` caught the scan during a low-opacity frame.\n\trunning: \"bg-muted text-foreground font-medium animate-pulse\",\n\tresult: \"bg-accent/30 text-accent-foreground\",\n\terror: \"bg-destructive/15 text-destructive\",\n};\n\n/**\n * Collapsible card displaying a tool / function invocation. Header shows the\n * tool name and lifecycle state badge; body reveals the JSON-stringified\n * arguments and result on expand.\n *\n * Display-only — the component does not run the tool. Wire it up in the\n * consumer (AI SDK `tool-*` parts → ToolCall props, LangChain\n * `AIMessage.tool_calls` → ToolCall props).\n *\n * @example\n * <ToolCall\n * name=\"searchDocs\"\n * state=\"result\"\n * args={{ query: \"auth\" }}\n * result={{ hits: 12 }}\n * />\n */\nexport interface ToolCallProps {\n\tname: string;\n\tstate: ToolCallState;\n\targs?: unknown;\n\tresult?: unknown;\n\tdefaultOpen?: boolean;\n\tclassName?: string;\n}\n\n/**\n * Renders a tool-invocation card with collapsible details.\n * @param props - tool name, state, optional args/result\n * @returns A Collapsible wrapping a header + JSON body\n */\nfunction ToolCall({\n\tname,\n\tstate,\n\targs,\n\tresult,\n\tdefaultOpen = false,\n\tclassName,\n}: ToolCallProps) {\n\treturn (\n\t\t<CollapsiblePrimitive.Root\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tclassName={cn(\n\t\t\t\t\"overflow-hidden rounded-md border bg-card text-card-foreground\",\n\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\"data-[state=open]:shadow-sm\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<CollapsiblePrimitive.Trigger\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm\",\n\t\t\t\t\t\"hover:bg-muted/40\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<span className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t\t<ToolGlyph />\n\t\t\t\t\t<span className=\"truncate font-mono text-xs text-foreground\">{name}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium\",\n\t\t\t\t\t\t\tSTATE_CLASSES[state],\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{STATE_LABEL[state]}\n\t\t\t\t\t</span>\n\t\t\t\t</span>\n\t\t\t\t<Chevron />\n\t\t\t</CollapsiblePrimitive.Trigger>\n\t\t\t<CollapsiblePrimitive.Content className=\"overflow-hidden border-t bg-muted/20 px-3 py-2 text-xs\">\n\t\t\t\t{args !== undefined ? <CodeSection label=\"Arguments\" value={args} /> : null}\n\t\t\t\t{result !== undefined ? <CodeSection label=\"Result\" value={result} /> : null}\n\t\t\t\t{args === undefined && result === undefined ? (\n\t\t\t\t\t<p className=\"text-muted-foreground\">No arguments or result yet.</p>\n\t\t\t\t) : null}\n\t\t\t</CollapsiblePrimitive.Content>\n\t\t</CollapsiblePrimitive.Root>\n\t);\n}\n\nfunction CodeSection({ label, value }: { label: string; value: unknown }) {\n\tconst text = typeof value === \"string\" ? value : safeStringify(value);\n\treturn (\n\t\t<div className=\"space-y-1 py-1\">\n\t\t\t<div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">{label}</div>\n\t\t\t<pre className=\"overflow-x-auto rounded bg-background/60 p-2 font-mono text-[11px] leading-snug\">\n\t\t\t\t{text}\n\t\t\t</pre>\n\t\t</div>\n\t);\n}\n\nfunction safeStringify(value: unknown): string {\n\ttry {\n\t\treturn JSON.stringify(value, null, 2);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction ToolGlyph() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"14\"\n\t\t\theight=\"14\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0 text-muted-foreground\"\n\t\t>\n\t\t\t<path d=\"M11.5 1.5l3 3-2.5 2.5-3-3 2.5-2.5z\" />\n\t\t\t<path d=\"M9 4l-7 7v3h3l7-7\" />\n\t\t</svg>\n\t);\n}\n\nfunction Chevron() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"14\"\n\t\t\theight=\"14\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180\"\n\t\t>\n\t\t\t<path d=\"M4 6l4 4 4-4\" />\n\t\t</svg>\n\t);\n}\n\nexport { ToolCall };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/components/toolbar/toolbar.tsx"],"names":[],"mappings":"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","file":"toolbar.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as ToolbarPrimitive from \"@radix-ui/react-toolbar\";\nimport { cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst toolbarVariants = cva(\"flex items-center gap-[var(--gap-xs,0.25rem)] rounded-md border border-border bg-card p-[var(--space-1,0.25rem)]\", {\n\tvariants: {\n\t\torientation: {\n\t\t\thorizontal: \"flex-row\",\n\t\t\tvertical: \"flex-col items-stretch\",\n\t\t},\n\t},\n\tdefaultVariants: { orientation: \"horizontal\" },\n});\n\nconst toolbarItemBaseClasses = [\n\t\"inline-flex items-center justify-center gap-[var(--gap-xs,0.25rem)] rounded-sm\",\n\t\"px-[var(--space-2,0.5rem)] h-[var(--control-height-sm,2.25rem)] text-sm font-medium\",\n\t\"text-foreground/80 hover:text-foreground hover:bg-accent\",\n\t\"transition-colors duration-[var(--duration-normal,200ms)] ease-out\",\n\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\",\n\t\"active:scale-[0.98]\",\n\t\"[&_svg]:size-4 [&_svg]:shrink-0\",\n].join(\" \");\n\n/**\n * Toolbar root props. `aria-label` is required — Radix Toolbar.Root\n * mounts as a `role=\"toolbar\"` landmark, and AT users will hear an\n * unlabelled \"toolbar\" landmark when no visible heading sits adjacent.\n * If a visible heading IS present, pair it via `aria-labelledby` instead.\n */\nexport interface ToolbarProps\n\textends Omit<\n\t\tReact.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Root>,\n\t\t\"aria-label\"\n\t> {\n\t/** Forwarded ref onto the Radix `Toolbar.Root`. */\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Root>>;\n\t/** Required accessible name for the toolbar landmark. */\n\t\"aria-label\": string;\n}\n\n/**\n * Root toolbar element. Wraps Radix `Toolbar.Root` with the canonical\n * Hex Core token + visual styling. Pass children consisting of\n * `ToolbarButton`, `ToolbarToggleGroup`, `ToolbarSeparator`, and\n * `ToolbarLink` — Radix handles arrow-key roving focus across them\n * automatically.\n *\n * @example\n * ```tsx\n * <Toolbar aria-label=\"Editor controls\">\n * <ToolbarButton onClick={onUndo}>Undo</ToolbarButton>\n * <ToolbarButton onClick={onRedo}>Redo</ToolbarButton>\n * <ToolbarSeparator />\n * <ToolbarToggleGroup type=\"single\" defaultValue=\"left\">\n * <ToolbarToggleItem value=\"left\">Left</ToolbarToggleItem>\n * <ToolbarToggleItem value=\"center\">Center</ToolbarToggleItem>\n * <ToolbarToggleItem value=\"right\">Right</ToolbarToggleItem>\n * </ToolbarToggleGroup>\n * </Toolbar>\n * ```\n */\nfunction Toolbar({ className, orientation = \"horizontal\", ref, ...props }: ToolbarProps) {\n\treturn (\n\t\t<ToolbarPrimitive.Root\n\t\t\tref={ref}\n\t\t\torientation={orientation}\n\t\t\tclassName={cn(toolbarVariants({ orientation }), className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A push button inside the toolbar. */\nfunction ToolbarButton({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Button> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Button>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.Button\n\t\t\tref={ref}\n\t\t\tclassName={cn(toolbarItemBaseClasses, className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A link inside the toolbar — renders an `<a>` with toolbar focus semantics. */\nfunction ToolbarLink({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Link> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Link>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.Link\n\t\t\tref={ref}\n\t\t\tclassName={cn(toolbarItemBaseClasses, \"underline-offset-4 hover:underline\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A group of mutually-exclusive (`type='single'`) or independent (`type='multiple'`) toggle items. */\nfunction ToolbarToggleGroup({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.ToggleGroup> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.ToggleGroup>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.ToggleGroup\n\t\t\tref={ref}\n\t\t\tclassName={cn(\"flex items-center gap-[var(--gap-xs,0.25rem)]\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** Individual toggle item — exposes `data-state=\"on\"` for the on style. */\nfunction ToolbarToggleItem({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.ToggleItem> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.ToggleItem>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.ToggleItem\n\t\t\tref={ref}\n\t\t\tclassName={cn(toolbarItemBaseClasses, className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A vertical (or horizontal, in vertical toolbars) divider. */\nfunction ToolbarSeparator({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Separator> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Separator>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.Separator\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"shrink-0 bg-border\",\n\t\t\t\t\"data-[orientation=horizontal]:h-4 data-[orientation=horizontal]:w-px data-[orientation=horizontal]:mx-[var(--space-1,0.25rem)]\",\n\t\t\t\t\"data-[orientation=vertical]:w-4 data-[orientation=vertical]:h-px data-[orientation=vertical]:my-[var(--space-1,0.25rem)]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport {\n\tToolbar,\n\tToolbarButton,\n\tToolbarLink,\n\tToolbarSeparator,\n\tToolbarToggleGroup,\n\tToolbarToggleItem,\n\ttoolbarVariants,\n};\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/toolbar/toolbar.tsx"],"names":[],"mappings":"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","file":"toolbar.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as ToolbarPrimitive from \"@radix-ui/react-toolbar\";\nimport { cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst toolbarVariants = cva(\"flex items-center gap-[var(--gap-xs,0.25rem)] rounded-md border border-border bg-card p-[var(--space-1,0.25rem)]\", {\n\tvariants: {\n\t\torientation: {\n\t\t\thorizontal: \"flex-row\",\n\t\t\tvertical: \"flex-col items-stretch\",\n\t\t},\n\t},\n\tdefaultVariants: { orientation: \"horizontal\" },\n});\n\nconst toolbarItemBaseClasses = [\n\t\"inline-flex items-center justify-center gap-[var(--gap-xs,0.25rem)] rounded-sm\",\n\t\"px-[var(--space-2,0.5rem)] h-[var(--control-height-sm,2.25rem)] text-sm font-medium\",\n\t\"text-foreground/80 hover:text-foreground hover:bg-accent\",\n\t\"transition-colors duration-[var(--duration-normal,200ms)] ease-out\",\n\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\"disabled:pointer-events-none disabled:opacity-50\",\n\t\"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground\",\n\t\"active:scale-[0.98]\",\n\t\"[&_svg]:size-4 [&_svg]:shrink-0\",\n].join(\" \");\n\n/**\n * Toolbar root props. `aria-label` is required — Radix Toolbar.Root\n * mounts as a `role=\"toolbar\"` landmark, and AT users will hear an\n * unlabelled \"toolbar\" landmark when no visible heading sits adjacent.\n * If a visible heading IS present, pair it via `aria-labelledby` instead.\n */\nexport interface ToolbarProps\n\textends Omit<\n\t\tReact.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Root>,\n\t\t\"aria-label\"\n\t> {\n\t/** Forwarded ref onto the Radix `Toolbar.Root`. */\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Root>>;\n\t/** Required accessible name for the toolbar landmark. */\n\t\"aria-label\": string;\n}\n\n/**\n * Root toolbar element. Wraps Radix `Toolbar.Root` with the canonical\n * Hex Core token + visual styling. Pass children consisting of\n * `ToolbarButton`, `ToolbarToggleGroup`, `ToolbarSeparator`, and\n * `ToolbarLink` — Radix handles arrow-key roving focus across them\n * automatically.\n *\n * @example\n * ```tsx\n * <Toolbar aria-label=\"Editor controls\">\n * <ToolbarButton onClick={onUndo}>Undo</ToolbarButton>\n * <ToolbarButton onClick={onRedo}>Redo</ToolbarButton>\n * <ToolbarSeparator />\n * <ToolbarToggleGroup type=\"single\" defaultValue=\"left\">\n * <ToolbarToggleItem value=\"left\">Left</ToolbarToggleItem>\n * <ToolbarToggleItem value=\"center\">Center</ToolbarToggleItem>\n * <ToolbarToggleItem value=\"right\">Right</ToolbarToggleItem>\n * </ToolbarToggleGroup>\n * </Toolbar>\n * ```\n */\nfunction Toolbar({ className, orientation = \"horizontal\", ref, ...props }: ToolbarProps) {\n\treturn (\n\t\t<ToolbarPrimitive.Root\n\t\t\tref={ref}\n\t\t\torientation={orientation}\n\t\t\tclassName={cn(toolbarVariants({ orientation }), className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A push button inside the toolbar. */\nfunction ToolbarButton({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Button> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Button>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.Button\n\t\t\tref={ref}\n\t\t\tclassName={cn(toolbarItemBaseClasses, className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A link inside the toolbar — renders an `<a>` with toolbar focus semantics. */\nfunction ToolbarLink({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Link> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Link>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.Link\n\t\t\tref={ref}\n\t\t\tclassName={cn(toolbarItemBaseClasses, \"underline-offset-4 hover:underline\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A group of mutually-exclusive (`type='single'`) or independent (`type='multiple'`) toggle items. */\nfunction ToolbarToggleGroup({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.ToggleGroup> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.ToggleGroup>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.ToggleGroup\n\t\t\tref={ref}\n\t\t\tclassName={cn(\"flex items-center gap-[var(--gap-xs,0.25rem)]\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** Individual toggle item — exposes `data-state=\"on\"` for the on style. */\nfunction ToolbarToggleItem({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.ToggleItem> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.ToggleItem>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.ToggleItem\n\t\t\tref={ref}\n\t\t\tclassName={cn(toolbarItemBaseClasses, className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** A vertical (or horizontal, in vertical toolbars) divider. */\nfunction ToolbarSeparator({\n\tclassName,\n\tref,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ToolbarPrimitive.Separator> & {\n\tref?: React.Ref<React.ElementRef<typeof ToolbarPrimitive.Separator>>;\n}) {\n\treturn (\n\t\t<ToolbarPrimitive.Separator\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"shrink-0 bg-border\",\n\t\t\t\t\"data-[orientation=horizontal]:h-4 data-[orientation=horizontal]:w-px data-[orientation=horizontal]:mx-[var(--space-1,0.25rem)]\",\n\t\t\t\t\"data-[orientation=vertical]:w-4 data-[orientation=vertical]:h-px data-[orientation=vertical]:my-[var(--space-1,0.25rem)]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport {\n\tToolbar,\n\tToolbarButton,\n\tToolbarLink,\n\tToolbarSeparator,\n\tToolbarToggleGroup,\n\tToolbarToggleItem,\n\ttoolbarVariants,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/components/tooltip/tooltip.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,eAAA,GAAmC,gBAAA,CAAA;AAGzC,IAAM,OAAA,GAA2B,gBAAA,CAAA;AAGjC,IAAM,cAAA,GAAkC,gBAAA,CAAA;AAGxC,IAAM,cAAA,GAAuB,KAAA,CAAA,UAAA,CAG3B,CAAC,EAAE,SAAA,EAAW,UAAA,GAAa,CAAA,EAAG,GAAG,KAAA,EAAM,EAAG,GAAA,qBAC3C,GAAA,CAAkB,yBAAjB,EACA,QAAA,kBAAA,GAAA;AAAA,EAAkB,gBAAA,CAAA,OAAA;AAAA,EAAjB;AAAA,IACA,GAAA;AAAA,IACA,UAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,yHAAA;AAAA,MACA,iCAAA;AAAA,MACA,gGAAA;AAAA,MACA,6JAAA;AAAA,MACA;AAAA,KACD;AAAA,IACC,GAAG;AAAA;AACL,CAAA,EACD,CACA;AACD,cAAA,CAAe,WAAA,GAAc,gBAAA","file":"tooltip.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Provider required at the root of the app or tree where tooltips are used. */\nconst TooltipProvider = TooltipPrimitive.Provider;\n\n/** Root container for a single tooltip. */\nconst Tooltip = TooltipPrimitive.Root;\n\n/** The element that shows the tooltip on hover/focus. */\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\n/** The floating tooltip content (small, hover-reveal info). */\nconst TooltipContent = React.forwardRef<\n\tReact.ComponentRef<typeof TooltipPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n\t<TooltipPrimitive.Portal>\n\t\t<TooltipPrimitive.Content\n\t\t\tref={ref}\n\t\t\tsideOffset={sideOffset}\n\t\t\tclassName={cn(\n\t\t\t\t\"z-50 overflow-hidden rounded-md bg-primary px-[var(--space-3,0.75rem)] py-1.5 text-xs text-primary-foreground shadow-md\",\n\t\t\t\t\"animate-in fade-in-0 zoom-in-95\",\n\t\t\t\t\"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\",\n\t\t\t\t\"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t</TooltipPrimitive.Portal>\n));\nTooltipContent.displayName = \"TooltipContent\";\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/tooltip/tooltip.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,eAAA,GAAmC,gBAAA,CAAA;AAGzC,IAAM,OAAA,GAA2B,gBAAA,CAAA;AAGjC,IAAM,cAAA,GAAkC,gBAAA,CAAA;AAGxC,IAAM,cAAA,GAAuB,KAAA,CAAA,UAAA,CAG3B,CAAC,EAAE,SAAA,EAAW,UAAA,GAAa,CAAA,EAAG,GAAG,KAAA,EAAM,EAAG,GAAA,qBAC3C,GAAA,CAAkB,yBAAjB,EACA,QAAA,kBAAA,GAAA;AAAA,EAAkB,gBAAA,CAAA,OAAA;AAAA,EAAjB;AAAA,IACA,GAAA;AAAA,IACA,UAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,yHAAA;AAAA,MACA,iCAAA;AAAA,MACA,gGAAA;AAAA,MACA,6JAAA;AAAA,MACA;AAAA,KACD;AAAA,IACC,GAAG;AAAA;AACL,CAAA,EACD,CACA;AACD,cAAA,CAAe,WAAA,GAAc,gBAAA","file":"tooltip.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Provider required at the root of the app or tree where tooltips are used. */\nconst TooltipProvider = TooltipPrimitive.Provider;\n\n/** Root container for a single tooltip. */\nconst Tooltip = TooltipPrimitive.Root;\n\n/** The element that shows the tooltip on hover/focus. */\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\n/** The floating tooltip content (small, hover-reveal info). */\nconst TooltipContent = React.forwardRef<\n\tReact.ComponentRef<typeof TooltipPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n\t<TooltipPrimitive.Portal>\n\t\t<TooltipPrimitive.Content\n\t\t\tref={ref}\n\t\t\tsideOffset={sideOffset}\n\t\t\tclassName={cn(\n\t\t\t\t\"z-50 overflow-hidden rounded-md bg-primary px-[var(--space-3,0.75rem)] py-1.5 text-xs text-primary-foreground shadow-md\",\n\t\t\t\t\"animate-in fade-in-0 zoom-in-95\",\n\t\t\t\t\"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\",\n\t\t\t\t\"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t</TooltipPrimitive.Portal>\n));\nTooltipContent.displayName = \"TooltipContent\";\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/tree-map/tree-map.tsx"],"names":[],"mappings":"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","file":"tree-map.js","sourcesContent":["/**\n * Categorical chart palette for diagram primitives that encode categorical\n * data (sunburst, treemap, sankey, chord, funnel, pyramid, venn, matrix).\n * Cycled by an integer key — node index, leaf index, depth-1 ancestor, etc.\n *\n * The values reach into `--chart-1` through `--chart-6`, the dedicated\n * diagram-encoding tokens added in `@hex-core/tokens` 1.4. Each token has a\n * `var(--primary)` fallback so consumers on theme presets that haven't been\n * updated to ship `--chart-N` (or who run a custom theme without the chart\n * family) still see a coherent monochrome rendering instead of black SVG\n * fills.\n *\n * Why a chart palette and not the semantic tokens: `--primary`, `--accent`,\n * `--secondary`, and `--muted` collapse to a single hue family in the\n * default monochrome theme — adjacent segments of a chart end up\n * indistinguishable. Chart tokens are tuned for perceptual differentiation.\n */\nexport const CHART_PALETTE = [\n\t\"hsl(var(--chart-1, var(--primary)))\",\n\t\"hsl(var(--chart-2, var(--primary)))\",\n\t\"hsl(var(--chart-3, var(--primary)))\",\n\t\"hsl(var(--chart-4, var(--primary)))\",\n\t\"hsl(var(--chart-5, var(--primary)))\",\n\t\"hsl(var(--chart-6, var(--primary)))\",\n] as const;\n\n/**\n * Return the chart hue at a stable index. Cycles modulo `CHART_PALETTE.length`\n * so the caller doesn't have to range-check.\n *\n * @param index - Integer key (node index, leaf index, depth-1 ancestor index, …).\n * @returns A `hsl(var(...))` string suitable for SVG `fill` / `stroke`.\n */\nexport function pickChartHue(index: number): string {\n\tconst safe = ((index % CHART_PALETTE.length) + CHART_PALETTE.length) % CHART_PALETTE.length;\n\t// `safe` is provably 0..CHART_PALETTE.length-1 — the assertion documents that.\n\treturn CHART_PALETTE[safe] as string;\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { pickChartHue } from \"../../lib/chart-palette.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Typed React TreeMap. Each leaf is rendered as a rectangle whose area is\n * proportional to its `value`. Internal node values are summed automatically;\n * d3-hierarchy's squarified treemap layout keeps rectangles close to square\n * for legibility.\n *\n * Heavy peer: requires `d3-hierarchy` (~3 KB gzip).\n *\n * @example\n * <TreeMap\n * root={{\n * id: \"root\",\n * label: \"Files\",\n * children: [\n * { id: \"src\", label: \"src\", value: 240 },\n * { id: \"node_modules\", label: \"node_modules\", value: 980 },\n * ],\n * }}\n * />\n */\nexport type TreeMapNode = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n\tchildren?: TreeMapNode[];\n};\n\nexport interface TreeMapProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Root of the hierarchy. Leaves require a positive `value`. */\n\troot: TreeMapNode;\n\t/** Pixel width of the rendered SVG. Default 600. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 400. */\n\theight?: number;\n\t/** Inner padding between sibling rectangles, in pixels. Default 2. */\n\tpadding?: number;\n\t/** Tiling algorithm. \"squarify\" (default) keeps rectangles close to square. */\n\ttile?: \"squarify\" | \"binary\" | \"slice-dice\";\n\t/** Choose a fill color per leaf. \"depth\" cycles through theme tokens; \"value\" interpolates by value; or pass a function. */\n\tcolorBy?: \"depth\" | \"value\" | ((node: TreeMapNode, depth: number) => string);\n\t/** Fired when a leaf is clicked. */\n\tonLeafClick?: (node: TreeMapNode) => void;\n}\n\ninterface LaidOutLeaf {\n\tnode: TreeMapNode;\n\tdepth: number;\n\tx0: number;\n\ty0: number;\n\tx1: number;\n\ty1: number;\n\tvalue: number;\n\t/** Leaf index in pre-order traversal — used to cycle through CHART_PALETTE\n\t * so adjacent rectangles read as distinct categories. */\n\tleafIdx: number;\n\t/** Index of this leaf's depth-1 ancestor; multi-level trees use this so\n\t * descendants of the same branch share a hue family. */\n\trootSiblingIdx: number;\n}\n\ntype D3HierarchyMod = typeof import(\"d3-hierarchy\");\n\nfunction TreeMap({\n\troot,\n\twidth = 600,\n\theight = 400,\n\tpadding = 2,\n\ttile = \"squarify\",\n\tcolorBy = \"depth\",\n\tonLeafClick,\n\tclassName,\n\t...rest\n}: TreeMapProps) {\n\tconst [d3h, setD3h] = React.useState<D3HierarchyMod | null>(null);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-hierarchy\").then((mod) => {\n\t\t\tif (!cancelled) setD3h(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\tif (!d3h) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-tree-map-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst leaves = layout(d3h, root, width, height, padding, tile);\n\tconst maxValue = leaves.reduce((m, l) => (l.value > m ? l.value : m), 0) || 1;\n\tconst desc = `Tree map of ${leaves.length} leaf${leaves.length === 1 ? \"\" : \"s\"}, rooted at \"${root.label}\"`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-tree-map\n\t\t\tdata-tile={tile}\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Tree map</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t{leaves.map((l) => {\n\t\t\t\tconst w = l.x1 - l.x0;\n\t\t\t\tconst h = l.y1 - l.y0;\n\t\t\t\tconst fill = resolveFill(l, maxValue, colorBy);\n\t\t\t\treturn (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={l.node.id}\n\t\t\t\t\t\tdata-hex-tree-map-leaf\n\t\t\t\t\t\tdata-depth={l.depth}\n\t\t\t\t\t\ttransform={`translate(${l.x0},${l.y0})`}\n\t\t\t\t\t\tstyle={onLeafClick ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={onLeafClick ? () => onLeafClick(l.node) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\twidth={w}\n\t\t\t\t\t\t\theight={h}\n\t\t\t\t\t\t\tfill={fill}\n\t\t\t\t\t\t\tfillOpacity={0.85}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{w > 40 && h > 16 ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={6}\n\t\t\t\t\t\t\t\ty={16}\n\t\t\t\t\t\t\t\tfontSize={12}\n\t\t\t\t\t\t\t\tfontWeight={500}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.35)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{l.node.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{w > 60 && h > 32 ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={6}\n\t\t\t\t\t\t\t\ty={32}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background) / 0.85)\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.3)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{l.value.toLocaleString()}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</g>\n\t\t\t\t);\n\t\t\t})}\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3h: D3HierarchyMod,\n\troot: TreeMapNode,\n\twidth: number,\n\theight: number,\n\tpadding: number,\n\ttile: \"squarify\" | \"binary\" | \"slice-dice\",\n): LaidOutLeaf[] {\n\tconst tileFn = tile === \"binary\" ? d3h.treemapBinary : tile === \"slice-dice\" ? d3h.treemapSliceDice : d3h.treemapSquarify;\n\tconst hierarchy = d3h\n\t\t.hierarchy<TreeMapNode>(root)\n\t\t.sum((d) => (d.children && d.children.length > 0 ? 0 : d.value ?? 0))\n\t\t.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));\n\t// treemap(hierarchy) returns HierarchyRectangularNode<T> with typed\n\t// x0/x1/y0/y1 (pixel coords).\n\tconst layoutRoot = d3h\n\t\t.treemap<TreeMapNode>()\n\t\t.tile(tileFn)\n\t\t.size([width, height])\n\t\t.padding(padding)(hierarchy);\n\treturn layoutRoot.leaves().map((leaf, leafIdx) => {\n\t\t// Walk up to depth-1 ancestor; descendants of the same top-level\n\t\t// branch share a hue family for tree visual cohesion.\n\t\tlet cursor: typeof leaf | null = leaf;\n\t\twhile (cursor && cursor.depth > 1) cursor = cursor.parent;\n\t\tconst ancestorIdx = cursor?.parent?.children?.indexOf(cursor) ?? leafIdx;\n\t\treturn {\n\t\t\tnode: leaf.data,\n\t\t\tdepth: leaf.depth,\n\t\t\tx0: leaf.x0,\n\t\t\ty0: leaf.y0,\n\t\t\tx1: leaf.x1,\n\t\t\ty1: leaf.y1,\n\t\t\tvalue: leaf.value ?? 0,\n\t\t\tleafIdx,\n\t\t\trootSiblingIdx: Math.max(0, ancestorIdx),\n\t\t};\n\t});\n}\n\nfunction resolveFill(\n\tleaf: LaidOutLeaf,\n\tmaxValue: number,\n\tcolorBy: TreeMapProps[\"colorBy\"],\n): string {\n\tif (typeof colorBy === \"function\") return colorBy(leaf.node, leaf.depth);\n\tif (colorBy === \"value\") {\n\t\tconst t = Math.max(0.2, Math.min(1, leaf.value / maxValue));\n\t\treturn `hsl(var(--chart-1) / ${t.toFixed(2)})`;\n\t}\n\t// \"depth\" cycles through CHART_PALETTE by ancestor for nested trees,\n\t// or by leaf index for single-level trees (where every leaf is at the\n\t// same depth and would otherwise collapse to one color).\n\tconst idx = leaf.depth > 1 ? leaf.rootSiblingIdx : leaf.leafIdx;\n\treturn pickChartHue(idx);\n}\n\nexport { TreeMap };\n"]}
1
+ {"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/tree-map/tree-map.tsx"],"names":[],"mappings":"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","file":"tree-map.js","sourcesContent":["/**\n * Categorical chart palette for diagram primitives that encode categorical\n * data (sunburst, treemap, sankey, chord, funnel, pyramid, venn, matrix).\n * Cycled by an integer key — node index, leaf index, depth-1 ancestor, etc.\n *\n * The values reach into `--chart-1` through `--chart-6`, the dedicated\n * diagram-encoding tokens added in `@hex-core/tokens` 1.4. Each token has a\n * `var(--primary)` fallback so consumers on theme presets that haven't been\n * updated to ship `--chart-N` (or who run a custom theme without the chart\n * family) still see a coherent monochrome rendering instead of black SVG\n * fills.\n *\n * Why a chart palette and not the semantic tokens: `--primary`, `--accent`,\n * `--secondary`, and `--muted` collapse to a single hue family in the\n * default monochrome theme — adjacent segments of a chart end up\n * indistinguishable. Chart tokens are tuned for perceptual differentiation.\n */\nexport const CHART_PALETTE = [\n\t\"hsl(var(--chart-1, var(--primary)))\",\n\t\"hsl(var(--chart-2, var(--primary)))\",\n\t\"hsl(var(--chart-3, var(--primary)))\",\n\t\"hsl(var(--chart-4, var(--primary)))\",\n\t\"hsl(var(--chart-5, var(--primary)))\",\n\t\"hsl(var(--chart-6, var(--primary)))\",\n] as const;\n\n/**\n * Return the chart hue at a stable index. Cycles modulo `CHART_PALETTE.length`\n * so the caller doesn't have to range-check.\n *\n * @param index - Integer key (node index, leaf index, depth-1 ancestor index, …).\n * @returns A `hsl(var(...))` string suitable for SVG `fill` / `stroke`.\n */\nexport function pickChartHue(index: number): string {\n\tconst safe = ((index % CHART_PALETTE.length) + CHART_PALETTE.length) % CHART_PALETTE.length;\n\t// `safe` is provably 0..CHART_PALETTE.length-1 — the assertion documents that.\n\treturn CHART_PALETTE[safe] as string;\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { pickChartHue } from \"../../lib/chart-palette.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Typed React TreeMap. Each leaf is rendered as a rectangle whose area is\n * proportional to its `value`. Internal node values are summed automatically;\n * d3-hierarchy's squarified treemap layout keeps rectangles close to square\n * for legibility.\n *\n * Heavy peer: requires `d3-hierarchy` (~3 KB gzip).\n *\n * @example\n * <TreeMap\n * root={{\n * id: \"root\",\n * label: \"Files\",\n * children: [\n * { id: \"src\", label: \"src\", value: 240 },\n * { id: \"node_modules\", label: \"node_modules\", value: 980 },\n * ],\n * }}\n * />\n */\nexport type TreeMapNode = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n\tchildren?: TreeMapNode[];\n};\n\nexport interface TreeMapProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Root of the hierarchy. Leaves require a positive `value`. */\n\troot: TreeMapNode;\n\t/** Pixel width of the rendered SVG. Default 600. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 400. */\n\theight?: number;\n\t/** Inner padding between sibling rectangles, in pixels. Default 2. */\n\tpadding?: number;\n\t/** Tiling algorithm. \"squarify\" (default) keeps rectangles close to square. */\n\ttile?: \"squarify\" | \"binary\" | \"slice-dice\";\n\t/** Choose a fill color per leaf. \"depth\" cycles through theme tokens; \"value\" interpolates by value; or pass a function. */\n\tcolorBy?: \"depth\" | \"value\" | ((node: TreeMapNode, depth: number) => string);\n\t/** Fired when a leaf is clicked. */\n\tonLeafClick?: (node: TreeMapNode) => void;\n}\n\ninterface LaidOutLeaf {\n\tnode: TreeMapNode;\n\tdepth: number;\n\tx0: number;\n\ty0: number;\n\tx1: number;\n\ty1: number;\n\tvalue: number;\n\t/** Leaf index in pre-order traversal — used to cycle through CHART_PALETTE\n\t * so adjacent rectangles read as distinct categories. */\n\tleafIdx: number;\n\t/** Index of this leaf's depth-1 ancestor; multi-level trees use this so\n\t * descendants of the same branch share a hue family. */\n\trootSiblingIdx: number;\n}\n\ntype D3HierarchyMod = typeof import(\"d3-hierarchy\");\n\nfunction TreeMap({\n\troot,\n\twidth = 600,\n\theight = 400,\n\tpadding = 2,\n\ttile = \"squarify\",\n\tcolorBy = \"depth\",\n\tonLeafClick,\n\tclassName,\n\t...rest\n}: TreeMapProps) {\n\tconst [d3h, setD3h] = React.useState<D3HierarchyMod | null>(null);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-hierarchy\").then((mod) => {\n\t\t\tif (!cancelled) setD3h(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\tif (!d3h) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-tree-map-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst leaves = layout(d3h, root, width, height, padding, tile);\n\tconst maxValue = leaves.reduce((m, l) => (l.value > m ? l.value : m), 0) || 1;\n\tconst desc = `Tree map of ${leaves.length} leaf${leaves.length === 1 ? \"\" : \"s\"}, rooted at \"${root.label}\"`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-tree-map\n\t\t\tdata-tile={tile}\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Tree map</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t{leaves.map((l) => {\n\t\t\t\tconst w = l.x1 - l.x0;\n\t\t\t\tconst h = l.y1 - l.y0;\n\t\t\t\tconst fill = resolveFill(l, maxValue, colorBy);\n\t\t\t\treturn (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={l.node.id}\n\t\t\t\t\t\tdata-hex-tree-map-leaf\n\t\t\t\t\t\tdata-depth={l.depth}\n\t\t\t\t\t\ttransform={`translate(${l.x0},${l.y0})`}\n\t\t\t\t\t\tstyle={onLeafClick ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={onLeafClick ? () => onLeafClick(l.node) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\twidth={w}\n\t\t\t\t\t\t\theight={h}\n\t\t\t\t\t\t\tfill={fill}\n\t\t\t\t\t\t\tfillOpacity={0.85}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{w > 40 && h > 16 ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={6}\n\t\t\t\t\t\t\t\ty={16}\n\t\t\t\t\t\t\t\tfontSize={12}\n\t\t\t\t\t\t\t\tfontWeight={500}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.35)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{l.node.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{w > 60 && h > 32 ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={6}\n\t\t\t\t\t\t\t\ty={32}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background) / 0.85)\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.3)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{l.value.toLocaleString()}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</g>\n\t\t\t\t);\n\t\t\t})}\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3h: D3HierarchyMod,\n\troot: TreeMapNode,\n\twidth: number,\n\theight: number,\n\tpadding: number,\n\ttile: \"squarify\" | \"binary\" | \"slice-dice\",\n): LaidOutLeaf[] {\n\tconst tileFn = tile === \"binary\" ? d3h.treemapBinary : tile === \"slice-dice\" ? d3h.treemapSliceDice : d3h.treemapSquarify;\n\tconst hierarchy = d3h\n\t\t.hierarchy<TreeMapNode>(root)\n\t\t.sum((d) => (d.children && d.children.length > 0 ? 0 : d.value ?? 0))\n\t\t.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));\n\t// treemap(hierarchy) returns HierarchyRectangularNode<T> with typed\n\t// x0/x1/y0/y1 (pixel coords).\n\tconst layoutRoot = d3h\n\t\t.treemap<TreeMapNode>()\n\t\t.tile(tileFn)\n\t\t.size([width, height])\n\t\t.padding(padding)(hierarchy);\n\treturn layoutRoot.leaves().map((leaf, leafIdx) => {\n\t\t// Walk up to depth-1 ancestor; descendants of the same top-level\n\t\t// branch share a hue family for tree visual cohesion.\n\t\tlet cursor: typeof leaf | null = leaf;\n\t\twhile (cursor && cursor.depth > 1) cursor = cursor.parent;\n\t\tconst ancestorIdx = cursor?.parent?.children?.indexOf(cursor) ?? leafIdx;\n\t\treturn {\n\t\t\tnode: leaf.data,\n\t\t\tdepth: leaf.depth,\n\t\t\tx0: leaf.x0,\n\t\t\ty0: leaf.y0,\n\t\t\tx1: leaf.x1,\n\t\t\ty1: leaf.y1,\n\t\t\tvalue: leaf.value ?? 0,\n\t\t\tleafIdx,\n\t\t\trootSiblingIdx: Math.max(0, ancestorIdx),\n\t\t};\n\t});\n}\n\nfunction resolveFill(\n\tleaf: LaidOutLeaf,\n\tmaxValue: number,\n\tcolorBy: TreeMapProps[\"colorBy\"],\n): string {\n\tif (typeof colorBy === \"function\") return colorBy(leaf.node, leaf.depth);\n\tif (colorBy === \"value\") {\n\t\tconst t = Math.max(0.2, Math.min(1, leaf.value / maxValue));\n\t\treturn `hsl(var(--chart-1) / ${t.toFixed(2)})`;\n\t}\n\t// \"depth\" cycles through CHART_PALETTE by ancestor for nested trees,\n\t// or by leaf index for single-level trees (where every leaf is at the\n\t// same depth and would otherwise collapse to one color).\n\tconst idx = leaf.depth > 1 ? leaf.rootSiblingIdx : leaf.leafIdx;\n\treturn pickChartHue(idx);\n}\n\nexport { TreeMap };\n"]}
package/dist/tree.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/components/tree/tree.tsx"],"names":["row"],"mappings":"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","file":"tree.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * One node in a Tree. Generic — the shape is content-agnostic so consumers\n * can render org charts, taxonomy pickers, navigation trees, etc.\n *\n * For filesystem-shaped data (with file-extension icon logic baked in),\n * prefer {@link FileTree} instead.\n */\nexport interface TreeNode {\n\t/** Stable unique id used as React key + ARIA target. */\n\tid: string;\n\t/** Display name. */\n\tlabel: React.ReactNode;\n\t/** Nested children. Presence (even empty array) marks the node as a parent. */\n\tchildren?: TreeNode[];\n\t/** Optional icon (default: a chevron + dot). */\n\ticon?: React.ReactNode;\n\t/** Disable selection + expand toggle. */\n\tdisabled?: boolean;\n}\n\nexport interface TreeProps {\n\t/** Forwarded ref onto the root `<ul role=\"tree\">`. */\n\tref?: React.Ref<HTMLUListElement>;\n\t/** Root nodes. */\n\tdata: TreeNode[];\n\t/** Uncontrolled initial expanded ids. */\n\tdefaultExpanded?: string[];\n\t/** Controlled expanded ids. */\n\texpanded?: string[];\n\t/** Fired when expanded set changes (array of ids). */\n\tonExpandedChange?: (ids: string[]) => void;\n\t/** Controlled selected node id. */\n\tselected?: string;\n\t/** Fired when the user activates a node (click, Enter, or Space). */\n\tonSelect?: (id: string) => void;\n\t/** Required accessible name. */\n\t\"aria-label\": string;\n\t/** Optional additional class names. */\n\tclassName?: string;\n}\n\n/** Recursively flatten a tree into the visible-row order (respecting collapsed parents). */\nfunction flattenVisible(nodes: TreeNode[], expanded: Set<string>): Array<{ node: TreeNode; depth: number; parentId: string | null }> {\n\tconst out: Array<{ node: TreeNode; depth: number; parentId: string | null }> = [];\n\tfunction walk(items: TreeNode[], depth: number, parentId: string | null) {\n\t\tfor (const node of items) {\n\t\t\tout.push({ node, depth, parentId });\n\t\t\tif (node.children && expanded.has(node.id)) {\n\t\t\t\twalk(node.children, depth + 1, node.id);\n\t\t\t}\n\t\t}\n\t}\n\twalk(nodes, 0, null);\n\treturn out;\n}\n\n/**\n * Generic hierarchical list with roving-tabindex keyboard navigation —\n * arrow keys move focus, → expands, ← collapses, Home / End jump to\n * first / last visible row, Enter / Space activates the focused node.\n *\n * Distinct from {@link FileTree} (which adds folder/file icon-by-extension\n * logic baked in) and {@link Accordion} (two-level groupings without\n * item-selection semantics).\n *\n * @example\n * ```tsx\n * <Tree\n * aria-label=\"Org chart\"\n * data={[\n * { id: \"ceo\", label: \"CEO\", children: [\n * { id: \"cto\", label: \"CTO\", children: [\n * { id: \"eng-lead\", label: \"Eng Lead\" },\n * ]},\n * { id: \"cmo\", label: \"CMO\" },\n * ]},\n * ]}\n * defaultExpanded={[\"ceo\"]}\n * onSelect={(id) => console.log(id)}\n * />\n * ```\n */\nfunction Tree({\n\tdata,\n\tdefaultExpanded,\n\texpanded: expandedProp,\n\tonExpandedChange,\n\tselected: selectedProp,\n\tonSelect,\n\t\"aria-label\": ariaLabel,\n\tclassName,\n\tref,\n}: TreeProps) {\n\tconst [internalExpanded, setInternalExpanded] = React.useState<Set<string>>(\n\t\t() => new Set(defaultExpanded ?? []),\n\t);\n\tconst expanded = React.useMemo(\n\t\t() => (expandedProp ? new Set(expandedProp) : internalExpanded),\n\t\t[expandedProp, internalExpanded],\n\t);\n\n\t\tconst [internalSelected, setInternalSelected] = React.useState<string | null>(null);\n\t\tconst selected = selectedProp ?? internalSelected;\n\n\t\tconst visible = React.useMemo(() => flattenVisible(data, expanded), [data, expanded]);\n\n\t\tconst [focusedId, setFocusedId] = React.useState<string | null>(\n\t\t\t() => visible[0]?.node.id ?? null,\n\t\t);\n\n\t\t// If the focused node disappeared (parent collapsed), retarget to the parent.\n\t\tReact.useEffect(() => {\n\t\t\tif (focusedId && !visible.some((row) => row.node.id === focusedId)) {\n\t\t\t\tsetFocusedId(visible[0]?.node.id ?? null);\n\t\t\t}\n\t\t}, [visible, focusedId]);\n\n\t\tconst setExpandedSet = React.useCallback(\n\t\t\t(next: Set<string>) => {\n\t\t\t\tif (expandedProp) {\n\t\t\t\t\tonExpandedChange?.([...next]);\n\t\t\t\t} else {\n\t\t\t\t\tsetInternalExpanded(next);\n\t\t\t\t\tonExpandedChange?.([...next]);\n\t\t\t\t}\n\t\t\t},\n\t\t\t[expandedProp, onExpandedChange],\n\t\t);\n\n\t\tconst toggleExpand = React.useCallback(\n\t\t\t(id: string) => {\n\t\t\t\tconst next = new Set(expanded);\n\t\t\t\tif (next.has(id)) next.delete(id);\n\t\t\t\telse next.add(id);\n\t\t\t\tsetExpandedSet(next);\n\t\t\t},\n\t\t\t[expanded, setExpandedSet],\n\t\t);\n\n\t\tconst activate = React.useCallback(\n\t\t\t(id: string) => {\n\t\t\t\tif (selectedProp === undefined) setInternalSelected(id);\n\t\t\t\tonSelect?.(id);\n\t\t\t},\n\t\t\t[selectedProp, onSelect],\n\t\t);\n\n\t\tconst handleKeyDown = React.useCallback(\n\t\t\t(e: React.KeyboardEvent<HTMLUListElement>) => {\n\t\t\t\tif (!focusedId) return;\n\t\t\t\tconst idx = visible.findIndex((row) => row.node.id === focusedId);\n\t\t\t\tif (idx < 0) return;\n\t\t\t\tconst row = visible[idx];\n\t\t\t\tif (!row) return;\n\t\t\t\tconst node = row.node;\n\t\t\t\tconst isParent = !!node.children;\n\t\t\t\tconst isExpanded = expanded.has(node.id);\n\n\t\t\t\tswitch (e.key) {\n\t\t\t\t\tcase \"ArrowDown\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tconst next = visible[idx + 1];\n\t\t\t\t\t\tif (next) setFocusedId(next.node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"ArrowUp\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tconst prev = visible[idx - 1];\n\t\t\t\t\t\tif (prev) setFocusedId(prev.node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"ArrowRight\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (isParent && !isExpanded) toggleExpand(node.id);\n\t\t\t\t\t\telse if (isParent && isExpanded) {\n\t\t\t\t\t\t\tconst next = visible[idx + 1];\n\t\t\t\t\t\t\tif (next) setFocusedId(next.node.id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"ArrowLeft\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (isParent && isExpanded) toggleExpand(node.id);\n\t\t\t\t\t\telse if (row.parentId) {\n\t\t\t\t\t\t\tsetFocusedId(row.parentId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"Home\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (visible[0]) setFocusedId(visible[0].node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"End\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tconst last = visible[visible.length - 1];\n\t\t\t\t\t\tif (last) setFocusedId(last.node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"Enter\":\n\t\t\t\t\tcase \" \": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (node.disabled) return;\n\t\t\t\t\t\tif (isParent) toggleExpand(node.id);\n\t\t\t\t\t\tactivate(node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t},\n\t\t\t[focusedId, visible, expanded, toggleExpand, activate],\n\t\t);\n\n\t// H3: emit boolean aria-selected when selection is wired (so AT can\n\t// announce \"1 of N, not selected\" on non-selected items); leave it\n\t// undefined entirely when nothing on the tree is selectable.\n\tconst isSelectable = onSelect !== undefined || selectedProp !== undefined;\n\n\treturn (\n\t\t<ul\n\t\t\tref={ref}\n\t\t\trole=\"tree\"\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\"flex flex-col text-sm text-foreground\", className)}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t>\n\t\t\t{data.map((node) => (\n\t\t\t\t<TreeItem\n\t\t\t\t\tkey={node.id}\n\t\t\t\t\tnode={node}\n\t\t\t\t\tdepth={0}\n\t\t\t\t\texpanded={expanded}\n\t\t\t\t\tselected={selected}\n\t\t\t\t\tisSelectable={isSelectable}\n\t\t\t\t\tfocusedId={focusedId}\n\t\t\t\t\tonFocus={setFocusedId}\n\t\t\t\t\tonToggleExpand={toggleExpand}\n\t\t\t\t\tonActivate={activate}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\n\ninterface TreeItemProps {\n\tnode: TreeNode;\n\tdepth: number;\n\texpanded: Set<string>;\n\tselected: string | null;\n\tisSelectable: boolean;\n\tfocusedId: string | null;\n\tonFocus: (id: string) => void;\n\tonToggleExpand: (id: string) => void;\n\tonActivate: (id: string) => void;\n}\n\n/** One row inside a Tree. Recurses through children when expanded. */\nfunction TreeItem({\n\tnode,\n\tdepth,\n\texpanded,\n\tselected,\n\tisSelectable,\n\tfocusedId,\n\tonFocus,\n\tonToggleExpand,\n\tonActivate,\n}: TreeItemProps) {\n\tconst children = node.children;\n\tconst isParent = children !== undefined;\n\tconst isExpanded = expanded.has(node.id);\n\tconst isSelected = selected === node.id;\n\tconst isFocused = focusedId === node.id;\n\n\tconst handleClick = () => {\n\t\tif (node.disabled) return;\n\t\tonFocus(node.id);\n\t\tif (isParent) onToggleExpand(node.id);\n\t\tonActivate(node.id);\n\t};\n\n\tconst labelId = React.useId();\n\treturn (\n\t\t<li\n\t\t\trole=\"treeitem\"\n\t\t\taria-labelledby={labelId}\n\t\t\taria-level={depth + 1}\n\t\t\taria-expanded={isParent ? isExpanded : undefined}\n\t\t\t// H3: when the tree is selectable (caller wired onSelect or\n\t\t\t// selected), emit boolean aria-selected on every item — AT\n\t\t\t// announces \"1 of N, not selected\" instead of dropping the\n\t\t\t// attribute entirely. When nothing is selectable, leave it off.\n\t\t\taria-selected={isSelectable ? isSelected : undefined}\n\t\t\taria-disabled={node.disabled || undefined}\n\t\t\ttabIndex={isFocused ? 0 : -1}\n\t\t\tonClick={(e) => {\n\t\t\t\t// Stop bubbling so a parent treeitem doesn't refire on a\n\t\t\t\t// nested-child click — each li carries its own click handler.\n\t\t\t\te.stopPropagation();\n\t\t\t\thandleClick();\n\t\t\t}}\n\t\t\tonFocus={(e) => {\n\t\t\t\t// stopPropagation prevents the parent treeitem's onFocus from\n\t\t\t\t// also firing when a deeper child gains focus, racing the\n\t\t\t\t// focusedId state. Side effect: focus-trap libraries listening\n\t\t\t\t// at a wrapping container don't see nested-tree focus events.\n\t\t\t\t// In practice tree-inside-Drawer / Dialog still works because\n\t\t\t\t// those focus traps activate on outer focusin, not inner.\n\t\t\t\te.stopPropagation();\n\t\t\t\tif (!node.disabled) onFocus(node.id);\n\t\t\t}}\n\t\t\tclassName={cn(\n\t\t\t\t\"outline-none rounded-sm\",\n\t\t\t\t// H1: focus-visible-driven ring (NOT state-driven) — the ring\n\t\t\t\t// only shows on keyboard focus, not on mouse clicks.\n\t\t\t\t\"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\t\t)}\n\t\t>\n\t\t\t{/*\n\t\t\t Row element — visible surface. Style only; the li carries the\n\t\t\t interactivity + focus + ARIA. aria-hidden on chevron + icon\n\t\t\t spans keeps the accessible name pinned to the labelled\n\t\t\t <span id={labelId}>.\n\t\t\t*/}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex cursor-pointer select-none items-center gap-[var(--gap-xs,0.25rem)] rounded-sm px-[var(--space-2,0.5rem)] py-[var(--space-1,0.25rem)]\",\n\t\t\t\t\t\"transition-colors duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\"hover:bg-accent hover:text-accent-foreground\",\n\t\t\t\t\tisSelected && \"bg-accent text-accent-foreground font-medium\",\n\t\t\t\t\tnode.disabled && \"cursor-not-allowed opacity-50\",\n\t\t\t\t)}\n\t\t\t\tstyle={{ paddingLeft: `calc(${depth} * var(--space-4, 1rem) + var(--space-2, 0.5rem))` }}\n\t\t\t>\n\t\t\t\t{isParent ? (\n\t\t\t\t\t<span aria-hidden=\"true\" className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center\">\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"h-3 w-3 transition-transform duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\t\t\tisExpanded ? \"rotate-90\" : \"rotate-0\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<polyline points=\"9 18 15 12 9 6\" />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</span>\n\t\t\t\t) : (\n\t\t\t\t\t<span aria-hidden=\"true\" className=\"inline-block h-4 w-4 shrink-0\" />\n\t\t\t\t)}\n\t\t\t\t{node.icon ? (\n\t\t\t\t\t<span aria-hidden=\"true\" className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center [&_svg]:size-4\">\n\t\t\t\t\t\t{node.icon}\n\t\t\t\t\t</span>\n\t\t\t\t) : null}\n\t\t\t\t<span id={labelId} className=\"truncate\">\n\t\t\t\t\t{node.label}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t{isParent && isExpanded && children ? (\n\t\t\t\t<ul role=\"group\" aria-labelledby={labelId} className=\"flex flex-col\">\n\t\t\t\t\t{children.map((child) => (\n\t\t\t\t\t\t<TreeItem\n\t\t\t\t\t\t\tkey={child.id}\n\t\t\t\t\t\t\tnode={child}\n\t\t\t\t\t\t\tdepth={depth + 1}\n\t\t\t\t\t\t\texpanded={expanded}\n\t\t\t\t\t\t\tselected={selected}\n\t\t\t\t\t\t\tisSelectable={isSelectable}\n\t\t\t\t\t\t\tfocusedId={focusedId}\n\t\t\t\t\t\t\tonFocus={onFocus}\n\t\t\t\t\t\t\tonToggleExpand={onToggleExpand}\n\t\t\t\t\t\t\tonActivate={onActivate}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t) : null}\n\t\t</li>\n\t);\n}\n\nexport { Tree };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/tree/tree.tsx"],"names":["row"],"mappings":"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","file":"tree.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * One node in a Tree. Generic — the shape is content-agnostic so consumers\n * can render org charts, taxonomy pickers, navigation trees, etc.\n *\n * For filesystem-shaped data (with file-extension icon logic baked in),\n * prefer {@link FileTree} instead.\n */\nexport interface TreeNode {\n\t/** Stable unique id used as React key + ARIA target. */\n\tid: string;\n\t/** Display name. */\n\tlabel: React.ReactNode;\n\t/** Nested children. Presence (even empty array) marks the node as a parent. */\n\tchildren?: TreeNode[];\n\t/** Optional icon (default: a chevron + dot). */\n\ticon?: React.ReactNode;\n\t/** Disable selection + expand toggle. */\n\tdisabled?: boolean;\n}\n\nexport interface TreeProps {\n\t/** Forwarded ref onto the root `<ul role=\"tree\">`. */\n\tref?: React.Ref<HTMLUListElement>;\n\t/** Root nodes. */\n\tdata: TreeNode[];\n\t/** Uncontrolled initial expanded ids. */\n\tdefaultExpanded?: string[];\n\t/** Controlled expanded ids. */\n\texpanded?: string[];\n\t/** Fired when expanded set changes (array of ids). */\n\tonExpandedChange?: (ids: string[]) => void;\n\t/** Controlled selected node id. */\n\tselected?: string;\n\t/** Fired when the user activates a node (click, Enter, or Space). */\n\tonSelect?: (id: string) => void;\n\t/** Required accessible name. */\n\t\"aria-label\": string;\n\t/** Optional additional class names. */\n\tclassName?: string;\n}\n\n/** Recursively flatten a tree into the visible-row order (respecting collapsed parents). */\nfunction flattenVisible(nodes: TreeNode[], expanded: Set<string>): Array<{ node: TreeNode; depth: number; parentId: string | null }> {\n\tconst out: Array<{ node: TreeNode; depth: number; parentId: string | null }> = [];\n\tfunction walk(items: TreeNode[], depth: number, parentId: string | null) {\n\t\tfor (const node of items) {\n\t\t\tout.push({ node, depth, parentId });\n\t\t\tif (node.children && expanded.has(node.id)) {\n\t\t\t\twalk(node.children, depth + 1, node.id);\n\t\t\t}\n\t\t}\n\t}\n\twalk(nodes, 0, null);\n\treturn out;\n}\n\n/**\n * Generic hierarchical list with roving-tabindex keyboard navigation —\n * arrow keys move focus, → expands, ← collapses, Home / End jump to\n * first / last visible row, Enter / Space activates the focused node.\n *\n * Distinct from {@link FileTree} (which adds folder/file icon-by-extension\n * logic baked in) and {@link Accordion} (two-level groupings without\n * item-selection semantics).\n *\n * @example\n * ```tsx\n * <Tree\n * aria-label=\"Org chart\"\n * data={[\n * { id: \"ceo\", label: \"CEO\", children: [\n * { id: \"cto\", label: \"CTO\", children: [\n * { id: \"eng-lead\", label: \"Eng Lead\" },\n * ]},\n * { id: \"cmo\", label: \"CMO\" },\n * ]},\n * ]}\n * defaultExpanded={[\"ceo\"]}\n * onSelect={(id) => console.log(id)}\n * />\n * ```\n */\nfunction Tree({\n\tdata,\n\tdefaultExpanded,\n\texpanded: expandedProp,\n\tonExpandedChange,\n\tselected: selectedProp,\n\tonSelect,\n\t\"aria-label\": ariaLabel,\n\tclassName,\n\tref,\n}: TreeProps) {\n\tconst [internalExpanded, setInternalExpanded] = React.useState<Set<string>>(\n\t\t() => new Set(defaultExpanded ?? []),\n\t);\n\tconst expanded = React.useMemo(\n\t\t() => (expandedProp ? new Set(expandedProp) : internalExpanded),\n\t\t[expandedProp, internalExpanded],\n\t);\n\n\t\tconst [internalSelected, setInternalSelected] = React.useState<string | null>(null);\n\t\tconst selected = selectedProp ?? internalSelected;\n\n\t\tconst visible = React.useMemo(() => flattenVisible(data, expanded), [data, expanded]);\n\n\t\tconst [focusedId, setFocusedId] = React.useState<string | null>(\n\t\t\t() => visible[0]?.node.id ?? null,\n\t\t);\n\n\t\t// If the focused node disappeared (parent collapsed), retarget to the parent.\n\t\tReact.useEffect(() => {\n\t\t\tif (focusedId && !visible.some((row) => row.node.id === focusedId)) {\n\t\t\t\tsetFocusedId(visible[0]?.node.id ?? null);\n\t\t\t}\n\t\t}, [visible, focusedId]);\n\n\t\tconst setExpandedSet = React.useCallback(\n\t\t\t(next: Set<string>) => {\n\t\t\t\tif (expandedProp) {\n\t\t\t\t\tonExpandedChange?.([...next]);\n\t\t\t\t} else {\n\t\t\t\t\tsetInternalExpanded(next);\n\t\t\t\t\tonExpandedChange?.([...next]);\n\t\t\t\t}\n\t\t\t},\n\t\t\t[expandedProp, onExpandedChange],\n\t\t);\n\n\t\tconst toggleExpand = React.useCallback(\n\t\t\t(id: string) => {\n\t\t\t\tconst next = new Set(expanded);\n\t\t\t\tif (next.has(id)) next.delete(id);\n\t\t\t\telse next.add(id);\n\t\t\t\tsetExpandedSet(next);\n\t\t\t},\n\t\t\t[expanded, setExpandedSet],\n\t\t);\n\n\t\tconst activate = React.useCallback(\n\t\t\t(id: string) => {\n\t\t\t\tif (selectedProp === undefined) setInternalSelected(id);\n\t\t\t\tonSelect?.(id);\n\t\t\t},\n\t\t\t[selectedProp, onSelect],\n\t\t);\n\n\t\tconst handleKeyDown = React.useCallback(\n\t\t\t(e: React.KeyboardEvent<HTMLUListElement>) => {\n\t\t\t\tif (!focusedId) return;\n\t\t\t\tconst idx = visible.findIndex((row) => row.node.id === focusedId);\n\t\t\t\tif (idx < 0) return;\n\t\t\t\tconst row = visible[idx];\n\t\t\t\tif (!row) return;\n\t\t\t\tconst node = row.node;\n\t\t\t\tconst isParent = !!node.children;\n\t\t\t\tconst isExpanded = expanded.has(node.id);\n\n\t\t\t\tswitch (e.key) {\n\t\t\t\t\tcase \"ArrowDown\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tconst next = visible[idx + 1];\n\t\t\t\t\t\tif (next) setFocusedId(next.node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"ArrowUp\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tconst prev = visible[idx - 1];\n\t\t\t\t\t\tif (prev) setFocusedId(prev.node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"ArrowRight\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (isParent && !isExpanded) toggleExpand(node.id);\n\t\t\t\t\t\telse if (isParent && isExpanded) {\n\t\t\t\t\t\t\tconst next = visible[idx + 1];\n\t\t\t\t\t\t\tif (next) setFocusedId(next.node.id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"ArrowLeft\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (isParent && isExpanded) toggleExpand(node.id);\n\t\t\t\t\t\telse if (row.parentId) {\n\t\t\t\t\t\t\tsetFocusedId(row.parentId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"Home\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (visible[0]) setFocusedId(visible[0].node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"End\": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tconst last = visible[visible.length - 1];\n\t\t\t\t\t\tif (last) setFocusedId(last.node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"Enter\":\n\t\t\t\t\tcase \" \": {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tif (node.disabled) return;\n\t\t\t\t\t\tif (isParent) toggleExpand(node.id);\n\t\t\t\t\t\tactivate(node.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t},\n\t\t\t[focusedId, visible, expanded, toggleExpand, activate],\n\t\t);\n\n\t// H3: emit boolean aria-selected when selection is wired (so AT can\n\t// announce \"1 of N, not selected\" on non-selected items); leave it\n\t// undefined entirely when nothing on the tree is selectable.\n\tconst isSelectable = onSelect !== undefined || selectedProp !== undefined;\n\n\treturn (\n\t\t<ul\n\t\t\tref={ref}\n\t\t\trole=\"tree\"\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\"flex flex-col text-sm text-foreground\", className)}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t>\n\t\t\t{data.map((node) => (\n\t\t\t\t<TreeItem\n\t\t\t\t\tkey={node.id}\n\t\t\t\t\tnode={node}\n\t\t\t\t\tdepth={0}\n\t\t\t\t\texpanded={expanded}\n\t\t\t\t\tselected={selected}\n\t\t\t\t\tisSelectable={isSelectable}\n\t\t\t\t\tfocusedId={focusedId}\n\t\t\t\t\tonFocus={setFocusedId}\n\t\t\t\t\tonToggleExpand={toggleExpand}\n\t\t\t\t\tonActivate={activate}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\n\ninterface TreeItemProps {\n\tnode: TreeNode;\n\tdepth: number;\n\texpanded: Set<string>;\n\tselected: string | null;\n\tisSelectable: boolean;\n\tfocusedId: string | null;\n\tonFocus: (id: string) => void;\n\tonToggleExpand: (id: string) => void;\n\tonActivate: (id: string) => void;\n}\n\n/** One row inside a Tree. Recurses through children when expanded. */\nfunction TreeItem({\n\tnode,\n\tdepth,\n\texpanded,\n\tselected,\n\tisSelectable,\n\tfocusedId,\n\tonFocus,\n\tonToggleExpand,\n\tonActivate,\n}: TreeItemProps) {\n\tconst children = node.children;\n\tconst isParent = children !== undefined;\n\tconst isExpanded = expanded.has(node.id);\n\tconst isSelected = selected === node.id;\n\tconst isFocused = focusedId === node.id;\n\n\tconst handleClick = () => {\n\t\tif (node.disabled) return;\n\t\tonFocus(node.id);\n\t\tif (isParent) onToggleExpand(node.id);\n\t\tonActivate(node.id);\n\t};\n\n\tconst labelId = React.useId();\n\treturn (\n\t\t<li\n\t\t\trole=\"treeitem\"\n\t\t\taria-labelledby={labelId}\n\t\t\taria-level={depth + 1}\n\t\t\taria-expanded={isParent ? isExpanded : undefined}\n\t\t\t// H3: when the tree is selectable (caller wired onSelect or\n\t\t\t// selected), emit boolean aria-selected on every item — AT\n\t\t\t// announces \"1 of N, not selected\" instead of dropping the\n\t\t\t// attribute entirely. When nothing is selectable, leave it off.\n\t\t\taria-selected={isSelectable ? isSelected : undefined}\n\t\t\taria-disabled={node.disabled || undefined}\n\t\t\ttabIndex={isFocused ? 0 : -1}\n\t\t\tonClick={(e) => {\n\t\t\t\t// Stop bubbling so a parent treeitem doesn't refire on a\n\t\t\t\t// nested-child click — each li carries its own click handler.\n\t\t\t\te.stopPropagation();\n\t\t\t\thandleClick();\n\t\t\t}}\n\t\t\tonFocus={(e) => {\n\t\t\t\t// stopPropagation prevents the parent treeitem's onFocus from\n\t\t\t\t// also firing when a deeper child gains focus, racing the\n\t\t\t\t// focusedId state. Side effect: focus-trap libraries listening\n\t\t\t\t// at a wrapping container don't see nested-tree focus events.\n\t\t\t\t// In practice tree-inside-Drawer / Dialog still works because\n\t\t\t\t// those focus traps activate on outer focusin, not inner.\n\t\t\t\te.stopPropagation();\n\t\t\t\tif (!node.disabled) onFocus(node.id);\n\t\t\t}}\n\t\t\tclassName={cn(\n\t\t\t\t\"outline-none rounded-sm\",\n\t\t\t\t// H1: focus-visible-driven ring (NOT state-driven) — the ring\n\t\t\t\t// only shows on keyboard focus, not on mouse clicks.\n\t\t\t\t\"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\t\t)}\n\t\t>\n\t\t\t{/*\n\t\t\t Row element — visible surface. Style only; the li carries the\n\t\t\t interactivity + focus + ARIA. aria-hidden on chevron + icon\n\t\t\t spans keeps the accessible name pinned to the labelled\n\t\t\t <span id={labelId}>.\n\t\t\t*/}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex cursor-pointer select-none items-center gap-[var(--gap-xs,0.25rem)] rounded-sm px-[var(--space-2,0.5rem)] py-[var(--space-1,0.25rem)]\",\n\t\t\t\t\t\"transition-colors duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\"hover:bg-accent hover:text-accent-foreground\",\n\t\t\t\t\tisSelected && \"bg-accent text-accent-foreground font-medium\",\n\t\t\t\t\tnode.disabled && \"cursor-not-allowed opacity-50\",\n\t\t\t\t)}\n\t\t\t\tstyle={{ paddingLeft: `calc(${depth} * var(--space-4, 1rem) + var(--space-2, 0.5rem))` }}\n\t\t\t>\n\t\t\t\t{isParent ? (\n\t\t\t\t\t<span aria-hidden=\"true\" className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center\">\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"h-3 w-3 transition-transform duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\t\t\tisExpanded ? \"rotate-90\" : \"rotate-0\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<polyline points=\"9 18 15 12 9 6\" />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</span>\n\t\t\t\t) : (\n\t\t\t\t\t<span aria-hidden=\"true\" className=\"inline-block h-4 w-4 shrink-0\" />\n\t\t\t\t)}\n\t\t\t\t{node.icon ? (\n\t\t\t\t\t<span aria-hidden=\"true\" className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center [&_svg]:size-4\">\n\t\t\t\t\t\t{node.icon}\n\t\t\t\t\t</span>\n\t\t\t\t) : null}\n\t\t\t\t<span id={labelId} className=\"truncate\">\n\t\t\t\t\t{node.label}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t{isParent && isExpanded && children ? (\n\t\t\t\t<ul role=\"group\" aria-labelledby={labelId} className=\"flex flex-col\">\n\t\t\t\t\t{children.map((child) => (\n\t\t\t\t\t\t<TreeItem\n\t\t\t\t\t\t\tkey={child.id}\n\t\t\t\t\t\t\tnode={child}\n\t\t\t\t\t\t\tdepth={depth + 1}\n\t\t\t\t\t\t\texpanded={expanded}\n\t\t\t\t\t\t\tselected={selected}\n\t\t\t\t\t\t\tisSelectable={isSelectable}\n\t\t\t\t\t\t\tfocusedId={focusedId}\n\t\t\t\t\t\t\tonFocus={onFocus}\n\t\t\t\t\t\t\tonToggleExpand={onToggleExpand}\n\t\t\t\t\t\t\tonActivate={onActivate}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t) : null}\n\t\t</li>\n\t);\n}\n\nexport { Tree };\n"]}