@hex-core/components 1.8.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.ts +855 -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.d.ts +2 -0
- package/dist/auth-forgot-password.js +400 -0
- package/dist/auth-forgot-password.js.map +1 -0
- package/dist/auth-reset-password.d.ts +2 -0
- package/dist/auth-reset-password.js +323 -0
- package/dist/auth-reset-password.js.map +1 -0
- package/dist/auth-sign-in-split.d.ts +3 -0
- package/dist/auth-sign-in-split.js +443 -0
- package/dist/auth-sign-in-split.js.map +1 -0
- package/dist/auth-sign-up-card.d.ts +3 -0
- package/dist/auth-sign-up-card.js +590 -0
- package/dist/auth-sign-up-card.js.map +1 -0
- package/dist/auth-verify-email.d.ts +2 -0
- package/dist/auth-verify-email.js +339 -0
- package/dist/auth-verify-email.js.map +1 -0
- package/dist/auth-verify-otp.d.ts +2 -0
- package/dist/auth-verify-otp.js +349 -0
- package/dist/auth-verify-otp.js.map +1 -0
- 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 +40 -0
- package/dist/index.js +4839 -2813
- 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 +9 -4
package/dist/accordion.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/components/accordion/accordion.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,SAAA,GAA+B,kBAAA,CAAA;AAGrC,IAAM,aAAA,GAAsB,iBAG1B,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA,CAAoB,yBAAnB,EAAwB,GAAA,EAAU,WAAW,EAAA,CAAG,qCAAA,EAAuC,SAAS,CAAA,EAAI,GAAG,OAAO,CAC/G;AACD,aAAA,CAAc,WAAA,GAAc,eAAA;AAG5B,IAAM,gBAAA,GAAyB,KAAA,CAAA,UAAA,CAG7B,CAAC,EAAE,WAAW,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACrC,GAAA,CAAoB,kBAAA,CAAA,MAAA,EAAnB,EAA0B,WAAU,MAAA,EACpC,QAAA,kBAAA,IAAA;AAAA,EAAoB,kBAAA,CAAA,OAAA;AAAA,EAAnB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,+EAAA;AAAA,MACA,iEAAA;AAAA,MACA,iBAAA;AAAA,MACA,qCAAA;AAAA,MACA;AAAA,KACD;AAAA,IACC,GAAG,KAAA;AAAA,IAEH,QAAA,EAAA;AAAA,MAAA,QAAA;AAAA,sBACD,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACA,KAAA,EAAM,4BAAA;AAAA,UACN,KAAA,EAAM,IAAA;AAAA,UACN,MAAA,EAAO,IAAA;AAAA,UACP,OAAA,EAAQ,WAAA;AAAA,UACR,IAAA,EAAK,MAAA;AAAA,UACL,MAAA,EAAO,cAAA;AAAA,UACP,WAAA,EAAY,GAAA;AAAA,UACZ,aAAA,EAAc,OAAA;AAAA,UACd,cAAA,EAAe,OAAA;AAAA,UACf,SAAA,EAAU,+EAAA;AAAA,UACV,aAAA,EAAY,MAAA;AAAA,UAEZ,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,gBAAA,EAAiB;AAAA;AAAA;AACnC;AAAA;AACD,CAAA,EACD,CACA;AACD,gBAAA,CAAiB,WAAA,GAAc,kBAAA;AAG/B,IAAM,gBAAA,GAAyB,iBAG7B,CAAC,EAAE,WAAW,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACrC,GAAA;AAAA,EAAoB,kBAAA,CAAA,OAAA;AAAA,EAAnB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAU,0HAAA;AAAA,IACT,GAAG,KAAA;AAAA,IAEJ,8BAAC,KAAA,EAAA,EAAI,SAAA,EAAW,GAAG,+BAAA,EAAiC,SAAS,GAAI,QAAA,EAAS;AAAA;AAC3E,CACA;AACD,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"accordion.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 * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for accordion items. Supports single or multiple open items. */\nconst Accordion = AccordionPrimitive.Root;\n\n/** A single collapsible item within an Accordion. */\nconst AccordionItem = React.forwardRef<\n\tReact.ComponentRef<typeof AccordionPrimitive.Item>,\n\tReact.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>\n>(({ className, ...props }, ref) => (\n\t<AccordionPrimitive.Item ref={ref} className={cn(\"border-b border-b-foreground/[0.08]\", className)} {...props} />\n));\nAccordionItem.displayName = \"AccordionItem\";\n\n/** The clickable header that toggles an AccordionItem open/closed. */\nconst AccordionTrigger = React.forwardRef<\n\tReact.ComponentRef<typeof AccordionPrimitive.Trigger>,\n\tReact.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n\t<AccordionPrimitive.Header className=\"flex\">\n\t\t<AccordionPrimitive.Trigger\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"flex flex-1 items-center justify-between py-[var(--space-4,1rem)] font-medium\",\n\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\"hover:underline\",\n\t\t\t\t\"[&[data-state=open]>svg]:rotate-180\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t\t<svg\n\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\twidth=\"24\"\n\t\t\t\theight=\"24\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\tclassName=\"h-4 w-4 shrink-0 transition-transform duration-[var(--duration-normal,200ms)]\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t<polyline points=\"6 9 12 15 18 9\" />\n\t\t\t</svg>\n\t\t</AccordionPrimitive.Trigger>\n\t</AccordionPrimitive.Header>\n));\nAccordionTrigger.displayName = \"AccordionTrigger\";\n\n/** The collapsible content panel of an AccordionItem. */\nconst AccordionContent = React.forwardRef<\n\tReact.ComponentRef<typeof AccordionPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n\t<AccordionPrimitive.Content\n\t\tref={ref}\n\t\tclassName=\"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\"\n\t\t{...props}\n\t>\n\t\t<div className={cn(\"pb-[var(--space-4,1rem)] pt-0\", className)}>{children}</div>\n\t</AccordionPrimitive.Content>\n));\nAccordionContent.displayName = \"AccordionContent\";\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/components/accordion/accordion.tsx"],"names":[],"mappings":";;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACHA,IAAM,SAAA,GAA+B,kBAAA,CAAA;AAGrC,IAAM,aAAA,GAAsB,iBAG1B,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA,CAAoB,yBAAnB,EAAwB,GAAA,EAAU,WAAW,EAAA,CAAG,qCAAA,EAAuC,SAAS,CAAA,EAAI,GAAG,OAAO,CAC/G;AACD,aAAA,CAAc,WAAA,GAAc,eAAA;AAG5B,IAAM,gBAAA,GAAyB,KAAA,CAAA,UAAA,CAG7B,CAAC,EAAE,WAAW,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACrC,GAAA,CAAoB,kBAAA,CAAA,MAAA,EAAnB,EAA0B,WAAU,MAAA,EACpC,QAAA,kBAAA,IAAA;AAAA,EAAoB,kBAAA,CAAA,OAAA;AAAA,EAAnB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA;AAAA,MACV,+EAAA;AAAA,MACA,iEAAA;AAAA,MACA,iBAAA;AAAA,MACA,qCAAA;AAAA,MACA;AAAA,KACD;AAAA,IACC,GAAG,KAAA;AAAA,IAEH,QAAA,EAAA;AAAA,MAAA,QAAA;AAAA,sBACD,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACA,KAAA,EAAM,4BAAA;AAAA,UACN,KAAA,EAAM,IAAA;AAAA,UACN,MAAA,EAAO,IAAA;AAAA,UACP,OAAA,EAAQ,WAAA;AAAA,UACR,IAAA,EAAK,MAAA;AAAA,UACL,MAAA,EAAO,cAAA;AAAA,UACP,WAAA,EAAY,GAAA;AAAA,UACZ,aAAA,EAAc,OAAA;AAAA,UACd,cAAA,EAAe,OAAA;AAAA,UACf,SAAA,EAAU,+EAAA;AAAA,UACV,aAAA,EAAY,MAAA;AAAA,UAEZ,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAS,MAAA,EAAO,gBAAA,EAAiB;AAAA;AAAA;AACnC;AAAA;AACD,CAAA,EACD,CACA;AACD,gBAAA,CAAiB,WAAA,GAAc,kBAAA;AAG/B,IAAM,gBAAA,GAAyB,iBAG7B,CAAC,EAAE,WAAW,QAAA,EAAU,GAAG,KAAA,EAAM,EAAG,GAAA,qBACrC,GAAA;AAAA,EAAoB,kBAAA,CAAA,OAAA;AAAA,EAAnB;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAU,0HAAA;AAAA,IACT,GAAG,KAAA;AAAA,IAEJ,8BAAC,KAAA,EAAA,EAAI,SAAA,EAAW,GAAG,+BAAA,EAAiC,SAAS,GAAI,QAAA,EAAS;AAAA;AAC3E,CACA;AACD,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"accordion.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 * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for accordion items. Supports single or multiple open items. */\nconst Accordion = AccordionPrimitive.Root;\n\n/** A single collapsible item within an Accordion. */\nconst AccordionItem = React.forwardRef<\n\tReact.ComponentRef<typeof AccordionPrimitive.Item>,\n\tReact.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>\n>(({ className, ...props }, ref) => (\n\t<AccordionPrimitive.Item ref={ref} className={cn(\"border-b border-b-foreground/[0.08]\", className)} {...props} />\n));\nAccordionItem.displayName = \"AccordionItem\";\n\n/** The clickable header that toggles an AccordionItem open/closed. */\nconst AccordionTrigger = React.forwardRef<\n\tReact.ComponentRef<typeof AccordionPrimitive.Trigger>,\n\tReact.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n\t<AccordionPrimitive.Header className=\"flex\">\n\t\t<AccordionPrimitive.Trigger\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"flex flex-1 items-center justify-between py-[var(--space-4,1rem)] font-medium\",\n\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\"hover:underline\",\n\t\t\t\t\"[&[data-state=open]>svg]:rotate-180\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t\t<svg\n\t\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\t\twidth=\"24\"\n\t\t\t\theight=\"24\"\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\tclassName=\"h-4 w-4 shrink-0 transition-transform duration-[var(--duration-normal,200ms)]\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t<polyline points=\"6 9 12 15 18 9\" />\n\t\t\t</svg>\n\t\t</AccordionPrimitive.Trigger>\n\t</AccordionPrimitive.Header>\n));\nAccordionTrigger.displayName = \"AccordionTrigger\";\n\n/** The collapsible content panel of an AccordionItem. */\nconst AccordionContent = React.forwardRef<\n\tReact.ComponentRef<typeof AccordionPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n\t<AccordionPrimitive.Content\n\t\tref={ref}\n\t\tclassName=\"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\"\n\t\t{...props}\n\t>\n\t\t<div className={cn(\"pb-[var(--space-4,1rem)] pt-0\", className)}>{children}</div>\n\t</AccordionPrimitive.Content>\n));\nAccordionContent.displayName = \"AccordionContent\";\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent };\n"]}
|
package/dist/alert-dialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/components/alert-dialog/alert-dialog.tsx"],"names":[],"mappings":"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","file":"alert-dialog.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 AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for a destructive-action confirmation dialog. */\nconst AlertDialog = AlertDialogPrimitive.Root;\n\n/** The element that opens the alert dialog. */\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\n/** Portals alert dialog content into the body. */\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\n\n/** Dimmed backdrop behind the alert dialog. */\nconst AlertDialogOverlay = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Overlay>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Overlay\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm\",\n\t\t\t\"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n\t\t\t\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogOverlay.displayName = \"AlertDialogOverlay\";\n\n/** Content panel for the alert dialog. No close button — user must choose action or cancel. */\nconst AlertDialogContent = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPortal>\n\t\t<AlertDialogOverlay />\n\t\t<AlertDialogPrimitive.Content\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-[var(--gap-md,1rem)]\",\n\t\t\t\t\"border border-foreground/[0.08] bg-background p-[var(--space-6,1.5rem)] shadow-lg rounded-lg\",\n\t\t\t\t\"duration-[var(--duration-normal,200ms)] data-[state=open]:animate-in data-[state=closed]:animate-out\",\n\t\t\t\t\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n\t\t\t\t\"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t</AlertDialogPortal>\n));\nAlertDialogContent.displayName = \"AlertDialogContent\";\n\n/**\n * Header container for title + description.\n * @returns A div wrapping title/description with vertical rhythm\n */\nfunction AlertDialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"flex flex-col space-y-2 text-center sm:text-left\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/**\n * Footer container for Cancel/Action buttons.\n * @returns A div stacking buttons on mobile and right-aligning on desktop\n */\nfunction AlertDialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** Accessible title for the alert dialog. */\nconst AlertDialogTitle = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Title>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Title\n\t\tref={ref}\n\t\tclassName={cn(\"text-lg font-semibold\", className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogTitle.displayName = \"AlertDialogTitle\";\n\n/** Accessible description for the alert dialog. */\nconst AlertDialogDescription = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Description>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Description\n\t\tref={ref}\n\t\tclassName={cn(\"text-sm text-muted-foreground\", className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogDescription.displayName = \"AlertDialogDescription\";\n\n/** The destructive action button (e.g. Delete). Receives focus by default. */\nconst AlertDialogAction = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Action>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Action\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"inline-flex h-[var(--control-height-md,2.5rem)] items-center justify-center rounded-md px-[var(--space-4,1rem)] py-[var(--space-2,0.5rem)] text-sm font-medium\",\n\t\t\t\"bg-destructive text-destructive-foreground shadow-sm\",\n\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\"hover:bg-destructive/90 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:pointer-events-none disabled:opacity-50\",\n\t\t\t\"active:scale-[0.98]\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogAction.displayName = \"AlertDialogAction\";\n\n/** The cancel button. Closes the dialog. */\nconst AlertDialogCancel = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Cancel>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Cancel\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"inline-flex h-[var(--control-height-md,2.5rem)] items-center justify-center rounded-md px-[var(--space-4,1rem)] py-[var(--space-2,0.5rem)] text-sm font-medium\",\n\t\t\t\"border border-input bg-background shadow-sm inset-ring-1 inset-ring-foreground/[0.06]\",\n\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\"hover:bg-accent hover:text-accent-foreground hover:shadow-md hover:inset-ring-foreground/12\",\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\"mt-[var(--space-2,0.5rem)] sm:mt-0\",\n\t\t\t\"active:scale-[0.98]\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogCancel.displayName = \"AlertDialogCancel\";\n\nexport {\n\tAlertDialog,\n\tAlertDialogPortal,\n\tAlertDialogOverlay,\n\tAlertDialogTrigger,\n\tAlertDialogContent,\n\tAlertDialogHeader,\n\tAlertDialogFooter,\n\tAlertDialogTitle,\n\tAlertDialogDescription,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/components/alert-dialog/alert-dialog.tsx"],"names":[],"mappings":"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","file":"alert-dialog.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 AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** Root container for a destructive-action confirmation dialog. */\nconst AlertDialog = AlertDialogPrimitive.Root;\n\n/** The element that opens the alert dialog. */\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\n/** Portals alert dialog content into the body. */\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\n\n/** Dimmed backdrop behind the alert dialog. */\nconst AlertDialogOverlay = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Overlay>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Overlay\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm\",\n\t\t\t\"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n\t\t\t\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogOverlay.displayName = \"AlertDialogOverlay\";\n\n/** Content panel for the alert dialog. No close button — user must choose action or cancel. */\nconst AlertDialogContent = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPortal>\n\t\t<AlertDialogOverlay />\n\t\t<AlertDialogPrimitive.Content\n\t\t\tref={ref}\n\t\t\tclassName={cn(\n\t\t\t\t\"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-[var(--gap-md,1rem)]\",\n\t\t\t\t\"border border-foreground/[0.08] bg-background p-[var(--space-6,1.5rem)] shadow-lg rounded-lg\",\n\t\t\t\t\"duration-[var(--duration-normal,200ms)] data-[state=open]:animate-in data-[state=closed]:animate-out\",\n\t\t\t\t\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n\t\t\t\t\"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t</AlertDialogPortal>\n));\nAlertDialogContent.displayName = \"AlertDialogContent\";\n\n/**\n * Header container for title + description.\n * @returns A div wrapping title/description with vertical rhythm\n */\nfunction AlertDialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"flex flex-col space-y-2 text-center sm:text-left\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/**\n * Footer container for Cancel/Action buttons.\n * @returns A div stacking buttons on mobile and right-aligning on desktop\n */\nfunction AlertDialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\n/** Accessible title for the alert dialog. */\nconst AlertDialogTitle = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Title>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Title\n\t\tref={ref}\n\t\tclassName={cn(\"text-lg font-semibold\", className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogTitle.displayName = \"AlertDialogTitle\";\n\n/** Accessible description for the alert dialog. */\nconst AlertDialogDescription = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Description>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Description\n\t\tref={ref}\n\t\tclassName={cn(\"text-sm text-muted-foreground\", className)}\n\t\t{...props}\n\t/>\n));\nAlertDialogDescription.displayName = \"AlertDialogDescription\";\n\n/** The destructive action button (e.g. Delete). Receives focus by default. */\nconst AlertDialogAction = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Action>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Action\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"inline-flex h-[var(--control-height-md,2.5rem)] items-center justify-center rounded-md px-[var(--space-4,1rem)] py-[var(--space-2,0.5rem)] text-sm font-medium\",\n\t\t\t\"bg-destructive text-destructive-foreground shadow-sm\",\n\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\"hover:bg-destructive/90 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:pointer-events-none disabled:opacity-50\",\n\t\t\t\"active:scale-[0.98]\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogAction.displayName = \"AlertDialogAction\";\n\n/** The cancel button. Closes the dialog. */\nconst AlertDialogCancel = React.forwardRef<\n\tReact.ComponentRef<typeof AlertDialogPrimitive.Cancel>,\n\tReact.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n\t<AlertDialogPrimitive.Cancel\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"inline-flex h-[var(--control-height-md,2.5rem)] items-center justify-center rounded-md px-[var(--space-4,1rem)] py-[var(--space-2,0.5rem)] text-sm font-medium\",\n\t\t\t\"border border-input bg-background shadow-sm inset-ring-1 inset-ring-foreground/[0.06]\",\n\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\"hover:bg-accent hover:text-accent-foreground hover:shadow-md hover:inset-ring-foreground/12\",\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\"mt-[var(--space-2,0.5rem)] sm:mt-0\",\n\t\t\t\"active:scale-[0.98]\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n));\nAlertDialogCancel.displayName = \"AlertDialogCancel\";\n\nexport {\n\tAlertDialog,\n\tAlertDialogPortal,\n\tAlertDialogOverlay,\n\tAlertDialogTrigger,\n\tAlertDialogContent,\n\tAlertDialogHeader,\n\tAlertDialogFooter,\n\tAlertDialogTitle,\n\tAlertDialogDescription,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n};\n"]}
|
package/dist/alert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/components/alert/alert.tsx"],"names":[],"mappings":";;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACNA,IAAM,aAAA,GAAgB,GAAA;AAAA,EACrB;AAAA,IACC,gGAAA;AAAA,IACA,iEAAA;AAAA,IACA,+FAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,wDAAA;AAAA,QACT,WAAA,EACC;AAAA;AACF,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA;AAAU;AAExC;AAGA,IAAM,KAAA,GAAc,iBAGlB,CAAC,EAAE,WAAW,OAAA,EAAS,GAAG,KAAA,EAAM,EAAG,GAAA,qBACpC,GAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACA,GAAA;AAAA,IACA,IAAA,EAAK,OAAA;AAAA,IACL,WAAW,EAAA,CAAG,aAAA,CAAc,EAAE,OAAA,EAAS,GAAG,SAAS,CAAA;AAAA,IAClD,GAAG;AAAA;AACL,CACA;AACD,KAAA,CAAM,WAAA,GAAc,OAAA;AAGpB,IAAM,UAAA,GAAmB,iBAGvB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA;AAAA,EAAC,IAAA;AAAA,EAAA;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA,CAAG,qEAAA,EAAuE,SAAS,CAAA;AAAA,IAC7F,GAAG;AAAA;AACL,CACA;AACD,UAAA,CAAW,WAAA,GAAc,YAAA;AAGzB,IAAM,gBAAA,GAAyB,KAAA,CAAA,UAAA;AAAA,EAC9B,CAAC,EAAE,SAAA,EAAW,GAAG,KAAA,IAAS,GAAA,qBACzB,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAU,WAAW,EAAA,CAAG,+BAAA,EAAiC,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO;AAEvF;AACA,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"alert.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","import { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst alertVariants = cva(\n\t[\n\t\t\"relative w-full rounded-lg border px-[var(--space-4,1rem)] py-[var(--space-3,0.75rem)] text-sm\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\"[&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:text-foreground\",\n\t\t\"[&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px]\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"border-foreground/[0.08] bg-background text-foreground\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"border-destructive/50 text-destructive [&>svg]:text-destructive bg-destructive/5\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\" },\n\t},\n);\n\n/** An inline notification banner for important messages. */\nconst Alert = React.forwardRef<\n\tHTMLDivElement,\n\tReact.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n>(({ className, variant, ...props }, ref) => (\n\t<div\n\t\tref={ref}\n\t\trole=\"alert\"\n\t\tclassName={cn(alertVariants({ variant }), className)}\n\t\t{...props}\n\t/>\n));\nAlert.displayName = \"Alert\";\n\n/** The alert title heading. */\nconst AlertTitle = React.forwardRef<\n\tHTMLHeadingElement,\n\tReact.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n\t<h5\n\t\tref={ref}\n\t\tclassName={cn(\"mb-[var(--space-1,0.25rem)] font-medium leading-none tracking-tight\", className)}\n\t\t{...props}\n\t/>\n));\nAlertTitle.displayName = \"AlertTitle\";\n\n/** The alert description. Renders a div so paragraph children can be styled via [&_p]. */\nconst AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n\t({ className, ...props }, ref) => (\n\t\t<div ref={ref} className={cn(\"text-sm [&_p]:leading-relaxed\", className)} {...props} />\n\t),\n);\nAlertDescription.displayName = \"AlertDescription\";\n\nexport { Alert, AlertTitle, AlertDescription, alertVariants };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/components/alert/alert.tsx"],"names":[],"mappings":";;;;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;ACNA,IAAM,aAAA,GAAgB,GAAA;AAAA,EACrB;AAAA,IACC,gGAAA;AAAA,IACA,iEAAA;AAAA,IACA,+FAAA;AAAA,IACA;AAAA,GACD,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACC,QAAA,EAAU;AAAA,MACT,OAAA,EAAS;AAAA,QACR,OAAA,EAAS,wDAAA;AAAA,QACT,WAAA,EACC;AAAA;AACF,KACD;AAAA,IACA,eAAA,EAAiB,EAAE,OAAA,EAAS,SAAA;AAAU;AAExC;AAGA,IAAM,KAAA,GAAc,iBAGlB,CAAC,EAAE,WAAW,OAAA,EAAS,GAAG,KAAA,EAAM,EAAG,GAAA,qBACpC,GAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACA,GAAA;AAAA,IACA,IAAA,EAAK,OAAA;AAAA,IACL,WAAW,EAAA,CAAG,aAAA,CAAc,EAAE,OAAA,EAAS,GAAG,SAAS,CAAA;AAAA,IAClD,GAAG;AAAA;AACL,CACA;AACD,KAAA,CAAM,WAAA,GAAc,OAAA;AAGpB,IAAM,UAAA,GAAmB,iBAGvB,CAAC,EAAE,WAAW,GAAG,KAAA,IAAS,GAAA,qBAC3B,GAAA;AAAA,EAAC,IAAA;AAAA,EAAA;AAAA,IACA,GAAA;AAAA,IACA,SAAA,EAAW,EAAA,CAAG,qEAAA,EAAuE,SAAS,CAAA;AAAA,IAC7F,GAAG;AAAA;AACL,CACA;AACD,UAAA,CAAW,WAAA,GAAc,YAAA;AAGzB,IAAM,gBAAA,GAAyB,KAAA,CAAA,UAAA;AAAA,EAC9B,CAAC,EAAE,SAAA,EAAW,GAAG,KAAA,IAAS,GAAA,qBACzB,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAU,WAAW,EAAA,CAAG,+BAAA,EAAiC,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO;AAEvF;AACA,gBAAA,CAAiB,WAAA,GAAc,kBAAA","file":"alert.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\nconst SAFE_URL_SCHEMES = [\"http:\", \"https:\", \"mailto:\"] as const;\n\n/**\n * Allowlist a URL for use as an `<a href>` against untrusted input.\n *\n * Returns the raw string when it's `http(s):` / `mailto:` / a relative\n * URL (no scheme); returns `undefined` otherwise. Defends against\n * `javascript:` / `data:` / `vbscript:` injection from streamed model\n * output or third-party JSON payloads that flow into citation chips.\n *\n * Inside a Markdown render path, `rehype-sanitize` already strips\n * `javascript:` from inline-link hrefs — but it does NOT introspect\n * JSON nested inside attribute values (e.g. `<sources data='[…]'/>`),\n * so the components that consume that data must guard themselves.\n *\n * @param raw - The candidate URL, or `undefined` when none was supplied.\n * @returns The URL when safe to render, otherwise `undefined`.\n */\nexport function safeUrl(raw: string | undefined): string | undefined {\n\tif (raw === undefined || raw === \"\") return undefined;\n\t// Relative URLs have no scheme — `new URL(relative)` throws without a base,\n\t// so a thrown URL means either malformed input OR a relative path. Treat\n\t// \"throws + does not contain a colon\" as a relative path (safe).\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn raw.includes(\":\") ? undefined : raw;\n\t}\n\treturn SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : undefined;\n}\n","import { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst alertVariants = cva(\n\t[\n\t\t\"relative w-full rounded-lg border px-[var(--space-4,1rem)] py-[var(--space-3,0.75rem)] text-sm\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\"[&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:text-foreground\",\n\t\t\"[&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px]\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"border-foreground/[0.08] bg-background text-foreground\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"border-destructive/50 text-destructive [&>svg]:text-destructive bg-destructive/5\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"default\" },\n\t},\n);\n\n/** An inline notification banner for important messages. */\nconst Alert = React.forwardRef<\n\tHTMLDivElement,\n\tReact.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n>(({ className, variant, ...props }, ref) => (\n\t<div\n\t\tref={ref}\n\t\trole=\"alert\"\n\t\tclassName={cn(alertVariants({ variant }), className)}\n\t\t{...props}\n\t/>\n));\nAlert.displayName = \"Alert\";\n\n/** The alert title heading. */\nconst AlertTitle = React.forwardRef<\n\tHTMLHeadingElement,\n\tReact.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n\t<h5\n\t\tref={ref}\n\t\tclassName={cn(\"mb-[var(--space-1,0.25rem)] font-medium leading-none tracking-tight\", className)}\n\t\t{...props}\n\t/>\n));\nAlertTitle.displayName = \"AlertTitle\";\n\n/** The alert description. Renders a div so paragraph children can be styled via [&_p]. */\nconst AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n\t({ className, ...props }, ref) => (\n\t\t<div ref={ref} className={cn(\"text-sm [&_p]:leading-relaxed\", className)} {...props} />\n\t),\n);\nAlertDescription.displayName = \"AlertDescription\";\n\nexport { Alert, AlertTitle, AlertDescription, alertVariants };\n"]}
|
package/dist/arc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/arc/arc.tsx"],"names":[],"mappings":"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","file":"arc.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 * Arc diagram. Nodes lie on a horizontal baseline; relationships are\n * drawn as semicircle arcs above the line. Pure SVG; no heavy peer\n * dependency. Excellent for sequence-aware relational data:\n * co-occurrence in a story, train-stop transfer connections,\n * citation chains where node order is meaningful.\n *\n * Distinct from Chord: Chord wraps nodes around a ring (no inherent\n * order); Arc keeps them on a line (order matters).\n *\n * @example\n * <Arc\n * nodes={[\n * { id: \"alice\", label: \"Alice\" },\n * { id: \"bob\", label: \"Bob\" },\n * { id: \"carol\", label: \"Carol\" },\n * ]}\n * edges={[\n * { source: \"alice\", target: \"bob\", value: 3 },\n * { source: \"alice\", target: \"carol\", value: 1 },\n * ]}\n * />\n */\nexport type ArcNode = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n};\n\nexport type ArcEdge = {\n\tsource: string;\n\ttarget: string;\n\tvalue?: number;\n};\n\nexport interface ArcProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Nodes in display order along the baseline. */\n\tnodes: ArcNode[];\n\t/** Edges between nodes. Edges whose source or target id is missing are skipped. */\n\tedges: ArcEdge[];\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 360. */\n\theight?: number;\n\t/** Pixel radius of each node circle. Default 5. */\n\tnodeRadius?: number;\n\t/** Fired when an edge is hovered (or hover ends, with `null`). */\n\tonEdgeHover?: (edge: ArcEdge | null) => void;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: ArcNode) => void;\n}\n\ninterface LaidOutNode {\n\tnode: ArcNode;\n\tx: number;\n\ty: number;\n\tdepth: number;\n}\n\ninterface LaidOutEdge {\n\tedge: ArcEdge;\n\td: string;\n\twidth: number;\n}\n\nfunction Arc({\n\tnodes,\n\tedges,\n\twidth = 720,\n\theight = 360,\n\tnodeRadius = 5,\n\tonEdgeHover,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: ArcProps) {\n\tconst laidOut = React.useMemo(() => layout(nodes, edges, width, height), [nodes, edges, width, height]);\n\tconst desc = `Arc diagram with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${edges.length} edge${edges.length === 1 ? \"\" : \"s\"}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-arc\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>Arc diagram</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-arc-edges fill=\"none\">\n\t\t\t\t{laidOut.edges.map((e, i) => {\n\t\t\t\t\tconst interactive = Boolean(onEdgeHover);\n\t\t\t\t\tconst fireHover = (edge: ArcEdge | null) => onEdgeHover?.(edge);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tkey={`${e.edge.source}-${e.edge.target}-${i}`}\n\t\t\t\t\t\t\tdata-hex-arc-edge\n\t\t\t\t\t\t\td={e.d}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\tstrokeOpacity={0.5}\n\t\t\t\t\t\t\tstrokeWidth={Math.max(1, e.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={\n\t\t\t\t\t\t\t\tinteractive\n\t\t\t\t\t\t\t\t\t? `Edge between ${e.edge.source} and ${e.edge.target}${e.edge.value != null ? `, value ${e.edge.value}` : \"\"}`\n\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t}\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(e.edge) : undefined}\n\t\t\t\t\t\t\tonMouseLeave={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonFocus={interactive ? () => fireHover(e.edge) : 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-arc-nodes>\n\t\t\t\t{laidOut.nodes.map((n) => {\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(n.node);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={n.node.id}\n\t\t\t\t\t\t\tdata-hex-arc-node\n\t\t\t\t\t\t\tdata-depth={n.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 ? n.node.label : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={interactive ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={n.x}\n\t\t\t\t\t\t\t\tcy={n.y}\n\t\t\t\t\t\t\t\tr={nodeRadius}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={n.x}\n\t\t\t\t\t\t\t\ty={n.y + nodeRadius + 14}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{n.node.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\tnodes: ArcNode[],\n\tedges: ArcEdge[],\n\twidth: number,\n\theight: number,\n): { nodes: LaidOutNode[]; edges: LaidOutEdge[] } {\n\tif (nodes.length === 0) return { nodes: [], edges: [] };\n\n\tconst margin = 32;\n\tconst baselineY = height - 32;\n\tconst usable = width - margin * 2;\n\tconst step = nodes.length > 1 ? usable / (nodes.length - 1) : 0;\n\n\tconst positions = new Map<string, { x: number; y: number; index: number }>();\n\tconst laidOutNodes: LaidOutNode[] = nodes.map((node, i) => {\n\t\t// Center the lone node when the input has only one — `step * 0` would\n\t\t// otherwise leave it pinned at the left margin.\n\t\tconst x = nodes.length === 1 ? width / 2 : margin + step * i;\n\t\tpositions.set(node.id, { x, y: baselineY, index: i });\n\t\treturn { node, x, y: baselineY, depth: i };\n\t});\n\n\tconst maxValue = edges.reduce((m, e) => Math.max(m, e.value ?? 1), 1);\n\n\tconst laidOutEdges: LaidOutEdge[] = edges\n\t\t.map((edge) => {\n\t\t\tconst sp = positions.get(edge.source);\n\t\t\tconst tp = positions.get(edge.target);\n\t\t\tif (!sp || !tp) return null;\n\t\t\tconst [a, b] = sp.x < tp.x ? [sp, tp] : [tp, sp];\n\t\t\tconst span = (b.x - a.x) / 2;\n\t\t\t// Cubic bezier with two control points to approximate a clean\n\t\t\t// semicircle without trigonometry. Height scales with span.\n\t\t\tconst ctrlY = baselineY - span;\n\t\t\tconst d = `M${a.x},${baselineY} C${a.x},${ctrlY} ${b.x},${ctrlY} ${b.x},${baselineY}`;\n\t\t\treturn {\n\t\t\t\tedge,\n\t\t\t\td,\n\t\t\t\twidth: 1 + ((edge.value ?? 1) / maxValue) * 3,\n\t\t\t};\n\t\t})\n\t\t.filter((e): e is LaidOutEdge => e !== null);\n\n\treturn { nodes: laidOutNodes, edges: laidOutEdges };\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 { Arc };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/arc/arc.tsx"],"names":[],"mappings":"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","file":"arc.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 * Arc diagram. Nodes lie on a horizontal baseline; relationships are\n * drawn as semicircle arcs above the line. Pure SVG; no heavy peer\n * dependency. Excellent for sequence-aware relational data:\n * co-occurrence in a story, train-stop transfer connections,\n * citation chains where node order is meaningful.\n *\n * Distinct from Chord: Chord wraps nodes around a ring (no inherent\n * order); Arc keeps them on a line (order matters).\n *\n * @example\n * <Arc\n * nodes={[\n * { id: \"alice\", label: \"Alice\" },\n * { id: \"bob\", label: \"Bob\" },\n * { id: \"carol\", label: \"Carol\" },\n * ]}\n * edges={[\n * { source: \"alice\", target: \"bob\", value: 3 },\n * { source: \"alice\", target: \"carol\", value: 1 },\n * ]}\n * />\n */\nexport type ArcNode = {\n\tid: string;\n\tlabel: string;\n\tvalue?: number;\n};\n\nexport type ArcEdge = {\n\tsource: string;\n\ttarget: string;\n\tvalue?: number;\n};\n\nexport interface ArcProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Nodes in display order along the baseline. */\n\tnodes: ArcNode[];\n\t/** Edges between nodes. Edges whose source or target id is missing are skipped. */\n\tedges: ArcEdge[];\n\t/** Pixel width of the rendered SVG. Default 720. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 360. */\n\theight?: number;\n\t/** Pixel radius of each node circle. Default 5. */\n\tnodeRadius?: number;\n\t/** Fired when an edge is hovered (or hover ends, with `null`). */\n\tonEdgeHover?: (edge: ArcEdge | null) => void;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: ArcNode) => void;\n}\n\ninterface LaidOutNode {\n\tnode: ArcNode;\n\tx: number;\n\ty: number;\n\tdepth: number;\n}\n\ninterface LaidOutEdge {\n\tedge: ArcEdge;\n\td: string;\n\twidth: number;\n}\n\nfunction Arc({\n\tnodes,\n\tedges,\n\twidth = 720,\n\theight = 360,\n\tnodeRadius = 5,\n\tonEdgeHover,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: ArcProps) {\n\tconst laidOut = React.useMemo(() => layout(nodes, edges, width, height), [nodes, edges, width, height]);\n\tconst desc = `Arc diagram with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} and ${edges.length} edge${edges.length === 1 ? \"\" : \"s\"}`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-arc\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>Arc diagram</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-arc-edges fill=\"none\">\n\t\t\t\t{laidOut.edges.map((e, i) => {\n\t\t\t\t\tconst interactive = Boolean(onEdgeHover);\n\t\t\t\t\tconst fireHover = (edge: ArcEdge | null) => onEdgeHover?.(edge);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tkey={`${e.edge.source}-${e.edge.target}-${i}`}\n\t\t\t\t\t\t\tdata-hex-arc-edge\n\t\t\t\t\t\t\td={e.d}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\tstrokeOpacity={0.5}\n\t\t\t\t\t\t\tstrokeWidth={Math.max(1, e.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={\n\t\t\t\t\t\t\t\tinteractive\n\t\t\t\t\t\t\t\t\t? `Edge between ${e.edge.source} and ${e.edge.target}${e.edge.value != null ? `, value ${e.edge.value}` : \"\"}`\n\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t}\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(e.edge) : undefined}\n\t\t\t\t\t\t\tonMouseLeave={interactive ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonFocus={interactive ? () => fireHover(e.edge) : 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-arc-nodes>\n\t\t\t\t{laidOut.nodes.map((n) => {\n\t\t\t\t\tconst interactive = Boolean(onNodeClick);\n\t\t\t\t\tconst handleActivate = () => onNodeClick?.(n.node);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={n.node.id}\n\t\t\t\t\t\t\tdata-hex-arc-node\n\t\t\t\t\t\t\tdata-depth={n.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 ? n.node.label : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonClick={interactive ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={interactive ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={n.x}\n\t\t\t\t\t\t\t\tcy={n.y}\n\t\t\t\t\t\t\t\tr={nodeRadius}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={n.x}\n\t\t\t\t\t\t\t\ty={n.y + nodeRadius + 14}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tstyle={{ pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{n.node.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\tnodes: ArcNode[],\n\tedges: ArcEdge[],\n\twidth: number,\n\theight: number,\n): { nodes: LaidOutNode[]; edges: LaidOutEdge[] } {\n\tif (nodes.length === 0) return { nodes: [], edges: [] };\n\n\tconst margin = 32;\n\tconst baselineY = height - 32;\n\tconst usable = width - margin * 2;\n\tconst step = nodes.length > 1 ? usable / (nodes.length - 1) : 0;\n\n\tconst positions = new Map<string, { x: number; y: number; index: number }>();\n\tconst laidOutNodes: LaidOutNode[] = nodes.map((node, i) => {\n\t\t// Center the lone node when the input has only one — `step * 0` would\n\t\t// otherwise leave it pinned at the left margin.\n\t\tconst x = nodes.length === 1 ? width / 2 : margin + step * i;\n\t\tpositions.set(node.id, { x, y: baselineY, index: i });\n\t\treturn { node, x, y: baselineY, depth: i };\n\t});\n\n\tconst maxValue = edges.reduce((m, e) => Math.max(m, e.value ?? 1), 1);\n\n\tconst laidOutEdges: LaidOutEdge[] = edges\n\t\t.map((edge) => {\n\t\t\tconst sp = positions.get(edge.source);\n\t\t\tconst tp = positions.get(edge.target);\n\t\t\tif (!sp || !tp) return null;\n\t\t\tconst [a, b] = sp.x < tp.x ? [sp, tp] : [tp, sp];\n\t\t\tconst span = (b.x - a.x) / 2;\n\t\t\t// Cubic bezier with two control points to approximate a clean\n\t\t\t// semicircle without trigonometry. Height scales with span.\n\t\t\tconst ctrlY = baselineY - span;\n\t\t\tconst d = `M${a.x},${baselineY} C${a.x},${ctrlY} ${b.x},${ctrlY} ${b.x},${baselineY}`;\n\t\t\treturn {\n\t\t\t\tedge,\n\t\t\t\td,\n\t\t\t\twidth: 1 + ((edge.value ?? 1) / maxValue) * 3,\n\t\t\t};\n\t\t})\n\t\t.filter((e): e is LaidOutEdge => e !== null);\n\n\treturn { nodes: laidOutNodes, edges: laidOutEdges };\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 { Arc };\n"]}
|
package/dist/attachment.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/attachment/attachment.tsx"],"names":[],"mappings":"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","file":"attachment.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 { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** A file-shaped attachment metadata object accepted by `<Attachment>`. */\nexport interface AttachmentFile {\n\t/** Display name. */\n\tname: string;\n\t/** Bytes. Used to render a human-readable size. */\n\tsize: number;\n\t/** MIME type. Drives the auto-detected `variant`. */\n\ttype: string;\n\t/**\n\t * Optional preview URL for images. When `type` starts with `image/`\n\t * AND `preview` is set, the attachment renders a thumbnail; otherwise\n\t * it falls back to a typed file icon + name + size.\n\t */\n\tpreview?: string;\n}\n\nconst attachmentVariants = cva(\n\t[\n\t\t\"group/attachment relative inline-flex items-center gap-[var(--gap-sm,0.5rem)] rounded-md border border-border bg-card\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tfile: \"px-[var(--space-3,0.75rem)] py-[var(--space-2,0.5rem)] max-w-xs\",\n\t\t\t\timage: \"p-0 overflow-hidden\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"file\" },\n\t},\n);\n\nexport interface AttachmentProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"onProgress\">,\n\t\tVariantProps<typeof attachmentVariants> {\n\t/** Forwarded ref onto the root element. */\n\tref?: React.Ref<HTMLDivElement>;\n\t/** Native `File` OR a metadata object. */\n\tfile: File | AttachmentFile;\n\t/** Click handler for the remove button. When provided, a × button overlays the attachment. */\n\tonRemove?: () => void;\n\t/**\n\t * Upload progress in `[0, 1)`. Values `>= 1` (or `undefined`) hide the\n\t * progressbar — pass `undefined` once the upload completes. Internally\n\t * scaled to `aria-valuenow` on a 0–100 progressbar so AT engines\n\t * announce \"42 percent\" rather than \"0.42 of 1.\"\n\t */\n\tprogress?: number;\n}\n\n/**\n * Format a byte count as a human-readable size (`\"24.3 KB\"`, `\"1.2 MB\"`).\n *\n * @param bytes - Raw byte count.\n * @returns A short human-readable string.\n */\nfunction formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n\tif (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n}\n\n/**\n * Decide whether to render the image variant (thumbnail) vs the file\n * variant (icon + name + size). Images need both an image MIME type AND\n * a `preview` URL — without preview we can't actually show the image, so\n * we fall back to the file variant.\n *\n * @param file - The attachment input.\n * @returns `\"image\"` when both conditions hold, else `\"file\"`.\n */\nfunction detectVariant(file: File | AttachmentFile): \"image\" | \"file\" {\n\tconst preview = \"preview\" in file ? file.preview : undefined;\n\tconst isImage = file.type.startsWith(\"image/\");\n\treturn isImage && preview ? \"image\" : \"file\";\n}\n\n/**\n * Resolve the preview URL for an attachment input. Native `File`s don't\n * carry a preview; consumers create one via `URL.createObjectURL(file)`\n * before handing the file in.\n *\n * @param file - The attachment input.\n * @returns The preview URL, or undefined.\n */\nfunction resolvePreview(file: File | AttachmentFile): string | undefined {\n\treturn \"preview\" in file ? file.preview : undefined;\n}\n\n/**\n * A file / image attachment thumbnail with an optional remove affordance\n * and optional upload-progress overlay. Composes with the existing\n * `Composer` AI primitive (drop into Composer's children slot to render\n * as part of the message draft).\n *\n * Auto-detects the visual variant: image MIME + `preview` URL → image\n * thumbnail; everything else → typed file icon with name + size.\n *\n * Distinct from {@link Dropzone} (the upload-input affordance) and\n * static {@link Card} previews (no remove/progress UI).\n *\n * @example\n * ```tsx\n * <Attachment\n * file={{ name: \"screenshot.png\", size: 124000, type: \"image/png\", preview: objectUrl }}\n * onRemove={() => removeFromDraft(\"screenshot.png\")}\n * />\n * ```\n */\nfunction Attachment({\n\tclassName,\n\tvariant,\n\tfile,\n\tonRemove,\n\tprogress,\n\tref,\n\t...props\n}: AttachmentProps) {\n\tconst preview = resolvePreview(file);\n\t// M3: when caller forces variant=\"image\" but no preview is available,\n\t// fall back to \"file\" so the wrapper padding matches the rendered\n\t// content (file icon + name + size needs px/py; image uses zero-pad).\n\tconst detected = detectVariant(file);\n\tconst resolvedVariant: \"image\" | \"file\" =\n\t\tvariant === \"image\" && !preview ? \"file\" : (variant ?? detected);\n\tconst showProgress = typeof progress === \"number\" && progress >= 0 && progress < 1;\n\t// M5: scale to 0–100 so AT announces \"42 percent\" not \"0.42 of 1.\"\n\tconst progressPercent = showProgress ? Math.round(progress * 100) : 0;\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={cn(attachmentVariants({ variant: resolvedVariant }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{resolvedVariant === \"image\" && preview ? (\n\t\t\t\t// Intentional plain <img> (not next/image) — Attachment is\n\t\t\t\t// framework-agnostic. Consumers can swap in next/image at the\n\t\t\t\t// callsite when they want optimization.\n\t\t\t\t<img\n\t\t\t\t\tsrc={preview}\n\t\t\t\t\talt={file.name}\n\t\t\t\t\tclassName=\"block h-20 w-20 object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<FileIcon className=\"h-5 w-5 shrink-0 text-muted-foreground\" />\n\t\t\t\t\t<div className=\"flex min-w-0 flex-col\">\n\t\t\t\t\t\t<span className=\"truncate text-sm font-medium text-foreground\">{file.name}</span>\n\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{formatSize(file.size)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{showProgress ? (\n\t\t\t\t<div\n\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\taria-valuemin={0}\n\t\t\t\t\taria-valuemax={100}\n\t\t\t\t\taria-valuenow={progressPercent}\n\t\t\t\t\taria-label={`Uploading ${file.name}`}\n\t\t\t\t\tclassName=\"absolute inset-x-0 bottom-0 h-1 overflow-hidden rounded-b-md bg-muted\"\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"h-full bg-primary transition-[width] duration-[var(--duration-normal,200ms)] ease-out\"\n\t\t\t\t\t\tstyle={{ width: `${progressPercent}%` }}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t) : null}\n\n\t\t\t{onRemove ? (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\taria-label={`Remove ${file.name}`}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"absolute -right-2 -top-2 inline-flex h-5 w-5 items-center justify-center rounded-full\",\n\t\t\t\t\t\t\"bg-card border border-border text-foreground shadow-sm\",\n\t\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\t\"hover:bg-accent hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\t\t\t\t\t\"active:scale-90\",\n\t\t\t\t\t)}\n\t\t\t\t>\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=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstrokeWidth=\"2.5\"\n\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\tclassName=\"size-3\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n\t\t\t\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</button>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n\n/** Generic file icon (page-with-fold). */\nfunction FileIcon(props: React.SVGAttributes<SVGElement>) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n\t\t\t<polyline points=\"14 2 14 8 20 8\" />\n\t\t</svg>\n\t);\n}\n\nexport { Attachment, attachmentVariants };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/attachment/attachment.tsx"],"names":[],"mappings":"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","file":"attachment.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 { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** A file-shaped attachment metadata object accepted by `<Attachment>`. */\nexport interface AttachmentFile {\n\t/** Display name. */\n\tname: string;\n\t/** Bytes. Used to render a human-readable size. */\n\tsize: number;\n\t/** MIME type. Drives the auto-detected `variant`. */\n\ttype: string;\n\t/**\n\t * Optional preview URL for images. When `type` starts with `image/`\n\t * AND `preview` is set, the attachment renders a thumbnail; otherwise\n\t * it falls back to a typed file icon + name + size.\n\t */\n\tpreview?: string;\n}\n\nconst attachmentVariants = cva(\n\t[\n\t\t\"group/attachment relative inline-flex items-center gap-[var(--gap-sm,0.5rem)] rounded-md border border-border bg-card\",\n\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t].join(\" \"),\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tfile: \"px-[var(--space-3,0.75rem)] py-[var(--space-2,0.5rem)] max-w-xs\",\n\t\t\t\timage: \"p-0 overflow-hidden\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: { variant: \"file\" },\n\t},\n);\n\nexport interface AttachmentProps\n\textends Omit<React.HTMLAttributes<HTMLDivElement>, \"onProgress\">,\n\t\tVariantProps<typeof attachmentVariants> {\n\t/** Forwarded ref onto the root element. */\n\tref?: React.Ref<HTMLDivElement>;\n\t/** Native `File` OR a metadata object. */\n\tfile: File | AttachmentFile;\n\t/** Click handler for the remove button. When provided, a × button overlays the attachment. */\n\tonRemove?: () => void;\n\t/**\n\t * Upload progress in `[0, 1)`. Values `>= 1` (or `undefined`) hide the\n\t * progressbar — pass `undefined` once the upload completes. Internally\n\t * scaled to `aria-valuenow` on a 0–100 progressbar so AT engines\n\t * announce \"42 percent\" rather than \"0.42 of 1.\"\n\t */\n\tprogress?: number;\n}\n\n/**\n * Format a byte count as a human-readable size (`\"24.3 KB\"`, `\"1.2 MB\"`).\n *\n * @param bytes - Raw byte count.\n * @returns A short human-readable string.\n */\nfunction formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n\tif (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n}\n\n/**\n * Decide whether to render the image variant (thumbnail) vs the file\n * variant (icon + name + size). Images need both an image MIME type AND\n * a `preview` URL — without preview we can't actually show the image, so\n * we fall back to the file variant.\n *\n * @param file - The attachment input.\n * @returns `\"image\"` when both conditions hold, else `\"file\"`.\n */\nfunction detectVariant(file: File | AttachmentFile): \"image\" | \"file\" {\n\tconst preview = \"preview\" in file ? file.preview : undefined;\n\tconst isImage = file.type.startsWith(\"image/\");\n\treturn isImage && preview ? \"image\" : \"file\";\n}\n\n/**\n * Resolve the preview URL for an attachment input. Native `File`s don't\n * carry a preview; consumers create one via `URL.createObjectURL(file)`\n * before handing the file in.\n *\n * @param file - The attachment input.\n * @returns The preview URL, or undefined.\n */\nfunction resolvePreview(file: File | AttachmentFile): string | undefined {\n\treturn \"preview\" in file ? file.preview : undefined;\n}\n\n/**\n * A file / image attachment thumbnail with an optional remove affordance\n * and optional upload-progress overlay. Composes with the existing\n * `Composer` AI primitive (drop into Composer's children slot to render\n * as part of the message draft).\n *\n * Auto-detects the visual variant: image MIME + `preview` URL → image\n * thumbnail; everything else → typed file icon with name + size.\n *\n * Distinct from {@link Dropzone} (the upload-input affordance) and\n * static {@link Card} previews (no remove/progress UI).\n *\n * @example\n * ```tsx\n * <Attachment\n * file={{ name: \"screenshot.png\", size: 124000, type: \"image/png\", preview: objectUrl }}\n * onRemove={() => removeFromDraft(\"screenshot.png\")}\n * />\n * ```\n */\nfunction Attachment({\n\tclassName,\n\tvariant,\n\tfile,\n\tonRemove,\n\tprogress,\n\tref,\n\t...props\n}: AttachmentProps) {\n\tconst preview = resolvePreview(file);\n\t// M3: when caller forces variant=\"image\" but no preview is available,\n\t// fall back to \"file\" so the wrapper padding matches the rendered\n\t// content (file icon + name + size needs px/py; image uses zero-pad).\n\tconst detected = detectVariant(file);\n\tconst resolvedVariant: \"image\" | \"file\" =\n\t\tvariant === \"image\" && !preview ? \"file\" : (variant ?? detected);\n\tconst showProgress = typeof progress === \"number\" && progress >= 0 && progress < 1;\n\t// M5: scale to 0–100 so AT announces \"42 percent\" not \"0.42 of 1.\"\n\tconst progressPercent = showProgress ? Math.round(progress * 100) : 0;\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={cn(attachmentVariants({ variant: resolvedVariant }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{resolvedVariant === \"image\" && preview ? (\n\t\t\t\t// Intentional plain <img> (not next/image) — Attachment is\n\t\t\t\t// framework-agnostic. Consumers can swap in next/image at the\n\t\t\t\t// callsite when they want optimization.\n\t\t\t\t<img\n\t\t\t\t\tsrc={preview}\n\t\t\t\t\talt={file.name}\n\t\t\t\t\tclassName=\"block h-20 w-20 object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<FileIcon className=\"h-5 w-5 shrink-0 text-muted-foreground\" />\n\t\t\t\t\t<div className=\"flex min-w-0 flex-col\">\n\t\t\t\t\t\t<span className=\"truncate text-sm font-medium text-foreground\">{file.name}</span>\n\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{formatSize(file.size)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{showProgress ? (\n\t\t\t\t<div\n\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\taria-valuemin={0}\n\t\t\t\t\taria-valuemax={100}\n\t\t\t\t\taria-valuenow={progressPercent}\n\t\t\t\t\taria-label={`Uploading ${file.name}`}\n\t\t\t\t\tclassName=\"absolute inset-x-0 bottom-0 h-1 overflow-hidden rounded-b-md bg-muted\"\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"h-full bg-primary transition-[width] duration-[var(--duration-normal,200ms)] ease-out\"\n\t\t\t\t\t\tstyle={{ width: `${progressPercent}%` }}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t) : null}\n\n\t\t\t{onRemove ? (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\taria-label={`Remove ${file.name}`}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"absolute -right-2 -top-2 inline-flex h-5 w-5 items-center justify-center rounded-full\",\n\t\t\t\t\t\t\"bg-card border border-border text-foreground shadow-sm\",\n\t\t\t\t\t\t\"transition-all duration-[var(--duration-normal,200ms)] ease-out\",\n\t\t\t\t\t\t\"hover:bg-accent hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\",\n\t\t\t\t\t\t\"active:scale-90\",\n\t\t\t\t\t)}\n\t\t\t\t>\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=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstrokeWidth=\"2.5\"\n\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\tclassName=\"size-3\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n\t\t\t\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</button>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n\n/** Generic file icon (page-with-fold). */\nfunction FileIcon(props: React.SVGAttributes<SVGElement>) {\n\treturn (\n\t\t<svg\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n\t\t\t<polyline points=\"14 2 14 8 20 8\" />\n\t\t</svg>\n\t);\n}\n\nexport { Attachment, attachmentVariants };\n"]}
|
package/dist/audio-player.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/audio-player/audio-player.tsx"],"names":[],"mappings":"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","file":"audio-player.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 * Audio playback control backed by wavesurfer.js. Renders a play/pause\n * button + linear progress bar + duration. Pair with `<AudioWaveform>`\n * for the visual waveform display (separate component, same engine).\n *\n * Headless on data: pass `src` (URL or Blob); the engine streams + decodes.\n *\n * Heavy peer: requires `wavesurfer.js` (~50 KB gzip, shared with\n * AudioWaveform). The hex-core CLI's `add` flow prompts before installing.\n *\n * @example\n * <AudioPlayer src=\"/podcast.mp3\" />\n * <AudioPlayer src={voicemailBlob} autoPlay onEnded={markRead} />\n */\nexport interface AudioPlayerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\" | \"onError\"> {\n\t/** Audio source — URL string or Blob. Re-loads when changed. */\n\tsrc: string | Blob;\n\t/** Auto-play once loaded. Default false. */\n\tautoPlay?: boolean;\n\t/** Called when playback finishes. */\n\tonEnded?: () => void;\n\t/** Called on engine error (network, decode, etc.). */\n\tonError?: (message: string) => void;\n}\n\n/**\n * Renders an audio player UI driven by wavesurfer.js.\n * @param props - Audio source + playback callbacks\n * @returns A div containing play/pause control + progress bar\n */\nfunction AudioPlayer({ src, autoPlay = false, onEnded, onError, className, ...rest }: AudioPlayerProps) {\n\tconst containerRef = React.useRef<HTMLDivElement | null>(null);\n\tconst wsRef = React.useRef<import(\"wavesurfer.js\").default | null>(null);\n\tconst onEndedRef = React.useRef(onEnded);\n\tconst onErrorRef = React.useRef(onError);\n\tonEndedRef.current = onEnded;\n\tonErrorRef.current = onError;\n\n\tconst [isPlaying, setIsPlaying] = React.useState(false);\n\tconst [duration, setDuration] = React.useState(0);\n\tconst [currentTime, setCurrentTime] = React.useState(0);\n\tconst [isReady, setIsReady] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tif (!containerRef.current) return;\n\t\tlet disposed = false;\n\t\t// Track Blob ObjectURLs so we can revoke on teardown — without this,\n\t\t// each src change leaks one URL + retains the underlying Blob until\n\t\t// page unload (a real leak for swap-heavy UIs like voicemail lists).\n\t\tconst objectUrl = src instanceof Blob ? URL.createObjectURL(src) : null;\n\n\t\tvoid (async () => {\n\t\t\tconst { default: WaveSurfer } = await import(\"wavesurfer.js\");\n\t\t\tif (disposed || !containerRef.current) return;\n\n\t\t\tconst ws = WaveSurfer.create({\n\t\t\t\tcontainer: containerRef.current,\n\t\t\t\twaveColor: \"#a3a3a3\",\n\t\t\t\tprogressColor: \"#171717\",\n\t\t\t\tcursorColor: \"transparent\",\n\t\t\t\theight: 32,\n\t\t\t\tbarWidth: 2,\n\t\t\t\tbarGap: 1,\n\t\t\t\tbarRadius: 1,\n\t\t\t\tnormalize: true,\n\t\t\t});\n\n\t\t\tws.on(\"ready\", () => {\n\t\t\t\tsetIsReady(true);\n\t\t\t\tsetDuration(ws.getDuration());\n\t\t\t\tif (autoPlay) void ws.play();\n\t\t\t});\n\t\t\tws.on(\"play\", () => setIsPlaying(true));\n\t\t\tws.on(\"pause\", () => setIsPlaying(false));\n\t\t\tws.on(\"finish\", () => {\n\t\t\t\tsetIsPlaying(false);\n\t\t\t\tonEndedRef.current?.();\n\t\t\t});\n\t\t\tws.on(\"timeupdate\", (t) => setCurrentTime(t));\n\t\t\tws.on(\"error\", (err: Error) => {\n\t\t\t\tonErrorRef.current?.(err.message);\n\t\t\t});\n\n\t\t\tvoid ws.load(objectUrl ?? (src as string));\n\t\t\twsRef.current = ws;\n\t\t})();\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\twsRef.current?.destroy();\n\t\t\twsRef.current = null;\n\t\t\tif (objectUrl) URL.revokeObjectURL(objectUrl);\n\t\t};\n\t\t// autoPlay is mount-only; src changes trigger a separate effect.\n\t}, [src]);\n\n\tfunction togglePlay() {\n\t\tconst ws = wsRef.current;\n\t\tif (!ws || !isReady) return;\n\t\tvoid ws.playPause();\n\t}\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-audio-player\n\t\t\tclassName={cn(\"flex items-center gap-3 rounded-md border bg-background p-2\", className)}\n\t\t>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={togglePlay}\n\t\t\t\tdisabled={!isReady}\n\t\t\t\taria-label={isPlaying ? \"Pause\" : \"Play\"}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md\",\n\t\t\t\t\t\"bg-foreground text-background transition-opacity\",\n\t\t\t\t\t\"hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{isPlaying ? <PauseIcon /> : <PlayIcon />}\n\t\t\t</button>\n\t\t\t<div ref={containerRef} className=\"flex-1 min-w-0\" />\n\t\t\t<span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">\n\t\t\t\t{formatTime(currentTime)} / {formatTime(duration)}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nfunction formatTime(seconds: number): string {\n\tif (!Number.isFinite(seconds) || seconds < 0) return \"0:00\";\n\tconst m = Math.floor(seconds / 60);\n\tconst s = Math.floor(seconds % 60);\n\treturn `${m}:${s.toString().padStart(2, \"0\")}`;\n}\n\nfunction PlayIcon() {\n\treturn (\n\t\t<svg aria-hidden viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"currentColor\">\n\t\t\t<path d=\"M5 3.5v9l8-4.5z\" />\n\t\t</svg>\n\t);\n}\n\nfunction PauseIcon() {\n\treturn (\n\t\t<svg aria-hidden viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"currentColor\">\n\t\t\t<rect x=\"4\" y=\"3\" width=\"3\" height=\"10\" rx=\"0.5\" />\n\t\t\t<rect x=\"9\" y=\"3\" width=\"3\" height=\"10\" rx=\"0.5\" />\n\t\t</svg>\n\t);\n}\n\nexport { AudioPlayer };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/audio-player/audio-player.tsx"],"names":[],"mappings":"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","file":"audio-player.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 * Audio playback control backed by wavesurfer.js. Renders a play/pause\n * button + linear progress bar + duration. Pair with `<AudioWaveform>`\n * for the visual waveform display (separate component, same engine).\n *\n * Headless on data: pass `src` (URL or Blob); the engine streams + decodes.\n *\n * Heavy peer: requires `wavesurfer.js` (~50 KB gzip, shared with\n * AudioWaveform). The hex-core CLI's `add` flow prompts before installing.\n *\n * @example\n * <AudioPlayer src=\"/podcast.mp3\" />\n * <AudioPlayer src={voicemailBlob} autoPlay onEnded={markRead} />\n */\nexport interface AudioPlayerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\" | \"onError\"> {\n\t/** Audio source — URL string or Blob. Re-loads when changed. */\n\tsrc: string | Blob;\n\t/** Auto-play once loaded. Default false. */\n\tautoPlay?: boolean;\n\t/** Called when playback finishes. */\n\tonEnded?: () => void;\n\t/** Called on engine error (network, decode, etc.). */\n\tonError?: (message: string) => void;\n}\n\n/**\n * Renders an audio player UI driven by wavesurfer.js.\n * @param props - Audio source + playback callbacks\n * @returns A div containing play/pause control + progress bar\n */\nfunction AudioPlayer({ src, autoPlay = false, onEnded, onError, className, ...rest }: AudioPlayerProps) {\n\tconst containerRef = React.useRef<HTMLDivElement | null>(null);\n\tconst wsRef = React.useRef<import(\"wavesurfer.js\").default | null>(null);\n\tconst onEndedRef = React.useRef(onEnded);\n\tconst onErrorRef = React.useRef(onError);\n\tonEndedRef.current = onEnded;\n\tonErrorRef.current = onError;\n\n\tconst [isPlaying, setIsPlaying] = React.useState(false);\n\tconst [duration, setDuration] = React.useState(0);\n\tconst [currentTime, setCurrentTime] = React.useState(0);\n\tconst [isReady, setIsReady] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tif (!containerRef.current) return;\n\t\tlet disposed = false;\n\t\t// Track Blob ObjectURLs so we can revoke on teardown — without this,\n\t\t// each src change leaks one URL + retains the underlying Blob until\n\t\t// page unload (a real leak for swap-heavy UIs like voicemail lists).\n\t\tconst objectUrl = src instanceof Blob ? URL.createObjectURL(src) : null;\n\n\t\tvoid (async () => {\n\t\t\tconst { default: WaveSurfer } = await import(\"wavesurfer.js\");\n\t\t\tif (disposed || !containerRef.current) return;\n\n\t\t\tconst ws = WaveSurfer.create({\n\t\t\t\tcontainer: containerRef.current,\n\t\t\t\twaveColor: \"#a3a3a3\",\n\t\t\t\tprogressColor: \"#171717\",\n\t\t\t\tcursorColor: \"transparent\",\n\t\t\t\theight: 32,\n\t\t\t\tbarWidth: 2,\n\t\t\t\tbarGap: 1,\n\t\t\t\tbarRadius: 1,\n\t\t\t\tnormalize: true,\n\t\t\t});\n\n\t\t\tws.on(\"ready\", () => {\n\t\t\t\tsetIsReady(true);\n\t\t\t\tsetDuration(ws.getDuration());\n\t\t\t\tif (autoPlay) void ws.play();\n\t\t\t});\n\t\t\tws.on(\"play\", () => setIsPlaying(true));\n\t\t\tws.on(\"pause\", () => setIsPlaying(false));\n\t\t\tws.on(\"finish\", () => {\n\t\t\t\tsetIsPlaying(false);\n\t\t\t\tonEndedRef.current?.();\n\t\t\t});\n\t\t\tws.on(\"timeupdate\", (t) => setCurrentTime(t));\n\t\t\tws.on(\"error\", (err: Error) => {\n\t\t\t\tonErrorRef.current?.(err.message);\n\t\t\t});\n\n\t\t\tvoid ws.load(objectUrl ?? (src as string));\n\t\t\twsRef.current = ws;\n\t\t})();\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\twsRef.current?.destroy();\n\t\t\twsRef.current = null;\n\t\t\tif (objectUrl) URL.revokeObjectURL(objectUrl);\n\t\t};\n\t\t// autoPlay is mount-only; src changes trigger a separate effect.\n\t}, [src]);\n\n\tfunction togglePlay() {\n\t\tconst ws = wsRef.current;\n\t\tif (!ws || !isReady) return;\n\t\tvoid ws.playPause();\n\t}\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-hex-audio-player\n\t\t\tclassName={cn(\"flex items-center gap-3 rounded-md border bg-background p-2\", className)}\n\t\t>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={togglePlay}\n\t\t\t\tdisabled={!isReady}\n\t\t\t\taria-label={isPlaying ? \"Pause\" : \"Play\"}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md\",\n\t\t\t\t\t\"bg-foreground text-background transition-opacity\",\n\t\t\t\t\t\"hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{isPlaying ? <PauseIcon /> : <PlayIcon />}\n\t\t\t</button>\n\t\t\t<div ref={containerRef} className=\"flex-1 min-w-0\" />\n\t\t\t<span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">\n\t\t\t\t{formatTime(currentTime)} / {formatTime(duration)}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nfunction formatTime(seconds: number): string {\n\tif (!Number.isFinite(seconds) || seconds < 0) return \"0:00\";\n\tconst m = Math.floor(seconds / 60);\n\tconst s = Math.floor(seconds % 60);\n\treturn `${m}:${s.toString().padStart(2, \"0\")}`;\n}\n\nfunction PlayIcon() {\n\treturn (\n\t\t<svg aria-hidden viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"currentColor\">\n\t\t\t<path d=\"M5 3.5v9l8-4.5z\" />\n\t\t</svg>\n\t);\n}\n\nfunction PauseIcon() {\n\treturn (\n\t\t<svg aria-hidden viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"currentColor\">\n\t\t\t<rect x=\"4\" y=\"3\" width=\"3\" height=\"10\" rx=\"0.5\" />\n\t\t\t<rect x=\"9\" y=\"3\" width=\"3\" height=\"10\" rx=\"0.5\" />\n\t\t</svg>\n\t);\n}\n\nexport { AudioPlayer };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/audio-waveform/audio-waveform.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;AC6BA,SAAS,aAAA,CAAc;AAAA,EACtB,GAAA;AAAA,EACA,MAAA,GAAS,EAAA;AAAA,EACT,SAAA,GAAY,SAAA;AAAA,EACZ,aAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAuB;AACtB,EAAA,MAAM,YAAA,GAAqB,aAA8B,IAAI,CAAA;AAC7D,EAAA,MAAM,UAAA,GAAmB,aAAO,OAAO,CAAA;AACvC,EAAA,MAAM,UAAA,GAAmB,aAAO,OAAO,CAAA;AACvC,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,EAAM,gBAAU,MAAM;AACrB,IAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,IAAI,EAAA,GAA6C,IAAA;AAEjD,IAAA,MAAM,YAAY,GAAA,YAAe,IAAA,GAAO,GAAA,CAAI,eAAA,CAAgB,GAAG,CAAA,GAAI,IAAA;AAEnE,IAAA,KAAA,CAAM,YAAY;AACjB,MAAA,MAAM,EAAE,OAAA,EAAS,UAAA,EAAW,GAAI,MAAM,OAAO,eAAe,CAAA;AAC5D,MAAA,IAAI,QAAA,IAAY,CAAC,YAAA,CAAa,OAAA,EAAS;AAEvC,MAAA,EAAA,GAAK,WAAW,MAAA,CAAO;AAAA,QACtB,WAAW,YAAA,CAAa,OAAA;AAAA,QACxB,SAAA;AAAA,QACA,eAAe,aAAA,IAAiB,SAAA;AAAA,QAChC,WAAA,EAAa,aAAA;AAAA,QACb,MAAA;AAAA,QACA,QAAA,EAAU,CAAA;AAAA,QACV,MAAA,EAAQ,CAAA;AAAA,QACR,SAAA,EAAW,CAAA;AAAA,QACX,SAAA,EAAW,IAAA;AAAA,QACX,QAAA,EAAU;AAAA,OACV,CAAA;AAED,MAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACpB,QAAA,IAAI,EAAA,EAAI,UAAA,CAAW,OAAA,GAAU,EAAA,CAAG,aAAa,CAAA;AAAA,MAC9C,CAAC,CAAA;AACD,MAAA,EAAA,CAAG,EAAA,CAAG,OAAA,EAAS,CAAC,GAAA,KAAe;AAC9B,QAAA,UAAA,CAAW,OAAA,GAAU,IAAI,OAAO,CAAA;AAAA,MACjC,CAAC,CAAA;AAED,MAAA,KAAK,EAAA,CAAG,IAAA,CAAK,SAAA,IAAc,GAAc,CAAA;AAAA,IAC1C,CAAA,GAAG;AAEH,IAAA,OAAO,MAAM;AACZ,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,EAAA,EAAI,OAAA,EAAQ;AACZ,MAAA,IAAI,SAAA,EAAW,GAAA,CAAI,eAAA,CAAgB,SAAS,CAAA;AAAA,IAC7C,CAAA;AAAA,EACD,GAAG,CAAC,GAAA,EAAK,MAAA,EAAQ,SAAA,EAAW,aAAa,CAAC,CAAA;AAE1C,EAAA,uBACC,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAG,IAAA;AAAA,MACJ,GAAA,EAAK,YAAA;AAAA,MACL,yBAAA,EAAuB,IAAA;AAAA,MACvB,SAAA,EAAW,EAAA,CAAG,QAAA,EAAU,SAAS;AAAA;AAAA,GAClC;AAEF","file":"audio-waveform.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 * Standalone audio waveform display backed by wavesurfer.js. Renders the\n * waveform of a source (URL or Blob) without playback controls — for\n * voice-message previews, audio thumbnails, recording indicators, anywhere\n * the visual is the point.\n *\n * Heavy peer: requires `wavesurfer.js` (~50 KB gzip, shared with\n * AudioPlayer). The hex-core CLI's `add` flow prompts before installing.\n *\n * For interactive playback + waveform together, use `<AudioPlayer>`.\n *\n * @example\n * <AudioWaveform src=\"/voice-message.mp3\" height={48} />\n */\nexport interface AudioWaveformProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\" | \"onError\"> {\n\t/** Audio source — URL string or Blob. */\n\tsrc: string | Blob;\n\t/** Pixel height of the waveform. Default 48. */\n\theight?: number;\n\t/** Bar color for unplayed regions. CSS color string. */\n\twaveColor?: string;\n\t/** Bar color for played regions. Default same as `waveColor`. */\n\tprogressColor?: string;\n\t/** Called when wavesurfer finishes decoding the source. */\n\tonReady?: (durationSeconds: number) => void;\n\t/** Called on engine error (network, decode). */\n\tonError?: (message: string) => void;\n}\n\n/**\n * Renders an audio waveform without playback controls.\n * @param props - Audio source + visual options + lifecycle callbacks\n * @returns A div containing the wavesurfer-rendered waveform\n */\nfunction AudioWaveform({\n\tsrc,\n\theight = 48,\n\twaveColor = \"#a3a3a3\",\n\tprogressColor,\n\tonReady,\n\tonError,\n\tclassName,\n\t...rest\n}: AudioWaveformProps) {\n\tconst containerRef = React.useRef<HTMLDivElement | null>(null);\n\tconst onReadyRef = React.useRef(onReady);\n\tconst onErrorRef = React.useRef(onError);\n\tonReadyRef.current = onReady;\n\tonErrorRef.current = onError;\n\n\tReact.useEffect(() => {\n\t\tif (!containerRef.current) return;\n\t\tlet disposed = false;\n\t\tlet ws: import(\"wavesurfer.js\").default | null = null;\n\t\t// Track Blob ObjectURLs so we can revoke on teardown.\n\t\tconst objectUrl = src instanceof Blob ? URL.createObjectURL(src) : null;\n\n\t\tvoid (async () => {\n\t\t\tconst { default: WaveSurfer } = await import(\"wavesurfer.js\");\n\t\t\tif (disposed || !containerRef.current) return;\n\n\t\t\tws = WaveSurfer.create({\n\t\t\t\tcontainer: containerRef.current,\n\t\t\t\twaveColor,\n\t\t\t\tprogressColor: progressColor ?? waveColor,\n\t\t\t\tcursorColor: \"transparent\",\n\t\t\t\theight,\n\t\t\t\tbarWidth: 2,\n\t\t\t\tbarGap: 1,\n\t\t\t\tbarRadius: 1,\n\t\t\t\tnormalize: true,\n\t\t\t\tinteract: false,\n\t\t\t});\n\n\t\t\tws.on(\"ready\", () => {\n\t\t\t\tif (ws) onReadyRef.current?.(ws.getDuration());\n\t\t\t});\n\t\t\tws.on(\"error\", (err: Error) => {\n\t\t\t\tonErrorRef.current?.(err.message);\n\t\t\t});\n\n\t\t\tvoid ws.load(objectUrl ?? (src as string));\n\t\t})();\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tws?.destroy();\n\t\t\tif (objectUrl) URL.revokeObjectURL(objectUrl);\n\t\t};\n\t}, [src, height, waveColor, progressColor]);\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tref={containerRef}\n\t\t\tdata-hex-audio-waveform\n\t\t\tclassName={cn(\"w-full\", className)}\n\t\t/>\n\t);\n}\n\nexport { AudioWaveform };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/ai/audio-waveform/audio-waveform.tsx"],"names":[],"mappings":";;;;;AAQO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B;AC6BA,SAAS,aAAA,CAAc;AAAA,EACtB,GAAA;AAAA,EACA,MAAA,GAAS,EAAA;AAAA,EACT,SAAA,GAAY,SAAA;AAAA,EACZ,aAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACJ,CAAA,EAAuB;AACtB,EAAA,MAAM,YAAA,GAAqB,aAA8B,IAAI,CAAA;AAC7D,EAAA,MAAM,UAAA,GAAmB,aAAO,OAAO,CAAA;AACvC,EAAA,MAAM,UAAA,GAAmB,aAAO,OAAO,CAAA;AACvC,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,EAAM,gBAAU,MAAM;AACrB,IAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,IAAI,EAAA,GAA6C,IAAA;AAEjD,IAAA,MAAM,YAAY,GAAA,YAAe,IAAA,GAAO,GAAA,CAAI,eAAA,CAAgB,GAAG,CAAA,GAAI,IAAA;AAEnE,IAAA,KAAA,CAAM,YAAY;AACjB,MAAA,MAAM,EAAE,OAAA,EAAS,UAAA,EAAW,GAAI,MAAM,OAAO,eAAe,CAAA;AAC5D,MAAA,IAAI,QAAA,IAAY,CAAC,YAAA,CAAa,OAAA,EAAS;AAEvC,MAAA,EAAA,GAAK,WAAW,MAAA,CAAO;AAAA,QACtB,WAAW,YAAA,CAAa,OAAA;AAAA,QACxB,SAAA;AAAA,QACA,eAAe,aAAA,IAAiB,SAAA;AAAA,QAChC,WAAA,EAAa,aAAA;AAAA,QACb,MAAA;AAAA,QACA,QAAA,EAAU,CAAA;AAAA,QACV,MAAA,EAAQ,CAAA;AAAA,QACR,SAAA,EAAW,CAAA;AAAA,QACX,SAAA,EAAW,IAAA;AAAA,QACX,QAAA,EAAU;AAAA,OACV,CAAA;AAED,MAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACpB,QAAA,IAAI,EAAA,EAAI,UAAA,CAAW,OAAA,GAAU,EAAA,CAAG,aAAa,CAAA;AAAA,MAC9C,CAAC,CAAA;AACD,MAAA,EAAA,CAAG,EAAA,CAAG,OAAA,EAAS,CAAC,GAAA,KAAe;AAC9B,QAAA,UAAA,CAAW,OAAA,GAAU,IAAI,OAAO,CAAA;AAAA,MACjC,CAAC,CAAA;AAED,MAAA,KAAK,EAAA,CAAG,IAAA,CAAK,SAAA,IAAc,GAAc,CAAA;AAAA,IAC1C,CAAA,GAAG;AAEH,IAAA,OAAO,MAAM;AACZ,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,EAAA,EAAI,OAAA,EAAQ;AACZ,MAAA,IAAI,SAAA,EAAW,GAAA,CAAI,eAAA,CAAgB,SAAS,CAAA;AAAA,IAC7C,CAAA;AAAA,EACD,GAAG,CAAC,GAAA,EAAK,MAAA,EAAQ,SAAA,EAAW,aAAa,CAAC,CAAA;AAE1C,EAAA,uBACC,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAG,IAAA;AAAA,MACJ,GAAA,EAAK,YAAA;AAAA,MACL,yBAAA,EAAuB,IAAA;AAAA,MACvB,SAAA,EAAW,EAAA,CAAG,QAAA,EAAU,SAAS;AAAA;AAAA,GAClC;AAEF","file":"audio-waveform.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 * Standalone audio waveform display backed by wavesurfer.js. Renders the\n * waveform of a source (URL or Blob) without playback controls — for\n * voice-message previews, audio thumbnails, recording indicators, anywhere\n * the visual is the point.\n *\n * Heavy peer: requires `wavesurfer.js` (~50 KB gzip, shared with\n * AudioPlayer). The hex-core CLI's `add` flow prompts before installing.\n *\n * For interactive playback + waveform together, use `<AudioPlayer>`.\n *\n * @example\n * <AudioWaveform src=\"/voice-message.mp3\" height={48} />\n */\nexport interface AudioWaveformProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\" | \"onError\"> {\n\t/** Audio source — URL string or Blob. */\n\tsrc: string | Blob;\n\t/** Pixel height of the waveform. Default 48. */\n\theight?: number;\n\t/** Bar color for unplayed regions. CSS color string. */\n\twaveColor?: string;\n\t/** Bar color for played regions. Default same as `waveColor`. */\n\tprogressColor?: string;\n\t/** Called when wavesurfer finishes decoding the source. */\n\tonReady?: (durationSeconds: number) => void;\n\t/** Called on engine error (network, decode). */\n\tonError?: (message: string) => void;\n}\n\n/**\n * Renders an audio waveform without playback controls.\n * @param props - Audio source + visual options + lifecycle callbacks\n * @returns A div containing the wavesurfer-rendered waveform\n */\nfunction AudioWaveform({\n\tsrc,\n\theight = 48,\n\twaveColor = \"#a3a3a3\",\n\tprogressColor,\n\tonReady,\n\tonError,\n\tclassName,\n\t...rest\n}: AudioWaveformProps) {\n\tconst containerRef = React.useRef<HTMLDivElement | null>(null);\n\tconst onReadyRef = React.useRef(onReady);\n\tconst onErrorRef = React.useRef(onError);\n\tonReadyRef.current = onReady;\n\tonErrorRef.current = onError;\n\n\tReact.useEffect(() => {\n\t\tif (!containerRef.current) return;\n\t\tlet disposed = false;\n\t\tlet ws: import(\"wavesurfer.js\").default | null = null;\n\t\t// Track Blob ObjectURLs so we can revoke on teardown.\n\t\tconst objectUrl = src instanceof Blob ? URL.createObjectURL(src) : null;\n\n\t\tvoid (async () => {\n\t\t\tconst { default: WaveSurfer } = await import(\"wavesurfer.js\");\n\t\t\tif (disposed || !containerRef.current) return;\n\n\t\t\tws = WaveSurfer.create({\n\t\t\t\tcontainer: containerRef.current,\n\t\t\t\twaveColor,\n\t\t\t\tprogressColor: progressColor ?? waveColor,\n\t\t\t\tcursorColor: \"transparent\",\n\t\t\t\theight,\n\t\t\t\tbarWidth: 2,\n\t\t\t\tbarGap: 1,\n\t\t\t\tbarRadius: 1,\n\t\t\t\tnormalize: true,\n\t\t\t\tinteract: false,\n\t\t\t});\n\n\t\t\tws.on(\"ready\", () => {\n\t\t\t\tif (ws) onReadyRef.current?.(ws.getDuration());\n\t\t\t});\n\t\t\tws.on(\"error\", (err: Error) => {\n\t\t\t\tonErrorRef.current?.(err.message);\n\t\t\t});\n\n\t\t\tvoid ws.load(objectUrl ?? (src as string));\n\t\t})();\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tws?.destroy();\n\t\t\tif (objectUrl) URL.revokeObjectURL(objectUrl);\n\t\t};\n\t}, [src, height, waveColor, progressColor]);\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tref={containerRef}\n\t\t\tdata-hex-audio-waveform\n\t\t\tclassName={cn(\"w-full\", className)}\n\t\t/>\n\t);\n}\n\nexport { AudioWaveform };\n"]}
|