@hex-core/components 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.ts +575 -18
- package/dist/accordion.js.map +1 -1
- package/dist/alert-dialog.js.map +1 -1
- package/dist/alert.js.map +1 -1
- package/dist/arc.js.map +1 -1
- package/dist/attachment.js.map +1 -1
- package/dist/audio-player.js.map +1 -1
- package/dist/audio-waveform.js.map +1 -1
- package/dist/auth-forgot-password.js.map +1 -1
- package/dist/auth-reset-password.js.map +1 -1
- package/dist/auth-sign-in-split.js.map +1 -1
- package/dist/auth-sign-up-card.js.map +1 -1
- package/dist/auth-verify-email.js.map +1 -1
- package/dist/auth-verify-otp.js.map +1 -1
- package/dist/avatar.js.map +1 -1
- package/dist/badge.js.map +1 -1
- package/dist/branch.d.ts +2 -0
- package/dist/branch.js +136 -0
- package/dist/branch.js.map +1 -0
- package/dist/breadcrumb.js.map +1 -1
- package/dist/button.js.map +1 -1
- package/dist/calendar.js.map +1 -1
- package/dist/canvas.js.map +1 -1
- package/dist/card.js.map +1 -1
- package/dist/chain-of-thought.d.ts +3 -0
- package/dist/chain-of-thought.js +119 -0
- package/dist/chain-of-thought.js.map +1 -0
- package/dist/checkbox.js.map +1 -1
- package/dist/chord.js.map +1 -1
- package/dist/citation.js.map +1 -1
- package/dist/cloze.js.map +1 -1
- package/dist/cluster.js.map +1 -1
- package/dist/code-block-copy.js.map +1 -1
- package/dist/code-block.js.map +1 -1
- package/dist/color-picker.js.map +1 -1
- package/dist/combobox.js.map +1 -1
- package/dist/command.js.map +1 -1
- package/dist/compare-table.js.map +1 -1
- package/dist/composer.js.map +1 -1
- package/dist/container.js.map +1 -1
- package/dist/context-menu.js.map +1 -1
- package/dist/conversation.d.ts +3 -0
- package/dist/conversation.js +358 -0
- package/dist/conversation.js.map +1 -0
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.js.map +1 -1
- package/dist/deck.js.map +1 -1
- package/dist/dendrogram.js.map +1 -1
- package/dist/diagram.js.map +1 -1
- package/dist/dialog.js.map +1 -1
- package/dist/drawer.js.map +1 -1
- package/dist/dropdown-menu.js.map +1 -1
- package/dist/dropzone.js.map +1 -1
- package/dist/empty.js.map +1 -1
- package/dist/error-state.js.map +1 -1
- package/dist/file-tree.js.map +1 -1
- package/dist/flashcard.js.map +1 -1
- package/dist/flowchart.js.map +1 -1
- package/dist/form.js.map +1 -1
- package/dist/funnel.js.map +1 -1
- package/dist/gantt.js.map +1 -1
- package/dist/grid.js.map +1 -1
- package/dist/hover-card.js.map +1 -1
- package/dist/image-occlusion.js.map +1 -1
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1011 -13
- package/dist/index.js.map +1 -1
- package/dist/inline-citation.d.ts +2 -0
- package/dist/inline-citation.js +108 -0
- package/dist/inline-citation.js.map +1 -0
- package/dist/input-otp.js.map +1 -1
- package/dist/input.js.map +1 -1
- package/dist/label.js.map +1 -1
- package/dist/loading-indicator.js.map +1 -1
- package/dist/loading.js.map +1 -1
- package/dist/markdown.d.ts +1 -0
- package/dist/markdown.js +784 -4
- package/dist/markdown.js.map +1 -1
- package/dist/matrix.js.map +1 -1
- package/dist/menubar.js.map +1 -1
- package/dist/message-actions.js.map +1 -1
- package/dist/message-list.js.map +1 -1
- package/dist/message.js.map +1 -1
- package/dist/mind-map.js.map +1 -1
- package/dist/multi-combobox.js.map +1 -1
- package/dist/navigation-menu.js.map +1 -1
- package/dist/org-chart.js.map +1 -1
- package/dist/pagination.js.map +1 -1
- package/dist/plan.d.ts +3 -0
- package/dist/plan.js +183 -0
- package/dist/plan.js.map +1 -0
- package/dist/popover.js.map +1 -1
- package/dist/progress.js.map +1 -1
- package/dist/pyramid.js.map +1 -1
- package/dist/quiz.js.map +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/reasoning.js.map +1 -1
- package/dist/resizable.js.map +1 -1
- package/dist/sankey.js.map +1 -1
- package/dist/schemas.d.ts +8 -0
- package/dist/schemas.js +774 -17
- package/dist/schemas.js.map +1 -1
- package/dist/scroll-area.js.map +1 -1
- package/dist/select.js.map +1 -1
- package/dist/separator.js.map +1 -1
- package/dist/sequence.js.map +1 -1
- package/dist/sheet.js.map +1 -1
- package/dist/shimmer.d.ts +2 -0
- package/dist/shimmer.js +39 -0
- package/dist/shimmer.js.map +1 -0
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.js.map +1 -1
- package/dist/sources.d.ts +3 -0
- package/dist/sources.js +164 -0
- package/dist/sources.js.map +1 -0
- package/dist/spaced-repetition.js.map +1 -1
- package/dist/spacer.js.map +1 -1
- package/dist/speech-recognition.js.map +1 -1
- package/dist/stack.js.map +1 -1
- package/dist/stepper.js.map +1 -1
- package/dist/suggestion.js.map +1 -1
- package/dist/sunburst.js.map +1 -1
- package/dist/switch.js.map +1 -1
- package/dist/table.js.map +1 -1
- package/dist/tabs.js.map +1 -1
- package/dist/tag.js.map +1 -1
- package/dist/task.d.ts +3 -0
- package/dist/task.js +189 -0
- package/dist/task.js.map +1 -0
- package/dist/terminal.js +11 -0
- package/dist/terminal.js.map +1 -1
- package/dist/textarea.js.map +1 -1
- package/dist/time-axis.js.map +1 -1
- package/dist/time-picker.js.map +1 -1
- package/dist/timeline.js.map +1 -1
- package/dist/toggle-group.js.map +1 -1
- package/dist/toggle.js.map +1 -1
- package/dist/tool-call.js +5 -6
- package/dist/tool-call.js.map +1 -1
- package/dist/toolbar.js.map +1 -1
- package/dist/tooltip.js.map +1 -1
- package/dist/tree-map.js.map +1 -1
- package/dist/tree.js.map +1 -1
- package/dist/venn.js.map +1 -1
- package/package.json +8 -3
package/dist/progress.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/primitives/progress/progress.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACAA,IAAM,QAAA,GAAiB,KAAA,CAAA,UAAA,CAGrB,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,GAAA,GAAM,GAAA,EAAK,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AACrD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,EAAA,CAAO,KAAA,IAAS,CAAA,IAAK,GAAA,GAAO,GAAG,CAAC,CAAA;AACjE,EAAA,uBACC,GAAA;AAAA,IAAmB,iBAAA,CAAA,IAAA;AAAA,IAAlB;AAAA,MACA,GAAA;AAAA,MAGA,OAAO,KAAA,IAAS,CAAA;AAAA,MAChB,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,+FAAA;AAAA,QACA;AAAA,OACD;AAAA,MACC,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA;AAAA,QAAmB,iBAAA,CAAA,SAAA;AAAA,QAAlB;AAAA,UACA,SAAA,EAAU,4EAAA;AAAA,UACV,OAAO,EAAE,SAAA,EAAW,CAAA,YAAA,EAAe,GAAA,GAAM,GAAG,CAAA,EAAA,CAAA;AAAK;AAAA;AAClD;AAAA,GACD;AAEF,CAAC;AACD,QAAA,CAAS,WAAA,GAAc,UAAA","file":"progress.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 ProgressPrimitive from \"@radix-ui/react-progress\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A horizontal progress bar from 0–100%.\n * Built on Radix UI Progress for aria-valuenow/max wiring.\n */\nconst Progress = React.forwardRef<\n\tReact.ComponentRef<typeof ProgressPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>\n>(({ className, value, max = 100, ...props }, ref) => {\n\tconst pct = Math.max(0, Math.min(100, ((value ?? 0) / max) * 100));\n\treturn (\n\t\t<ProgressPrimitive.Root\n\t\t\tref={ref}\n\t\t\t// Clamp undefined → 0 so ARIA (aria-valuenow) matches the visual fill.\n\t\t\t// Consumers who want an indeterminate loading bar should use <Skeleton />.\n\t\t\tvalue={value ?? 0}\n\t\t\tmax={max}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative h-2 w-full overflow-hidden rounded-full border border-foreground/[0.08] bg-secondary\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<ProgressPrimitive.Indicator\n\t\t\t\tclassName=\"h-full w-full flex-1 bg-primary transition-transform duration-500 ease-out\"\n\t\t\t\tstyle={{ transform: `translateX(-${100 - pct}%)` }}\n\t\t\t/>\n\t\t</ProgressPrimitive.Root>\n\t);\n});\nProgress.displayName = \"Progress\";\n\nexport { Progress };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/primitives/progress/progress.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACAA,IAAM,QAAA,GAAiB,KAAA,CAAA,UAAA,CAGrB,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,GAAA,GAAM,GAAA,EAAK,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AACrD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,EAAA,CAAO,KAAA,IAAS,CAAA,IAAK,GAAA,GAAO,GAAG,CAAC,CAAA;AACjE,EAAA,uBACC,GAAA;AAAA,IAAmB,iBAAA,CAAA,IAAA;AAAA,IAAlB;AAAA,MACA,GAAA;AAAA,MAGA,OAAO,KAAA,IAAS,CAAA;AAAA,MAChB,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,+FAAA;AAAA,QACA;AAAA,OACD;AAAA,MACC,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA;AAAA,QAAmB,iBAAA,CAAA,SAAA;AAAA,QAAlB;AAAA,UACA,SAAA,EAAU,4EAAA;AAAA,UACV,OAAO,EAAE,SAAA,EAAW,CAAA,YAAA,EAAe,GAAA,GAAM,GAAG,CAAA,EAAA,CAAA;AAAK;AAAA;AAClD;AAAA,GACD;AAEF,CAAC;AACD,QAAA,CAAS,WAAA,GAAc,UAAA","file":"progress.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 ProgressPrimitive from \"@radix-ui/react-progress\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A horizontal progress bar from 0–100%.\n * Built on Radix UI Progress for aria-valuenow/max wiring.\n */\nconst Progress = React.forwardRef<\n\tReact.ComponentRef<typeof ProgressPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>\n>(({ className, value, max = 100, ...props }, ref) => {\n\tconst pct = Math.max(0, Math.min(100, ((value ?? 0) / max) * 100));\n\treturn (\n\t\t<ProgressPrimitive.Root\n\t\t\tref={ref}\n\t\t\t// Clamp undefined → 0 so ARIA (aria-valuenow) matches the visual fill.\n\t\t\t// Consumers who want an indeterminate loading bar should use <Skeleton />.\n\t\t\tvalue={value ?? 0}\n\t\t\tmax={max}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative h-2 w-full overflow-hidden rounded-full border border-foreground/[0.08] bg-secondary\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<ProgressPrimitive.Indicator\n\t\t\t\tclassName=\"h-full w-full flex-1 bg-primary transition-transform duration-500 ease-out\"\n\t\t\t\tstyle={{ transform: `translateX(-${100 - pct}%)` }}\n\t\t\t/>\n\t\t</ProgressPrimitive.Root>\n\t);\n});\nProgress.displayName = \"Progress\";\n\nexport { Progress };\n"]}
|
package/dist/pyramid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/pyramid/pyramid.tsx"],"names":[],"mappings":"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","file":"pyramid.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 * Ranked-tier pyramid. Tiers stack top-to-bottom; each tier's width can\n * either grow toward the base (\"widening\" — Maslow's hierarchy, fewer\n * elites at the top) or shrink (\"narrowing\" — population pyramid by age).\n * Pure SVG; no heavy peer dependency.\n *\n * Distinct from Funnel: Pyramid encodes RANK (each tier is a distinct\n * categorical level), Funnel encodes FLOW (each stage is a subset of the\n * previous, with a conversion ratio).\n *\n * @example\n * <Pyramid\n * tiers={[\n * { id: \"self-actualization\", label: \"Self-actualization\" },\n * { id: \"esteem\", label: \"Esteem\" },\n * { id: \"love\", label: \"Love & belonging\" },\n * { id: \"safety\", label: \"Safety\" },\n * { id: \"physiological\", label: \"Physiological\" },\n * ]}\n * shape=\"widening\"\n * />\n */\nexport type PyramidTier = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n};\n\nexport interface PyramidProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Ordered top-to-bottom tiers. The first entry is the apex. */\n\ttiers: PyramidTier[];\n\t/** Tier-width direction. \"widening\" grows toward the base; \"narrowing\" shrinks toward it. Default \"widening\". */\n\tshape?: \"widening\" | \"narrowing\";\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 tiers. Default 4. */\n\tgap?: number;\n\t/** Show each tier's `value` next to its label when present. Default true. */\n\tshowValues?: boolean;\n\t/** Fired when a tier is clicked. */\n\tonTierClick?: (tier: PyramidTier) => void;\n}\n\ninterface LaidOutTier {\n\ttier: PyramidTier;\n\tdepth: number;\n\ttopLeft: number;\n\ttopRight: number;\n\tbottomLeft: number;\n\tbottomRight: number;\n\tyTop: number;\n\tyBottom: number;\n}\n\nfunction Pyramid({\n\ttiers,\n\tshape = \"widening\",\n\twidth = 480,\n\theight = 360,\n\tgap = 4,\n\tshowValues = true,\n\tonTierClick,\n\tclassName,\n\t...rest\n}: PyramidProps) {\n\tconst laidOut = layout(tiers, shape, width, height, gap);\n\tconst desc = `Pyramid with ${tiers.length} tier${tiers.length === 1 ? \"\" : \"s\"} (${shape})`;\n\n\t// Pyramid's `whenNotToUse` warns about >7 tiers — surface a dev-only\n\t// console warning so consumers feel the friction in development without\n\t// punishing prod runtime. Uses a ref so the warning fires once per mount,\n\t// not on every render. Read NODE_ENV via globalThis to avoid pulling\n\t// `@types/node` into the components package — bundlers inline the value\n\t// in browser builds; in tests vitest/Node provides `process` at runtime.\n\tconst warnedRef = React.useRef(false);\n\tconst nodeEnv = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV;\n\tif (nodeEnv !== \"production\" && !warnedRef.current && tiers.length > 7) {\n\t\twarnedRef.current = true;\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(\n\t\t\t`[hex-core/Pyramid] ${tiers.length} tiers — labels become unreadable past ~7. Group adjacent tiers or switch to TreeMap / OrgChart.`,\n\t\t);\n\t}\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-pyramid\n\t\t\tdata-shape={shape}\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>Pyramid chart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-pyramid-tiers>\n\t\t\t\t{laidOut.map((t) => {\n\t\t\t\t\tconst valueText =\n\t\t\t\t\t\tshowValues && t.tier.value != null ? ` · ${t.tier.value.toLocaleString()}` : \"\";\n\t\t\t\t\tconst interactive = Boolean(onTierClick);\n\t\t\t\t\tconst handleActivate = () => onTierClick?.(t.tier);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={t.tier.id}\n\t\t\t\t\t\t\tdata-hex-pyramid-tier\n\t\t\t\t\t\t\tdata-depth={t.depth}\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 ? `${t.tier.label}${valueText}` : 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<polygon\n\t\t\t\t\t\t\t\tpoints={`${t.topLeft},${t.yTop} ${t.topRight},${t.yTop} ${t.bottomRight},${t.yBottom} ${t.bottomLeft},${t.yBottom}`}\n\t\t\t\t\t\t\t\tfill={pickChartHue(t.depth)}\n\t\t\t\t\t\t\t\tfillOpacity={0.85}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\t// Page-bg fill + foreground-tinted stroke gives\n\t\t\t\t\t\t\t\t// a readable \"outline label\" on any chart-1..6\n\t\t\t\t\t\t\t\t// fill — same technique as Funnel.\n\t\t\t\t\t\t\t\tx={width / 2}\n\t\t\t\t\t\t\t\ty={(t.yTop + t.yBottom) / 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={600}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.45)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{`${t.tier.label}${valueText}`}\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\ttiers: PyramidTier[],\n\tshape: \"widening\" | \"narrowing\",\n\twidth: number,\n\theight: number,\n\tgap: number,\n): LaidOutTier[] {\n\tif (tiers.length === 0) return [];\n\tconst usableHeight = height - gap * Math.max(0, tiers.length - 1);\n\tconst tierHeight = usableHeight / tiers.length;\n\tconst cx = width / 2;\n\tconst total = tiers.length;\n\n\t// Linear ramp: top tier ratio 0.2, bottom tier ratio 1.0 (or reverse).\n\tconst ratioAt = (i: number): number => {\n\t\tconst t = total === 1 ? 0 : i / (total - 1);\n\t\treturn shape === \"widening\" ? 0.2 + 0.8 * t : 1.0 - 0.8 * t;\n\t};\n\n\treturn tiers.map((tier, i) => {\n\t\tconst yTop = i * (tierHeight + gap);\n\t\tconst yBottom = yTop + tierHeight;\n\t\tconst topRatio = ratioAt(i);\n\t\tconst bottomRatio = i + 1 < total ? ratioAt(i + 1) : topRatio;\n\t\t// For widening, the visual base of each tier should match the top of\n\t\t// the next tier — using the next tier's ratio at the bottom edge.\n\t\t// For narrowing, same logic. The penultimate-to-last bottom matches\n\t\t// `topRatio` of the last tier we don't have, so we hold steady.\n\t\tconst topHalfWidth = (width / 2) * topRatio;\n\t\tconst bottomHalfWidth = (width / 2) * bottomRatio;\n\t\treturn {\n\t\t\ttier,\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};\n\t});\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 { Pyramid };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/pyramid/pyramid.tsx"],"names":[],"mappings":"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","file":"pyramid.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 * Ranked-tier pyramid. Tiers stack top-to-bottom; each tier's width can\n * either grow toward the base (\"widening\" — Maslow's hierarchy, fewer\n * elites at the top) or shrink (\"narrowing\" — population pyramid by age).\n * Pure SVG; no heavy peer dependency.\n *\n * Distinct from Funnel: Pyramid encodes RANK (each tier is a distinct\n * categorical level), Funnel encodes FLOW (each stage is a subset of the\n * previous, with a conversion ratio).\n *\n * @example\n * <Pyramid\n * tiers={[\n * { id: \"self-actualization\", label: \"Self-actualization\" },\n * { id: \"esteem\", label: \"Esteem\" },\n * { id: \"love\", label: \"Love & belonging\" },\n * { id: \"safety\", label: \"Safety\" },\n * { id: \"physiological\", label: \"Physiological\" },\n * ]}\n * shape=\"widening\"\n * />\n */\nexport type PyramidTier = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n};\n\nexport interface PyramidProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Ordered top-to-bottom tiers. The first entry is the apex. */\n\ttiers: PyramidTier[];\n\t/** Tier-width direction. \"widening\" grows toward the base; \"narrowing\" shrinks toward it. Default \"widening\". */\n\tshape?: \"widening\" | \"narrowing\";\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 tiers. Default 4. */\n\tgap?: number;\n\t/** Show each tier's `value` next to its label when present. Default true. */\n\tshowValues?: boolean;\n\t/** Fired when a tier is clicked. */\n\tonTierClick?: (tier: PyramidTier) => void;\n}\n\ninterface LaidOutTier {\n\ttier: PyramidTier;\n\tdepth: number;\n\ttopLeft: number;\n\ttopRight: number;\n\tbottomLeft: number;\n\tbottomRight: number;\n\tyTop: number;\n\tyBottom: number;\n}\n\nfunction Pyramid({\n\ttiers,\n\tshape = \"widening\",\n\twidth = 480,\n\theight = 360,\n\tgap = 4,\n\tshowValues = true,\n\tonTierClick,\n\tclassName,\n\t...rest\n}: PyramidProps) {\n\tconst laidOut = layout(tiers, shape, width, height, gap);\n\tconst desc = `Pyramid with ${tiers.length} tier${tiers.length === 1 ? \"\" : \"s\"} (${shape})`;\n\n\t// Pyramid's `whenNotToUse` warns about >7 tiers — surface a dev-only\n\t// console warning so consumers feel the friction in development without\n\t// punishing prod runtime. Uses a ref so the warning fires once per mount,\n\t// not on every render. Read NODE_ENV via globalThis to avoid pulling\n\t// `@types/node` into the components package — bundlers inline the value\n\t// in browser builds; in tests vitest/Node provides `process` at runtime.\n\tconst warnedRef = React.useRef(false);\n\tconst nodeEnv = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process?.env?.NODE_ENV;\n\tif (nodeEnv !== \"production\" && !warnedRef.current && tiers.length > 7) {\n\t\twarnedRef.current = true;\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(\n\t\t\t`[hex-core/Pyramid] ${tiers.length} tiers — labels become unreadable past ~7. Group adjacent tiers or switch to TreeMap / OrgChart.`,\n\t\t);\n\t}\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-pyramid\n\t\t\tdata-shape={shape}\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>Pyramid chart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-pyramid-tiers>\n\t\t\t\t{laidOut.map((t) => {\n\t\t\t\t\tconst valueText =\n\t\t\t\t\t\tshowValues && t.tier.value != null ? ` · ${t.tier.value.toLocaleString()}` : \"\";\n\t\t\t\t\tconst interactive = Boolean(onTierClick);\n\t\t\t\t\tconst handleActivate = () => onTierClick?.(t.tier);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={t.tier.id}\n\t\t\t\t\t\t\tdata-hex-pyramid-tier\n\t\t\t\t\t\t\tdata-depth={t.depth}\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 ? `${t.tier.label}${valueText}` : 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<polygon\n\t\t\t\t\t\t\t\tpoints={`${t.topLeft},${t.yTop} ${t.topRight},${t.yTop} ${t.bottomRight},${t.yBottom} ${t.bottomLeft},${t.yBottom}`}\n\t\t\t\t\t\t\t\tfill={pickChartHue(t.depth)}\n\t\t\t\t\t\t\t\tfillOpacity={0.85}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\t// Page-bg fill + foreground-tinted stroke gives\n\t\t\t\t\t\t\t\t// a readable \"outline label\" on any chart-1..6\n\t\t\t\t\t\t\t\t// fill — same technique as Funnel.\n\t\t\t\t\t\t\t\tx={width / 2}\n\t\t\t\t\t\t\t\ty={(t.yTop + t.yBottom) / 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={600}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\t\t\tpaintOrder: \"stroke\",\n\t\t\t\t\t\t\t\t\tstroke: \"hsl(var(--foreground) / 0.45)\",\n\t\t\t\t\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{`${t.tier.label}${valueText}`}\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\ttiers: PyramidTier[],\n\tshape: \"widening\" | \"narrowing\",\n\twidth: number,\n\theight: number,\n\tgap: number,\n): LaidOutTier[] {\n\tif (tiers.length === 0) return [];\n\tconst usableHeight = height - gap * Math.max(0, tiers.length - 1);\n\tconst tierHeight = usableHeight / tiers.length;\n\tconst cx = width / 2;\n\tconst total = tiers.length;\n\n\t// Linear ramp: top tier ratio 0.2, bottom tier ratio 1.0 (or reverse).\n\tconst ratioAt = (i: number): number => {\n\t\tconst t = total === 1 ? 0 : i / (total - 1);\n\t\treturn shape === \"widening\" ? 0.2 + 0.8 * t : 1.0 - 0.8 * t;\n\t};\n\n\treturn tiers.map((tier, i) => {\n\t\tconst yTop = i * (tierHeight + gap);\n\t\tconst yBottom = yTop + tierHeight;\n\t\tconst topRatio = ratioAt(i);\n\t\tconst bottomRatio = i + 1 < total ? ratioAt(i + 1) : topRatio;\n\t\t// For widening, the visual base of each tier should match the top of\n\t\t// the next tier — using the next tier's ratio at the bottom edge.\n\t\t// For narrowing, same logic. The penultimate-to-last bottom matches\n\t\t// `topRatio` of the last tier we don't have, so we hold steady.\n\t\tconst topHalfWidth = (width / 2) * topRatio;\n\t\tconst bottomHalfWidth = (width / 2) * bottomRatio;\n\t\treturn {\n\t\t\ttier,\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};\n\t});\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 { Pyramid };\n"]}
|
package/dist/quiz.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/quiz/quiz.tsx"],"names":[],"mappings":"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","file":"quiz.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 * Single-question multiple-choice quiz. Renders the question + options\n * as native radio (single-select) or checkbox (multi-select) inputs;\n * after Submit, each option flips to data-state=\"correct|incorrect|missed\"\n * so consumers can theme right / wrong / unselected-but-correct\n * differently. Pure HTML; no heavy peer.\n *\n * Headless on grading: the schema honors what the consumer passes for\n * `correct`. Reset between runs by clearing the `selectedIds` you\n * forwarded as `value` (controlled) or unmounting (uncontrolled).\n *\n * @example\n * <Quiz\n * question=\"Which planets are gas giants?\"\n * selectionMode=\"multi\"\n * options={[\n * { id: \"j\", label: \"Jupiter\", correct: true },\n * { id: \"v\", label: \"Venus\" },\n * { id: \"s\", label: \"Saturn\", correct: true, explanation: \"Saturn's atmosphere is mostly hydrogen and helium.\" },\n * { id: \"m\", label: \"Mercury\" },\n * ]}\n * onAnswer={(ids, allCorrect) => track(ids, allCorrect)}\n * />\n */\nexport type QuizOption = {\n\tid: string;\n\tlabel: React.ReactNode;\n\tcorrect?: boolean;\n\texplanation?: React.ReactNode;\n};\n\nexport interface QuizProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n\t/** Question prompt (any ReactNode — supports rich content). */\n\tquestion: React.ReactNode;\n\t/** Options the learner picks from. */\n\toptions: QuizOption[];\n\t/** \"single\" — radio inputs (one selection). \"multi\" — checkboxes. Default \"single\". */\n\tselectionMode?: \"single\" | \"multi\";\n\t/** Custom Submit button label. Default \"Submit\". */\n\tsubmitLabel?: string;\n\t/** Fired with the selected option ids and a boolean indicating whether the entire selection matches the correct set. */\n\tonAnswer?: (selectedIds: string[], allCorrect: boolean) => void;\n}\n\ntype CellState = \"correct\" | \"incorrect\" | \"missed\" | \"unanswered\";\n\nfunction Quiz({\n\tquestion,\n\toptions,\n\tselectionMode = \"single\",\n\tsubmitLabel = \"Submit\",\n\tonAnswer,\n\tclassName,\n\t...rest\n}: QuizProps) {\n\tconst [selected, setSelected] = React.useState<Set<string>>(() => new Set());\n\tconst [submitted, setSubmitted] = React.useState(false);\n\tconst onAnswerRef = React.useRef(onAnswer);\n\tonAnswerRef.current = onAnswer;\n\tconst groupName = React.useId();\n\n\tconst correctIds = React.useMemo(\n\t\t() => new Set(options.filter((o) => o.correct).map((o) => o.id)),\n\t\t[options],\n\t);\n\n\tconst allCorrect = React.useMemo(() => {\n\t\tif (selected.size !== correctIds.size) return false;\n\t\tfor (const id of correctIds) if (!selected.has(id)) return false;\n\t\treturn true;\n\t}, [selected, correctIds]);\n\n\tconst stateFor = (option: QuizOption): CellState => {\n\t\tif (!submitted) return \"unanswered\";\n\t\tconst picked = selected.has(option.id);\n\t\tconst isCorrect = correctIds.has(option.id);\n\t\tif (picked && isCorrect) return \"correct\";\n\t\tif (picked && !isCorrect) return \"incorrect\";\n\t\tif (!picked && isCorrect) return \"missed\";\n\t\treturn \"unanswered\";\n\t};\n\n\tconst toggle = (id: string) => {\n\t\t// Submit is sticky: data-state always reflects the CURRENT selection\n\t\t// against the correct set, so re-picking after submit auto-updates\n\t\t// the correct/incorrect/missed indicators without clearing the\n\t\t// \"I've tried this\" affordance. Next Submit re-grades and re-fires\n\t\t// onAnswer with the updated selection.\n\t\tsetSelected((prev) => {\n\t\t\tif (selectionMode === \"single\") return new Set([id]);\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(id)) next.delete(id);\n\t\t\telse next.add(id);\n\t\t\treturn next;\n\t\t});\n\t};\n\n\tconst handleSubmit = () => {\n\t\tsetSubmitted(true);\n\t\tonAnswerRef.current?.(Array.from(selected), allCorrect);\n\t};\n\n\tconst canSubmit = selected.size > 0;\n\tconst statusText = submitted\n\t\t? allCorrect\n\t\t\t? \"Correct — well done.\"\n\t\t\t: \"Some selections are incorrect or missed. Review the highlighted options.\"\n\t\t: \"\";\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-quiz\n\t\t\tdata-submitted={submitted}\n\t\t\tdata-all-correct={submitted && allCorrect}\n\t\t\tclassName={cn(\"rounded-lg border bg-card p-4 text-card-foreground\", className)}\n\t\t>\n\t\t\t<div data-hex-quiz-question className=\"mb-3 text-base font-medium\">\n\t\t\t\t{question}\n\t\t\t</div>\n\t\t\t<div data-hex-quiz-options className=\"space-y-2\" role={selectionMode === \"single\" ? \"radiogroup\" : \"group\"}>\n\t\t\t\t{options.map((option) => {\n\t\t\t\t\tconst state = stateFor(option);\n\t\t\t\t\tconst isPicked = selected.has(option.id);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div key={option.id}>\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\tdata-hex-quiz-option\n\t\t\t\t\t\t\t\tdata-state={state}\n\t\t\t\t\t\t\t\tdata-picked={isPicked}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex cursor-pointer items-start gap-2 rounded-md border bg-background p-2 transition-colors\",\n\t\t\t\t\t\t\t\t\t\"hover:bg-muted/50\",\n\t\t\t\t\t\t\t\t\tstate === \"correct\" && \"border-primary bg-primary/10\",\n\t\t\t\t\t\t\t\t\tstate === \"incorrect\" && \"border-destructive bg-destructive/10\",\n\t\t\t\t\t\t\t\t\tstate === \"missed\" && \"border-primary/60 bg-primary/5 ring-1 ring-primary/40\",\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<input\n\t\t\t\t\t\t\t\t\ttype={selectionMode === \"single\" ? \"radio\" : \"checkbox\"}\n\t\t\t\t\t\t\t\t\tname={selectionMode === \"single\" ? groupName : undefined}\n\t\t\t\t\t\t\t\t\tvalue={option.id}\n\t\t\t\t\t\t\t\t\tchecked={isPicked}\n\t\t\t\t\t\t\t\t\tonChange={() => toggle(option.id)}\n\t\t\t\t\t\t\t\t\tclassName=\"mt-0.5\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t\t<div data-hex-quiz-label>{option.label}</div>\n\t\t\t\t\t\t\t\t\t{submitted && option.explanation && (state === \"correct\" || state === \"incorrect\" || state === \"missed\") ? (\n\t\t\t\t\t\t\t\t\t\t<div data-hex-quiz-explanation className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{option.explanation}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<div className=\"mt-3 flex items-center gap-3\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-hex-quiz-submit\n\t\t\t\t\tdisabled={!canSubmit}\n\t\t\t\t\tonClick={handleSubmit}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground transition-opacity\",\n\t\t\t\t\t\t\"hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{submitLabel}\n\t\t\t\t</button>\n\t\t\t\t<div data-hex-quiz-status role=\"status\" aria-live=\"polite\" className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t{statusText}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport { Quiz };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/quiz/quiz.tsx"],"names":[],"mappings":"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","file":"quiz.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 * Single-question multiple-choice quiz. Renders the question + options\n * as native radio (single-select) or checkbox (multi-select) inputs;\n * after Submit, each option flips to data-state=\"correct|incorrect|missed\"\n * so consumers can theme right / wrong / unselected-but-correct\n * differently. Pure HTML; no heavy peer.\n *\n * Headless on grading: the schema honors what the consumer passes for\n * `correct`. Reset between runs by clearing the `selectedIds` you\n * forwarded as `value` (controlled) or unmounting (uncontrolled).\n *\n * @example\n * <Quiz\n * question=\"Which planets are gas giants?\"\n * selectionMode=\"multi\"\n * options={[\n * { id: \"j\", label: \"Jupiter\", correct: true },\n * { id: \"v\", label: \"Venus\" },\n * { id: \"s\", label: \"Saturn\", correct: true, explanation: \"Saturn's atmosphere is mostly hydrogen and helium.\" },\n * { id: \"m\", label: \"Mercury\" },\n * ]}\n * onAnswer={(ids, allCorrect) => track(ids, allCorrect)}\n * />\n */\nexport type QuizOption = {\n\tid: string;\n\tlabel: React.ReactNode;\n\tcorrect?: boolean;\n\texplanation?: React.ReactNode;\n};\n\nexport interface QuizProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n\t/** Question prompt (any ReactNode — supports rich content). */\n\tquestion: React.ReactNode;\n\t/** Options the learner picks from. */\n\toptions: QuizOption[];\n\t/** \"single\" — radio inputs (one selection). \"multi\" — checkboxes. Default \"single\". */\n\tselectionMode?: \"single\" | \"multi\";\n\t/** Custom Submit button label. Default \"Submit\". */\n\tsubmitLabel?: string;\n\t/** Fired with the selected option ids and a boolean indicating whether the entire selection matches the correct set. */\n\tonAnswer?: (selectedIds: string[], allCorrect: boolean) => void;\n}\n\ntype CellState = \"correct\" | \"incorrect\" | \"missed\" | \"unanswered\";\n\nfunction Quiz({\n\tquestion,\n\toptions,\n\tselectionMode = \"single\",\n\tsubmitLabel = \"Submit\",\n\tonAnswer,\n\tclassName,\n\t...rest\n}: QuizProps) {\n\tconst [selected, setSelected] = React.useState<Set<string>>(() => new Set());\n\tconst [submitted, setSubmitted] = React.useState(false);\n\tconst onAnswerRef = React.useRef(onAnswer);\n\tonAnswerRef.current = onAnswer;\n\tconst groupName = React.useId();\n\n\tconst correctIds = React.useMemo(\n\t\t() => new Set(options.filter((o) => o.correct).map((o) => o.id)),\n\t\t[options],\n\t);\n\n\tconst allCorrect = React.useMemo(() => {\n\t\tif (selected.size !== correctIds.size) return false;\n\t\tfor (const id of correctIds) if (!selected.has(id)) return false;\n\t\treturn true;\n\t}, [selected, correctIds]);\n\n\tconst stateFor = (option: QuizOption): CellState => {\n\t\tif (!submitted) return \"unanswered\";\n\t\tconst picked = selected.has(option.id);\n\t\tconst isCorrect = correctIds.has(option.id);\n\t\tif (picked && isCorrect) return \"correct\";\n\t\tif (picked && !isCorrect) return \"incorrect\";\n\t\tif (!picked && isCorrect) return \"missed\";\n\t\treturn \"unanswered\";\n\t};\n\n\tconst toggle = (id: string) => {\n\t\t// Submit is sticky: data-state always reflects the CURRENT selection\n\t\t// against the correct set, so re-picking after submit auto-updates\n\t\t// the correct/incorrect/missed indicators without clearing the\n\t\t// \"I've tried this\" affordance. Next Submit re-grades and re-fires\n\t\t// onAnswer with the updated selection.\n\t\tsetSelected((prev) => {\n\t\t\tif (selectionMode === \"single\") return new Set([id]);\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(id)) next.delete(id);\n\t\t\telse next.add(id);\n\t\t\treturn next;\n\t\t});\n\t};\n\n\tconst handleSubmit = () => {\n\t\tsetSubmitted(true);\n\t\tonAnswerRef.current?.(Array.from(selected), allCorrect);\n\t};\n\n\tconst canSubmit = selected.size > 0;\n\tconst statusText = submitted\n\t\t? allCorrect\n\t\t\t? \"Correct — well done.\"\n\t\t\t: \"Some selections are incorrect or missed. Review the highlighted options.\"\n\t\t: \"\";\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-quiz\n\t\t\tdata-submitted={submitted}\n\t\t\tdata-all-correct={submitted && allCorrect}\n\t\t\tclassName={cn(\"rounded-lg border bg-card p-4 text-card-foreground\", className)}\n\t\t>\n\t\t\t<div data-hex-quiz-question className=\"mb-3 text-base font-medium\">\n\t\t\t\t{question}\n\t\t\t</div>\n\t\t\t<div data-hex-quiz-options className=\"space-y-2\" role={selectionMode === \"single\" ? \"radiogroup\" : \"group\"}>\n\t\t\t\t{options.map((option) => {\n\t\t\t\t\tconst state = stateFor(option);\n\t\t\t\t\tconst isPicked = selected.has(option.id);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div key={option.id}>\n\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\tdata-hex-quiz-option\n\t\t\t\t\t\t\t\tdata-state={state}\n\t\t\t\t\t\t\t\tdata-picked={isPicked}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex cursor-pointer items-start gap-2 rounded-md border bg-background p-2 transition-colors\",\n\t\t\t\t\t\t\t\t\t\"hover:bg-muted/50\",\n\t\t\t\t\t\t\t\t\tstate === \"correct\" && \"border-primary bg-primary/10\",\n\t\t\t\t\t\t\t\t\tstate === \"incorrect\" && \"border-destructive bg-destructive/10\",\n\t\t\t\t\t\t\t\t\tstate === \"missed\" && \"border-primary/60 bg-primary/5 ring-1 ring-primary/40\",\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<input\n\t\t\t\t\t\t\t\t\ttype={selectionMode === \"single\" ? \"radio\" : \"checkbox\"}\n\t\t\t\t\t\t\t\t\tname={selectionMode === \"single\" ? groupName : undefined}\n\t\t\t\t\t\t\t\t\tvalue={option.id}\n\t\t\t\t\t\t\t\t\tchecked={isPicked}\n\t\t\t\t\t\t\t\t\tonChange={() => toggle(option.id)}\n\t\t\t\t\t\t\t\t\tclassName=\"mt-0.5\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t\t<div data-hex-quiz-label>{option.label}</div>\n\t\t\t\t\t\t\t\t\t{submitted && option.explanation && (state === \"correct\" || state === \"incorrect\" || state === \"missed\") ? (\n\t\t\t\t\t\t\t\t\t\t<div data-hex-quiz-explanation className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{option.explanation}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<div className=\"mt-3 flex items-center gap-3\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-hex-quiz-submit\n\t\t\t\t\tdisabled={!canSubmit}\n\t\t\t\t\tonClick={handleSubmit}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground transition-opacity\",\n\t\t\t\t\t\t\"hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t\t\t\"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{submitLabel}\n\t\t\t\t</button>\n\t\t\t\t<div data-hex-quiz-status role=\"status\" aria-live=\"polite\" className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t{statusText}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport { Quiz };\n"]}
|
package/dist/radio-group.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/primitives/radio-group/radio-group.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,UAAA,GAAmB,iBAGvB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA;AAAA,EAAqB,mBAAA,CAAA,IAAA;AAAA,EAApB;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,2GAAA;AAAA,MACA;AAAA,KACD;AAAA,IACA,GAAA;AAAA,IACC,GAAG;AAAA;AACL,CACA;AACD,UAAA,CAAW,WAAA,GAAc,YAAA;AAGzB,IAAM,cAAA,GAAuB,iBAG3B,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA;AAAA,EAAqB,mBAAA,CAAA,IAAA;AAAA,EAApB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,wDAAA;AAAA,MACA,qHAAA;AAAA,MACA,sCAAA;AAAA,MACA,qGAAA;AAAA,MACA,iDAAA;AAAA,MACA,qCAAA;AAAA,MACA;AAAA,KACD;AAAA,IACC,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAA,GAAA,CAAqB,mBAAA,CAAA,SAAA,EAApB,EAA8B,SAAA,EAAU,kCAAA,EACxC,8BAAC,KAAA,EAAA,EAAI,OAAA,EAAQ,WAAA,EAAY,IAAA,EAAK,cAAA,EAAe,SAAA,EAAU,wBAAuB,aAAA,EAAY,MAAA,EACzF,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,IAAG,IAAA,EAAK,CAAA,EAAE,IAAA,EAAK,CAAA,EAChC,CAAA,EACD;AAAA;AACD,CACA;AACD,cAAA,CAAe,WAAA,GAAc,gBAAA","file":"radio-group.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for a radio group. Pair with one or more RadioGroupItem. */\nconst RadioGroup = React.forwardRef<\n\tReact.ComponentRef<typeof RadioGroupPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>\n>(({ className, ...props }, ref) => (\n\t<RadioGroupPrimitive.Root\n\t\tclassName={cn(\n\t\t\t\"grid gap-[var(--gap-sm,0.5rem)] data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-row\",\n\t\t\tclassName,\n\t\t)}\n\t\tref={ref}\n\t\t{...props}\n\t/>\n));\nRadioGroup.displayName = \"RadioGroup\";\n\n/** A single radio option within a RadioGroup. */\nconst RadioGroupItem = React.forwardRef<\n\tReact.ComponentRef<typeof RadioGroupPrimitive.Item>,\n\tReact.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n>(({ className, ...props }, ref) => (\n\t<RadioGroupPrimitive.Item\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"aspect-square h-4 w-4 rounded-full border border-input\",\n\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out shadow-sm inset-ring-1 inset-ring-foreground/[0.06]\",\n\t\t\t\"hover:border-ring/50 hover:shadow-md\",\n\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\"disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\"data-[state=checked]:border-primary\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t<RadioGroupPrimitive.Indicator className=\"flex items-center justify-center\">\n\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className=\"h-2 w-2 text-primary\" aria-hidden=\"true\">\n\t\t\t\t<circle cx=\"12\" cy=\"12\" r=\"10\" />\n\t\t\t</svg>\n\t\t</RadioGroupPrimitive.Indicator>\n\t</RadioGroupPrimitive.Item>\n));\nRadioGroupItem.displayName = \"RadioGroupItem\";\n\nexport { RadioGroup, RadioGroupItem };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/primitives/radio-group/radio-group.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,UAAA,GAAmB,iBAGvB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA;AAAA,EAAqB,mBAAA,CAAA,IAAA;AAAA,EAApB;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,2GAAA;AAAA,MACA;AAAA,KACD;AAAA,IACA,GAAA;AAAA,IACC,GAAG;AAAA;AACL,CACA;AACD,UAAA,CAAW,WAAA,GAAc,YAAA;AAGzB,IAAM,cAAA,GAAuB,iBAG3B,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA;AAAA,EAAqB,mBAAA,CAAA,IAAA;AAAA,EAApB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,wDAAA;AAAA,MACA,qHAAA;AAAA,MACA,sCAAA;AAAA,MACA,qGAAA;AAAA,MACA,iDAAA;AAAA,MACA,qCAAA;AAAA,MACA;AAAA,KACD;AAAA,IACC,GAAG,KAAA;AAAA,IAEJ,QAAA,kBAAA,GAAA,CAAqB,mBAAA,CAAA,SAAA,EAApB,EAA8B,SAAA,EAAU,kCAAA,EACxC,8BAAC,KAAA,EAAA,EAAI,OAAA,EAAQ,WAAA,EAAY,IAAA,EAAK,cAAA,EAAe,SAAA,EAAU,wBAAuB,aAAA,EAAY,MAAA,EACzF,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,IAAG,IAAA,EAAK,CAAA,EAAE,IAAA,EAAK,CAAA,EAChC,CAAA,EACD;AAAA;AACD,CACA;AACD,cAAA,CAAe,WAAA,GAAc,gBAAA","file":"radio-group.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for a radio group. Pair with one or more RadioGroupItem. */\nconst RadioGroup = React.forwardRef<\n\tReact.ComponentRef<typeof RadioGroupPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>\n>(({ className, ...props }, ref) => (\n\t<RadioGroupPrimitive.Root\n\t\tclassName={cn(\n\t\t\t\"grid gap-[var(--gap-sm,0.5rem)] data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-row\",\n\t\t\tclassName,\n\t\t)}\n\t\tref={ref}\n\t\t{...props}\n\t/>\n));\nRadioGroup.displayName = \"RadioGroup\";\n\n/** A single radio option within a RadioGroup. */\nconst RadioGroupItem = React.forwardRef<\n\tReact.ComponentRef<typeof RadioGroupPrimitive.Item>,\n\tReact.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n>(({ className, ...props }, ref) => (\n\t<RadioGroupPrimitive.Item\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"aspect-square h-4 w-4 rounded-full border border-input\",\n\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out shadow-sm inset-ring-1 inset-ring-foreground/[0.06]\",\n\t\t\t\"hover:border-ring/50 hover:shadow-md\",\n\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\"disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\"data-[state=checked]:border-primary\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t<RadioGroupPrimitive.Indicator className=\"flex items-center justify-center\">\n\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className=\"h-2 w-2 text-primary\" aria-hidden=\"true\">\n\t\t\t\t<circle cx=\"12\" cy=\"12\" r=\"10\" />\n\t\t\t</svg>\n\t\t</RadioGroupPrimitive.Indicator>\n\t</RadioGroupPrimitive.Item>\n));\nRadioGroupItem.displayName = \"RadioGroupItem\";\n\nexport { RadioGroup, RadioGroupItem };\n"]}
|
package/dist/reasoning.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/reasoning/reasoning.tsx"],"names":[],"mappings":"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","file":"reasoning.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Collapsible \"thinking\" block. Designed for Anthropic-style reasoning\n * traces or Chain-of-Thought scratchpads — content the user can optionally\n * inspect without it dominating the response. Header shows a label and the\n * thinking duration if provided.\n *\n * Headless on content: pass any `children`. Pair with `Markdown` if the\n * reasoning is markdown-formatted.\n *\n * @example\n * <Reasoning durationMs={4200}>\n * <Markdown>{thinking}</Markdown>\n * </Reasoning>\n */\nexport interface ReasoningProps {\n\tchildren: React.ReactNode;\n\tdefaultOpen?: boolean;\n\t/** Time spent thinking, in milliseconds. Renders as \"Thought for 4.2s\". */\n\tdurationMs?: number;\n\t/** Override the default \"Thinking\" / \"Thought for X\" label. */\n\tlabel?: string;\n\tclassName?: string;\n}\n\n/**\n * Renders a collapsible thinking-trace block.\n * @param props - children + optional duration\n * @returns A Collapsible with a labelled header and content body\n */\nfunction Reasoning({\n\tchildren,\n\tdefaultOpen = false,\n\tdurationMs,\n\tlabel,\n\tclassName,\n}: ReasoningProps) {\n\tconst headerLabel =\n\t\tlabel ?? (typeof durationMs === \"number\" ? formatThoughtFor(durationMs) : \"Thinking\");\n\n\treturn (\n\t\t<CollapsiblePrimitive.Root\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tclassName={cn(\"overflow-hidden rounded-md border-l-2 border-foreground/15 bg-muted/20\", className)}\n\t\t>\n\t\t\t<CollapsiblePrimitive.Trigger\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-muted-foreground\",\n\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\"hover:text-foreground\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<SparkleGlyph />\n\t\t\t\t<span className=\"font-medium italic\">{headerLabel}</span>\n\t\t\t\t<Chevron />\n\t\t\t</CollapsiblePrimitive.Trigger>\n\t\t\t<CollapsiblePrimitive.Content className=\"overflow-hidden border-t border-foreground/[0.06] px-3 py-2 text-sm text-muted-foreground\">\n\t\t\t\t{children}\n\t\t\t</CollapsiblePrimitive.Content>\n\t\t</CollapsiblePrimitive.Root>\n\t);\n}\n\nfunction formatThoughtFor(ms: number): string {\n\tif (ms < 1000) return `Thought for ${ms}ms`;\n\tconst seconds = ms / 1000;\n\tconst formatted = seconds >= 10 ? Math.round(seconds).toString() : seconds.toFixed(1);\n\treturn `Thought for ${formatted}s`;\n}\n\nfunction SparkleGlyph() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"12\"\n\t\t\theight=\"12\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0\"\n\t\t>\n\t\t\t<path d=\"M8 1.5l1.5 4 4 1.5-4 1.5L8 12.5 6.5 8.5l-4-1.5 4-1.5L8 1.5z\" />\n\t\t</svg>\n\t);\n}\n\nfunction Chevron() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"12\"\n\t\t\theight=\"12\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"ml-auto shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-180\"\n\t\t>\n\t\t\t<path d=\"M4 6l4 4 4-4\" />\n\t\t</svg>\n\t);\n}\n\nexport { Reasoning };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/reasoning/reasoning.tsx"],"names":[],"mappings":"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","file":"reasoning.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Collapsible \"thinking\" block. Designed for Anthropic-style reasoning\n * traces or Chain-of-Thought scratchpads — content the user can optionally\n * inspect without it dominating the response. Header shows a label and the\n * thinking duration if provided.\n *\n * Headless on content: pass any `children`. Pair with `Markdown` if the\n * reasoning is markdown-formatted.\n *\n * @example\n * <Reasoning durationMs={4200}>\n * <Markdown>{thinking}</Markdown>\n * </Reasoning>\n */\nexport interface ReasoningProps {\n\tchildren: React.ReactNode;\n\tdefaultOpen?: boolean;\n\t/** Time spent thinking, in milliseconds. Renders as \"Thought for 4.2s\". */\n\tdurationMs?: number;\n\t/** Override the default \"Thinking\" / \"Thought for X\" label. */\n\tlabel?: string;\n\tclassName?: string;\n}\n\n/**\n * Renders a collapsible thinking-trace block.\n * @param props - children + optional duration\n * @returns A Collapsible with a labelled header and content body\n */\nfunction Reasoning({\n\tchildren,\n\tdefaultOpen = false,\n\tdurationMs,\n\tlabel,\n\tclassName,\n}: ReasoningProps) {\n\tconst headerLabel =\n\t\tlabel ?? (typeof durationMs === \"number\" ? formatThoughtFor(durationMs) : \"Thinking\");\n\n\treturn (\n\t\t<CollapsiblePrimitive.Root\n\t\t\tdefaultOpen={defaultOpen}\n\t\t\tclassName={cn(\"overflow-hidden rounded-md border-l-2 border-foreground/15 bg-muted/20\", className)}\n\t\t>\n\t\t\t<CollapsiblePrimitive.Trigger\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"group flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-muted-foreground\",\n\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\"hover:text-foreground\",\n\t\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<SparkleGlyph />\n\t\t\t\t<span className=\"font-medium italic\">{headerLabel}</span>\n\t\t\t\t<Chevron />\n\t\t\t</CollapsiblePrimitive.Trigger>\n\t\t\t<CollapsiblePrimitive.Content className=\"overflow-hidden border-t border-foreground/[0.06] px-3 py-2 text-sm text-muted-foreground\">\n\t\t\t\t{children}\n\t\t\t</CollapsiblePrimitive.Content>\n\t\t</CollapsiblePrimitive.Root>\n\t);\n}\n\nfunction formatThoughtFor(ms: number): string {\n\tif (ms < 1000) return `Thought for ${ms}ms`;\n\tconst seconds = ms / 1000;\n\tconst formatted = seconds >= 10 ? Math.round(seconds).toString() : seconds.toFixed(1);\n\treturn `Thought for ${formatted}s`;\n}\n\nfunction SparkleGlyph() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"12\"\n\t\t\theight=\"12\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"shrink-0\"\n\t\t>\n\t\t\t<path d=\"M8 1.5l1.5 4 4 1.5-4 1.5L8 12.5 6.5 8.5l-4-1.5 4-1.5L8 1.5z\" />\n\t\t</svg>\n\t);\n}\n\nfunction Chevron() {\n\treturn (\n\t\t<svg\n\t\t\taria-hidden\n\t\t\tviewBox=\"0 0 16 16\"\n\t\t\twidth=\"12\"\n\t\t\theight=\"12\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"1.5\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\tclassName=\"ml-auto shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-180\"\n\t\t>\n\t\t\t<path d=\"M4 6l4 4 4-4\" />\n\t\t</svg>\n\t);\n}\n\nexport { Reasoning };\n"]}
|
package/dist/resizable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/components/resizable/resizable.tsx"],"names":["ResizablePrimitiveGroup","ResizablePrimitivePanel","ResizablePrimitiveSeparator"],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACIA,SAAS,mBAAA,CAAoB;AAAA,EAC5B,SAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAmE;AAClE,EAAA,uBACC,GAAA;AAAA,IAACA,KAAA;AAAA,IAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,yDAAA;AAAA,QACA;AAAA,OACD;AAAA,MACC,GAAG;AAAA;AAAA,GACL;AAEF;AACA,mBAAA,CAAoB,WAAA,GAAc,qBAAA;AAGlC,IAAM,cAAA,GAAiBC;AAYvB,SAAS,gBAAgB,EAAE,UAAA,EAAY,SAAA,EAAW,GAAG,OAAM,EAAyB;AACnF,EAAA,uBACC,GAAA;AAAA,IAACC,SAAA;AAAA,IAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,mLAAA;AAAA,QACA,gFAAA;AAAA,QACA,qGAAA;AAAA,QACA,yEAAA;AAAA,QACA,sOAAA;AAAA,QACA,gDAAA;AAAA,QACA;AAAA,OACD;AAAA,MACC,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA,UAAA,oBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2EAAA,EACd,QAAA,kBAAA,IAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACA,KAAA,EAAM,4BAAA;AAAA,UACN,OAAA,EAAQ,WAAA;AAAA,UACR,IAAA,EAAK,cAAA;AAAA,UACL,SAAA,EAAU,aAAA;AAAA,UACV,aAAA,EAAY,MAAA;AAAA,UAEZ,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC3B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC5B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC5B,QAAA,EAAA,EAAO,EAAA,EAAG,MAAK,EAAA,EAAG,GAAA,EAAI,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC5B,QAAA,EAAA,EAAO,EAAA,EAAG,MAAK,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC7B,QAAA,EAAA,EAAO,EAAA,EAAG,MAAK,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI;AAAA;AAAA;AAAA,OAC/B,EACD;AAAA;AAAA,GAEF;AAEF;AACA,eAAA,CAAgB,WAAA,GAAc,iBAAA","file":"resizable.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 {\n\tGroup as ResizablePrimitiveGroup,\n\tPanel as ResizablePrimitivePanel,\n\tSeparator as ResizablePrimitiveSeparator,\n} from \"react-resizable-panels\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Root container for a group of resizable panels.\n * @returns A flex container that coordinates panel sizing.\n */\nfunction ResizablePanelGroup({\n\tclassName,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ResizablePrimitiveGroup>) {\n\treturn (\n\t\t<ResizablePrimitiveGroup\n\t\t\tclassName={cn(\n\t\t\t\t\"flex h-full w-full aria-[orientation=vertical]:flex-col\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\nResizablePanelGroup.displayName = \"ResizablePanelGroup\";\n\n/** A single resizable panel. Configure via defaultSize, minSize, maxSize. */\nconst ResizablePanel = ResizablePrimitivePanel;\n\ninterface ResizableHandleProps\n\textends React.ComponentPropsWithoutRef<typeof ResizablePrimitiveSeparator> {\n\t/** Show a grab-grip on the handle for affordance. */\n\twithHandle?: boolean;\n}\n\n/**\n * Draggable separator between panels. Optionally renders a grab-grip dot.\n * @returns A slim, focusable resize handle.\n */\nfunction ResizableHandle({ withHandle, className, ...props }: ResizableHandleProps) {\n\treturn (\n\t\t<ResizablePrimitiveSeparator\n\t\t\tclassName={cn(\n\t\t\t\t\"relative flex w-px items-center justify-center bg-foreground/[0.12] transition-all duration-[var(--duration-normal,200ms)] ease-out hover:bg-ring data-[separator=active]:bg-ring\",\n\t\t\t\t\"after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2\",\n\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\"aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full\",\n\t\t\t\t\"aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:-translate-y-1/2 aria-[orientation=horizontal]:after:translate-x-0\",\n\t\t\t\t\"[&[aria-orientation=horizontal]>div]:rotate-90\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{withHandle && (\n\t\t\t\t<div className=\"z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\tclassName=\"h-2.5 w-2.5\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<circle cx=\"9\" cy=\"5\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"9\" cy=\"12\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"9\" cy=\"19\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"15\" cy=\"5\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"15\" cy=\"12\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"15\" cy=\"19\" r=\"1\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</ResizablePrimitiveSeparator>\n\t);\n}\nResizableHandle.displayName = \"ResizableHandle\";\n\nexport { ResizablePanelGroup, ResizablePanel, ResizableHandle };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/components/resizable/resizable.tsx"],"names":["ResizablePrimitiveGroup","ResizablePrimitivePanel","ResizablePrimitiveSeparator"],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACIA,SAAS,mBAAA,CAAoB;AAAA,EAC5B,SAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAmE;AAClE,EAAA,uBACC,GAAA;AAAA,IAACA,KAAA;AAAA,IAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,yDAAA;AAAA,QACA;AAAA,OACD;AAAA,MACC,GAAG;AAAA;AAAA,GACL;AAEF;AACA,mBAAA,CAAoB,WAAA,GAAc,qBAAA;AAGlC,IAAM,cAAA,GAAiBC;AAYvB,SAAS,gBAAgB,EAAE,UAAA,EAAY,SAAA,EAAW,GAAG,OAAM,EAAyB;AACnF,EAAA,uBACC,GAAA;AAAA,IAACC,SAAA;AAAA,IAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACV,mLAAA;AAAA,QACA,gFAAA;AAAA,QACA,qGAAA;AAAA,QACA,yEAAA;AAAA,QACA,sOAAA;AAAA,QACA,gDAAA;AAAA,QACA;AAAA,OACD;AAAA,MACC,GAAG,KAAA;AAAA,MAEH,QAAA,EAAA,UAAA,oBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2EAAA,EACd,QAAA,kBAAA,IAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACA,KAAA,EAAM,4BAAA;AAAA,UACN,OAAA,EAAQ,WAAA;AAAA,UACR,IAAA,EAAK,cAAA;AAAA,UACL,SAAA,EAAU,aAAA;AAAA,UACV,aAAA,EAAY,MAAA;AAAA,UAEZ,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,YAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC3B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC5B,QAAA,EAAA,EAAO,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC5B,QAAA,EAAA,EAAO,EAAA,EAAG,MAAK,EAAA,EAAG,GAAA,EAAI,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC5B,QAAA,EAAA,EAAO,EAAA,EAAG,MAAK,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,CAAA;AAAA,gCAC7B,QAAA,EAAA,EAAO,EAAA,EAAG,MAAK,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI;AAAA;AAAA;AAAA,OAC/B,EACD;AAAA;AAAA,GAEF;AAEF;AACA,eAAA,CAAgB,WAAA,GAAc,iBAAA","file":"resizable.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 {\n\tGroup as ResizablePrimitiveGroup,\n\tPanel as ResizablePrimitivePanel,\n\tSeparator as ResizablePrimitiveSeparator,\n} from \"react-resizable-panels\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Root container for a group of resizable panels.\n * @returns A flex container that coordinates panel sizing.\n */\nfunction ResizablePanelGroup({\n\tclassName,\n\t...props\n}: React.ComponentPropsWithoutRef<typeof ResizablePrimitiveGroup>) {\n\treturn (\n\t\t<ResizablePrimitiveGroup\n\t\t\tclassName={cn(\n\t\t\t\t\"flex h-full w-full aria-[orientation=vertical]:flex-col\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\nResizablePanelGroup.displayName = \"ResizablePanelGroup\";\n\n/** A single resizable panel. Configure via defaultSize, minSize, maxSize. */\nconst ResizablePanel = ResizablePrimitivePanel;\n\ninterface ResizableHandleProps\n\textends React.ComponentPropsWithoutRef<typeof ResizablePrimitiveSeparator> {\n\t/** Show a grab-grip on the handle for affordance. */\n\twithHandle?: boolean;\n}\n\n/**\n * Draggable separator between panels. Optionally renders a grab-grip dot.\n * @returns A slim, focusable resize handle.\n */\nfunction ResizableHandle({ withHandle, className, ...props }: ResizableHandleProps) {\n\treturn (\n\t\t<ResizablePrimitiveSeparator\n\t\t\tclassName={cn(\n\t\t\t\t\"relative flex w-px items-center justify-center bg-foreground/[0.12] transition-all duration-[var(--duration-normal,200ms)] ease-out hover:bg-ring data-[separator=active]:bg-ring\",\n\t\t\t\t\"after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2\",\n\t\t\t\t\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\t\t\"aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full\",\n\t\t\t\t\"aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:-translate-y-1/2 aria-[orientation=horizontal]:after:translate-x-0\",\n\t\t\t\t\"[&[aria-orientation=horizontal]>div]:rotate-90\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{withHandle && (\n\t\t\t\t<div className=\"z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\tclassName=\"h-2.5 w-2.5\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<circle cx=\"9\" cy=\"5\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"9\" cy=\"12\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"9\" cy=\"19\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"15\" cy=\"5\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"15\" cy=\"12\" r=\"1\" />\n\t\t\t\t\t\t<circle cx=\"15\" cy=\"19\" r=\"1\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</ResizablePrimitiveSeparator>\n\t);\n}\nResizableHandle.displayName = \"ResizableHandle\";\n\nexport { ResizablePanelGroup, ResizablePanel, ResizableHandle };\n"]}
|
package/dist/sankey.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/sankey/sankey.tsx"],"names":[],"mappings":"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","file":"sankey.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 * Weighted-flow diagram. Nodes are arranged in horizontal columns by\n * topological depth; links between them are drawn as smooth curves whose\n * thickness encodes the flow value. Common for funnels, energy/material\n * flows, money flows, or any bipartite/multipartite \"value moving from A\n * to B\" visualization.\n *\n * Heavy peer: requires `d3-sankey` (~6 KB gzip; pulls in a small slice of\n * d3-shape too). The hex-core CLI's `add` flow prompts before installing.\n *\n * @example\n * <Sankey\n * nodes={[\n * { id: \"src-a\", label: \"Source A\" },\n * { id: \"src-b\", label: \"Source B\" },\n * { id: \"sink\", label: \"Sink\" },\n * ]}\n * links={[\n * { source: \"src-a\", target: \"sink\", value: 30 },\n * { source: \"src-b\", target: \"sink\", value: 10 },\n * ]}\n * />\n */\nexport type SankeyNode = {\n\tid: string;\n\tlabel: string;\n};\n\nexport type SankeyLink = {\n\tsource: string;\n\ttarget: string;\n\tvalue: number;\n};\n\nexport interface SankeyProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Node definitions. Every link's `source` and `target` MUST match an `id` in this array. */\n\tnodes: SankeyNode[];\n\t/** Weighted links between nodes. Values must be positive. */\n\tlinks: SankeyLink[];\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 420. */\n\theight?: number;\n\t/** How nodes within a column are aligned. Default \"justify\". */\n\tnodeAlign?: \"left\" | \"right\" | \"center\" | \"justify\";\n\t/** Pixel width of each node rectangle. Default 12. */\n\tnodeWidth?: number;\n\t/** Vertical pixel gap between nodes in the same column. Default 8. */\n\tnodePadding?: number;\n\t/** Fired when a user hovers a link (or hover ends, with `null`). */\n\tonLinkHover?: (link: SankeyLink | null) => void;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: SankeyNode) => void;\n}\n\ninterface LaidOutNode {\n\toriginal: SankeyNode;\n\tx0: number;\n\tx1: number;\n\ty0: number;\n\ty1: number;\n\t/** Index in the consumer-supplied `nodes` array — used to pick a hue\n\t * from CHART_PALETTE so adjacent columns read as distinct categories. */\n\tidx: number;\n}\n\ninterface LaidOutLink {\n\toriginal: SankeyLink;\n\td: string;\n\twidth: number;\n\t/** Index of the source node — links inherit their source's hue so the\n\t * eye can trace \"where did this volume come from\". */\n\tsourceIdx: number;\n}\n\ntype D3SankeyMod = typeof import(\"d3-sankey\");\n\nfunction Sankey({\n\tnodes,\n\tlinks,\n\twidth = 720,\n\theight = 420,\n\tnodeAlign = \"justify\",\n\tnodeWidth = 12,\n\tnodePadding = 8,\n\tonLinkHover,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: SankeyProps) {\n\tconst [d3s, setD3s] = React.useState<D3SankeyMod | null>(null);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-sankey\").then((mod) => {\n\t\t\tif (!cancelled) setD3s(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\t// Memoize the d3-sankey layout pass: it mutates clones every call (heavy\n\t// for large flows) and parents often re-render with stable nodes/links\n\t// identity. Hook order is stable since `d3s` only ever transitions\n\t// null → resolved once.\n\tconst laidOut = React.useMemo(() => {\n\t\tif (!d3s) return null;\n\t\treturn layout(d3s, nodes, links, width, height, nodeAlign, nodeWidth, nodePadding);\n\t}, [d3s, nodes, links, width, height, nodeAlign, nodeWidth, nodePadding]);\n\n\tif (!d3s || !laidOut) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-sankey-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\taria-label=\"Loading Sankey diagram\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst { nodes: laidOutNodes, links: laidOutLinks } = laidOut;\n\tconst desc = `Sankey diagram with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${links.length} link${links.length === 1 ? \"\" : \"s\"}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-sankey\n\t\t\tdata-node-align={nodeAlign}\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>Sankey diagram</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-sankey-links fill=\"none\">\n\t\t\t\t{laidOutLinks.map((l, i) => {\n\t\t\t\t\tconst interactive = Boolean(onLinkHover);\n\t\t\t\t\tconst fireHover = (link: SankeyLink | null) => onLinkHover?.(link);\n\t\t\t\t\tconst stroke = pickChartHue(l.sourceIdx);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tkey={`${l.original.source}-${l.original.target}-${i}`}\n\t\t\t\t\t\t\tdata-hex-sankey-link\n\t\t\t\t\t\t\td={l.d}\n\t\t\t\t\t\t\tstroke={stroke}\n\t\t\t\t\t\t\tstrokeOpacity={0.45}\n\t\t\t\t\t\t\tstrokeWidth={Math.max(1, l.width)}\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 ? `Flow: ${l.original.source} → ${l.original.target} (${l.original.value})` : undefined}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tcursor: interactive ? \"pointer\" : undefined,\n\t\t\t\t\t\t\t\ttransition: \"stroke-opacity 120ms ease\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonMouseEnter={interactive ? () => fireHover(l.original) : undefined}\n\t\t\t\t\t\t\tonMouseLeave={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonFocus={interactive ? () => fireHover(l.original) : undefined}\n\t\t\t\t\t\t\tonBlur={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t\t<g data-hex-sankey-nodes>\n\t\t\t\t{laidOutNodes.map((n) => {\n\t\t\t\t\tconst w = n.x1 - n.x0;\n\t\t\t\t\tconst h = n.y1 - n.y0;\n\t\t\t\t\tconst isRightSide = n.x0 > width / 2;\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(n.original);\n\t\t\t\t\tconst fill = pickChartHue(n.idx);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={n.original.id}\n\t\t\t\t\t\t\tdata-hex-sankey-node\n\t\t\t\t\t\t\ttransform={`translate(${n.x0},${n.y0})`}\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.original.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<rect width={w} height={h} fill={fill} stroke=\"hsl(var(--background))\" />\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={isRightSide ? -6 : w + 6}\n\t\t\t\t\t\t\t\ty={h / 2}\n\t\t\t\t\t\t\t\tdy=\"0.35em\"\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\ttextAnchor={isRightSide ? \"end\" : \"start\"}\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{n.original.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3s: D3SankeyMod,\n\tnodes: SankeyNode[],\n\tlinks: SankeyLink[],\n\twidth: number,\n\theight: number,\n\talign: \"left\" | \"right\" | \"center\" | \"justify\",\n\tnodeWidth: number,\n\tnodePadding: number,\n): { nodes: LaidOutNode[]; links: LaidOutLink[] } {\n\tconst alignFn =\n\t\talign === \"left\"\n\t\t\t? d3s.sankeyLeft\n\t\t\t: align === \"right\"\n\t\t\t? d3s.sankeyRight\n\t\t\t: align === \"center\"\n\t\t\t? d3s.sankeyCenter\n\t\t\t: d3s.sankeyJustify;\n\n\t// d3-sankey mutates its input — clone so consumer arrays stay pristine.\n\tconst nodesClone = nodes.map((n) => ({ ...n }));\n\tconst linksClone = links.map((l) => ({ ...l }));\n\n\ttype WorkingNode = SankeyNode & {\n\t\tindex?: number;\n\t\tx0?: number;\n\t\tx1?: number;\n\t\ty0?: number;\n\t\ty1?: number;\n\t};\n\ttype WorkingLink = SankeyLink & { width?: number };\n\n\tconst sankeyGen = d3s\n\t\t.sankey<WorkingNode, WorkingLink>()\n\t\t.nodeId((d) => d.id)\n\t\t.nodeAlign(alignFn)\n\t\t.nodeWidth(nodeWidth)\n\t\t.nodePadding(nodePadding)\n\t\t.extent([\n\t\t\t[1, 1],\n\t\t\t[width - 1, height - 1],\n\t\t]);\n\n\tconst result = sankeyGen({ nodes: nodesClone, links: linksClone });\n\tconst linkPath = d3s.sankeyLinkHorizontal<WorkingNode, WorkingLink>();\n\n\t// Carry the consumer-supplied node/link by index so future widenings of\n\t// SankeyNode / SankeyLink (color, group, metadata) round-trip into\n\t// callbacks without us having to re-cherry-pick fields here.\n\tconst idByIndex = new Map<string, number>();\n\tnodes.forEach((n, i) => idByIndex.set(n.id, i));\n\n\treturn {\n\t\tnodes: result.nodes.map((n, i) => ({\n\t\t\toriginal: { ...nodes[i] },\n\t\t\tx0: n.x0 ?? 0,\n\t\t\tx1: n.x1 ?? 0,\n\t\t\ty0: n.y0 ?? 0,\n\t\t\ty1: n.y1 ?? 0,\n\t\t\tidx: i,\n\t\t})),\n\t\tlinks: result.links.map((l, i) => {\n\t\t\t// Re-pin source/target to ids — d3-sankey replaced them with the\n\t\t\t// resolved node objects in place, but the consumer-facing shape\n\t\t\t// is `{ source: string, target: string, value: number }`.\n\t\t\tconst sourceId = typeof l.source === \"string\" ? l.source : (l.source as WorkingNode).id;\n\t\t\tconst targetId = typeof l.target === \"string\" ? l.target : (l.target as WorkingNode).id;\n\t\t\treturn {\n\t\t\t\toriginal: { ...links[i], source: sourceId, target: targetId, value: l.value },\n\t\t\t\td: linkPath(l) ?? \"\",\n\t\t\t\twidth: l.width ?? 1,\n\t\t\t\tsourceIdx: idByIndex.get(sourceId) ?? 0,\n\t\t\t};\n\t\t}),\n\t};\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 { Sankey };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/chart-palette.ts","../src/lib/utils.ts","../src/artifacts/sankey/sankey.tsx"],"names":[],"mappings":";;;;;;AAiBO,IAAM,aAAA,GAAgB;AAAA,EAC5B,qCAAA;AAAA,EACA,qCAAA;AAAA,EACA,qCAAA;AAAA,EACA,qCAAA;AAAA,EACA,qCAAA;AAAA,EACA;AACD,CAAA;AASO,SAAS,aAAa,KAAA,EAAuB;AACnD,EAAA,MAAM,QAAS,KAAA,GAAQ,aAAA,CAAc,MAAA,GAAU,aAAA,CAAc,UAAU,aAAA,CAAc,MAAA;AAErF,EAAA,OAAO,cAAc,IAAI,CAAA;AAC1B;AC7BO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACyEA,SAAS,MAAA,CAAO;AAAA,EACf,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA,GAAQ,GAAA;AAAA,EACR,MAAA,GAAS,GAAA;AAAA,EACT,SAAA,GAAY,SAAA;AAAA,EACZ,SAAA,GAAY,EAAA;AAAA,EACZ,WAAA,GAAc,CAAA;AAAA,EACd,WAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAgB;AACf,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAU,eAA6B,IAAI,CAAA;AAE7D,EAAM,gBAAU,MAAM;AACrB,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,KAAK,OAAO,WAAW,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ;AACtC,MAAA,IAAI,CAAC,SAAA,EAAW,MAAA,CAAO,GAAG,CAAA;AAAA,IAC3B,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACZ,MAAA,SAAA,GAAY,IAAA;AAAA,IACb,CAAA;AAAA,EACD,CAAA,EAAG,EAAE,CAAA;AAML,EAAA,MAAM,OAAA,GAAgB,cAAQ,MAAM;AACnC,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,OAAO,MAAA,CAAO,KAAK,KAAA,EAAO,KAAA,EAAO,OAAO,MAAA,EAAQ,SAAA,EAAW,WAAW,WAAW,CAAA;AAAA,EAClF,CAAA,EAAG,CAAC,GAAA,EAAK,KAAA,EAAO,KAAA,EAAO,OAAO,MAAA,EAAQ,SAAA,EAAW,SAAA,EAAW,WAAW,CAAC,CAAA;AAExE,EAAA,IAAI,CAAC,GAAA,IAAO,CAAC,OAAA,EAAS;AACrB,IAAA,uBACC,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACA,yBAAA,EAAuB,IAAA;AAAA,QACvB,WAAA,EAAU,MAAA;AAAA,QACV,YAAA,EAAW,wBAAA;AAAA,QACX,SAAA,EAAW,EAAA,CAAG,0BAAA,EAA4B,SAAS,CAAA;AAAA,QACnD,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA;AAAO;AAAA,KACxB;AAAA,EAEF;AAEA,EAAA,MAAM,EAAE,KAAA,EAAO,YAAA,EAAc,KAAA,EAAO,cAAa,GAAI,OAAA;AACrD,EAAA,MAAM,OAAO,CAAA,oBAAA,EAAuB,KAAA,CAAM,MAAM,CAAA,KAAA,EAAQ,KAAA,CAAM,WAAW,CAAA,GAAI,EAAA,GAAK,GAAG,CAAA,KAAA,EAAQ,MAAM,MAAM,CAAA,KAAA,EAAQ,MAAM,MAAA,KAAW,CAAA,GAAI,KAAK,GAAG,CAAA,CAAA;AAE9I,EAAA,uBACC,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAG,IAAA;AAAA,MACJ,iBAAA,EAAe,IAAA;AAAA,MACf,iBAAA,EAAiB,SAAA;AAAA,MACjB,IAAA,EAAK,KAAA;AAAA,MACL,KAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA,EAAS,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,MAC/B,SAAA,EAAW,EAAA,CAAG,OAAA,EAAS,SAAS,CAAA;AAAA,MAEhC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,WAAM,QAAA,EAAA,gBAAA,EAAc,CAAA;AAAA,wBACrB,GAAA,CAAC,UAAM,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,wBACZ,GAAA,CAAC,GAAA,EAAA,EAAE,uBAAA,EAAqB,IAAA,EAAC,IAAA,EAAK,QAC5B,QAAA,EAAA,YAAA,CAAa,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAM;AAC3B,UAAA,MAAM,WAAA,GAAc,QAAQ,WAAW,CAAA;AACvC,UAAA,MAAM,SAAA,GAAY,CAAC,IAAA,KAA4B,WAAA,GAAc,IAAI,CAAA;AACjE,UAAA,MAAM,MAAA,GAAS,YAAA,CAAa,CAAA,CAAE,SAAS,CAAA;AACvC,UAAA,uBACC,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cAEA,sBAAA,EAAoB,IAAA;AAAA,cACpB,GAAG,CAAA,CAAE,CAAA;AAAA,cACL,MAAA;AAAA,cACA,aAAA,EAAe,IAAA;AAAA,cACf,WAAA,EAAa,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAE,KAAK,CAAA;AAAA,cAChC,IAAA,EAAM,cAAc,QAAA,GAAW,MAAA;AAAA,cAC/B,QAAA,EAAU,cAAc,CAAA,GAAI,MAAA;AAAA,cAC5B,YAAA,EAAY,WAAA,GAAc,CAAA,MAAA,EAAS,CAAA,CAAE,SAAS,MAAM,CAAA,QAAA,EAAM,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,CAAA,CAAE,QAAA,CAAS,KAAK,CAAA,CAAA,CAAA,GAAM,MAAA;AAAA,cACtG,KAAA,EAAO;AAAA,gBACN,MAAA,EAAQ,cAAc,SAAA,GAAY,MAAA;AAAA,gBAClC,UAAA,EAAY;AAAA,eACb;AAAA,cACA,cAAc,WAAA,GAAc,MAAM,SAAA,CAAU,CAAA,CAAE,QAAQ,CAAA,GAAI,MAAA;AAAA,cAC1D,YAAA,EAAc,WAAA,GAAc,MAAM,SAAA,CAAU,IAAI,CAAA,GAAI,MAAA;AAAA,cACpD,SAAS,WAAA,GAAc,MAAM,SAAA,CAAU,CAAA,CAAE,QAAQ,CAAA,GAAI,MAAA;AAAA,cACrD,MAAA,EAAQ,WAAA,GAAc,MAAM,SAAA,CAAU,IAAI,CAAA,GAAI;AAAA,aAAA;AAAA,YAhBzC,CAAA,EAAG,EAAE,QAAA,CAAS,MAAM,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,CAAC,CAAA;AAAA,WAiBpD;AAAA,QAEF,CAAC,CAAA,EACF,CAAA;AAAA,4BACC,GAAA,EAAA,EAAE,uBAAA,EAAqB,MACtB,QAAA,EAAA,YAAA,CAAa,GAAA,CAAI,CAAC,CAAA,KAAM;AACxB,UAAA,MAAM,CAAA,GAAI,CAAA,CAAE,EAAA,GAAK,CAAA,CAAE,EAAA;AACnB,UAAA,MAAM,CAAA,GAAI,CAAA,CAAE,EAAA,GAAK,CAAA,CAAE,EAAA;AACnB,UAAA,MAAM,WAAA,GAAc,CAAA,CAAE,EAAA,GAAK,KAAA,GAAQ,CAAA;AACnC,UAAA,MAAM,WAAA,GAAc,QAAQ,WAAW,CAAA;AACvC,UAAA,MAAM,cAAA,GAAiB,MAAM,WAAA,GAAc,CAAA,CAAE,QAAQ,CAAA;AACrD,UAAA,MAAM,IAAA,GAAO,YAAA,CAAa,CAAA,CAAE,GAAG,CAAA;AAC/B,UAAA,uBACC,IAAA;AAAA,YAAC,GAAA;AAAA,YAAA;AAAA,cAEA,sBAAA,EAAoB,IAAA;AAAA,cACpB,WAAW,CAAA,UAAA,EAAa,CAAA,CAAE,EAAE,CAAA,CAAA,EAAI,EAAE,EAAE,CAAA,CAAA,CAAA;AAAA,cACpC,IAAA,EAAM,cAAc,QAAA,GAAW,MAAA;AAAA,cAC/B,QAAA,EAAU,cAAc,CAAA,GAAI,MAAA;AAAA,cAC5B,YAAA,EAAY,WAAA,GAAc,CAAA,CAAE,QAAA,CAAS,KAAA,GAAQ,MAAA;AAAA,cAC7C,KAAA,EAAO,WAAA,GAAc,EAAE,MAAA,EAAQ,WAAU,GAAI,MAAA;AAAA,cAC7C,OAAA,EAAS,cAAc,cAAA,GAAiB,MAAA;AAAA,cACxC,WAAW,WAAA,GAAc,CAAC,MAAM,aAAA,CAAc,CAAA,EAAG,cAAc,CAAA,GAAI,MAAA;AAAA,cAEnE,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,UAAK,KAAA,EAAO,CAAA,EAAG,QAAQ,CAAA,EAAG,IAAA,EAAY,QAAO,wBAAA,EAAyB,CAAA;AAAA,gCACvE,GAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACA,CAAA,EAAG,WAAA,GAAc,EAAA,GAAK,CAAA,GAAI,CAAA;AAAA,oBAC1B,GAAG,CAAA,GAAI,CAAA;AAAA,oBACP,EAAA,EAAG,QAAA;AAAA,oBACH,QAAA,EAAU,EAAA;AAAA,oBACV,UAAA,EAAY,GAAA;AAAA,oBACZ,IAAA,EAAK,wBAAA;AAAA,oBACL,UAAA,EAAY,cAAc,KAAA,GAAQ,OAAA;AAAA,oBAClC,KAAA,EAAO,EAAE,aAAA,EAAe,MAAA,EAAO;AAAA,oBAE9B,YAAE,QAAA,CAAS;AAAA;AAAA;AACb;AAAA,aAAA;AAAA,YAtBK,EAAE,QAAA,CAAS;AAAA,WAuBjB;AAAA,QAEF,CAAC,CAAA,EACF;AAAA;AAAA;AAAA,GACD;AAEF;AAEA,SAAS,MAAA,CACR,KACA,KAAA,EACA,KAAA,EACA,OACA,MAAA,EACA,KAAA,EACA,WACA,WAAA,EACiD;AACjD,EAAA,MAAM,OAAA,GACL,KAAA,KAAU,MAAA,GACP,GAAA,CAAI,UAAA,GACJ,KAAA,KAAU,OAAA,GACV,GAAA,CAAI,WAAA,GACJ,KAAA,KAAU,QAAA,GACV,GAAA,CAAI,eACJ,GAAA,CAAI,aAAA;AAGR,EAAA,MAAM,UAAA,GAAa,MAAM,GAAA,CAAI,CAAC,OAAO,EAAE,GAAG,GAAE,CAAE,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,GAAA,CAAI,CAAC,OAAO,EAAE,GAAG,GAAE,CAAE,CAAA;AAW9C,EAAA,MAAM,YAAY,GAAA,CAChB,MAAA,GACA,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,EAAE,EAClB,SAAA,CAAU,OAAO,EACjB,SAAA,CAAU,SAAS,EACnB,WAAA,CAAY,WAAW,EACvB,MAAA,CAAO;AAAA,IACP,CAAC,GAAG,CAAC,CAAA;AAAA,IACL,CAAC,KAAA,GAAQ,CAAA,EAAG,MAAA,GAAS,CAAC;AAAA,GACtB,CAAA;AAEF,EAAA,MAAM,SAAS,SAAA,CAAU,EAAE,OAAO,UAAA,EAAY,KAAA,EAAO,YAAY,CAAA;AACjE,EAAA,MAAM,QAAA,GAAW,IAAI,oBAAA,EAA+C;AAKpE,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoB;AAC1C,EAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM,UAAU,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAA;AAE9C,EAAA,OAAO;AAAA,IACN,OAAO,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAC,GAAG,CAAA,MAAO;AAAA,MAClC,QAAA,EAAU,EAAE,GAAG,KAAA,CAAM,CAAC,CAAA,EAAE;AAAA,MACxB,EAAA,EAAI,EAAE,EAAA,IAAM,CAAA;AAAA,MACZ,EAAA,EAAI,EAAE,EAAA,IAAM,CAAA;AAAA,MACZ,EAAA,EAAI,EAAE,EAAA,IAAM,CAAA;AAAA,MACZ,EAAA,EAAI,EAAE,EAAA,IAAM,CAAA;AAAA,MACZ,GAAA,EAAK;AAAA,KACN,CAAE,CAAA;AAAA,IACF,OAAO,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AAIjC,MAAA,MAAM,QAAA,GAAW,OAAO,CAAA,CAAE,MAAA,KAAW,WAAW,CAAA,CAAE,MAAA,GAAU,EAAE,MAAA,CAAuB,EAAA;AACrF,MAAA,MAAM,QAAA,GAAW,OAAO,CAAA,CAAE,MAAA,KAAW,WAAW,CAAA,CAAE,MAAA,GAAU,EAAE,MAAA,CAAuB,EAAA;AACrF,MAAA,OAAO;AAAA,QACN,QAAA,EAAU,EAAE,GAAG,KAAA,CAAM,CAAC,CAAA,EAAG,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM;AAAA,QAC5E,CAAA,EAAG,QAAA,CAAS,CAAC,CAAA,IAAK,EAAA;AAAA,QAClB,KAAA,EAAO,EAAE,KAAA,IAAS,CAAA;AAAA,QAClB,SAAA,EAAW,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAA,IAAK;AAAA,OACvC;AAAA,IACD,CAAC;AAAA,GACF;AACD;AAEA,SAAS,aAAA,CAAc,GAAwB,EAAA,EAAsB;AACpE,EAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACvC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,EAAA,EAAG;AAAA,EACJ;AACD","file":"sankey.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 * Weighted-flow diagram. Nodes are arranged in horizontal columns by\n * topological depth; links between them are drawn as smooth curves whose\n * thickness encodes the flow value. Common for funnels, energy/material\n * flows, money flows, or any bipartite/multipartite \"value moving from A\n * to B\" visualization.\n *\n * Heavy peer: requires `d3-sankey` (~6 KB gzip; pulls in a small slice of\n * d3-shape too). The hex-core CLI's `add` flow prompts before installing.\n *\n * @example\n * <Sankey\n * nodes={[\n * { id: \"src-a\", label: \"Source A\" },\n * { id: \"src-b\", label: \"Source B\" },\n * { id: \"sink\", label: \"Sink\" },\n * ]}\n * links={[\n * { source: \"src-a\", target: \"sink\", value: 30 },\n * { source: \"src-b\", target: \"sink\", value: 10 },\n * ]}\n * />\n */\nexport type SankeyNode = {\n\tid: string;\n\tlabel: string;\n};\n\nexport type SankeyLink = {\n\tsource: string;\n\ttarget: string;\n\tvalue: number;\n};\n\nexport interface SankeyProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Node definitions. Every link's `source` and `target` MUST match an `id` in this array. */\n\tnodes: SankeyNode[];\n\t/** Weighted links between nodes. Values must be positive. */\n\tlinks: SankeyLink[];\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 420. */\n\theight?: number;\n\t/** How nodes within a column are aligned. Default \"justify\". */\n\tnodeAlign?: \"left\" | \"right\" | \"center\" | \"justify\";\n\t/** Pixel width of each node rectangle. Default 12. */\n\tnodeWidth?: number;\n\t/** Vertical pixel gap between nodes in the same column. Default 8. */\n\tnodePadding?: number;\n\t/** Fired when a user hovers a link (or hover ends, with `null`). */\n\tonLinkHover?: (link: SankeyLink | null) => void;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: SankeyNode) => void;\n}\n\ninterface LaidOutNode {\n\toriginal: SankeyNode;\n\tx0: number;\n\tx1: number;\n\ty0: number;\n\ty1: number;\n\t/** Index in the consumer-supplied `nodes` array — used to pick a hue\n\t * from CHART_PALETTE so adjacent columns read as distinct categories. */\n\tidx: number;\n}\n\ninterface LaidOutLink {\n\toriginal: SankeyLink;\n\td: string;\n\twidth: number;\n\t/** Index of the source node — links inherit their source's hue so the\n\t * eye can trace \"where did this volume come from\". */\n\tsourceIdx: number;\n}\n\ntype D3SankeyMod = typeof import(\"d3-sankey\");\n\nfunction Sankey({\n\tnodes,\n\tlinks,\n\twidth = 720,\n\theight = 420,\n\tnodeAlign = \"justify\",\n\tnodeWidth = 12,\n\tnodePadding = 8,\n\tonLinkHover,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: SankeyProps) {\n\tconst [d3s, setD3s] = React.useState<D3SankeyMod | null>(null);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-sankey\").then((mod) => {\n\t\t\tif (!cancelled) setD3s(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\t// Memoize the d3-sankey layout pass: it mutates clones every call (heavy\n\t// for large flows) and parents often re-render with stable nodes/links\n\t// identity. Hook order is stable since `d3s` only ever transitions\n\t// null → resolved once.\n\tconst laidOut = React.useMemo(() => {\n\t\tif (!d3s) return null;\n\t\treturn layout(d3s, nodes, links, width, height, nodeAlign, nodeWidth, nodePadding);\n\t}, [d3s, nodes, links, width, height, nodeAlign, nodeWidth, nodePadding]);\n\n\tif (!d3s || !laidOut) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-sankey-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\taria-label=\"Loading Sankey diagram\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst { nodes: laidOutNodes, links: laidOutLinks } = laidOut;\n\tconst desc = `Sankey diagram with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${links.length} link${links.length === 1 ? \"\" : \"s\"}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-sankey\n\t\t\tdata-node-align={nodeAlign}\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>Sankey diagram</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-sankey-links fill=\"none\">\n\t\t\t\t{laidOutLinks.map((l, i) => {\n\t\t\t\t\tconst interactive = Boolean(onLinkHover);\n\t\t\t\t\tconst fireHover = (link: SankeyLink | null) => onLinkHover?.(link);\n\t\t\t\t\tconst stroke = pickChartHue(l.sourceIdx);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tkey={`${l.original.source}-${l.original.target}-${i}`}\n\t\t\t\t\t\t\tdata-hex-sankey-link\n\t\t\t\t\t\t\td={l.d}\n\t\t\t\t\t\t\tstroke={stroke}\n\t\t\t\t\t\t\tstrokeOpacity={0.45}\n\t\t\t\t\t\t\tstrokeWidth={Math.max(1, l.width)}\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 ? `Flow: ${l.original.source} → ${l.original.target} (${l.original.value})` : undefined}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tcursor: interactive ? \"pointer\" : undefined,\n\t\t\t\t\t\t\t\ttransition: \"stroke-opacity 120ms ease\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonMouseEnter={interactive ? () => fireHover(l.original) : undefined}\n\t\t\t\t\t\t\tonMouseLeave={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonFocus={interactive ? () => fireHover(l.original) : undefined}\n\t\t\t\t\t\t\tonBlur={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t\t<g data-hex-sankey-nodes>\n\t\t\t\t{laidOutNodes.map((n) => {\n\t\t\t\t\tconst w = n.x1 - n.x0;\n\t\t\t\t\tconst h = n.y1 - n.y0;\n\t\t\t\t\tconst isRightSide = n.x0 > width / 2;\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(n.original);\n\t\t\t\t\tconst fill = pickChartHue(n.idx);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={n.original.id}\n\t\t\t\t\t\t\tdata-hex-sankey-node\n\t\t\t\t\t\t\ttransform={`translate(${n.x0},${n.y0})`}\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.original.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<rect width={w} height={h} fill={fill} stroke=\"hsl(var(--background))\" />\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={isRightSide ? -6 : w + 6}\n\t\t\t\t\t\t\t\ty={h / 2}\n\t\t\t\t\t\t\t\tdy=\"0.35em\"\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\ttextAnchor={isRightSide ? \"end\" : \"start\"}\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{n.original.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3s: D3SankeyMod,\n\tnodes: SankeyNode[],\n\tlinks: SankeyLink[],\n\twidth: number,\n\theight: number,\n\talign: \"left\" | \"right\" | \"center\" | \"justify\",\n\tnodeWidth: number,\n\tnodePadding: number,\n): { nodes: LaidOutNode[]; links: LaidOutLink[] } {\n\tconst alignFn =\n\t\talign === \"left\"\n\t\t\t? d3s.sankeyLeft\n\t\t\t: align === \"right\"\n\t\t\t? d3s.sankeyRight\n\t\t\t: align === \"center\"\n\t\t\t? d3s.sankeyCenter\n\t\t\t: d3s.sankeyJustify;\n\n\t// d3-sankey mutates its input — clone so consumer arrays stay pristine.\n\tconst nodesClone = nodes.map((n) => ({ ...n }));\n\tconst linksClone = links.map((l) => ({ ...l }));\n\n\ttype WorkingNode = SankeyNode & {\n\t\tindex?: number;\n\t\tx0?: number;\n\t\tx1?: number;\n\t\ty0?: number;\n\t\ty1?: number;\n\t};\n\ttype WorkingLink = SankeyLink & { width?: number };\n\n\tconst sankeyGen = d3s\n\t\t.sankey<WorkingNode, WorkingLink>()\n\t\t.nodeId((d) => d.id)\n\t\t.nodeAlign(alignFn)\n\t\t.nodeWidth(nodeWidth)\n\t\t.nodePadding(nodePadding)\n\t\t.extent([\n\t\t\t[1, 1],\n\t\t\t[width - 1, height - 1],\n\t\t]);\n\n\tconst result = sankeyGen({ nodes: nodesClone, links: linksClone });\n\tconst linkPath = d3s.sankeyLinkHorizontal<WorkingNode, WorkingLink>();\n\n\t// Carry the consumer-supplied node/link by index so future widenings of\n\t// SankeyNode / SankeyLink (color, group, metadata) round-trip into\n\t// callbacks without us having to re-cherry-pick fields here.\n\tconst idByIndex = new Map<string, number>();\n\tnodes.forEach((n, i) => idByIndex.set(n.id, i));\n\n\treturn {\n\t\tnodes: result.nodes.map((n, i) => ({\n\t\t\toriginal: { ...nodes[i] },\n\t\t\tx0: n.x0 ?? 0,\n\t\t\tx1: n.x1 ?? 0,\n\t\t\ty0: n.y0 ?? 0,\n\t\t\ty1: n.y1 ?? 0,\n\t\t\tidx: i,\n\t\t})),\n\t\tlinks: result.links.map((l, i) => {\n\t\t\t// Re-pin source/target to ids — d3-sankey replaced them with the\n\t\t\t// resolved node objects in place, but the consumer-facing shape\n\t\t\t// is `{ source: string, target: string, value: number }`.\n\t\t\tconst sourceId = typeof l.source === \"string\" ? l.source : (l.source as WorkingNode).id;\n\t\t\tconst targetId = typeof l.target === \"string\" ? l.target : (l.target as WorkingNode).id;\n\t\t\treturn {\n\t\t\t\toriginal: { ...links[i], source: sourceId, target: targetId, value: l.value },\n\t\t\t\td: linkPath(l) ?? \"\",\n\t\t\t\twidth: l.width ?? 1,\n\t\t\t\tsourceIdx: idByIndex.get(sourceId) ?? 0,\n\t\t\t};\n\t\t}),\n\t};\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 { Sankey };\n"]}
|
package/dist/schemas.d.ts
CHANGED
|
@@ -77,6 +77,14 @@ export { messageActionsSchema } from './_tsup-dts-rollup.js';
|
|
|
77
77
|
export { citationSchema } from './_tsup-dts-rollup.js';
|
|
78
78
|
export { markdownSchema } from './_tsup-dts-rollup.js';
|
|
79
79
|
export { codeBlockSchema } from './_tsup-dts-rollup.js';
|
|
80
|
+
export { sourcesSchema } from './_tsup-dts-rollup.js';
|
|
81
|
+
export { inlineCitationSchema } from './_tsup-dts-rollup.js';
|
|
82
|
+
export { taskSchema } from './_tsup-dts-rollup.js';
|
|
83
|
+
export { shimmerSchema } from './_tsup-dts-rollup.js';
|
|
84
|
+
export { branchSchema } from './_tsup-dts-rollup.js';
|
|
85
|
+
export { planSchema } from './_tsup-dts-rollup.js';
|
|
86
|
+
export { conversationSchema } from './_tsup-dts-rollup.js';
|
|
87
|
+
export { chainOfThoughtSchema } from './_tsup-dts-rollup.js';
|
|
80
88
|
export { mindMapSchema } from './_tsup-dts-rollup.js';
|
|
81
89
|
export { treeMapSchema } from './_tsup-dts-rollup.js';
|
|
82
90
|
export { orgChartSchema } from './_tsup-dts-rollup.js';
|