@hex-core/components 1.8.1 → 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 (158) hide show
  1. package/dist/_tsup-dts-rollup.d.ts +855 -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.d.ts +2 -0
  10. package/dist/auth-forgot-password.js +400 -0
  11. package/dist/auth-forgot-password.js.map +1 -0
  12. package/dist/auth-reset-password.d.ts +2 -0
  13. package/dist/auth-reset-password.js +323 -0
  14. package/dist/auth-reset-password.js.map +1 -0
  15. package/dist/auth-sign-in-split.d.ts +3 -0
  16. package/dist/auth-sign-in-split.js +443 -0
  17. package/dist/auth-sign-in-split.js.map +1 -0
  18. package/dist/auth-sign-up-card.d.ts +3 -0
  19. package/dist/auth-sign-up-card.js +590 -0
  20. package/dist/auth-sign-up-card.js.map +1 -0
  21. package/dist/auth-verify-email.d.ts +2 -0
  22. package/dist/auth-verify-email.js +339 -0
  23. package/dist/auth-verify-email.js.map +1 -0
  24. package/dist/auth-verify-otp.d.ts +2 -0
  25. package/dist/auth-verify-otp.js +349 -0
  26. package/dist/auth-verify-otp.js.map +1 -0
  27. package/dist/avatar.js.map +1 -1
  28. package/dist/badge.js.map +1 -1
  29. package/dist/branch.d.ts +2 -0
  30. package/dist/branch.js +136 -0
  31. package/dist/branch.js.map +1 -0
  32. package/dist/breadcrumb.js.map +1 -1
  33. package/dist/button.js.map +1 -1
  34. package/dist/calendar.js.map +1 -1
  35. package/dist/canvas.js.map +1 -1
  36. package/dist/card.js.map +1 -1
  37. package/dist/chain-of-thought.d.ts +3 -0
  38. package/dist/chain-of-thought.js +119 -0
  39. package/dist/chain-of-thought.js.map +1 -0
  40. package/dist/checkbox.js.map +1 -1
  41. package/dist/chord.js.map +1 -1
  42. package/dist/citation.js.map +1 -1
  43. package/dist/cloze.js.map +1 -1
  44. package/dist/cluster.js.map +1 -1
  45. package/dist/code-block-copy.js.map +1 -1
  46. package/dist/code-block.js.map +1 -1
  47. package/dist/color-picker.js.map +1 -1
  48. package/dist/combobox.js.map +1 -1
  49. package/dist/command.js.map +1 -1
  50. package/dist/compare-table.js.map +1 -1
  51. package/dist/composer.js.map +1 -1
  52. package/dist/container.js.map +1 -1
  53. package/dist/context-menu.js.map +1 -1
  54. package/dist/conversation.d.ts +3 -0
  55. package/dist/conversation.js +358 -0
  56. package/dist/conversation.js.map +1 -0
  57. package/dist/data-table.js.map +1 -1
  58. package/dist/date-picker.js.map +1 -1
  59. package/dist/deck.js.map +1 -1
  60. package/dist/dendrogram.js.map +1 -1
  61. package/dist/diagram.js.map +1 -1
  62. package/dist/dialog.js.map +1 -1
  63. package/dist/drawer.js.map +1 -1
  64. package/dist/dropdown-menu.js.map +1 -1
  65. package/dist/dropzone.js.map +1 -1
  66. package/dist/empty.js.map +1 -1
  67. package/dist/error-state.js.map +1 -1
  68. package/dist/file-tree.js.map +1 -1
  69. package/dist/flashcard.js.map +1 -1
  70. package/dist/flowchart.js.map +1 -1
  71. package/dist/form.js.map +1 -1
  72. package/dist/funnel.js.map +1 -1
  73. package/dist/gantt.js.map +1 -1
  74. package/dist/grid.js.map +1 -1
  75. package/dist/hover-card.js.map +1 -1
  76. package/dist/image-occlusion.js.map +1 -1
  77. package/dist/index.d.ts +40 -0
  78. package/dist/index.js +4839 -2813
  79. package/dist/index.js.map +1 -1
  80. package/dist/inline-citation.d.ts +2 -0
  81. package/dist/inline-citation.js +108 -0
  82. package/dist/inline-citation.js.map +1 -0
  83. package/dist/input-otp.js.map +1 -1
  84. package/dist/input.js.map +1 -1
  85. package/dist/label.js.map +1 -1
  86. package/dist/loading-indicator.js.map +1 -1
  87. package/dist/loading.js.map +1 -1
  88. package/dist/markdown.d.ts +1 -0
  89. package/dist/markdown.js +784 -4
  90. package/dist/markdown.js.map +1 -1
  91. package/dist/matrix.js.map +1 -1
  92. package/dist/menubar.js.map +1 -1
  93. package/dist/message-actions.js.map +1 -1
  94. package/dist/message-list.js.map +1 -1
  95. package/dist/message.js.map +1 -1
  96. package/dist/mind-map.js.map +1 -1
  97. package/dist/multi-combobox.js.map +1 -1
  98. package/dist/navigation-menu.js.map +1 -1
  99. package/dist/org-chart.js.map +1 -1
  100. package/dist/pagination.js.map +1 -1
  101. package/dist/plan.d.ts +3 -0
  102. package/dist/plan.js +183 -0
  103. package/dist/plan.js.map +1 -0
  104. package/dist/popover.js.map +1 -1
  105. package/dist/progress.js.map +1 -1
  106. package/dist/pyramid.js.map +1 -1
  107. package/dist/quiz.js.map +1 -1
  108. package/dist/radio-group.js.map +1 -1
  109. package/dist/reasoning.js.map +1 -1
  110. package/dist/resizable.js.map +1 -1
  111. package/dist/sankey.js.map +1 -1
  112. package/dist/schemas.d.ts +8 -0
  113. package/dist/schemas.js +774 -17
  114. package/dist/schemas.js.map +1 -1
  115. package/dist/scroll-area.js.map +1 -1
  116. package/dist/select.js.map +1 -1
  117. package/dist/separator.js.map +1 -1
  118. package/dist/sequence.js.map +1 -1
  119. package/dist/sheet.js.map +1 -1
  120. package/dist/shimmer.d.ts +2 -0
  121. package/dist/shimmer.js +39 -0
  122. package/dist/shimmer.js.map +1 -0
  123. package/dist/sidebar.js.map +1 -1
  124. package/dist/skeleton.js.map +1 -1
  125. package/dist/slider.js.map +1 -1
  126. package/dist/sources.d.ts +3 -0
  127. package/dist/sources.js +164 -0
  128. package/dist/sources.js.map +1 -0
  129. package/dist/spaced-repetition.js.map +1 -1
  130. package/dist/spacer.js.map +1 -1
  131. package/dist/speech-recognition.js.map +1 -1
  132. package/dist/stack.js.map +1 -1
  133. package/dist/stepper.js.map +1 -1
  134. package/dist/suggestion.js.map +1 -1
  135. package/dist/sunburst.js.map +1 -1
  136. package/dist/switch.js.map +1 -1
  137. package/dist/table.js.map +1 -1
  138. package/dist/tabs.js.map +1 -1
  139. package/dist/tag.js.map +1 -1
  140. package/dist/task.d.ts +3 -0
  141. package/dist/task.js +189 -0
  142. package/dist/task.js.map +1 -0
  143. package/dist/terminal.js +11 -0
  144. package/dist/terminal.js.map +1 -1
  145. package/dist/textarea.js.map +1 -1
  146. package/dist/time-axis.js.map +1 -1
  147. package/dist/time-picker.js.map +1 -1
  148. package/dist/timeline.js.map +1 -1
  149. package/dist/toggle-group.js.map +1 -1
  150. package/dist/toggle.js.map +1 -1
  151. package/dist/tool-call.js +5 -6
  152. package/dist/tool-call.js.map +1 -1
  153. package/dist/toolbar.js.map +1 -1
  154. package/dist/tooltip.js.map +1 -1
  155. package/dist/tree-map.js.map +1 -1
  156. package/dist/tree.js.map +1 -1
  157. package/dist/venn.js.map +1 -1
  158. package/package.json +9 -4
package/dist/empty.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/empty/empty.tsx"],"names":[],"mappings":"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","file":"empty.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 { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst emptyVariants = cva(\n\t[\n\t\t\"flex flex-col items-center justify-center text-center\",\n\t\t\"rounded-md border border-dashed border-border bg-muted/30\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tsize: {\n\t\t\t\tsm: \"gap-[var(--space-2,0.5rem)] px-[var(--space-4,1rem)] py-[var(--space-6,1.5rem)] text-sm\",\n\t\t\t\tdefault: \"gap-[var(--space-3,0.75rem)] px-[var(--space-6,1.5rem)] py-[var(--space-8,2rem)]\",\n\t\t\t\tlg: \"gap-[var(--space-4,1rem)] px-[var(--space-8,2rem)] py-[var(--space-12,3rem)]\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { size: \"default\" },\n\t},\n);\n\nconst emptyIconWrapperVariants = cva(\n\t\"flex shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5\",\n\t{\n\t\tvariants: {\n\t\t\tsize: {\n\t\t\t\tsm: \"h-9 w-9\",\n\t\t\t\tdefault: \"h-12 w-12 [&_svg]:size-6\",\n\t\t\t\tlg: \"h-16 w-16 [&_svg]:size-7\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { size: \"default\" },\n\t},\n);\n\nconst emptyTitleVariants = cva(\"font-semibold text-foreground\", {\n\tvariants: {\n\t\tsize: {\n\t\t\tsm: \"text-sm\",\n\t\t\tdefault: \"text-base\",\n\t\t\tlg: \"text-lg\",\n\t\t},\n\t},\n\tdefaultVariants: { size: \"default\" },\n});\n\nconst emptyDescriptionVariants = cva(\"max-w-md text-muted-foreground\", {\n\tvariants: {\n\t\tsize: {\n\t\t\tsm: \"text-xs\",\n\t\t\tdefault: \"text-sm\",\n\t\t\tlg: \"text-base\",\n\t\t},\n\t},\n\tdefaultVariants: { size: \"default\" },\n});\n\n/** Heading element used to render the Empty title. Defaults to `h3`. */\ntype EmptyTitleAs = \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\";\n\nexport interface EmptyProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\">,\n\t\tVariantProps<typeof emptyVariants> {\n\t/** Forwarded ref onto the root region element. */\n\tref?: React.Ref<HTMLDivElement>;\n\t/** Optional icon (typically an `<svg>`) rendered in a circular muted container. */\n\ticon?: React.ReactNode;\n\t/** Required heading copy. Becomes the region's accessible name via `aria-labelledby`. */\n\ttitle: React.ReactNode;\n\t/** Optional supporting copy that explains why the slot is empty + what to do next. */\n\tdescription?: React.ReactNode;\n\t/** Optional call-to-action — typically a `<Button>` that creates the missing record. */\n\taction?: React.ReactNode;\n\t/** Heading level for the title — pick to match surrounding hierarchy (default `h3`). */\n\ttitleAs?: EmptyTitleAs;\n}\n\n/**\n * A \"zero-state\" surface for lists, dashboards, and search results that have\n * no content to show. Use to explain *why* the slot is empty and *what to do*\n * next; pair the `action` slot with a button that creates the missing record.\n *\n * Distinct from {@link Loading} (transient, has a measurable wait) and\n * {@link ErrorState} (something failed and may need a retry). If you're\n * thinking \"show a message because the request just hasn't returned yet,\"\n * reach for `Loading` — Empty is for \"the request returned, and there's\n * nothing to show.\"\n *\n * @example\n * ```tsx\n * <Empty\n * icon={<InboxIcon />}\n * title=\"No messages yet\"\n * description=\"When someone sends you a message, it'll show up here.\"\n * action={<Button>Compose</Button>}\n * />\n * ```\n *\n * @returns A region landmark labeled by the title.\n */\nfunction Empty({\n\tclassName,\n\tsize,\n\ticon,\n\ttitle,\n\tdescription,\n\taction,\n\ttitleAs = \"h3\",\n\tref,\n\t...props\n}: EmptyProps) {\n\tconst titleId = React.useId();\n\tconst TitleComp = titleAs;\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\trole=\"region\"\n\t\t\taria-labelledby={titleId}\n\t\t\tclassName={cn(emptyVariants({ size }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{icon ? (\n\t\t\t\t<div className={emptyIconWrapperVariants({ size })} aria-hidden=\"true\">\n\t\t\t\t\t{icon}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<TitleComp id={titleId} className={emptyTitleVariants({ size })}>\n\t\t\t\t{title}\n\t\t\t</TitleComp>\n\t\t\t{description ? (\n\t\t\t\t<div className={emptyDescriptionVariants({ size })}>{description}</div>\n\t\t\t) : null}\n\t\t\t{action ? <div className=\"mt-[var(--space-2,0.5rem)]\">{action}</div> : null}\n\t\t</div>\n\t);\n}\n\nexport { Empty, emptyVariants };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/empty/empty.tsx"],"names":[],"mappings":"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","file":"empty.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 { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst emptyVariants = cva(\n\t[\n\t\t\"flex flex-col items-center justify-center text-center\",\n\t\t\"rounded-md border border-dashed border-border bg-muted/30\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tsize: {\n\t\t\t\tsm: \"gap-[var(--space-2,0.5rem)] px-[var(--space-4,1rem)] py-[var(--space-6,1.5rem)] text-sm\",\n\t\t\t\tdefault: \"gap-[var(--space-3,0.75rem)] px-[var(--space-6,1.5rem)] py-[var(--space-8,2rem)]\",\n\t\t\t\tlg: \"gap-[var(--space-4,1rem)] px-[var(--space-8,2rem)] py-[var(--space-12,3rem)]\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { size: \"default\" },\n\t},\n);\n\nconst emptyIconWrapperVariants = cva(\n\t\"flex shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-5\",\n\t{\n\t\tvariants: {\n\t\t\tsize: {\n\t\t\t\tsm: \"h-9 w-9\",\n\t\t\t\tdefault: \"h-12 w-12 [&_svg]:size-6\",\n\t\t\t\tlg: \"h-16 w-16 [&_svg]:size-7\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { size: \"default\" },\n\t},\n);\n\nconst emptyTitleVariants = cva(\"font-semibold text-foreground\", {\n\tvariants: {\n\t\tsize: {\n\t\t\tsm: \"text-sm\",\n\t\t\tdefault: \"text-base\",\n\t\t\tlg: \"text-lg\",\n\t\t},\n\t},\n\tdefaultVariants: { size: \"default\" },\n});\n\nconst emptyDescriptionVariants = cva(\"max-w-md text-muted-foreground\", {\n\tvariants: {\n\t\tsize: {\n\t\t\tsm: \"text-xs\",\n\t\t\tdefault: \"text-sm\",\n\t\t\tlg: \"text-base\",\n\t\t},\n\t},\n\tdefaultVariants: { size: \"default\" },\n});\n\n/** Heading element used to render the Empty title. Defaults to `h3`. */\ntype EmptyTitleAs = \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\";\n\nexport interface EmptyProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\">,\n\t\tVariantProps<typeof emptyVariants> {\n\t/** Forwarded ref onto the root region element. */\n\tref?: React.Ref<HTMLDivElement>;\n\t/** Optional icon (typically an `<svg>`) rendered in a circular muted container. */\n\ticon?: React.ReactNode;\n\t/** Required heading copy. Becomes the region's accessible name via `aria-labelledby`. */\n\ttitle: React.ReactNode;\n\t/** Optional supporting copy that explains why the slot is empty + what to do next. */\n\tdescription?: React.ReactNode;\n\t/** Optional call-to-action — typically a `<Button>` that creates the missing record. */\n\taction?: React.ReactNode;\n\t/** Heading level for the title — pick to match surrounding hierarchy (default `h3`). */\n\ttitleAs?: EmptyTitleAs;\n}\n\n/**\n * A \"zero-state\" surface for lists, dashboards, and search results that have\n * no content to show. Use to explain *why* the slot is empty and *what to do*\n * next; pair the `action` slot with a button that creates the missing record.\n *\n * Distinct from {@link Loading} (transient, has a measurable wait) and\n * {@link ErrorState} (something failed and may need a retry). If you're\n * thinking \"show a message because the request just hasn't returned yet,\"\n * reach for `Loading` — Empty is for \"the request returned, and there's\n * nothing to show.\"\n *\n * @example\n * ```tsx\n * <Empty\n * icon={<InboxIcon />}\n * title=\"No messages yet\"\n * description=\"When someone sends you a message, it'll show up here.\"\n * action={<Button>Compose</Button>}\n * />\n * ```\n *\n * @returns A region landmark labeled by the title.\n */\nfunction Empty({\n\tclassName,\n\tsize,\n\ticon,\n\ttitle,\n\tdescription,\n\taction,\n\ttitleAs = \"h3\",\n\tref,\n\t...props\n}: EmptyProps) {\n\tconst titleId = React.useId();\n\tconst TitleComp = titleAs;\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\trole=\"region\"\n\t\t\taria-labelledby={titleId}\n\t\t\tclassName={cn(emptyVariants({ size }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{icon ? (\n\t\t\t\t<div className={emptyIconWrapperVariants({ size })} aria-hidden=\"true\">\n\t\t\t\t\t{icon}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<TitleComp id={titleId} className={emptyTitleVariants({ size })}>\n\t\t\t\t{title}\n\t\t\t</TitleComp>\n\t\t\t{description ? (\n\t\t\t\t<div className={emptyDescriptionVariants({ size })}>{description}</div>\n\t\t\t) : null}\n\t\t\t{action ? <div className=\"mt-[var(--space-2,0.5rem)]\">{action}</div> : null}\n\t\t</div>\n\t);\n}\n\nexport { Empty, emptyVariants };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/error-state/error-state.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACNA,IAAM,kBAAA,GAAqB,GAAA;AAAA,EAC1B;AAAA,IACC,uDAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,2BAAA;AAAA,QACT,WAAA,EAAa;AAAA;AACd,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA;AAAU;AAExC;AAEA,IAAM,wBAAA,GAA2B,GAAA;AAAA,EAChC,iFAAA;AAAA,EACA;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,gCAAA;AAAA,QACT,WAAA,EAAa;AAAA;AACd,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA;AAAU;AAExC,CAAA;AA6CA,SAAS,UAAA,CAAW;AAAA,EACnB,SAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA,GAAQ,sBAAA;AAAA,EACR,OAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAoB;AACnB,EAAA,uBACC,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,IAAA,EAAK,OAAA;AAAA,MACL,WAAW,EAAA,CAAG,kBAAA,CAAmB,EAAE,OAAA,EAAS,GAAG,SAAS,CAAA;AAAA,MACvD,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,IAAA,mBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,wBAAA,CAAyB,EAAE,OAAA,EAAS,CAAA,EAAG,aAAA,EAAY,MAAA,EACjE,QAAA,EAAA,IAAA,EACF,CAAA,GACG,IAAA;AAAA,wBACJ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+BAAA,EAAiC,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACtD,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EAA0C,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,QAChE,yBAAS,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4BAAA,EAA8B,kBAAO,CAAA,GAAS;AAAA;AAAA;AAAA,GACxE;AAEF","file":"error-state.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 { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst errorStateVariants = cva(\n\t[\n\t\t\"flex flex-col items-center justify-center text-center\",\n\t\t\"rounded-md border px-[var(--space-6,1.5rem)] py-[var(--space-8,2rem)] gap-[var(--space-3,0.75rem)]\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"border-border bg-muted/30\",\n\t\t\t\tdestructive: \"border-destructive/30 bg-destructive/5\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\" },\n\t},\n);\n\nconst errorIconWrapperVariants = cva(\n\t\"flex h-12 w-12 shrink-0 items-center justify-center rounded-full [&_svg]:size-6\",\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-muted text-muted-foreground\",\n\t\t\t\tdestructive: \"bg-destructive/10 text-destructive\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\" },\n\t},\n);\n\nexport interface ErrorStateProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\">,\n\t\tVariantProps<typeof errorStateVariants> {\n\t/** Forwarded ref onto the alert region. */\n\tref?: React.Ref<HTMLDivElement>;\n\t/** Optional icon (typically an alert / x-circle SVG). */\n\ticon?: React.ReactNode;\n\t/** Optional heading copy. Falls back to a generic \"Something went wrong\" if omitted. */\n\ttitle?: React.ReactNode;\n\t/** Required body copy explaining what failed. */\n\tmessage: React.ReactNode;\n\t/**\n\t * Optional call-to-action — typically a `<Button>` with `onClick={refetch}`.\n\t * Slot pattern (matching `Empty.action`) so consumers control the button's\n\t * variant / loading state / asChild composition without ErrorState\n\t * re-implementing those concerns.\n\t */\n\taction?: React.ReactNode;\n}\n\n/**\n * A surface for rendering a failed-fetch / failed-action state. Visually\n * similar to {@link Empty} but ships with a destructive-tone bias and\n * mounts with `role=\"alert\"` so screen readers announce the failure on\n * first render.\n *\n * Distinct from {@link Empty} (request returned, no items) and\n * {@link Loading} (request still in flight). For inline form-field\n * errors, use Form's `<FormMessage>` instead. For blocking destructive\n * confirmations, use AlertDialog.\n *\n * @example\n * ```tsx\n * <ErrorState\n * icon={<AlertCircleIcon />}\n * title=\"Couldn't load messages\"\n * message=\"The server didn't respond. Check your connection and try again.\"\n * action={<Button onClick={refetch}>Retry</Button>}\n * />\n * ```\n *\n * @returns A `role=\"alert\"` region with an optional action slot.\n */\nfunction ErrorState({\n\tclassName,\n\tvariant,\n\ticon,\n\ttitle = \"Something went wrong\",\n\tmessage,\n\taction,\n\tref,\n\t...props\n}: ErrorStateProps) {\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\trole=\"alert\"\n\t\t\tclassName={cn(errorStateVariants({ variant }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{icon ? (\n\t\t\t\t<div className={errorIconWrapperVariants({ variant })} aria-hidden=\"true\">\n\t\t\t\t\t{icon}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<div className=\"font-semibold text-foreground\">{title}</div>\n\t\t\t<div className=\"max-w-md text-sm text-muted-foreground\">{message}</div>\n\t\t\t{action ? <div className=\"mt-[var(--space-2,0.5rem)]\">{action}</div> : null}\n\t\t</div>\n\t);\n}\n\nexport { ErrorState, errorStateVariants };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/error-state/error-state.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACNA,IAAM,kBAAA,GAAqB,GAAA;AAAA,EAC1B;AAAA,IACC,uDAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,2BAAA;AAAA,QACT,WAAA,EAAa;AAAA;AACd,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA;AAAU;AAExC;AAEA,IAAM,wBAAA,GAA2B,GAAA;AAAA,EAChC,iFAAA;AAAA,EACA;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,gCAAA;AAAA,QACT,WAAA,EAAa;AAAA;AACd,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA;AAAU;AAExC,CAAA;AA6CA,SAAS,UAAA,CAAW;AAAA,EACnB,SAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA,GAAQ,sBAAA;AAAA,EACR,OAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAoB;AACnB,EAAA,uBACC,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,IAAA,EAAK,OAAA;AAAA,MACL,WAAW,EAAA,CAAG,kBAAA,CAAmB,EAAE,OAAA,EAAS,GAAG,SAAS,CAAA;AAAA,MACvD,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA;AAAA,QAAA,IAAA,mBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,wBAAA,CAAyB,EAAE,OAAA,EAAS,CAAA,EAAG,aAAA,EAAY,MAAA,EACjE,QAAA,EAAA,IAAA,EACF,CAAA,GACG,IAAA;AAAA,wBACJ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+BAAA,EAAiC,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACtD,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EAA0C,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,QAChE,yBAAS,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4BAAA,EAA8B,kBAAO,CAAA,GAAS;AAAA;AAAA;AAAA,GACxE;AAEF","file":"error-state.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 { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst errorStateVariants = cva(\n\t[\n\t\t\"flex flex-col items-center justify-center text-center\",\n\t\t\"rounded-md border px-[var(--space-6,1.5rem)] py-[var(--space-8,2rem)] gap-[var(--space-3,0.75rem)]\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"border-border bg-muted/30\",\n\t\t\t\tdestructive: \"border-destructive/30 bg-destructive/5\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\" },\n\t},\n);\n\nconst errorIconWrapperVariants = cva(\n\t\"flex h-12 w-12 shrink-0 items-center justify-center rounded-full [&_svg]:size-6\",\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-muted text-muted-foreground\",\n\t\t\t\tdestructive: \"bg-destructive/10 text-destructive\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\" },\n\t},\n);\n\nexport interface ErrorStateProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\">,\n\t\tVariantProps<typeof errorStateVariants> {\n\t/** Forwarded ref onto the alert region. */\n\tref?: React.Ref<HTMLDivElement>;\n\t/** Optional icon (typically an alert / x-circle SVG). */\n\ticon?: React.ReactNode;\n\t/** Optional heading copy. Falls back to a generic \"Something went wrong\" if omitted. */\n\ttitle?: React.ReactNode;\n\t/** Required body copy explaining what failed. */\n\tmessage: React.ReactNode;\n\t/**\n\t * Optional call-to-action — typically a `<Button>` with `onClick={refetch}`.\n\t * Slot pattern (matching `Empty.action`) so consumers control the button's\n\t * variant / loading state / asChild composition without ErrorState\n\t * re-implementing those concerns.\n\t */\n\taction?: React.ReactNode;\n}\n\n/**\n * A surface for rendering a failed-fetch / failed-action state. Visually\n * similar to {@link Empty} but ships with a destructive-tone bias and\n * mounts with `role=\"alert\"` so screen readers announce the failure on\n * first render.\n *\n * Distinct from {@link Empty} (request returned, no items) and\n * {@link Loading} (request still in flight). For inline form-field\n * errors, use Form's `<FormMessage>` instead. For blocking destructive\n * confirmations, use AlertDialog.\n *\n * @example\n * ```tsx\n * <ErrorState\n * icon={<AlertCircleIcon />}\n * title=\"Couldn't load messages\"\n * message=\"The server didn't respond. Check your connection and try again.\"\n * action={<Button onClick={refetch}>Retry</Button>}\n * />\n * ```\n *\n * @returns A `role=\"alert\"` region with an optional action slot.\n */\nfunction ErrorState({\n\tclassName,\n\tvariant,\n\ticon,\n\ttitle = \"Something went wrong\",\n\tmessage,\n\taction,\n\tref,\n\t...props\n}: ErrorStateProps) {\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\trole=\"alert\"\n\t\t\tclassName={cn(errorStateVariants({ variant }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{icon ? (\n\t\t\t\t<div className={errorIconWrapperVariants({ variant })} aria-hidden=\"true\">\n\t\t\t\t\t{icon}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<div className=\"font-semibold text-foreground\">{title}</div>\n\t\t\t<div className=\"max-w-md text-sm text-muted-foreground\">{message}</div>\n\t\t\t{action ? <div className=\"mt-[var(--space-2,0.5rem)]\">{action}</div> : null}\n\t\t</div>\n\t);\n}\n\nexport { ErrorState, errorStateVariants };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/components/file-tree/file-tree.tsx"],"names":[],"mappings":"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","file":"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\ninterface FileTreeNode {\n\t/** Stable unique id used as React key + ARIA target. */\n\tid: string;\n\t/** Display name (file or folder). */\n\tname: string;\n\t/** Nested children. Presence (even if empty array) marks the node as a folder. */\n\tchildren?: FileTreeNode[];\n\t/** Optional icon override. Default chooses folder/file based on `children`. */\n\ticon?: React.ReactNode;\n\t/** Disable selection + expand toggle. */\n\tdisabled?: boolean;\n}\n\ninterface FileTreeProps {\n\t/** Root nodes. */\n\tnodes: FileTreeNode[];\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 for the tree container. */\n\t\"aria-label\": string;\n\t/** Extra class names on the root tree element. */\n\tclassName?: string;\n}\n\ninterface FlatNode {\n\tid: string;\n\tname: string;\n\tlevel: number;\n\thasChildren: boolean;\n\tdisabled: boolean;\n\tparentId: string | null;\n\ticon: React.ReactNode | undefined;\n}\n\n/** Walk the tree once, emitting every visible node in document order. */\nfunction flatten(\n\tnodes: FileTreeNode[],\n\texpandedSet: Set<string>,\n\tlevel = 1,\n\tparentId: string | null = null,\n): FlatNode[] {\n\tconst out: FlatNode[] = [];\n\tfor (const node of nodes) {\n\t\tconst hasChildren = Array.isArray(node.children);\n\t\tout.push({\n\t\t\tid: node.id,\n\t\t\tname: node.name,\n\t\t\tlevel,\n\t\t\thasChildren,\n\t\t\tdisabled: !!node.disabled,\n\t\t\tparentId,\n\t\t\ticon: node.icon,\n\t\t});\n\t\tif (hasChildren && expandedSet.has(node.id) && node.children) {\n\t\t\tout.push(...flatten(node.children, expandedSet, level + 1, node.id));\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Default folder glyph; flips between open and closed shapes via `open`. */\nfunction FolderIcon({ open }: { open: boolean }) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"h-4 w-4 shrink-0\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t{open ? (\n\t\t\t\t<path d=\"M3 7v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-7l-2-2H5a2 2 0 0 0-2 2z\" />\n\t\t\t) : (\n\t\t\t\t<path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\n\t\t\t)}\n\t\t</svg>\n\t);\n}\n\n/** Default leaf-node glyph (generic file icon). */\nfunction FileIcon() {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"h-4 w-4 shrink-0\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n\t\t\t<polyline points=\"14 2 14 8 20 8\" />\n\t\t</svg>\n\t);\n}\n\n/** Disclosure chevron — rotates 90° when the folder is expanded. */\nfunction Chevron({ expanded }: { expanded: boolean }) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName={cn(\n\t\t\t\t\"h-3 w-3 shrink-0 text-muted-foreground transition-transform duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\texpanded ? \"rotate-90\" : \"\",\n\t\t\t)}\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<polyline points=\"9 18 15 12 9 6\" />\n\t\t</svg>\n\t);\n}\n\ninterface TreeItemProps {\n\tnode: FileTreeNode;\n\tlevel: number;\n\texpandedSet: Set<string>;\n\tselected?: string;\n\tonToggle: (id: string) => void;\n\tonSelect: (id: string) => void;\n\tonKeyDown: (e: React.KeyboardEvent<HTMLDivElement>, id: string) => void;\n\tregisterRef: (id: string, el: HTMLDivElement | null) => void;\n\ttabbableId: string | null;\n}\n\n/** Recursive single-node renderer; chevron toggles, row body selects. */\nfunction TreeItem({\n\tnode,\n\tlevel,\n\texpandedSet,\n\tselected,\n\tonToggle,\n\tonSelect,\n\tonKeyDown,\n\tregisterRef,\n\ttabbableId,\n}: TreeItemProps) {\n\tconst hasChildren = Array.isArray(node.children);\n\tconst isExpanded = hasChildren && expandedSet.has(node.id);\n\tconst isSelected = selected === node.id;\n\n\treturn (\n\t\t<li role=\"none\">\n\t\t\t<div\n\t\t\t\trole=\"treeitem\"\n\t\t\t\taria-level={level}\n\t\t\t\taria-expanded={hasChildren ? isExpanded : undefined}\n\t\t\t\taria-selected={isSelected}\n\t\t\t\taria-disabled={node.disabled || undefined}\n\t\t\t\ttabIndex={tabbableId === node.id ? 0 : -1}\n\t\t\t\tref={(el) => registerRef(node.id, el)}\n\t\t\t\tonClick={(e) => {\n\t\t\t\t\tif (node.disabled) return;\n\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t/*\n\t\t\t\t\t * WAI-ARIA tree pattern: row click selects only. Toggling\n\t\t\t\t\t * a folder is the chevron's job (or ArrowRight/Left, or\n\t\t\t\t\t * Enter/Space when the row is focused).\n\t\t\t\t\t */\n\t\t\t\t\tonSelect(node.id);\n\t\t\t\t}}\n\t\t\t\tonKeyDown={(e) => onKeyDown(e, node.id)}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex items-center gap-[var(--space-2,0.5rem)] rounded-md px-[var(--space-2,0.5rem)] py-[var(--space-1,0.25rem)] text-sm cursor-pointer select-none 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\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\t\t\t\tisSelected && \"bg-accent text-accent-foreground\",\n\t\t\t\t\tnode.disabled && \"opacity-50 cursor-not-allowed pointer-events-none\",\n\t\t\t\t)}\n\t\t\t\tstyle={{ paddingInlineStart: `calc(${level - 1} * 1rem + var(--space-2, 0.5rem))` }}\n\t\t\t>\n\t\t\t\t{hasChildren ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Decorative button — toggling is also reachable via\n\t\t\t\t\t\t * Enter/Space on the treeitem and ArrowRight/Left, so\n\t\t\t\t\t\t * we don't add this to the keyboard tour.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\tif (node.disabled) return;\n\t\t\t\t\t\t\tonToggle(node.id);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-sm hover:bg-accent-foreground/10\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Chevron expanded={isExpanded} />\n\t\t\t\t\t</button>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"w-3 shrink-0\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t\t{node.icon ?? (hasChildren ? <FolderIcon open={isExpanded} /> : <FileIcon />)}\n\t\t\t\t<span className=\"truncate\">{node.name}</span>\n\t\t\t</div>\n\t\t\t{hasChildren && isExpanded && node.children ? (\n\t\t\t\t<ul role=\"group\" className=\"m-0 list-none p-0\">\n\t\t\t\t\t{node.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\tlevel={level + 1}\n\t\t\t\t\t\t\texpandedSet={expandedSet}\n\t\t\t\t\t\t\tselected={selected}\n\t\t\t\t\t\t\tonToggle={onToggle}\n\t\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\t\t\tregisterRef={registerRef}\n\t\t\t\t\t\t\ttabbableId={tabbableId}\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\n/**\n * Hierarchical tree view for files, folders, settings sections, or any nested\n * navigation. Built on the WAI-ARIA tree pattern: `role=\"tree\"` root,\n * `role=\"treeitem\"` per node, `role=\"group\"` per child group, with\n * `aria-level` / `aria-expanded` / `aria-selected` reflecting state.\n *\n * Keyboard: Up/Down move between visible items; Right expands a folder or\n * moves to the first child; Left collapses or moves to the parent;\n * Enter/Space activate the focused node; Home/End jump to the first/last.\n *\n * Expanded state is uncontrolled by default (`defaultExpanded`). Pass\n * `expanded` + `onExpandedChange` for controlled mode.\n * @returns A keyboard-accessible nested tree.\n */\nfunction FileTree({\n\tnodes,\n\tdefaultExpanded,\n\texpanded: expandedProp,\n\tonExpandedChange,\n\tselected,\n\tonSelect,\n\t\"aria-label\": ariaLabel,\n\tclassName,\n}: FileTreeProps) {\n\tconst isControlled = expandedProp !== undefined;\n\tconst [internalExpanded, setInternalExpanded] = React.useState<string[]>(\n\t\tdefaultExpanded ?? [],\n\t);\n\tconst expanded = isControlled ? expandedProp : internalExpanded;\n\tconst expandedSet = React.useMemo(() => new Set(expanded), [expanded]);\n\n\tconst itemRefs = React.useRef(new Map<string, HTMLDivElement>());\n\tconst registerRef = React.useCallback(\n\t\t(id: string, el: HTMLDivElement | null) => {\n\t\t\tif (el) itemRefs.current.set(id, el);\n\t\t\telse itemRefs.current.delete(id);\n\t\t},\n\t\t[],\n\t);\n\n\tconst flat = React.useMemo(\n\t\t() => flatten(nodes, expandedSet),\n\t\t[nodes, expandedSet],\n\t);\n\n\tconst firstId = flat[0]?.id ?? null;\n\tconst [focusedId, setFocusedId] = React.useState<string | null>(null);\n\t/*\n\t * Resolve the roving-tabindex target against the *visible* (flattened)\n\t * node set. If `selected` lives inside a collapsed branch its <treeitem>\n\t * doesn't render, and pointing tabIndex=0 at it would silently skip the\n\t * whole tree from Tab navigation.\n\t */\n\tconst visibleIds = React.useMemo(\n\t\t() => new Set(flat.map((n) => n.id)),\n\t\t[flat],\n\t);\n\tconst candidate = focusedId ?? selected ?? firstId;\n\tconst tabbableId =\n\t\tcandidate && visibleIds.has(candidate) ? candidate : firstId;\n\n\tconst setExpanded = React.useCallback(\n\t\t(next: string[]) => {\n\t\t\tif (!isControlled) setInternalExpanded(next);\n\t\t\tonExpandedChange?.(next);\n\t\t},\n\t\t[isControlled, onExpandedChange],\n\t);\n\n\tconst toggle = React.useCallback(\n\t\t(id: string) => {\n\t\t\tconst set = new Set(expanded);\n\t\t\tif (set.has(id)) set.delete(id);\n\t\t\telse set.add(id);\n\t\t\tsetExpanded(Array.from(set));\n\t\t},\n\t\t[expanded, setExpanded],\n\t);\n\n\tconst handleSelect = React.useCallback(\n\t\t(id: string) => {\n\t\t\tonSelect?.(id);\n\t\t\tsetFocusedId(id);\n\t\t},\n\t\t[onSelect],\n\t);\n\n\tconst focusNode = (id: string) => {\n\t\tsetFocusedId(id);\n\t\t// Defer to next paint so the new tabbable element is in the DOM.\n\t\trequestAnimationFrame(() => itemRefs.current.get(id)?.focus());\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>, id: string) => {\n\t\tconst flatNodes = flat;\n\t\tconst idx = flatNodes.findIndex((n) => n.id === id);\n\t\tconst node = flatNodes[idx];\n\t\tif (!node) return;\n\n\t\t// Walk past disabled neighbours so arrow keys never park focus on a\n\t\t// non-actionable node — matches the convention used elsewhere in the\n\t\t// repo (see <Select> / <Combobox> disabled item handling in cmdk).\n\t\tconst findEnabled = (start: number, dir: 1 | -1) => {\n\t\t\tlet i = start;\n\t\t\twhile (i >= 0 && i < flatNodes.length) {\n\t\t\t\tif (!flatNodes[i].disabled) return flatNodes[i];\n\t\t\t\ti += dir;\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\n\t\tswitch (e.key) {\n\t\t\tcase \"ArrowDown\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst next = findEnabled(idx + 1, 1);\n\t\t\t\tif (next) focusNode(next.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"ArrowUp\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst prev = findEnabled(idx - 1, -1);\n\t\t\t\tif (prev) focusNode(prev.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"ArrowRight\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (node.hasChildren && !expandedSet.has(node.id)) {\n\t\t\t\t\ttoggle(node.id);\n\t\t\t\t} else if (node.hasChildren) {\n\t\t\t\t\tconst firstChild = flatNodes[idx + 1];\n\t\t\t\t\tif (firstChild && firstChild.parentId === node.id)\n\t\t\t\t\t\tfocusNode(firstChild.id);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"ArrowLeft\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (node.hasChildren && expandedSet.has(node.id)) {\n\t\t\t\t\ttoggle(node.id);\n\t\t\t\t} else if (node.parentId) {\n\t\t\t\t\tfocusNode(node.parentId);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"Home\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (flatNodes[0]) focusNode(flatNodes[0].id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"End\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst last = flatNodes[flatNodes.length - 1];\n\t\t\t\tif (last) focusNode(last.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"Enter\":\n\t\t\tcase \" \": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (!node.disabled) {\n\t\t\t\t\tif (node.hasChildren) toggle(node.id);\n\t\t\t\t\thandleSelect(node.id);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\n\treturn (\n\t\t<ul\n\t\t\trole=\"tree\"\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\"list-none p-0 m-0\", className)}\n\t\t>\n\t\t\t{nodes.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\tlevel={1}\n\t\t\t\t\texpandedSet={expandedSet}\n\t\t\t\t\tselected={selected}\n\t\t\t\t\tonToggle={toggle}\n\t\t\t\t\tonSelect={handleSelect}\n\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\tregisterRef={registerRef}\n\t\t\t\t\ttabbableId={tabbableId}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\nFileTree.displayName = \"FileTree\";\n\nexport { FileTree };\nexport type { FileTreeNode, FileTreeProps };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/components/file-tree/file-tree.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACsCA,SAAS,QACR,KAAA,EACA,WAAA,EACA,KAAA,GAAQ,CAAA,EACR,WAA0B,IAAA,EACb;AACb,EAAA,MAAM,MAAkB,EAAC;AACzB,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACzB,IAAA,MAAM,WAAA,GAAc,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA;AAC/C,IAAA,GAAA,CAAI,IAAA,CAAK;AAAA,MACR,IAAI,IAAA,CAAK,EAAA;AAAA,MACT,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,KAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA,EAAU,CAAC,CAAC,IAAA,CAAK,QAAA;AAAA,MACjB,QAAA;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,KACX,CAAA;AACD,IAAA,IAAI,eAAe,WAAA,CAAY,GAAA,CAAI,KAAK,EAAE,CAAA,IAAK,KAAK,QAAA,EAAU;AAC7D,MAAA,GAAA,CAAI,IAAA,CAAK,GAAG,OAAA,CAAQ,IAAA,CAAK,QAAA,EAAU,aAAa,KAAA,GAAQ,CAAA,EAAG,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,IACpE;AAAA,EACD;AACA,EAAA,OAAO,GAAA;AACR;AAGA,SAAS,UAAA,CAAW,EAAE,IAAA,EAAK,EAAsB;AAChD,EAAA,uBACC,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,KAAA,EAAM,4BAAA;AAAA,MACN,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,MAAA,EAAO,cAAA;AAAA,MACP,WAAA,EAAY,GAAA;AAAA,MACZ,aAAA,EAAc,OAAA;AAAA,MACd,cAAA,EAAe,OAAA;AAAA,MACf,SAAA,EAAU,kBAAA;AAAA,MACV,aAAA,EAAY,MAAA;AAAA,MAEX,QAAA,EAAA,IAAA,uBACC,MAAA,EAAA,EAAK,CAAA,EAAE,mFAAkF,CAAA,mBAE1F,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,6EAAA,EAA8E;AAAA;AAAA,GAExF;AAEF;AAGA,SAAS,QAAA,GAAW;AACnB,EAAA,uBACC,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,KAAA,EAAM,4BAAA;AAAA,MACN,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,MAAA,EAAO,cAAA;AAAA,MACP,WAAA,EAAY,GAAA;AAAA,MACZ,aAAA,EAAc,OAAA;AAAA,MACd,cAAA,EAAe,OAAA;AAAA,MACf,SAAA,EAAU,kBAAA;AAAA,MACV,aAAA,EAAY,MAAA;AAAA,MAEZ,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,GAAE,4DAAA,EAA6D,CAAA;AAAA,wBACrE,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,gBAAA,EAAiB;AAAA;AAAA;AAAA,GACnC;AAEF;AAGA,SAAS,OAAA,CAAQ,EAAE,QAAA,EAAS,EAA0B;AACrD,EAAA,uBACC,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,KAAA,EAAM,4BAAA;AAAA,MACN,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,MAAA,EAAO,cAAA;AAAA,MACP,WAAA,EAAY,GAAA;AAAA,MACZ,aAAA,EAAc,OAAA;AAAA,MACd,cAAA,EAAe,OAAA;AAAA,MACf,SAAA,EAAW,EAAA;AAAA,QACV,8GAAA;AAAA,QACA,WAAW,WAAA,GAAc;AAAA,OAC1B;AAAA,MACA,aAAA,EAAY,MAAA;AAAA,MAEZ,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,gBAAA,EAAiB;AAAA;AAAA,GACnC;AAEF;AAeA,SAAS,QAAA,CAAS;AAAA,EACjB,IAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA;AACD,CAAA,EAAkB;AACjB,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA;AAC/C,EAAA,MAAM,UAAA,GAAa,WAAA,IAAe,WAAA,CAAY,GAAA,CAAI,KAAK,EAAE,CAAA;AACzD,EAAA,MAAM,UAAA,GAAa,aAAa,IAAA,CAAK,EAAA;AAErC,EAAA,uBACC,IAAA,CAAC,IAAA,EAAA,EAAG,IAAA,EAAK,MAAA,EACR,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACA,IAAA,EAAK,UAAA;AAAA,QACL,YAAA,EAAY,KAAA;AAAA,QACZ,eAAA,EAAe,cAAc,UAAA,GAAa,MAAA;AAAA,QAC1C,eAAA,EAAe,UAAA;AAAA,QACf,eAAA,EAAe,KAAK,QAAA,IAAY,MAAA;AAAA,QAChC,QAAA,EAAU,UAAA,KAAe,IAAA,CAAK,EAAA,GAAK,CAAA,GAAI,EAAA;AAAA,QACvC,KAAK,CAAC,EAAA,KAAO,WAAA,CAAY,IAAA,CAAK,IAAI,EAAE,CAAA;AAAA,QACpC,OAAA,EAAS,CAAC,CAAA,KAAM;AACf,UAAA,IAAI,KAAK,QAAA,EAAU;AACnB,UAAA,CAAA,CAAE,eAAA,EAAgB;AAMlB,UAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAAA,QACjB,CAAA;AAAA,QACA,WAAW,CAAC,CAAA,KAAM,SAAA,CAAU,CAAA,EAAG,KAAK,EAAE,CAAA;AAAA,QACtC,SAAA,EAAW,EAAA;AAAA,UACV,uNAAA;AAAA,UACA,8CAAA;AAAA,UACA,qGAAA;AAAA,UACA,UAAA,IAAc,kCAAA;AAAA,UACd,KAAK,QAAA,IAAY;AAAA,SAClB;AAAA,QACA,OAAO,EAAE,kBAAA,EAAoB,CAAA,KAAA,EAAQ,KAAA,GAAQ,CAAC,CAAA,iCAAA,CAAA,EAAoC;AAAA,QAEjF,QAAA,EAAA;AAAA,UAAA,WAAA,mBACA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACA,IAAA,EAAK,QAAA;AAAA,cACL,QAAA,EAAU,EAAA;AAAA,cACV,aAAA,EAAY,MAAA;AAAA,cAMZ,OAAA,EAAS,CAAC,CAAA,KAAM;AACf,gBAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,gBAAA,IAAI,KAAK,QAAA,EAAU;AACnB,gBAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAAA,cACjB,CAAA;AAAA,cACA,SAAA,EAAU,mGAAA;AAAA,cAEV,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,QAAA,EAAU,UAAA,EAAY;AAAA;AAAA,8BAGhC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,cAAA,EAAe,eAAY,MAAA,EAAO,CAAA;AAAA,UAElD,IAAA,CAAK,SAAS,WAAA,mBAAc,GAAA,CAAC,cAAW,IAAA,EAAM,UAAA,EAAY,CAAA,mBAAK,GAAA,CAAC,QAAA,EAAA,EAAS,CAAA,CAAA;AAAA,0BAC1E,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAY,eAAK,IAAA,EAAK;AAAA;AAAA;AAAA,KACvC;AAAA,IACC,WAAA,IAAe,UAAA,IAAc,IAAA,CAAK,QAAA,uBACjC,IAAA,EAAA,EAAG,IAAA,EAAK,OAAA,EAAQ,SAAA,EAAU,mBAAA,EACzB,QAAA,EAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,CAAC,KAAA,qBACnB,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QAEA,IAAA,EAAM,KAAA;AAAA,QACN,OAAO,KAAA,GAAQ,CAAA;AAAA,QACf,WAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OAAA;AAAA,MATK,KAAA,CAAM;AAAA,KAWZ,GACF,CAAA,GACG;AAAA,GAAA,EACL,CAAA;AAEF;AAgBA,SAAS,QAAA,CAAS;AAAA,EACjB,KAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA,EAAU,YAAA;AAAA,EACV,gBAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,YAAA,EAAc,SAAA;AAAA,EACd;AACD,CAAA,EAAkB;AACjB,EAAA,MAAM,eAAe,YAAA,KAAiB,MAAA;AACtC,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAU,KAAA,CAAA,QAAA;AAAA,IACrD,mBAAmB;AAAC,GACrB;AACA,EAAA,MAAM,QAAA,GAAW,eAAe,YAAA,GAAe,gBAAA;AAC/C,EAAA,MAAM,WAAA,GAAoB,cAAQ,MAAM,IAAI,IAAI,QAAQ,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAErE,EAAA,MAAM,QAAA,GAAiB,KAAA,CAAA,MAAA,iBAAO,IAAI,GAAA,EAA6B,CAAA;AAC/D,EAAA,MAAM,WAAA,GAAoB,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,IAAY,EAAA,KAA8B;AAC1C,MAAA,IAAI,EAAA,EAAI,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,IAAI,EAAE,CAAA;AAAA,WAC9B,QAAA,CAAS,OAAA,CAAQ,MAAA,CAAO,EAAE,CAAA;AAAA,IAChC,CAAA;AAAA,IACA;AAAC,GACF;AAEA,EAAA,MAAM,IAAA,GAAa,KAAA,CAAA,OAAA;AAAA,IAClB,MAAM,OAAA,CAAQ,KAAA,EAAO,WAAW,CAAA;AAAA,IAChC,CAAC,OAAO,WAAW;AAAA,GACpB;AAEA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,CAAC,CAAA,EAAG,EAAA,IAAM,IAAA;AAC/B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAU,eAAwB,IAAI,CAAA;AAOpE,EAAA,MAAM,UAAA,GAAmB,KAAA,CAAA,OAAA;AAAA,IACxB,MAAM,IAAI,GAAA,CAAI,IAAA,CAAK,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AAAA,IACnC,CAAC,IAAI;AAAA,GACN;AACA,EAAA,MAAM,SAAA,GAAY,aAAa,QAAA,IAAY,OAAA;AAC3C,EAAA,MAAM,aACL,SAAA,IAAa,UAAA,CAAW,GAAA,CAAI,SAAS,IAAI,SAAA,GAAY,OAAA;AAEtD,EAAA,MAAM,WAAA,GAAoB,KAAA,CAAA,WAAA;AAAA,IACzB,CAAC,IAAA,KAAmB;AACnB,MAAA,IAAI,CAAC,YAAA,EAAc,mBAAA,CAAoB,IAAI,CAAA;AAC3C,MAAA,gBAAA,GAAmB,IAAI,CAAA;AAAA,IACxB,CAAA;AAAA,IACA,CAAC,cAAc,gBAAgB;AAAA,GAChC;AAEA,EAAA,MAAM,MAAA,GAAe,KAAA,CAAA,WAAA;AAAA,IACpB,CAAC,EAAA,KAAe;AACf,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,QAAQ,CAAA;AAC5B,MAAA,IAAI,IAAI,GAAA,CAAI,EAAE,CAAA,EAAG,GAAA,CAAI,OAAO,EAAE,CAAA;AAAA,WACzB,GAAA,CAAI,IAAI,EAAE,CAAA;AACf,MAAA,WAAA,CAAY,KAAA,CAAM,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,IAC5B,CAAA;AAAA,IACA,CAAC,UAAU,WAAW;AAAA,GACvB;AAEA,EAAA,MAAM,YAAA,GAAqB,KAAA,CAAA,WAAA;AAAA,IAC1B,CAAC,EAAA,KAAe;AACf,MAAA,QAAA,GAAW,EAAE,CAAA;AACb,MAAA,YAAA,CAAa,EAAE,CAAA;AAAA,IAChB,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACV;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,EAAA,KAAe;AACjC,IAAA,YAAA,CAAa,EAAE,CAAA;AAEf,IAAA,qBAAA,CAAsB,MAAM,QAAA,CAAS,OAAA,CAAQ,IAAI,EAAE,CAAA,EAAG,OAAO,CAAA;AAAA,EAC9D,CAAA;AAEA,EAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,EAAwC,EAAA,KAAe;AAC7E,IAAA,MAAM,SAAA,GAAY,IAAA;AAClB,IAAA,MAAM,MAAM,SAAA,CAAU,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA;AAClD,IAAA,MAAM,IAAA,GAAO,UAAU,GAAG,CAAA;AAC1B,IAAA,IAAI,CAAC,IAAA,EAAM;AAKX,IAAA,MAAM,WAAA,GAAc,CAAC,KAAA,EAAe,GAAA,KAAgB;AACnD,MAAA,IAAI,CAAA,GAAI,KAAA;AACR,MAAA,OAAO,CAAA,IAAK,CAAA,IAAK,CAAA,GAAI,SAAA,CAAU,MAAA,EAAQ;AACtC,QAAA,IAAI,CAAC,SAAA,CAAU,CAAC,EAAE,QAAA,EAAU,OAAO,UAAU,CAAC,CAAA;AAC9C,QAAA,CAAA,IAAK,GAAA;AAAA,MACN;AACA,MAAA,OAAO,IAAA;AAAA,IACR,CAAA;AAEA,IAAA,QAAQ,EAAE,GAAA;AAAK,MACd,KAAK,WAAA,EAAa;AACjB,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,MAAM,IAAA,GAAO,WAAA,CAAY,GAAA,GAAM,CAAA,EAAG,CAAC,CAAA;AACnC,QAAA,IAAI,IAAA,EAAM,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAC3B,QAAA;AAAA,MACD;AAAA,MACA,KAAK,SAAA,EAAW;AACf,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,MAAM,IAAA,GAAO,WAAA,CAAY,GAAA,GAAM,CAAA,EAAG,EAAE,CAAA;AACpC,QAAA,IAAI,IAAA,EAAM,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAC3B,QAAA;AAAA,MACD;AAAA,MACA,KAAK,YAAA,EAAc;AAClB,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,IAAI,KAAK,WAAA,IAAe,CAAC,YAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG;AAClD,UAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AAAA,QACf,CAAA,MAAA,IAAW,KAAK,WAAA,EAAa;AAC5B,UAAA,MAAM,UAAA,GAAa,SAAA,CAAU,GAAA,GAAM,CAAC,CAAA;AACpC,UAAA,IAAI,UAAA,IAAc,UAAA,CAAW,QAAA,KAAa,IAAA,CAAK,EAAA;AAC9C,YAAA,SAAA,CAAU,WAAW,EAAE,CAAA;AAAA,QACzB;AACA,QAAA;AAAA,MACD;AAAA,MACA,KAAK,WAAA,EAAa;AACjB,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,IAAI,KAAK,WAAA,IAAe,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG;AACjD,UAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AAAA,QACf,CAAA,MAAA,IAAW,KAAK,QAAA,EAAU;AACzB,UAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AAAA,QACxB;AACA,QAAA;AAAA,MACD;AAAA,MACA,KAAK,MAAA,EAAQ;AACZ,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,IAAI,UAAU,CAAC,CAAA,YAAa,SAAA,CAAU,CAAC,EAAE,EAAE,CAAA;AAC3C,QAAA;AAAA,MACD;AAAA,MACA,KAAK,KAAA,EAAO;AACX,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,MAAM,IAAA,GAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA;AAC3C,QAAA,IAAI,IAAA,EAAM,SAAA,CAAU,IAAA,CAAK,EAAE,CAAA;AAC3B,QAAA;AAAA,MACD;AAAA,MACA,KAAK,OAAA;AAAA,MACL,KAAK,GAAA,EAAK;AACT,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AACnB,UAAA,IAAI,IAAA,CAAK,WAAA,EAAa,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AACpC,UAAA,YAAA,CAAa,KAAK,EAAE,CAAA;AAAA,QACrB;AACA,QAAA;AAAA,MACD;AAAA;AACD,EACD,CAAA;AAEA,EAAA,uBACC,GAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACA,IAAA,EAAK,MAAA;AAAA,MACL,YAAA,EAAY,SAAA;AAAA,MACZ,SAAA,EAAW,EAAA,CAAG,mBAAA,EAAqB,SAAS,CAAA;AAAA,MAE3C,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,qBACX,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UAEA,IAAA;AAAA,UACA,KAAA,EAAO,CAAA;AAAA,UACP,WAAA;AAAA,UACA,QAAA;AAAA,UACA,QAAA,EAAU,MAAA;AAAA,UACV,QAAA,EAAU,YAAA;AAAA,UACV,SAAA,EAAW,aAAA;AAAA,UACX,WAAA;AAAA,UACA;AAAA,SAAA;AAAA,QATK,IAAA,CAAK;AAAA,OAWX;AAAA;AAAA,GACF;AAEF;AACA,QAAA,CAAS,WAAA,GAAc,UAAA","file":"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\ninterface FileTreeNode {\n\t/** Stable unique id used as React key + ARIA target. */\n\tid: string;\n\t/** Display name (file or folder). */\n\tname: string;\n\t/** Nested children. Presence (even if empty array) marks the node as a folder. */\n\tchildren?: FileTreeNode[];\n\t/** Optional icon override. Default chooses folder/file based on `children`. */\n\ticon?: React.ReactNode;\n\t/** Disable selection + expand toggle. */\n\tdisabled?: boolean;\n}\n\ninterface FileTreeProps {\n\t/** Root nodes. */\n\tnodes: FileTreeNode[];\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 for the tree container. */\n\t\"aria-label\": string;\n\t/** Extra class names on the root tree element. */\n\tclassName?: string;\n}\n\ninterface FlatNode {\n\tid: string;\n\tname: string;\n\tlevel: number;\n\thasChildren: boolean;\n\tdisabled: boolean;\n\tparentId: string | null;\n\ticon: React.ReactNode | undefined;\n}\n\n/** Walk the tree once, emitting every visible node in document order. */\nfunction flatten(\n\tnodes: FileTreeNode[],\n\texpandedSet: Set<string>,\n\tlevel = 1,\n\tparentId: string | null = null,\n): FlatNode[] {\n\tconst out: FlatNode[] = [];\n\tfor (const node of nodes) {\n\t\tconst hasChildren = Array.isArray(node.children);\n\t\tout.push({\n\t\t\tid: node.id,\n\t\t\tname: node.name,\n\t\t\tlevel,\n\t\t\thasChildren,\n\t\t\tdisabled: !!node.disabled,\n\t\t\tparentId,\n\t\t\ticon: node.icon,\n\t\t});\n\t\tif (hasChildren && expandedSet.has(node.id) && node.children) {\n\t\t\tout.push(...flatten(node.children, expandedSet, level + 1, node.id));\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Default folder glyph; flips between open and closed shapes via `open`. */\nfunction FolderIcon({ open }: { open: boolean }) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"h-4 w-4 shrink-0\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t{open ? (\n\t\t\t\t<path d=\"M3 7v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-7l-2-2H5a2 2 0 0 0-2 2z\" />\n\t\t\t) : (\n\t\t\t\t<path d=\"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z\" />\n\t\t\t)}\n\t\t</svg>\n\t);\n}\n\n/** Default leaf-node glyph (generic file icon). */\nfunction FileIcon() {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"h-4 w-4 shrink-0\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n\t\t\t<polyline points=\"14 2 14 8 20 8\" />\n\t\t</svg>\n\t);\n}\n\n/** Disclosure chevron — rotates 90° when the folder is expanded. */\nfunction Chevron({ expanded }: { expanded: boolean }) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName={cn(\n\t\t\t\t\"h-3 w-3 shrink-0 text-muted-foreground transition-transform duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\texpanded ? \"rotate-90\" : \"\",\n\t\t\t)}\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<polyline points=\"9 18 15 12 9 6\" />\n\t\t</svg>\n\t);\n}\n\ninterface TreeItemProps {\n\tnode: FileTreeNode;\n\tlevel: number;\n\texpandedSet: Set<string>;\n\tselected?: string;\n\tonToggle: (id: string) => void;\n\tonSelect: (id: string) => void;\n\tonKeyDown: (e: React.KeyboardEvent<HTMLDivElement>, id: string) => void;\n\tregisterRef: (id: string, el: HTMLDivElement | null) => void;\n\ttabbableId: string | null;\n}\n\n/** Recursive single-node renderer; chevron toggles, row body selects. */\nfunction TreeItem({\n\tnode,\n\tlevel,\n\texpandedSet,\n\tselected,\n\tonToggle,\n\tonSelect,\n\tonKeyDown,\n\tregisterRef,\n\ttabbableId,\n}: TreeItemProps) {\n\tconst hasChildren = Array.isArray(node.children);\n\tconst isExpanded = hasChildren && expandedSet.has(node.id);\n\tconst isSelected = selected === node.id;\n\n\treturn (\n\t\t<li role=\"none\">\n\t\t\t<div\n\t\t\t\trole=\"treeitem\"\n\t\t\t\taria-level={level}\n\t\t\t\taria-expanded={hasChildren ? isExpanded : undefined}\n\t\t\t\taria-selected={isSelected}\n\t\t\t\taria-disabled={node.disabled || undefined}\n\t\t\t\ttabIndex={tabbableId === node.id ? 0 : -1}\n\t\t\t\tref={(el) => registerRef(node.id, el)}\n\t\t\t\tonClick={(e) => {\n\t\t\t\t\tif (node.disabled) return;\n\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t/*\n\t\t\t\t\t * WAI-ARIA tree pattern: row click selects only. Toggling\n\t\t\t\t\t * a folder is the chevron's job (or ArrowRight/Left, or\n\t\t\t\t\t * Enter/Space when the row is focused).\n\t\t\t\t\t */\n\t\t\t\t\tonSelect(node.id);\n\t\t\t\t}}\n\t\t\t\tonKeyDown={(e) => onKeyDown(e, node.id)}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex items-center gap-[var(--space-2,0.5rem)] rounded-md px-[var(--space-2,0.5rem)] py-[var(--space-1,0.25rem)] text-sm cursor-pointer select-none 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\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\t\t\t\tisSelected && \"bg-accent text-accent-foreground\",\n\t\t\t\t\tnode.disabled && \"opacity-50 cursor-not-allowed pointer-events-none\",\n\t\t\t\t)}\n\t\t\t\tstyle={{ paddingInlineStart: `calc(${level - 1} * 1rem + var(--space-2, 0.5rem))` }}\n\t\t\t>\n\t\t\t\t{hasChildren ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Decorative button — toggling is also reachable via\n\t\t\t\t\t\t * Enter/Space on the treeitem and ArrowRight/Left, so\n\t\t\t\t\t\t * we don't add this to the keyboard tour.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\tif (node.disabled) return;\n\t\t\t\t\t\t\tonToggle(node.id);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-sm hover:bg-accent-foreground/10\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Chevron expanded={isExpanded} />\n\t\t\t\t\t</button>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"w-3 shrink-0\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t\t{node.icon ?? (hasChildren ? <FolderIcon open={isExpanded} /> : <FileIcon />)}\n\t\t\t\t<span className=\"truncate\">{node.name}</span>\n\t\t\t</div>\n\t\t\t{hasChildren && isExpanded && node.children ? (\n\t\t\t\t<ul role=\"group\" className=\"m-0 list-none p-0\">\n\t\t\t\t\t{node.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\tlevel={level + 1}\n\t\t\t\t\t\t\texpandedSet={expandedSet}\n\t\t\t\t\t\t\tselected={selected}\n\t\t\t\t\t\t\tonToggle={onToggle}\n\t\t\t\t\t\t\tonSelect={onSelect}\n\t\t\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\t\t\tregisterRef={registerRef}\n\t\t\t\t\t\t\ttabbableId={tabbableId}\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\n/**\n * Hierarchical tree view for files, folders, settings sections, or any nested\n * navigation. Built on the WAI-ARIA tree pattern: `role=\"tree\"` root,\n * `role=\"treeitem\"` per node, `role=\"group\"` per child group, with\n * `aria-level` / `aria-expanded` / `aria-selected` reflecting state.\n *\n * Keyboard: Up/Down move between visible items; Right expands a folder or\n * moves to the first child; Left collapses or moves to the parent;\n * Enter/Space activate the focused node; Home/End jump to the first/last.\n *\n * Expanded state is uncontrolled by default (`defaultExpanded`). Pass\n * `expanded` + `onExpandedChange` for controlled mode.\n * @returns A keyboard-accessible nested tree.\n */\nfunction FileTree({\n\tnodes,\n\tdefaultExpanded,\n\texpanded: expandedProp,\n\tonExpandedChange,\n\tselected,\n\tonSelect,\n\t\"aria-label\": ariaLabel,\n\tclassName,\n}: FileTreeProps) {\n\tconst isControlled = expandedProp !== undefined;\n\tconst [internalExpanded, setInternalExpanded] = React.useState<string[]>(\n\t\tdefaultExpanded ?? [],\n\t);\n\tconst expanded = isControlled ? expandedProp : internalExpanded;\n\tconst expandedSet = React.useMemo(() => new Set(expanded), [expanded]);\n\n\tconst itemRefs = React.useRef(new Map<string, HTMLDivElement>());\n\tconst registerRef = React.useCallback(\n\t\t(id: string, el: HTMLDivElement | null) => {\n\t\t\tif (el) itemRefs.current.set(id, el);\n\t\t\telse itemRefs.current.delete(id);\n\t\t},\n\t\t[],\n\t);\n\n\tconst flat = React.useMemo(\n\t\t() => flatten(nodes, expandedSet),\n\t\t[nodes, expandedSet],\n\t);\n\n\tconst firstId = flat[0]?.id ?? null;\n\tconst [focusedId, setFocusedId] = React.useState<string | null>(null);\n\t/*\n\t * Resolve the roving-tabindex target against the *visible* (flattened)\n\t * node set. If `selected` lives inside a collapsed branch its <treeitem>\n\t * doesn't render, and pointing tabIndex=0 at it would silently skip the\n\t * whole tree from Tab navigation.\n\t */\n\tconst visibleIds = React.useMemo(\n\t\t() => new Set(flat.map((n) => n.id)),\n\t\t[flat],\n\t);\n\tconst candidate = focusedId ?? selected ?? firstId;\n\tconst tabbableId =\n\t\tcandidate && visibleIds.has(candidate) ? candidate : firstId;\n\n\tconst setExpanded = React.useCallback(\n\t\t(next: string[]) => {\n\t\t\tif (!isControlled) setInternalExpanded(next);\n\t\t\tonExpandedChange?.(next);\n\t\t},\n\t\t[isControlled, onExpandedChange],\n\t);\n\n\tconst toggle = React.useCallback(\n\t\t(id: string) => {\n\t\t\tconst set = new Set(expanded);\n\t\t\tif (set.has(id)) set.delete(id);\n\t\t\telse set.add(id);\n\t\t\tsetExpanded(Array.from(set));\n\t\t},\n\t\t[expanded, setExpanded],\n\t);\n\n\tconst handleSelect = React.useCallback(\n\t\t(id: string) => {\n\t\t\tonSelect?.(id);\n\t\t\tsetFocusedId(id);\n\t\t},\n\t\t[onSelect],\n\t);\n\n\tconst focusNode = (id: string) => {\n\t\tsetFocusedId(id);\n\t\t// Defer to next paint so the new tabbable element is in the DOM.\n\t\trequestAnimationFrame(() => itemRefs.current.get(id)?.focus());\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>, id: string) => {\n\t\tconst flatNodes = flat;\n\t\tconst idx = flatNodes.findIndex((n) => n.id === id);\n\t\tconst node = flatNodes[idx];\n\t\tif (!node) return;\n\n\t\t// Walk past disabled neighbours so arrow keys never park focus on a\n\t\t// non-actionable node — matches the convention used elsewhere in the\n\t\t// repo (see <Select> / <Combobox> disabled item handling in cmdk).\n\t\tconst findEnabled = (start: number, dir: 1 | -1) => {\n\t\t\tlet i = start;\n\t\t\twhile (i >= 0 && i < flatNodes.length) {\n\t\t\t\tif (!flatNodes[i].disabled) return flatNodes[i];\n\t\t\t\ti += dir;\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\n\t\tswitch (e.key) {\n\t\t\tcase \"ArrowDown\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst next = findEnabled(idx + 1, 1);\n\t\t\t\tif (next) focusNode(next.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"ArrowUp\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst prev = findEnabled(idx - 1, -1);\n\t\t\t\tif (prev) focusNode(prev.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"ArrowRight\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (node.hasChildren && !expandedSet.has(node.id)) {\n\t\t\t\t\ttoggle(node.id);\n\t\t\t\t} else if (node.hasChildren) {\n\t\t\t\t\tconst firstChild = flatNodes[idx + 1];\n\t\t\t\t\tif (firstChild && firstChild.parentId === node.id)\n\t\t\t\t\t\tfocusNode(firstChild.id);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"ArrowLeft\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (node.hasChildren && expandedSet.has(node.id)) {\n\t\t\t\t\ttoggle(node.id);\n\t\t\t\t} else if (node.parentId) {\n\t\t\t\t\tfocusNode(node.parentId);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"Home\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (flatNodes[0]) focusNode(flatNodes[0].id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"End\": {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst last = flatNodes[flatNodes.length - 1];\n\t\t\t\tif (last) focusNode(last.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"Enter\":\n\t\t\tcase \" \": {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (!node.disabled) {\n\t\t\t\t\tif (node.hasChildren) toggle(node.id);\n\t\t\t\t\thandleSelect(node.id);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\n\treturn (\n\t\t<ul\n\t\t\trole=\"tree\"\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\"list-none p-0 m-0\", className)}\n\t\t>\n\t\t\t{nodes.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\tlevel={1}\n\t\t\t\t\texpandedSet={expandedSet}\n\t\t\t\t\tselected={selected}\n\t\t\t\t\tonToggle={toggle}\n\t\t\t\t\tonSelect={handleSelect}\n\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\tregisterRef={registerRef}\n\t\t\t\t\ttabbableId={tabbableId}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\nFileTree.displayName = \"FileTree\";\n\nexport { FileTree };\nexport type { FileTreeNode, FileTreeProps };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/flashcard/flashcard.tsx"],"names":[],"mappings":"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","file":"flashcard.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 * Flashcard — front/back card with a 3D flip animation. Click, Enter, or\n * Space to flip. Pure CSS 3D transform, no animation peer required.\n *\n * Headless on content: pass any ReactNode for `front` and `back`. Pair\n * with Deck (artifacts/deck) for shuffle / next / prev / progress, or\n * with SpacedRepetition (artifacts/spaced-repetition) for confidence\n * rating after each reveal.\n *\n * @example\n * <Flashcard\n * front={<>What is the capital of France?</>}\n * back={<>Paris</>}\n * />\n *\n * <Flashcard\n * flipped={isFlipped}\n * onFlipChange={setFlipped}\n * front={term}\n * back={definition}\n * />\n */\nexport interface FlashcardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n\t/** Content of the front face. */\n\tfront: React.ReactNode;\n\t/** Content of the back face. */\n\tback: React.ReactNode;\n\t/** Uncontrolled initial flipped state. Default false. */\n\tdefaultFlipped?: boolean;\n\t/** Controlled flipped state. */\n\tflipped?: boolean;\n\t/** Fired with the new flipped value when the user toggles. */\n\tonFlipChange?: (flipped: boolean) => void;\n\t/** Pixel width. Default 360. */\n\twidth?: number;\n\t/** Pixel height. Default 240. */\n\theight?: number;\n\t/** Flip animation duration in ms. Default 500. Set to 0 to disable the animation entirely. */\n\tflipDurationMs?: number;\n}\n\nfunction Flashcard({\n\tfront,\n\tback,\n\tdefaultFlipped = false,\n\tflipped: flippedProp,\n\tonFlipChange,\n\twidth = 360,\n\theight = 240,\n\tflipDurationMs = 500,\n\tclassName,\n\t...rest\n}: FlashcardProps) {\n\tconst [internalFlipped, setInternalFlipped] = React.useState(defaultFlipped);\n\tconst isControlled = flippedProp !== undefined;\n\tconst flipped = isControlled ? flippedProp : internalFlipped;\n\n\tconst toggle = React.useCallback(() => {\n\t\tconst next = !flipped;\n\t\tif (!isControlled) setInternalFlipped(next);\n\t\tonFlipChange?.(next);\n\t}, [flipped, isControlled, onFlipChange]);\n\n\tconst handleKey = React.useCallback(\n\t\t(e: React.KeyboardEvent) => {\n\t\t\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\t\t\te.preventDefault();\n\t\t\t\ttoggle();\n\t\t\t}\n\t\t},\n\t\t[toggle],\n\t);\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-flashcard\n\t\t\tdata-flipped={flipped}\n\t\t\trole=\"button\"\n\t\t\ttabIndex={0}\n\t\t\taria-pressed={flipped}\n\t\t\taria-label={flipped ? \"Flashcard, back side. Activate to flip to front.\" : \"Flashcard, front side. Activate to reveal back.\"}\n\t\t\tonClick={toggle}\n\t\t\tonKeyDown={handleKey}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative inline-block cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tstyle={{\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tperspective: 1000,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tdata-hex-flashcard-inner\n\t\t\t\tclassName=\"relative h-full w-full transition-transform\"\n\t\t\t\tstyle={{\n\t\t\t\t\ttransformStyle: \"preserve-3d\",\n\t\t\t\t\ttransform: flipped ? \"rotateY(180deg)\" : \"rotateY(0deg)\",\n\t\t\t\t\ttransitionDuration: `${flipDurationMs}ms`,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tdata-hex-flashcard-face\n\t\t\t\t\tdata-side=\"front\"\n\t\t\t\t\tclassName=\"absolute inset-0 flex items-center justify-center rounded-lg border bg-card p-4 text-center text-card-foreground shadow-sm\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackfaceVisibility: \"hidden\",\n\t\t\t\t\t\tWebkitBackfaceVisibility: \"hidden\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{front}\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tdata-hex-flashcard-face\n\t\t\t\t\tdata-side=\"back\"\n\t\t\t\t\tclassName=\"absolute inset-0 flex items-center justify-center rounded-lg border bg-card p-4 text-center text-card-foreground shadow-sm\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackfaceVisibility: \"hidden\",\n\t\t\t\t\t\tWebkitBackfaceVisibility: \"hidden\",\n\t\t\t\t\t\ttransform: \"rotateY(180deg)\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{back}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport { Flashcard };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/flashcard/flashcard.tsx"],"names":[],"mappings":"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","file":"flashcard.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 * Flashcard — front/back card with a 3D flip animation. Click, Enter, or\n * Space to flip. Pure CSS 3D transform, no animation peer required.\n *\n * Headless on content: pass any ReactNode for `front` and `back`. Pair\n * with Deck (artifacts/deck) for shuffle / next / prev / progress, or\n * with SpacedRepetition (artifacts/spaced-repetition) for confidence\n * rating after each reveal.\n *\n * @example\n * <Flashcard\n * front={<>What is the capital of France?</>}\n * back={<>Paris</>}\n * />\n *\n * <Flashcard\n * flipped={isFlipped}\n * onFlipChange={setFlipped}\n * front={term}\n * back={definition}\n * />\n */\nexport interface FlashcardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n\t/** Content of the front face. */\n\tfront: React.ReactNode;\n\t/** Content of the back face. */\n\tback: React.ReactNode;\n\t/** Uncontrolled initial flipped state. Default false. */\n\tdefaultFlipped?: boolean;\n\t/** Controlled flipped state. */\n\tflipped?: boolean;\n\t/** Fired with the new flipped value when the user toggles. */\n\tonFlipChange?: (flipped: boolean) => void;\n\t/** Pixel width. Default 360. */\n\twidth?: number;\n\t/** Pixel height. Default 240. */\n\theight?: number;\n\t/** Flip animation duration in ms. Default 500. Set to 0 to disable the animation entirely. */\n\tflipDurationMs?: number;\n}\n\nfunction Flashcard({\n\tfront,\n\tback,\n\tdefaultFlipped = false,\n\tflipped: flippedProp,\n\tonFlipChange,\n\twidth = 360,\n\theight = 240,\n\tflipDurationMs = 500,\n\tclassName,\n\t...rest\n}: FlashcardProps) {\n\tconst [internalFlipped, setInternalFlipped] = React.useState(defaultFlipped);\n\tconst isControlled = flippedProp !== undefined;\n\tconst flipped = isControlled ? flippedProp : internalFlipped;\n\n\tconst toggle = React.useCallback(() => {\n\t\tconst next = !flipped;\n\t\tif (!isControlled) setInternalFlipped(next);\n\t\tonFlipChange?.(next);\n\t}, [flipped, isControlled, onFlipChange]);\n\n\tconst handleKey = React.useCallback(\n\t\t(e: React.KeyboardEvent) => {\n\t\t\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\t\t\te.preventDefault();\n\t\t\t\ttoggle();\n\t\t\t}\n\t\t},\n\t\t[toggle],\n\t);\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-flashcard\n\t\t\tdata-flipped={flipped}\n\t\t\trole=\"button\"\n\t\t\ttabIndex={0}\n\t\t\taria-pressed={flipped}\n\t\t\taria-label={flipped ? \"Flashcard, back side. Activate to flip to front.\" : \"Flashcard, front side. Activate to reveal back.\"}\n\t\t\tonClick={toggle}\n\t\t\tonKeyDown={handleKey}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative inline-block cursor-pointer select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tstyle={{\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tperspective: 1000,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\tdata-hex-flashcard-inner\n\t\t\t\tclassName=\"relative h-full w-full transition-transform\"\n\t\t\t\tstyle={{\n\t\t\t\t\ttransformStyle: \"preserve-3d\",\n\t\t\t\t\ttransform: flipped ? \"rotateY(180deg)\" : \"rotateY(0deg)\",\n\t\t\t\t\ttransitionDuration: `${flipDurationMs}ms`,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tdata-hex-flashcard-face\n\t\t\t\t\tdata-side=\"front\"\n\t\t\t\t\tclassName=\"absolute inset-0 flex items-center justify-center rounded-lg border bg-card p-4 text-center text-card-foreground shadow-sm\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackfaceVisibility: \"hidden\",\n\t\t\t\t\t\tWebkitBackfaceVisibility: \"hidden\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{front}\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tdata-hex-flashcard-face\n\t\t\t\t\tdata-side=\"back\"\n\t\t\t\t\tclassName=\"absolute inset-0 flex items-center justify-center rounded-lg border bg-card p-4 text-center text-card-foreground shadow-sm\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackfaceVisibility: \"hidden\",\n\t\t\t\t\t\tWebkitBackfaceVisibility: \"hidden\",\n\t\t\t\t\t\ttransform: \"rotateY(180deg)\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{back}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport { Flashcard };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/flowchart/flowchart.tsx"],"names":[],"mappings":"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","file":"flowchart.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 * Typed React flowchart. Pass `nodes` (with optional `shape`/`type`) and\n * `edges`; the component runs a topological-rank auto-layout and renders\n * top-to-bottom or left-to-right with directional arrows. Pure SVG; no\n * heavy peer dependency, no DAG-layout library.\n *\n * Distinct from `<Diagram>` (Mermaid string DSL) and `<Canvas>` (free-form\n * ReactFlow). Use Flowchart when you have STRUCTURED data and want a\n * polished SVG without the bundle cost or DSL of those alternatives.\n *\n * @example\n * <Flowchart\n * nodes={[\n * { id: \"start\", label: \"Start\", shape: \"round\" },\n * { id: \"check\", label: \"Authorized?\", shape: \"diamond\" },\n * { id: \"ok\", label: \"Continue\" },\n * { id: \"denied\", label: \"Reject\", shape: \"round\" },\n * ]}\n * edges={[\n * { source: \"start\", target: \"check\" },\n * { source: \"check\", target: \"ok\", label: \"yes\" },\n * { source: \"check\", target: \"denied\", label: \"no\" },\n * ]}\n * />\n */\nexport type FlowchartNode = {\n\tid: string;\n\tlabel: string;\n\t/** Visual shape — \"rect\" (default), \"round\" (rounded rect, terminal markers), or \"diamond\" (decision). */\n\tshape?: \"rect\" | \"round\" | \"diamond\";\n\t/** Optional explicit rank/depth override; otherwise computed via topological sort. */\n\trank?: number;\n};\n\nexport type FlowchartEdge = {\n\tsource: string;\n\ttarget: string;\n\tlabel?: string;\n};\n\nexport interface FlowchartProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Node definitions. Every edge's `source`/`target` MUST match an `id` here. */\n\tnodes: FlowchartNode[];\n\t/** Directional edges. The graph MUST be a DAG (no cycles). */\n\tedges: FlowchartEdge[];\n\t/** Layout direction. Default \"vertical\" (top-to-bottom). */\n\tdirection?: \"vertical\" | \"horizontal\";\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 480. */\n\theight?: number;\n\t/** Pixel width of each node. Default 140. */\n\tnodeWidth?: number;\n\t/** Pixel height of each node. Default 48. */\n\tnodeHeight?: number;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: FlowchartNode) => void;\n}\n\ninterface LaidOutNode {\n\tnode: FlowchartNode;\n\tx: number;\n\ty: number;\n\trank: number;\n}\n\ninterface LaidOutEdge {\n\tedge: FlowchartEdge;\n\tfrom: { x: number; y: number };\n\tto: { x: number; y: number };\n}\n\nfunction Flowchart({\n\tnodes,\n\tedges,\n\tdirection = \"vertical\",\n\twidth = 720,\n\theight = 480,\n\tnodeWidth = 140,\n\tnodeHeight = 48,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: FlowchartProps) {\n\tconst { nodes: laidOutNodes, edges: laidOutEdges } = React.useMemo(\n\t\t() => layout(nodes, edges, direction, width, height, nodeWidth, nodeHeight),\n\t\t[nodes, edges, direction, width, height, nodeWidth, nodeHeight],\n\t);\n\tconst desc = `Flowchart with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${edges.length} edge${edges.length === 1 ? \"\" : \"s\"}, laid out ${direction}`;\n\tconst arrowId = React.useId().replace(/:/g, \"-\");\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-flowchart\n\t\t\tdata-direction={direction}\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>Flowchart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<defs>\n\t\t\t\t<marker\n\t\t\t\t\tid={`hex-flowchart-arrow-${arrowId}`}\n\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\trefX=\"10\"\n\t\t\t\t\trefY=\"5\"\n\t\t\t\t\tmarkerWidth=\"6\"\n\t\t\t\t\tmarkerHeight=\"6\"\n\t\t\t\t\torient=\"auto-start-reverse\"\n\t\t\t\t>\n\t\t\t\t\t<path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"hsl(var(--muted-foreground))\" />\n\t\t\t\t</marker>\n\t\t\t</defs>\n\t\t\t<g data-hex-flowchart-edges>\n\t\t\t\t{laidOutEdges.map((e, i) => (\n\t\t\t\t\t<g key={`${e.edge.source}-${e.edge.target}-${i}`} data-hex-flowchart-edge>\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\td={edgePath(e.from, e.to, direction)}\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\tstrokeOpacity={0.7}\n\t\t\t\t\t\t\tstrokeWidth={1.25}\n\t\t\t\t\t\t\tmarkerEnd={`url(#hex-flowchart-arrow-${arrowId})`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{e.edge.label ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={(e.from.x + e.to.x) / 2}\n\t\t\t\t\t\t\t\ty={(e.from.y + e.to.y) / 2 - 4}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t}}\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.edge.label}\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</g>\n\t\t\t<g data-hex-flowchart-nodes>\n\t\t\t\t{laidOutNodes.map((n) => {\n\t\t\t\t\tconst shape = n.node.shape ?? \"rect\";\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(n.node);\n\t\t\t\t\tconst truncated = truncate(n.node.label, Math.floor((nodeWidth - 16) / 7));\n\t\t\t\t\tconst isTruncated = truncated !== n.node.label;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={n.node.id}\n\t\t\t\t\t\t\tdata-hex-flowchart-node\n\t\t\t\t\t\t\tdata-shape={shape}\n\t\t\t\t\t\t\tdata-rank={n.rank}\n\t\t\t\t\t\t\ttransform={`translate(${n.x - nodeWidth / 2},${n.y - nodeHeight / 2})`}\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 ? n.node.label : 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 ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Native SVG tooltip for the full label — recovers any text\n\t\t\t\t\t\t\t truncated by the heuristic without relying on font metrics. */}\n\t\t\t\t\t\t\t{isTruncated ? <title>{n.node.label}</title> : null}\n\t\t\t\t\t\t\t{shape === \"diamond\" ? (\n\t\t\t\t\t\t\t\t<polygon\n\t\t\t\t\t\t\t\t\tpoints={`${nodeWidth / 2},0 ${nodeWidth},${nodeHeight / 2} ${nodeWidth / 2},${nodeHeight} 0,${nodeHeight / 2}`}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--card))\"\n\t\t\t\t\t\t\t\t\tstroke=\"hsl(var(--border))\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={1}\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<rect\n\t\t\t\t\t\t\t\t\twidth={nodeWidth}\n\t\t\t\t\t\t\t\t\theight={nodeHeight}\n\t\t\t\t\t\t\t\t\trx={shape === \"round\" ? nodeHeight / 2 : 6}\n\t\t\t\t\t\t\t\t\try={shape === \"round\" ? nodeHeight / 2 : 6}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--card))\"\n\t\t\t\t\t\t\t\t\tstroke=\"hsl(var(--border))\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={1}\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<text\n\t\t\t\t\t\t\t\tx={nodeWidth / 2}\n\t\t\t\t\t\t\t\ty={nodeHeight / 2}\n\t\t\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\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(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{truncated}\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\tnodes: FlowchartNode[],\n\tedges: FlowchartEdge[],\n\tdirection: \"vertical\" | \"horizontal\",\n\twidth: number,\n\theight: number,\n\tnodeWidth: number,\n\tnodeHeight: number,\n): { nodes: LaidOutNode[]; edges: LaidOutEdge[] } {\n\tif (nodes.length === 0) return { nodes: [], edges: [] };\n\n\tconst byId = new Map(nodes.map((n) => [n.id, n]));\n\tconst ranks = computeRanks(nodes, edges, byId);\n\tconst maxRank = Math.max(...ranks.values(), 0);\n\n\t// Group node ids by rank.\n\tconst rankGroups = new Map<number, string[]>();\n\tfor (const n of nodes) {\n\t\tconst r = ranks.get(n.id) ?? 0;\n\t\tconst arr = rankGroups.get(r) ?? [];\n\t\tarr.push(n.id);\n\t\trankGroups.set(r, arr);\n\t}\n\n\t// Single-pass barycenter sweep: at each rank > 0, sort the rank's nodes\n\t// by the mean cross-axis index of their parents at the previous rank. Cuts\n\t// most edge crossings without pulling in dagre / elkjs. The first rank\n\t// keeps insertion order — that anchors the rest of the sweep.\n\tconst parentsOf = new Map<string, string[]>();\n\tfor (const n of nodes) parentsOf.set(n.id, []);\n\tfor (const e of edges) {\n\t\tconst arr = parentsOf.get(e.target);\n\t\tif (arr && byId.has(e.source)) arr.push(e.source);\n\t}\n\tconst orderInRank = new Map<string, number>();\n\tfor (let r = 0; r <= maxRank; r++) {\n\t\tconst group = rankGroups.get(r) ?? [];\n\t\tif (r > 0) {\n\t\t\tgroup.sort((a, b) => meanParentIndex(a, parentsOf, orderInRank) - meanParentIndex(b, parentsOf, orderInRank));\n\t\t}\n\t\tgroup.forEach((id, i) => orderInRank.set(id, i));\n\t}\n\n\tconst positions = new Map<string, { x: number; y: number; rank: number }>();\n\tconst margin = 32;\n\tconst usableMain = (direction === \"vertical\" ? height : width) - margin * 2;\n\tconst usableCross = (direction === \"vertical\" ? width : height) - margin * 2;\n\tconst rankCount = maxRank + 1;\n\tconst mainStep = rankCount > 1 ? usableMain / (rankCount - 1) : 0;\n\n\tfor (let r = 0; r <= maxRank; r++) {\n\t\tconst group = rankGroups.get(r) ?? [];\n\t\tconst crossStep = group.length > 0 ? usableCross / (group.length + 1) : 0;\n\t\tgroup.forEach((id, i) => {\n\t\t\tconst cross = margin + crossStep * (i + 1);\n\t\t\tconst main = margin + mainStep * r;\n\t\t\tif (direction === \"vertical\") {\n\t\t\t\tpositions.set(id, { x: cross, y: main, rank: r });\n\t\t\t} else {\n\t\t\t\tpositions.set(id, { x: main, y: cross, rank: r });\n\t\t\t}\n\t\t});\n\t}\n\n\tconst laidOutNodes: LaidOutNode[] = nodes.map((n) => {\n\t\tconst p = positions.get(n.id) ?? { x: 0, y: 0, rank: 0 };\n\t\treturn { node: n, x: p.x, y: p.y, rank: p.rank };\n\t});\n\n\tconst laidOutEdges: LaidOutEdge[] = edges\n\t\t.map((edge) => {\n\t\t\tconst sp = positions.get(edge.source);\n\t\t\tconst tp = positions.get(edge.target);\n\t\t\tif (!sp || !tp) return null;\n\t\t\t// Connect from the appropriate edge of the source to the appropriate edge of the target\n\t\t\tconst from =\n\t\t\t\tdirection === \"vertical\"\n\t\t\t\t\t? { x: sp.x, y: sp.y + nodeHeight / 2 }\n\t\t\t\t\t: { x: sp.x + nodeWidth / 2, y: sp.y };\n\t\t\tconst to =\n\t\t\t\tdirection === \"vertical\"\n\t\t\t\t\t? { x: tp.x, y: tp.y - nodeHeight / 2 }\n\t\t\t\t\t: { x: tp.x - nodeWidth / 2, y: tp.y };\n\t\t\treturn { edge, from, to };\n\t\t})\n\t\t.filter((e): e is LaidOutEdge => e !== null);\n\n\treturn { nodes: laidOutNodes, edges: laidOutEdges };\n}\n\nfunction meanParentIndex(\n\tid: string,\n\tparentsOf: Map<string, string[]>,\n\torderInRank: Map<string, number>,\n): number {\n\tconst parents = parentsOf.get(id) ?? [];\n\tif (parents.length === 0) return Number.POSITIVE_INFINITY; // sort orphans last\n\tlet sum = 0;\n\tlet count = 0;\n\tfor (const p of parents) {\n\t\tconst idx = orderInRank.get(p);\n\t\tif (idx != null) {\n\t\t\tsum += idx;\n\t\t\tcount++;\n\t\t}\n\t}\n\treturn count === 0 ? Number.POSITIVE_INFINITY : sum / count;\n}\n\n/**\n * Compute the rank (0-indexed depth) of each node via topological longest-path.\n * Honors any explicit `rank` overrides on the input nodes.\n *\n * Cycles: nodes inside a cycle resolve to 0. Crucially the cycle-touched\n * value is NOT memoized — the previous version cached the placeholder under\n * the first node along the cycle and poisoned every later descendant of that\n * node, even paths that didn't touch the cycle. The schema's `commonMistakes`\n * still warns DAG-only, but the failure mode is now contained to the\n * actually-cyclic nodes rather than spreading downstream.\n */\nfunction computeRanks(\n\tnodes: FlowchartNode[],\n\tedges: FlowchartEdge[],\n\tbyId: Map<string, FlowchartNode>,\n): Map<string, number> {\n\tconst ranks = new Map<string, number>();\n\tconst incoming = new Map<string, string[]>();\n\tfor (const n of nodes) incoming.set(n.id, []);\n\tfor (const e of edges) {\n\t\tconst arr = incoming.get(e.target);\n\t\tif (arr && byId.has(e.source)) arr.push(e.source);\n\t}\n\tconst memo = new Map<string, number>();\n\tconst visiting = new Set<string>();\n\n\tconst rankOf = (id: string): number => {\n\t\tconst cached = memo.get(id);\n\t\tif (cached !== undefined) return cached;\n\t\tif (visiting.has(id)) return 0; // cycle short-circuit (NOT memoized)\n\t\tvisiting.add(id);\n\t\ttry {\n\t\t\tconst node = byId.get(id);\n\t\t\tif (node?.rank != null) {\n\t\t\t\tmemo.set(id, node.rank);\n\t\t\t\treturn node.rank;\n\t\t\t}\n\t\t\tconst parents = incoming.get(id) ?? [];\n\t\t\tconst r = parents.length === 0 ? 0 : 1 + Math.max(...parents.map((p) => rankOf(p)));\n\t\t\tmemo.set(id, r);\n\t\t\treturn r;\n\t\t} finally {\n\t\t\tvisiting.delete(id);\n\t\t}\n\t};\n\n\tfor (const n of nodes) ranks.set(n.id, rankOf(n.id));\n\treturn ranks;\n}\n\nfunction edgePath(\n\tfrom: { x: number; y: number },\n\tto: { x: number; y: number },\n\tdirection: \"vertical\" | \"horizontal\",\n): string {\n\tif (direction === \"vertical\") {\n\t\tconst my = (from.y + to.y) / 2;\n\t\treturn `M${from.x},${from.y} C${from.x},${my} ${to.x},${my} ${to.x},${to.y}`;\n\t}\n\tconst mx = (from.x + to.x) / 2;\n\treturn `M${from.x},${from.y} C${mx},${from.y} ${mx},${to.y} ${to.x},${to.y}`;\n}\n\nfunction truncate(s: string, max: number): string {\n\tif (s.length <= max) return s;\n\treturn `${s.slice(0, Math.max(1, max - 1))}…`;\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 { Flowchart };\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/flowchart/flowchart.tsx"],"names":[],"mappings":"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","file":"flowchart.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 * Typed React flowchart. Pass `nodes` (with optional `shape`/`type`) and\n * `edges`; the component runs a topological-rank auto-layout and renders\n * top-to-bottom or left-to-right with directional arrows. Pure SVG; no\n * heavy peer dependency, no DAG-layout library.\n *\n * Distinct from `<Diagram>` (Mermaid string DSL) and `<Canvas>` (free-form\n * ReactFlow). Use Flowchart when you have STRUCTURED data and want a\n * polished SVG without the bundle cost or DSL of those alternatives.\n *\n * @example\n * <Flowchart\n * nodes={[\n * { id: \"start\", label: \"Start\", shape: \"round\" },\n * { id: \"check\", label: \"Authorized?\", shape: \"diamond\" },\n * { id: \"ok\", label: \"Continue\" },\n * { id: \"denied\", label: \"Reject\", shape: \"round\" },\n * ]}\n * edges={[\n * { source: \"start\", target: \"check\" },\n * { source: \"check\", target: \"ok\", label: \"yes\" },\n * { source: \"check\", target: \"denied\", label: \"no\" },\n * ]}\n * />\n */\nexport type FlowchartNode = {\n\tid: string;\n\tlabel: string;\n\t/** Visual shape — \"rect\" (default), \"round\" (rounded rect, terminal markers), or \"diamond\" (decision). */\n\tshape?: \"rect\" | \"round\" | \"diamond\";\n\t/** Optional explicit rank/depth override; otherwise computed via topological sort. */\n\trank?: number;\n};\n\nexport type FlowchartEdge = {\n\tsource: string;\n\ttarget: string;\n\tlabel?: string;\n};\n\nexport interface FlowchartProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Node definitions. Every edge's `source`/`target` MUST match an `id` here. */\n\tnodes: FlowchartNode[];\n\t/** Directional edges. The graph MUST be a DAG (no cycles). */\n\tedges: FlowchartEdge[];\n\t/** Layout direction. Default \"vertical\" (top-to-bottom). */\n\tdirection?: \"vertical\" | \"horizontal\";\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 480. */\n\theight?: number;\n\t/** Pixel width of each node. Default 140. */\n\tnodeWidth?: number;\n\t/** Pixel height of each node. Default 48. */\n\tnodeHeight?: number;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: FlowchartNode) => void;\n}\n\ninterface LaidOutNode {\n\tnode: FlowchartNode;\n\tx: number;\n\ty: number;\n\trank: number;\n}\n\ninterface LaidOutEdge {\n\tedge: FlowchartEdge;\n\tfrom: { x: number; y: number };\n\tto: { x: number; y: number };\n}\n\nfunction Flowchart({\n\tnodes,\n\tedges,\n\tdirection = \"vertical\",\n\twidth = 720,\n\theight = 480,\n\tnodeWidth = 140,\n\tnodeHeight = 48,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: FlowchartProps) {\n\tconst { nodes: laidOutNodes, edges: laidOutEdges } = React.useMemo(\n\t\t() => layout(nodes, edges, direction, width, height, nodeWidth, nodeHeight),\n\t\t[nodes, edges, direction, width, height, nodeWidth, nodeHeight],\n\t);\n\tconst desc = `Flowchart with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${edges.length} edge${edges.length === 1 ? \"\" : \"s\"}, laid out ${direction}`;\n\tconst arrowId = React.useId().replace(/:/g, \"-\");\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-flowchart\n\t\t\tdata-direction={direction}\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>Flowchart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<defs>\n\t\t\t\t<marker\n\t\t\t\t\tid={`hex-flowchart-arrow-${arrowId}`}\n\t\t\t\t\tviewBox=\"0 0 10 10\"\n\t\t\t\t\trefX=\"10\"\n\t\t\t\t\trefY=\"5\"\n\t\t\t\t\tmarkerWidth=\"6\"\n\t\t\t\t\tmarkerHeight=\"6\"\n\t\t\t\t\torient=\"auto-start-reverse\"\n\t\t\t\t>\n\t\t\t\t\t<path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"hsl(var(--muted-foreground))\" />\n\t\t\t\t</marker>\n\t\t\t</defs>\n\t\t\t<g data-hex-flowchart-edges>\n\t\t\t\t{laidOutEdges.map((e, i) => (\n\t\t\t\t\t<g key={`${e.edge.source}-${e.edge.target}-${i}`} data-hex-flowchart-edge>\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\td={edgePath(e.from, e.to, direction)}\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\tstrokeOpacity={0.7}\n\t\t\t\t\t\t\tstrokeWidth={1.25}\n\t\t\t\t\t\t\tmarkerEnd={`url(#hex-flowchart-arrow-${arrowId})`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{e.edge.label ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={(e.from.x + e.to.x) / 2}\n\t\t\t\t\t\t\t\ty={(e.from.y + e.to.y) / 2 - 4}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t}}\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.edge.label}\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</g>\n\t\t\t<g data-hex-flowchart-nodes>\n\t\t\t\t{laidOutNodes.map((n) => {\n\t\t\t\t\tconst shape = n.node.shape ?? \"rect\";\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(n.node);\n\t\t\t\t\tconst truncated = truncate(n.node.label, Math.floor((nodeWidth - 16) / 7));\n\t\t\t\t\tconst isTruncated = truncated !== n.node.label;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={n.node.id}\n\t\t\t\t\t\t\tdata-hex-flowchart-node\n\t\t\t\t\t\t\tdata-shape={shape}\n\t\t\t\t\t\t\tdata-rank={n.rank}\n\t\t\t\t\t\t\ttransform={`translate(${n.x - nodeWidth / 2},${n.y - nodeHeight / 2})`}\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 ? n.node.label : 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 ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{/* Native SVG tooltip for the full label — recovers any text\n\t\t\t\t\t\t\t truncated by the heuristic without relying on font metrics. */}\n\t\t\t\t\t\t\t{isTruncated ? <title>{n.node.label}</title> : null}\n\t\t\t\t\t\t\t{shape === \"diamond\" ? (\n\t\t\t\t\t\t\t\t<polygon\n\t\t\t\t\t\t\t\t\tpoints={`${nodeWidth / 2},0 ${nodeWidth},${nodeHeight / 2} ${nodeWidth / 2},${nodeHeight} 0,${nodeHeight / 2}`}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--card))\"\n\t\t\t\t\t\t\t\t\tstroke=\"hsl(var(--border))\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={1}\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<rect\n\t\t\t\t\t\t\t\t\twidth={nodeWidth}\n\t\t\t\t\t\t\t\t\theight={nodeHeight}\n\t\t\t\t\t\t\t\t\trx={shape === \"round\" ? nodeHeight / 2 : 6}\n\t\t\t\t\t\t\t\t\try={shape === \"round\" ? nodeHeight / 2 : 6}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--card))\"\n\t\t\t\t\t\t\t\t\tstroke=\"hsl(var(--border))\"\n\t\t\t\t\t\t\t\t\tstrokeWidth={1}\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<text\n\t\t\t\t\t\t\t\tx={nodeWidth / 2}\n\t\t\t\t\t\t\t\ty={nodeHeight / 2}\n\t\t\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\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(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{truncated}\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\tnodes: FlowchartNode[],\n\tedges: FlowchartEdge[],\n\tdirection: \"vertical\" | \"horizontal\",\n\twidth: number,\n\theight: number,\n\tnodeWidth: number,\n\tnodeHeight: number,\n): { nodes: LaidOutNode[]; edges: LaidOutEdge[] } {\n\tif (nodes.length === 0) return { nodes: [], edges: [] };\n\n\tconst byId = new Map(nodes.map((n) => [n.id, n]));\n\tconst ranks = computeRanks(nodes, edges, byId);\n\tconst maxRank = Math.max(...ranks.values(), 0);\n\n\t// Group node ids by rank.\n\tconst rankGroups = new Map<number, string[]>();\n\tfor (const n of nodes) {\n\t\tconst r = ranks.get(n.id) ?? 0;\n\t\tconst arr = rankGroups.get(r) ?? [];\n\t\tarr.push(n.id);\n\t\trankGroups.set(r, arr);\n\t}\n\n\t// Single-pass barycenter sweep: at each rank > 0, sort the rank's nodes\n\t// by the mean cross-axis index of their parents at the previous rank. Cuts\n\t// most edge crossings without pulling in dagre / elkjs. The first rank\n\t// keeps insertion order — that anchors the rest of the sweep.\n\tconst parentsOf = new Map<string, string[]>();\n\tfor (const n of nodes) parentsOf.set(n.id, []);\n\tfor (const e of edges) {\n\t\tconst arr = parentsOf.get(e.target);\n\t\tif (arr && byId.has(e.source)) arr.push(e.source);\n\t}\n\tconst orderInRank = new Map<string, number>();\n\tfor (let r = 0; r <= maxRank; r++) {\n\t\tconst group = rankGroups.get(r) ?? [];\n\t\tif (r > 0) {\n\t\t\tgroup.sort((a, b) => meanParentIndex(a, parentsOf, orderInRank) - meanParentIndex(b, parentsOf, orderInRank));\n\t\t}\n\t\tgroup.forEach((id, i) => orderInRank.set(id, i));\n\t}\n\n\tconst positions = new Map<string, { x: number; y: number; rank: number }>();\n\tconst margin = 32;\n\tconst usableMain = (direction === \"vertical\" ? height : width) - margin * 2;\n\tconst usableCross = (direction === \"vertical\" ? width : height) - margin * 2;\n\tconst rankCount = maxRank + 1;\n\tconst mainStep = rankCount > 1 ? usableMain / (rankCount - 1) : 0;\n\n\tfor (let r = 0; r <= maxRank; r++) {\n\t\tconst group = rankGroups.get(r) ?? [];\n\t\tconst crossStep = group.length > 0 ? usableCross / (group.length + 1) : 0;\n\t\tgroup.forEach((id, i) => {\n\t\t\tconst cross = margin + crossStep * (i + 1);\n\t\t\tconst main = margin + mainStep * r;\n\t\t\tif (direction === \"vertical\") {\n\t\t\t\tpositions.set(id, { x: cross, y: main, rank: r });\n\t\t\t} else {\n\t\t\t\tpositions.set(id, { x: main, y: cross, rank: r });\n\t\t\t}\n\t\t});\n\t}\n\n\tconst laidOutNodes: LaidOutNode[] = nodes.map((n) => {\n\t\tconst p = positions.get(n.id) ?? { x: 0, y: 0, rank: 0 };\n\t\treturn { node: n, x: p.x, y: p.y, rank: p.rank };\n\t});\n\n\tconst laidOutEdges: LaidOutEdge[] = edges\n\t\t.map((edge) => {\n\t\t\tconst sp = positions.get(edge.source);\n\t\t\tconst tp = positions.get(edge.target);\n\t\t\tif (!sp || !tp) return null;\n\t\t\t// Connect from the appropriate edge of the source to the appropriate edge of the target\n\t\t\tconst from =\n\t\t\t\tdirection === \"vertical\"\n\t\t\t\t\t? { x: sp.x, y: sp.y + nodeHeight / 2 }\n\t\t\t\t\t: { x: sp.x + nodeWidth / 2, y: sp.y };\n\t\t\tconst to =\n\t\t\t\tdirection === \"vertical\"\n\t\t\t\t\t? { x: tp.x, y: tp.y - nodeHeight / 2 }\n\t\t\t\t\t: { x: tp.x - nodeWidth / 2, y: tp.y };\n\t\t\treturn { edge, from, to };\n\t\t})\n\t\t.filter((e): e is LaidOutEdge => e !== null);\n\n\treturn { nodes: laidOutNodes, edges: laidOutEdges };\n}\n\nfunction meanParentIndex(\n\tid: string,\n\tparentsOf: Map<string, string[]>,\n\torderInRank: Map<string, number>,\n): number {\n\tconst parents = parentsOf.get(id) ?? [];\n\tif (parents.length === 0) return Number.POSITIVE_INFINITY; // sort orphans last\n\tlet sum = 0;\n\tlet count = 0;\n\tfor (const p of parents) {\n\t\tconst idx = orderInRank.get(p);\n\t\tif (idx != null) {\n\t\t\tsum += idx;\n\t\t\tcount++;\n\t\t}\n\t}\n\treturn count === 0 ? Number.POSITIVE_INFINITY : sum / count;\n}\n\n/**\n * Compute the rank (0-indexed depth) of each node via topological longest-path.\n * Honors any explicit `rank` overrides on the input nodes.\n *\n * Cycles: nodes inside a cycle resolve to 0. Crucially the cycle-touched\n * value is NOT memoized — the previous version cached the placeholder under\n * the first node along the cycle and poisoned every later descendant of that\n * node, even paths that didn't touch the cycle. The schema's `commonMistakes`\n * still warns DAG-only, but the failure mode is now contained to the\n * actually-cyclic nodes rather than spreading downstream.\n */\nfunction computeRanks(\n\tnodes: FlowchartNode[],\n\tedges: FlowchartEdge[],\n\tbyId: Map<string, FlowchartNode>,\n): Map<string, number> {\n\tconst ranks = new Map<string, number>();\n\tconst incoming = new Map<string, string[]>();\n\tfor (const n of nodes) incoming.set(n.id, []);\n\tfor (const e of edges) {\n\t\tconst arr = incoming.get(e.target);\n\t\tif (arr && byId.has(e.source)) arr.push(e.source);\n\t}\n\tconst memo = new Map<string, number>();\n\tconst visiting = new Set<string>();\n\n\tconst rankOf = (id: string): number => {\n\t\tconst cached = memo.get(id);\n\t\tif (cached !== undefined) return cached;\n\t\tif (visiting.has(id)) return 0; // cycle short-circuit (NOT memoized)\n\t\tvisiting.add(id);\n\t\ttry {\n\t\t\tconst node = byId.get(id);\n\t\t\tif (node?.rank != null) {\n\t\t\t\tmemo.set(id, node.rank);\n\t\t\t\treturn node.rank;\n\t\t\t}\n\t\t\tconst parents = incoming.get(id) ?? [];\n\t\t\tconst r = parents.length === 0 ? 0 : 1 + Math.max(...parents.map((p) => rankOf(p)));\n\t\t\tmemo.set(id, r);\n\t\t\treturn r;\n\t\t} finally {\n\t\t\tvisiting.delete(id);\n\t\t}\n\t};\n\n\tfor (const n of nodes) ranks.set(n.id, rankOf(n.id));\n\treturn ranks;\n}\n\nfunction edgePath(\n\tfrom: { x: number; y: number },\n\tto: { x: number; y: number },\n\tdirection: \"vertical\" | \"horizontal\",\n): string {\n\tif (direction === \"vertical\") {\n\t\tconst my = (from.y + to.y) / 2;\n\t\treturn `M${from.x},${from.y} C${from.x},${my} ${to.x},${my} ${to.x},${to.y}`;\n\t}\n\tconst mx = (from.x + to.x) / 2;\n\treturn `M${from.x},${from.y} C${mx},${from.y} ${mx},${to.y} ${to.x},${to.y}`;\n}\n\nfunction truncate(s: string, max: number): string {\n\tif (s.length <= max) return s;\n\treturn `${s.slice(0, Math.max(1, max - 1))}…`;\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 { Flowchart };\n"]}
package/dist/form.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/label/label.tsx","../src/components/form/form.tsx"],"names":["React","jsx"],"mappings":";;;;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,aAAA,GAAgB,GAAA;AAAA,EACrB;AACD,CAAA;AAMA,IAAM,KAAA,GAAcA,MAAA,CAAA,UAAA;AAAA,EACnB,CAAC,EAAE,SAAA,EAAW,GAAG,KAAA,EAAM,EAAG,wBACzB,GAAA,CAAgB,cAAA,CAAA,IAAA,EAAf,EAAoB,GAAA,EAAU,WAAW,EAAA,CAAG,aAAA,IAAiB,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO;AAEvF,CAAA;AACA,KAAA,CAAM,WAAA,GAAc,OAAA;ACFpB,IAAM,IAAA,GAAO;AASb,IAAM,gBAAA,GAAyB,MAAA,CAAA,aAAA,CAAqC,EAA2B,CAAA;AAQ/F,IAAM,YAAY,CAGhB;AAAA,EACD,GAAG;AACJ,CAAA,KAA4C;AAC3C,EAAA,uBACCC,GAAAA,CAAC,gBAAA,CAAiB,QAAA,EAAjB,EAA0B,OAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,IAC/C,QAAA,kBAAAA,GAAAA,CAAC,UAAA,EAAA,EAAY,GAAG,OAAO,CAAA,EACxB,CAAA;AAEF;AAMA,IAAM,eAAA,GAAwB,MAAA,CAAA,aAAA,CAAoC,EAA0B,CAAA;AAO5F,SAAS,YAAA,GAAe;AACvB,EAAA,MAAM,YAAA,GAAqB,kBAAW,gBAAgB,CAAA;AACtD,EAAA,MAAM,WAAA,GAAoB,kBAAW,eAAe,CAAA;AACpD,EAAA,IAAI,CAAC,cAAc,IAAA,EAAM;AACxB,IAAA,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAAA,EACjE;AAEA,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,cAAA,EAAe;AACzC,EAAA,MAAM,YAAY,YAAA,CAAa,EAAE,IAAA,EAAM,YAAA,CAAa,MAAM,CAAA;AAC1D,EAAA,MAAM,UAAA,GAAa,aAAA,CAAc,YAAA,CAAa,IAAA,EAAM,SAAS,CAAA;AAE7D,EAAA,MAAM,EAAE,IAAG,GAAI,WAAA;AACf,EAAA,OAAO;AAAA,IACN,EAAA;AAAA,IACA,MAAM,YAAA,CAAa,IAAA;AAAA,IACnB,UAAA,EAAY,GAAG,EAAE,CAAA,UAAA,CAAA;AAAA,IACjB,iBAAA,EAAmB,GAAG,EAAE,CAAA,sBAAA,CAAA;AAAA,IACxB,aAAA,EAAe,GAAG,EAAE,CAAA,kBAAA,CAAA;AAAA,IACpB,GAAG;AAAA,GACJ;AACD;AAGA,IAAM,QAAA,GAAiB,MAAA,CAAA,UAAA;AAAA,EACtB,CAAC,EAAE,SAAA,EAAW,GAAG,KAAA,IAAS,GAAA,KAAQ;AACjC,IAAA,MAAM,KAAW,MAAA,CAAA,KAAA,EAAM;AACvB,IAAA,uBACCA,IAAC,eAAA,CAAgB,QAAA,EAAhB,EAAyB,KAAA,EAAO,EAAE,IAAG,EACrC,QAAA,kBAAAA,IAAC,KAAA,EAAA,EAAI,GAAA,EAAU,WAAW,EAAA,CAAG,WAAA,EAAa,SAAS,CAAA,EAAI,GAAG,OAAO,CAAA,EAClE,CAAA;AAAA,EAEF;AACD;AACA,QAAA,CAAS,WAAA,GAAc,UAAA;AAGvB,IAAM,SAAA,GAAkB,kBAGtB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,KAAQ;AACnC,EAAA,MAAM,EAAE,KAAA,EAAO,UAAA,EAAW,GAAI,YAAA,EAAa;AAC3C,EAAA,uBACCA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,SAAA,EAAW,EAAA,CAAG,KAAA,IAAS,kBAAA,EAAoB,SAAS,CAAA;AAAA,MACpD,OAAA,EAAS,UAAA;AAAA,MACR,GAAG;AAAA;AAAA,GACL;AAEF,CAAC;AACD,SAAA,CAAU,WAAA,GAAc,WAAA;AAGxB,IAAM,cAAoB,MAAA,CAAA,UAAA,CAGxB,CAAC,EAAE,GAAG,KAAA,IAAS,GAAA,KAAQ;AACxB,EAAA,MAAM,EAAE,KAAA,EAAO,UAAA,EAAY,iBAAA,EAAmB,aAAA,KAAkB,YAAA,EAAa;AAC7E,EAAA,uBACCA,GAAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAA,EAAI,UAAA;AAAA,MACJ,kBAAA,EACC,QAAQ,CAAA,EAAG,iBAAiB,IAAI,aAAa,CAAA,CAAA,GAAK,GAAG,iBAAiB,CAAA,CAAA;AAAA,MAEvE,cAAA,EAAc,CAAC,CAAC,KAAA;AAAA,MACf,GAAG;AAAA;AAAA,GACL;AAEF,CAAC;AACD,WAAA,CAAY,WAAA,GAAc,aAAA;AAG1B,IAAM,eAAA,GAAwB,kBAG5B,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,KAAQ;AACnC,EAAA,MAAM,EAAE,iBAAA,EAAkB,GAAI,YAAA,EAAa;AAC3C,EAAA,uBACCA,GAAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAA,EAAI,iBAAA;AAAA,MACJ,SAAA,EAAW,EAAA,CAAG,+BAAA,EAAiC,SAAS,CAAA;AAAA,MACvD,GAAG;AAAA;AAAA,GACL;AAEF,CAAC;AACD,eAAA,CAAgB,WAAA,GAAc,iBAAA;AAG9B,IAAM,WAAA,GAAoB,kBAGxB,CAAC,EAAE,WAAW,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AAC7C,EAAA,MAAM,EAAE,KAAA,EAAO,aAAA,EAAc,GAAI,YAAA,EAAa;AAC9C,EAAA,MAAM,OAAO,KAAA,EAAO,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,GAAI,QAAA;AACtD,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,uBACCA,GAAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAA,EAAI,aAAA;AAAA,MACJ,SAAA,EAAW,EAAA,CAAG,sCAAA,EAAwC,SAAS,CAAA;AAAA,MAC9D,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA;AAAA;AAAA,GACF;AAEF,CAAC;AACD,WAAA,CAAY,WAAA,GAAc,aAAA","file":"form.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 LabelPrimitive from \"@radix-ui/react-label\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst labelVariants = cva(\n\t\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nexport interface LabelProps\n\textends React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>,\n\t\tVariantProps<typeof labelVariants> {}\n\nconst Label = React.forwardRef<React.ComponentRef<typeof LabelPrimitive.Root>, LabelProps>(\n\t({ className, ...props }, ref) => (\n\t\t<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />\n\t),\n);\nLabel.displayName = \"Label\";\n\nexport { Label };\n","\"use client\";\n\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport {\n\tController,\n\ttype ControllerProps,\n\ttype FieldPath,\n\ttype FieldValues,\n\tFormProvider,\n\tuseFormContext,\n\tuseFormState,\n} from \"react-hook-form\";\nimport { Label } from \"../../primitives/label/label.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root form provider. Wraps react-hook-form's FormProvider. */\nconst Form = FormProvider;\n\ninterface FormFieldContextValue<\n\tTFieldValues extends FieldValues = FieldValues,\n\tTName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n\tname: TName;\n}\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);\n\n/**\n * Binds a field name to a react-hook-form Controller.\n * Provides context so FormItem children can access field state.\n * @param props - Controller props including name, control, render\n * @returns A Controller with FormFieldContext\n */\nconst FormField = <\n\tTFieldValues extends FieldValues = FieldValues,\n\tTName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n\t...props\n}: ControllerProps<TFieldValues, TName>) => {\n\treturn (\n\t\t<FormFieldContext.Provider value={{ name: props.name }}>\n\t\t\t<Controller {...props} />\n\t\t</FormFieldContext.Provider>\n\t);\n};\n\ninterface FormItemContextValue {\n\tid: string;\n}\n\nconst FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);\n\n/**\n * Hook that returns the current field's id, name, error, and derived aria ids.\n * Must be called inside a FormField + FormItem subtree.\n * @returns Field state + aria descriptors\n */\nfunction useFormField() {\n\tconst fieldContext = React.useContext(FormFieldContext);\n\tconst itemContext = React.useContext(FormItemContext);\n\tif (!fieldContext?.name) {\n\t\tthrow new Error(\"useFormField should be used within <FormField>\");\n\t}\n\n\tconst { getFieldState } = useFormContext();\n\tconst formState = useFormState({ name: fieldContext.name });\n\tconst fieldState = getFieldState(fieldContext.name, formState);\n\n\tconst { id } = itemContext;\n\treturn {\n\t\tid,\n\t\tname: fieldContext.name,\n\t\tformItemId: `${id}-form-item`,\n\t\tformDescriptionId: `${id}-form-item-description`,\n\t\tformMessageId: `${id}-form-item-message`,\n\t\t...fieldState,\n\t};\n}\n\n/** Wraps a single form field (label + control + description + message). */\nconst FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n\t({ className, ...props }, ref) => {\n\t\tconst id = React.useId();\n\t\treturn (\n\t\t\t<FormItemContext.Provider value={{ id }}>\n\t\t\t\t<div ref={ref} className={cn(\"space-y-2\", className)} {...props} />\n\t\t\t</FormItemContext.Provider>\n\t\t);\n\t},\n);\nFormItem.displayName = \"FormItem\";\n\n/** Accessible label wired to the FormItem's control. Turns red on error. */\nconst FormLabel = React.forwardRef<\n\tReact.ComponentRef<typeof LabelPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>\n>(({ className, ...props }, ref) => {\n\tconst { error, formItemId } = useFormField();\n\treturn (\n\t\t<Label\n\t\t\tref={ref}\n\t\t\tclassName={cn(error && \"text-destructive\", className)}\n\t\t\thtmlFor={formItemId}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nFormLabel.displayName = \"FormLabel\";\n\n/** Wraps the form control and wires id + aria-describedby + aria-invalid. */\nconst FormControl = React.forwardRef<\n\tReact.ComponentRef<typeof Slot>,\n\tReact.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n\tconst { error, formItemId, formDescriptionId, formMessageId } = useFormField();\n\treturn (\n\t\t<Slot\n\t\t\tref={ref}\n\t\t\tid={formItemId}\n\t\t\taria-describedby={\n\t\t\t\terror ? `${formDescriptionId} ${formMessageId}` : `${formDescriptionId}`\n\t\t\t}\n\t\t\taria-invalid={!!error}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nFormControl.displayName = \"FormControl\";\n\n/** Optional helper text below the control. */\nconst FormDescription = React.forwardRef<\n\tHTMLParagraphElement,\n\tReact.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n\tconst { formDescriptionId } = useFormField();\n\treturn (\n\t\t<p\n\t\t\tref={ref}\n\t\t\tid={formDescriptionId}\n\t\t\tclassName={cn(\"text-sm text-muted-foreground\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nFormDescription.displayName = \"FormDescription\";\n\n/** Validation error message. Renders the error string when the field is invalid. */\nconst FormMessage = React.forwardRef<\n\tHTMLParagraphElement,\n\tReact.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n\tconst { error, formMessageId } = useFormField();\n\tconst body = error?.message ? String(error.message) : children;\n\tif (!body) return null;\n\treturn (\n\t\t<p\n\t\t\tref={ref}\n\t\t\tid={formMessageId}\n\t\t\tclassName={cn(\"text-sm font-medium text-destructive\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{body}\n\t\t</p>\n\t);\n});\nFormMessage.displayName = \"FormMessage\";\n\nexport {\n\tuseFormField,\n\tForm,\n\tFormItem,\n\tFormLabel,\n\tFormControl,\n\tFormDescription,\n\tFormMessage,\n\tFormField,\n};\n"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/primitives/label/label.tsx","../src/components/form/form.tsx"],"names":["React","jsx"],"mappings":"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","file":"form.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 LabelPrimitive from \"@radix-ui/react-label\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst labelVariants = cva(\n\t\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nexport interface LabelProps\n\textends React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>,\n\t\tVariantProps<typeof labelVariants> {}\n\nconst Label = React.forwardRef<React.ComponentRef<typeof LabelPrimitive.Root>, LabelProps>(\n\t({ className, ...props }, ref) => (\n\t\t<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />\n\t),\n);\nLabel.displayName = \"Label\";\n\nexport { Label };\n","\"use client\";\n\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport {\n\tController,\n\ttype ControllerProps,\n\ttype FieldPath,\n\ttype FieldValues,\n\tFormProvider,\n\tuseFormContext,\n\tuseFormState,\n} from \"react-hook-form\";\nimport { Label } from \"../../primitives/label/label.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root form provider. Wraps react-hook-form's FormProvider. */\nconst Form = FormProvider;\n\ninterface FormFieldContextValue<\n\tTFieldValues extends FieldValues = FieldValues,\n\tTName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n\tname: TName;\n}\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);\n\n/**\n * Binds a field name to a react-hook-form Controller.\n * Provides context so FormItem children can access field state.\n * @param props - Controller props including name, control, render\n * @returns A Controller with FormFieldContext\n */\nconst FormField = <\n\tTFieldValues extends FieldValues = FieldValues,\n\tTName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n\t...props\n}: ControllerProps<TFieldValues, TName>) => {\n\treturn (\n\t\t<FormFieldContext.Provider value={{ name: props.name }}>\n\t\t\t<Controller {...props} />\n\t\t</FormFieldContext.Provider>\n\t);\n};\n\ninterface FormItemContextValue {\n\tid: string;\n}\n\nconst FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);\n\n/**\n * Hook that returns the current field's id, name, error, and derived aria ids.\n * Must be called inside a FormField + FormItem subtree.\n * @returns Field state + aria descriptors\n */\nfunction useFormField() {\n\tconst fieldContext = React.useContext(FormFieldContext);\n\tconst itemContext = React.useContext(FormItemContext);\n\tif (!fieldContext?.name) {\n\t\tthrow new Error(\"useFormField should be used within <FormField>\");\n\t}\n\n\tconst { getFieldState } = useFormContext();\n\tconst formState = useFormState({ name: fieldContext.name });\n\tconst fieldState = getFieldState(fieldContext.name, formState);\n\n\tconst { id } = itemContext;\n\treturn {\n\t\tid,\n\t\tname: fieldContext.name,\n\t\tformItemId: `${id}-form-item`,\n\t\tformDescriptionId: `${id}-form-item-description`,\n\t\tformMessageId: `${id}-form-item-message`,\n\t\t...fieldState,\n\t};\n}\n\n/** Wraps a single form field (label + control + description + message). */\nconst FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n\t({ className, ...props }, ref) => {\n\t\tconst id = React.useId();\n\t\treturn (\n\t\t\t<FormItemContext.Provider value={{ id }}>\n\t\t\t\t<div ref={ref} className={cn(\"space-y-2\", className)} {...props} />\n\t\t\t</FormItemContext.Provider>\n\t\t);\n\t},\n);\nFormItem.displayName = \"FormItem\";\n\n/** Accessible label wired to the FormItem's control. Turns red on error. */\nconst FormLabel = React.forwardRef<\n\tReact.ComponentRef<typeof LabelPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>\n>(({ className, ...props }, ref) => {\n\tconst { error, formItemId } = useFormField();\n\treturn (\n\t\t<Label\n\t\t\tref={ref}\n\t\t\tclassName={cn(error && \"text-destructive\", className)}\n\t\t\thtmlFor={formItemId}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nFormLabel.displayName = \"FormLabel\";\n\n/** Wraps the form control and wires id + aria-describedby + aria-invalid. */\nconst FormControl = React.forwardRef<\n\tReact.ComponentRef<typeof Slot>,\n\tReact.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n\tconst { error, formItemId, formDescriptionId, formMessageId } = useFormField();\n\treturn (\n\t\t<Slot\n\t\t\tref={ref}\n\t\t\tid={formItemId}\n\t\t\taria-describedby={\n\t\t\t\terror ? `${formDescriptionId} ${formMessageId}` : `${formDescriptionId}`\n\t\t\t}\n\t\t\taria-invalid={!!error}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nFormControl.displayName = \"FormControl\";\n\n/** Optional helper text below the control. */\nconst FormDescription = React.forwardRef<\n\tHTMLParagraphElement,\n\tReact.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n\tconst { formDescriptionId } = useFormField();\n\treturn (\n\t\t<p\n\t\t\tref={ref}\n\t\t\tid={formDescriptionId}\n\t\t\tclassName={cn(\"text-sm text-muted-foreground\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nFormDescription.displayName = \"FormDescription\";\n\n/** Validation error message. Renders the error string when the field is invalid. */\nconst FormMessage = React.forwardRef<\n\tHTMLParagraphElement,\n\tReact.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n\tconst { error, formMessageId } = useFormField();\n\tconst body = error?.message ? String(error.message) : children;\n\tif (!body) return null;\n\treturn (\n\t\t<p\n\t\t\tref={ref}\n\t\t\tid={formMessageId}\n\t\t\tclassName={cn(\"text-sm font-medium text-destructive\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{body}\n\t\t</p>\n\t);\n});\nFormMessage.displayName = \"FormMessage\";\n\nexport {\n\tuseFormField,\n\tForm,\n\tFormItem,\n\tFormLabel,\n\tFormControl,\n\tFormDescription,\n\tFormMessage,\n\tFormField,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/funnel/funnel.tsx"],"names":[],"mappings":"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","file":"funnel.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 * Conversion funnel — a vertical stack of trapezoidal stages whose width\n * is proportional to each stage's value. Pure SVG; no heavy peer\n * dependency. Pair with `<Sankey>` when stage-to-stage flow detail\n * matters; use Funnel when the conversion drop-off itself is the\n * message.\n *\n * @example\n * <Funnel\n * stages={[\n * { id: \"visit\", label: \"Visited\", value: 10000 },\n * { id: \"signup\", label: \"Signed up\", value: 1200 },\n * { id: \"active\", label: \"Active\", value: 480 },\n * { id: \"paid\", label: \"Paid\", value: 95 },\n * ]}\n * />\n */\nexport type FunnelStage = {\n\tid: string;\n\tlabel: string;\n\tvalue: number;\n};\n\nexport interface FunnelProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Ordered top-to-bottom stages. Values must be non-negative. */\n\tstages: FunnelStage[];\n\t/** Pixel width of the rendered SVG. Default 480. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 360. */\n\theight?: number;\n\t/** Pixel gap between stages. Default 4. */\n\tgap?: number;\n\t/** Show conversion-rate annotations between stages. Default true. */\n\tshowConversion?: boolean;\n\t/** Fired when a stage is clicked. */\n\tonStageClick?: (stage: FunnelStage) => void;\n}\n\ninterface LaidOutStage {\n\tstage: FunnelStage;\n\tdepth: number;\n\ttopLeft: number;\n\ttopRight: number;\n\tbottomLeft: number;\n\tbottomRight: number;\n\tyTop: number;\n\tyBottom: number;\n\tconversionFromPrev: number | null;\n}\n\nfunction Funnel({\n\tstages,\n\twidth = 480,\n\theight = 360,\n\tgap = 4,\n\tshowConversion = true,\n\tonStageClick,\n\tclassName,\n\t...rest\n}: FunnelProps) {\n\tconst laidOut = layout(stages, width, height, gap);\n\tconst desc = `Funnel with ${stages.length} stage${stages.length === 1 ? \"\" : \"s\"}, peak value ${stages[0]?.value ?? 0}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-funnel\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>Funnel chart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-funnel-stages>\n\t\t\t\t{laidOut.map((s) => {\n\t\t\t\t\tconst interactive = Boolean(onStageClick);\n\t\t\t\t\tconst handleActivate = () => onStageClick?.(s.stage);\n\t\t\t\t\treturn (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={s.stage.id}\n\t\t\t\t\t\tdata-hex-funnel-stage\n\t\t\t\t\t\tdata-depth={s.depth}\n\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\taria-label={interactive ? `${s.stage.label}: ${s.stage.value}` : undefined}\n\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\tonKeyDown={interactive ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<polygon\n\t\t\t\t\t\t\tpoints={`${s.topLeft},${s.yTop} ${s.topRight},${s.yTop} ${s.bottomRight},${s.yBottom} ${s.bottomLeft},${s.yBottom}`}\n\t\t\t\t\t\t\tfill={pickChartHue(s.depth)}\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<text\n\t\t\t\t\t\t\t// Page-bg fill + foreground-tinted stroke is the\n\t\t\t\t\t\t\t// classic \"outline label\" technique: text reads on\n\t\t\t\t\t\t\t// any chart-1..6 fill regardless of hue, and stays\n\t\t\t\t\t\t\t// readable when the trapezoid narrows below the\n\t\t\t\t\t\t\t// label's width (the label spans outside the\n\t\t\t\t\t\t\t// polygon onto the page bg).\n\t\t\t\t\t\t\tx={width / 2}\n\t\t\t\t\t\t\ty={(s.yTop + s.yBottom) / 2}\n\t\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\tfontSize={12}\n\t\t\t\t\t\t\tfontWeight={600}\n\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.45)\",\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>\n\t\t\t\t\t\t\t{`${s.stage.label} · ${s.stage.value.toLocaleString()}`}\n\t\t\t\t\t\t</text>\n\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\t{showConversion ? (\n\t\t\t\t<g data-hex-funnel-conversions>\n\t\t\t\t\t{laidOut.map((s) =>\n\t\t\t\t\t\ts.conversionFromPrev != null ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tkey={`conv-${s.stage.id}`}\n\t\t\t\t\t\t\t\tdata-hex-funnel-conversion\n\t\t\t\t\t\t\t\tx={width - 4}\n\t\t\t\t\t\t\t\ty={s.yTop - 2}\n\t\t\t\t\t\t\t\ttextAnchor=\"end\"\n\t\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{formatPct(s.conversionFromPrev)}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null,\n\t\t\t\t\t)}\n\t\t\t\t</g>\n\t\t\t) : null}\n\t\t</svg>\n\t);\n}\n\nfunction layout(stages: FunnelStage[], width: number, height: number, gap: number): LaidOutStage[] {\n\tif (stages.length === 0) return [];\n\tconst peak = Math.max(...stages.map((s) => s.value), 0) || 1;\n\tconst usableHeight = height - gap * Math.max(0, stages.length - 1);\n\tconst stageHeight = usableHeight / stages.length;\n\tconst cx = width / 2;\n\n\treturn stages.map((stage, i) => {\n\t\tconst yTop = i * (stageHeight + gap);\n\t\tconst yBottom = yTop + stageHeight;\n\t\tconst topRatio = stage.value / peak;\n\t\tconst next = stages[i + 1];\n\t\tconst bottomRatio = next ? next.value / peak : topRatio;\n\t\tconst topHalfWidth = (width / 2) * Math.max(0, Math.min(1, topRatio));\n\t\tconst bottomHalfWidth = (width / 2) * Math.max(0, Math.min(1, bottomRatio));\n\t\tconst prev = stages[i - 1];\n\t\tconst conversionFromPrev = prev && prev.value > 0 ? stage.value / prev.value : null;\n\t\treturn {\n\t\t\tstage,\n\t\t\tdepth: i,\n\t\t\ttopLeft: cx - topHalfWidth,\n\t\t\ttopRight: cx + topHalfWidth,\n\t\t\tbottomLeft: cx - bottomHalfWidth,\n\t\t\tbottomRight: cx + bottomHalfWidth,\n\t\t\tyTop,\n\t\t\tyBottom,\n\t\t\tconversionFromPrev,\n\t\t};\n\t});\n}\n\nfunction formatPct(ratio: number): string {\n\tif (!Number.isFinite(ratio)) return \"—\";\n\treturn `${(ratio * 100).toFixed(ratio < 0.1 ? 1 : 0)}%`;\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 { Funnel };\n"]}
1
+ {"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/funnel/funnel.tsx"],"names":[],"mappings":"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","file":"funnel.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 * Conversion funnel — a vertical stack of trapezoidal stages whose width\n * is proportional to each stage's value. Pure SVG; no heavy peer\n * dependency. Pair with `<Sankey>` when stage-to-stage flow detail\n * matters; use Funnel when the conversion drop-off itself is the\n * message.\n *\n * @example\n * <Funnel\n * stages={[\n * { id: \"visit\", label: \"Visited\", value: 10000 },\n * { id: \"signup\", label: \"Signed up\", value: 1200 },\n * { id: \"active\", label: \"Active\", value: 480 },\n * { id: \"paid\", label: \"Paid\", value: 95 },\n * ]}\n * />\n */\nexport type FunnelStage = {\n\tid: string;\n\tlabel: string;\n\tvalue: number;\n};\n\nexport interface FunnelProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Ordered top-to-bottom stages. Values must be non-negative. */\n\tstages: FunnelStage[];\n\t/** Pixel width of the rendered SVG. Default 480. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 360. */\n\theight?: number;\n\t/** Pixel gap between stages. Default 4. */\n\tgap?: number;\n\t/** Show conversion-rate annotations between stages. Default true. */\n\tshowConversion?: boolean;\n\t/** Fired when a stage is clicked. */\n\tonStageClick?: (stage: FunnelStage) => void;\n}\n\ninterface LaidOutStage {\n\tstage: FunnelStage;\n\tdepth: number;\n\ttopLeft: number;\n\ttopRight: number;\n\tbottomLeft: number;\n\tbottomRight: number;\n\tyTop: number;\n\tyBottom: number;\n\tconversionFromPrev: number | null;\n}\n\nfunction Funnel({\n\tstages,\n\twidth = 480,\n\theight = 360,\n\tgap = 4,\n\tshowConversion = true,\n\tonStageClick,\n\tclassName,\n\t...rest\n}: FunnelProps) {\n\tconst laidOut = layout(stages, width, height, gap);\n\tconst desc = `Funnel with ${stages.length} stage${stages.length === 1 ? \"\" : \"s\"}, peak value ${stages[0]?.value ?? 0}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-funnel\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>Funnel chart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-funnel-stages>\n\t\t\t\t{laidOut.map((s) => {\n\t\t\t\t\tconst interactive = Boolean(onStageClick);\n\t\t\t\t\tconst handleActivate = () => onStageClick?.(s.stage);\n\t\t\t\t\treturn (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={s.stage.id}\n\t\t\t\t\t\tdata-hex-funnel-stage\n\t\t\t\t\t\tdata-depth={s.depth}\n\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\taria-label={interactive ? `${s.stage.label}: ${s.stage.value}` : undefined}\n\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\tonKeyDown={interactive ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<polygon\n\t\t\t\t\t\t\tpoints={`${s.topLeft},${s.yTop} ${s.topRight},${s.yTop} ${s.bottomRight},${s.yBottom} ${s.bottomLeft},${s.yBottom}`}\n\t\t\t\t\t\t\tfill={pickChartHue(s.depth)}\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<text\n\t\t\t\t\t\t\t// Page-bg fill + foreground-tinted stroke is the\n\t\t\t\t\t\t\t// classic \"outline label\" technique: text reads on\n\t\t\t\t\t\t\t// any chart-1..6 fill regardless of hue, and stays\n\t\t\t\t\t\t\t// readable when the trapezoid narrows below the\n\t\t\t\t\t\t\t// label's width (the label spans outside the\n\t\t\t\t\t\t\t// polygon onto the page bg).\n\t\t\t\t\t\t\tx={width / 2}\n\t\t\t\t\t\t\ty={(s.yTop + s.yBottom) / 2}\n\t\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\tfontSize={12}\n\t\t\t\t\t\t\tfontWeight={600}\n\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.45)\",\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>\n\t\t\t\t\t\t\t{`${s.stage.label} · ${s.stage.value.toLocaleString()}`}\n\t\t\t\t\t\t</text>\n\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\t{showConversion ? (\n\t\t\t\t<g data-hex-funnel-conversions>\n\t\t\t\t\t{laidOut.map((s) =>\n\t\t\t\t\t\ts.conversionFromPrev != null ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tkey={`conv-${s.stage.id}`}\n\t\t\t\t\t\t\t\tdata-hex-funnel-conversion\n\t\t\t\t\t\t\t\tx={width - 4}\n\t\t\t\t\t\t\t\ty={s.yTop - 2}\n\t\t\t\t\t\t\t\ttextAnchor=\"end\"\n\t\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{formatPct(s.conversionFromPrev)}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null,\n\t\t\t\t\t)}\n\t\t\t\t</g>\n\t\t\t) : null}\n\t\t</svg>\n\t);\n}\n\nfunction layout(stages: FunnelStage[], width: number, height: number, gap: number): LaidOutStage[] {\n\tif (stages.length === 0) return [];\n\tconst peak = Math.max(...stages.map((s) => s.value), 0) || 1;\n\tconst usableHeight = height - gap * Math.max(0, stages.length - 1);\n\tconst stageHeight = usableHeight / stages.length;\n\tconst cx = width / 2;\n\n\treturn stages.map((stage, i) => {\n\t\tconst yTop = i * (stageHeight + gap);\n\t\tconst yBottom = yTop + stageHeight;\n\t\tconst topRatio = stage.value / peak;\n\t\tconst next = stages[i + 1];\n\t\tconst bottomRatio = next ? next.value / peak : topRatio;\n\t\tconst topHalfWidth = (width / 2) * Math.max(0, Math.min(1, topRatio));\n\t\tconst bottomHalfWidth = (width / 2) * Math.max(0, Math.min(1, bottomRatio));\n\t\tconst prev = stages[i - 1];\n\t\tconst conversionFromPrev = prev && prev.value > 0 ? stage.value / prev.value : null;\n\t\treturn {\n\t\t\tstage,\n\t\t\tdepth: i,\n\t\t\ttopLeft: cx - topHalfWidth,\n\t\t\ttopRight: cx + topHalfWidth,\n\t\t\tbottomLeft: cx - bottomHalfWidth,\n\t\t\tbottomRight: cx + bottomHalfWidth,\n\t\t\tyTop,\n\t\t\tyBottom,\n\t\t\tconversionFromPrev,\n\t\t};\n\t});\n}\n\nfunction formatPct(ratio: number): string {\n\tif (!Number.isFinite(ratio)) return \"—\";\n\treturn `${(ratio * 100).toFixed(ratio < 0.1 ? 1 : 0)}%`;\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 { Funnel };\n"]}