@dowel-ui/react 0.4.0 → 0.7.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/README.md +9 -5
- package/dist/blocks/admin-dashboard/meta.js +31 -0
- package/dist/blocks/admin-dashboard/meta.js.map +1 -0
- package/dist/blocks/agent-console/meta.js +27 -0
- package/dist/blocks/agent-console/meta.js.map +1 -0
- package/dist/blocks/ai-dashboard/meta.js +25 -0
- package/dist/blocks/ai-dashboard/meta.js.map +1 -0
- package/dist/blocks/ai-workspace/meta.js +33 -0
- package/dist/blocks/ai-workspace/meta.js.map +1 -0
- package/dist/blocks/analytics/meta.js +24 -0
- package/dist/blocks/analytics/meta.js.map +1 -0
- package/dist/blocks/billing/meta.js +25 -0
- package/dist/blocks/billing/meta.js.map +1 -0
- package/dist/blocks/command-center/meta.js +28 -0
- package/dist/blocks/command-center/meta.js.map +1 -0
- package/dist/blocks/crm/meta.js +32 -0
- package/dist/blocks/crm/meta.js.map +1 -0
- package/dist/blocks/onboarding/meta.js +23 -0
- package/dist/blocks/onboarding/meta.js.map +1 -0
- package/dist/components/accordion/accordion.js +1 -1
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/activity-feed/activity-feed.js +1 -1
- package/dist/components/activity-feed/activity-feed.js.map +1 -1
- package/dist/components/ai-agent-plan/ai-agent-plan.js +1 -1
- package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -1
- package/dist/components/ai-extraction-review/ai-extraction-review.d.ts +52 -0
- package/dist/components/ai-extraction-review/ai-extraction-review.d.ts.map +1 -0
- package/dist/components/ai-extraction-review/ai-extraction-review.js +249 -0
- package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -0
- package/dist/components/ai-extraction-review/extraction-model.d.ts +94 -0
- package/dist/components/ai-extraction-review/extraction-model.d.ts.map +1 -0
- package/dist/components/ai-extraction-review/extraction-model.js +91 -0
- package/dist/components/ai-extraction-review/extraction-model.js.map +1 -0
- package/dist/components/ai-extraction-review/index.d.ts +3 -0
- package/dist/components/ai-extraction-review/index.js +3 -0
- package/dist/components/ai-extraction-review/meta.js +17 -0
- package/dist/components/ai-extraction-review/meta.js.map +1 -0
- package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
- package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
- package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
- package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
- package/dist/components/ai-response/ai-response.js +2 -2
- package/dist/components/ai-response/ai-response.js.map +1 -1
- package/dist/components/ai-suggested-value/ai-suggested-value.d.ts +72 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.d.ts.map +1 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.js +126 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.js.map +1 -0
- package/dist/components/ai-suggested-value/index.d.ts +2 -0
- package/dist/components/ai-suggested-value/index.js +2 -0
- package/dist/components/ai-suggested-value/meta.js +17 -0
- package/dist/components/ai-suggested-value/meta.js.map +1 -0
- package/dist/components/ai-tool/ai-tool.js +2 -2
- package/dist/components/ai-tool/ai-tool.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.js +141 -0
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/breadcrumb/index.js +2 -0
- package/dist/components/breadcrumb/meta.js +17 -0
- package/dist/components/breadcrumb/meta.js.map +1 -0
- package/dist/components/calendar/calendar.js +2 -2
- package/dist/components/calendar/calendar.js.map +1 -1
- package/dist/components/code-block/code-block.js +1 -1
- package/dist/components/code-block/code-block.js.map +1 -1
- package/dist/components/collapsible/collapsible.d.ts +30 -0
- package/dist/components/collapsible/collapsible.d.ts.map +1 -0
- package/dist/components/collapsible/collapsible.js +38 -0
- package/dist/components/collapsible/collapsible.js.map +1 -0
- package/dist/components/collapsible/index.d.ts +2 -0
- package/dist/components/collapsible/index.js +2 -0
- package/dist/components/collapsible/meta.js +17 -0
- package/dist/components/collapsible/meta.js.map +1 -0
- package/dist/components/combobox/combobox.js +2 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/command/command.js +1 -1
- package/dist/components/command/command.js.map +1 -1
- package/dist/components/confirm-typed/confirm-typed.d.ts +47 -0
- package/dist/components/confirm-typed/confirm-typed.d.ts.map +1 -0
- package/dist/components/confirm-typed/confirm-typed.js +106 -0
- package/dist/components/confirm-typed/confirm-typed.js.map +1 -0
- package/dist/components/confirm-typed/index.d.ts +2 -0
- package/dist/components/confirm-typed/index.js +2 -0
- package/dist/components/confirm-typed/meta.js +17 -0
- package/dist/components/confirm-typed/meta.js.map +1 -0
- package/dist/components/cron-editor/cron-editor.d.ts +36 -0
- package/dist/components/cron-editor/cron-editor.d.ts.map +1 -0
- package/dist/components/cron-editor/cron-editor.js +493 -0
- package/dist/components/cron-editor/cron-editor.js.map +1 -0
- package/dist/components/cron-editor/cron-expression.d.ts +82 -0
- package/dist/components/cron-editor/cron-expression.d.ts.map +1 -0
- package/dist/components/cron-editor/cron-expression.js +352 -0
- package/dist/components/cron-editor/cron-expression.js.map +1 -0
- package/dist/components/cron-editor/index.d.ts +3 -0
- package/dist/components/cron-editor/index.js +3 -0
- package/dist/components/cron-editor/meta.js +17 -0
- package/dist/components/cron-editor/meta.js.map +1 -0
- package/dist/components/data-table/data-table.d.ts.map +1 -1
- package/dist/components/data-table/data-table.js +4 -1
- package/dist/components/data-table/data-table.js.map +1 -1
- package/dist/components/dialog/dialog.js +2 -2
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/diff-viewer/diff-viewer.js +4 -4
- package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
- package/dist/components/direction/direction.d.ts +35 -0
- package/dist/components/direction/direction.d.ts.map +1 -0
- package/dist/components/direction/direction.js +14 -0
- package/dist/components/direction/direction.js.map +1 -0
- package/dist/components/direction/index.d.ts +2 -0
- package/dist/components/direction/index.js +2 -0
- package/dist/components/direction/meta.js +17 -0
- package/dist/components/direction/meta.js.map +1 -0
- package/dist/components/dns-record/dns-record.d.ts +61 -0
- package/dist/components/dns-record/dns-record.d.ts.map +1 -0
- package/dist/components/dns-record/dns-record.js +227 -0
- package/dist/components/dns-record/dns-record.js.map +1 -0
- package/dist/components/dns-record/index.d.ts +2 -0
- package/dist/components/dns-record/index.js +2 -0
- package/dist/components/dns-record/meta.js +17 -0
- package/dist/components/dns-record/meta.js.map +1 -0
- package/dist/components/drawer/drawer.js +1 -1
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
- package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/input/input.js +1 -1
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/log-viewer/log-viewer.js +2 -2
- package/dist/components/log-viewer/log-viewer.js.map +1 -1
- package/dist/components/meter/meter.js +1 -1
- package/dist/components/meter/meter.js.map +1 -1
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +3 -2
- package/dist/components/pagination/pagination.js.map +1 -1
- package/dist/components/permission-matrix/index.d.ts +3 -0
- package/dist/components/permission-matrix/index.js +3 -0
- package/dist/components/permission-matrix/meta.js +17 -0
- package/dist/components/permission-matrix/meta.js.map +1 -0
- package/dist/components/permission-matrix/permission-matrix.d.ts +46 -0
- package/dist/components/permission-matrix/permission-matrix.d.ts.map +1 -0
- package/dist/components/permission-matrix/permission-matrix.js +199 -0
- package/dist/components/permission-matrix/permission-matrix.js.map +1 -0
- package/dist/components/permission-matrix/permission-model.d.ts +53 -0
- package/dist/components/permission-matrix/permission-model.d.ts.map +1 -0
- package/dist/components/permission-matrix/permission-model.js +64 -0
- package/dist/components/permission-matrix/permission-model.js.map +1 -0
- package/dist/components/record-diff/record-diff.js +4 -4
- package/dist/components/record-diff/record-diff.js.map +1 -1
- package/dist/components/secret-field/index.d.ts +2 -0
- package/dist/components/secret-field/index.js +2 -0
- package/dist/components/secret-field/meta.js +17 -0
- package/dist/components/secret-field/meta.js.map +1 -0
- package/dist/components/secret-field/secret-field.d.ts +58 -0
- package/dist/components/secret-field/secret-field.d.ts.map +1 -0
- package/dist/components/secret-field/secret-field.js +168 -0
- package/dist/components/secret-field/secret-field.js.map +1 -0
- package/dist/components/select/select.js +2 -2
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/session-expiry/index.d.ts +2 -0
- package/dist/components/session-expiry/index.js +2 -0
- package/dist/components/session-expiry/meta.js +17 -0
- package/dist/components/session-expiry/meta.js.map +1 -0
- package/dist/components/session-expiry/session-expiry.d.ts +64 -0
- package/dist/components/session-expiry/session-expiry.d.ts.map +1 -0
- package/dist/components/session-expiry/session-expiry.js +185 -0
- package/dist/components/session-expiry/session-expiry.js.map +1 -0
- package/dist/components/sheet/sheet.d.ts.map +1 -1
- package/dist/components/sheet/sheet.js +2 -2
- package/dist/components/sheet/sheet.js.map +1 -1
- package/dist/components/shortcut-recorder/index.d.ts +3 -0
- package/dist/components/shortcut-recorder/index.js +3 -0
- package/dist/components/shortcut-recorder/meta.js +17 -0
- package/dist/components/shortcut-recorder/meta.js.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-model.d.ts +67 -0
- package/dist/components/shortcut-recorder/shortcut-model.d.ts.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-model.js +168 -0
- package/dist/components/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.d.ts +47 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.d.ts.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.js +212 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.js +2 -0
- package/dist/components/sidebar/meta.js +17 -0
- package/dist/components/sidebar/meta.js.map +1 -0
- package/dist/components/sidebar/sidebar.d.ts +69 -0
- package/dist/components/sidebar/sidebar.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar.js +256 -0
- package/dist/components/sidebar/sidebar.js.map +1 -0
- package/dist/components/sync-status/index.d.ts +2 -0
- package/dist/components/sync-status/index.js +2 -0
- package/dist/components/sync-status/meta.js +17 -0
- package/dist/components/sync-status/meta.js.map +1 -0
- package/dist/components/sync-status/sync-status.d.ts +65 -0
- package/dist/components/sync-status/sync-status.d.ts.map +1 -0
- package/dist/components/sync-status/sync-status.js +103 -0
- package/dist/components/sync-status/sync-status.js.map +1 -0
- package/dist/components/table/table.js +2 -2
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tags-input/tags-input.js +1 -1
- package/dist/components/tags-input/tags-input.js.map +1 -1
- package/dist/components/textarea/index.d.ts +2 -0
- package/dist/components/textarea/index.js +2 -0
- package/dist/components/textarea/meta.js +17 -0
- package/dist/components/textarea/meta.js.map +1 -0
- package/dist/components/textarea/textarea.d.ts +43 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -0
- package/dist/components/textarea/textarea.js +119 -0
- package/dist/components/textarea/textarea.js.map +1 -0
- package/dist/components/time-range-picker/time-range-picker.js +3 -3
- package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +2 -2
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/index.d.ts +36 -2
- package/dist/index.js +22 -3
- package/dist/lib/styles.d.ts +16 -1
- package/dist/lib/styles.d.ts.map +1 -1
- package/dist/lib/styles.js +16 -1
- package/dist/lib/styles.js.map +1 -1
- package/dist/registry/blocks.d.ts.map +1 -1
- package/dist/registry/blocks.js +27 -9
- package/dist/registry/blocks.js.map +1 -1
- package/dist/registry/components.d.ts.map +1 -1
- package/dist/registry/components.js +84 -54
- package/dist/registry/components.js.map +1 -1
- package/dist/registry/schema.d.ts +8 -0
- package/dist/registry/schema.d.ts.map +1 -1
- package/dist/registry/schema.js.map +1 -1
- package/package.json +5 -4
- package/src/components/accordion/accordion.tsx +1 -1
- package/src/components/activity-feed/activity-feed.tsx +1 -1
- package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
- package/src/components/ai-extraction-review/ai-extraction-review.tsx +492 -0
- package/src/components/ai-extraction-review/extraction-model.ts +159 -0
- package/src/components/ai-extraction-review/index.ts +13 -0
- package/src/components/ai-extraction-review/meta.ts +24 -0
- package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
- package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
- package/src/components/ai-response/ai-response.tsx +2 -2
- package/src/components/ai-suggested-value/ai-suggested-value.tsx +295 -0
- package/src/components/ai-suggested-value/index.ts +6 -0
- package/src/components/ai-suggested-value/meta.ts +24 -0
- package/src/components/ai-tool/ai-tool.tsx +2 -2
- package/src/components/breadcrumb/breadcrumb.tsx +157 -0
- package/src/components/breadcrumb/index.ts +10 -0
- package/src/components/breadcrumb/meta.ts +19 -0
- package/src/components/calendar/calendar.tsx +2 -2
- package/src/components/code-block/code-block.tsx +1 -1
- package/src/components/collapsible/collapsible.tsx +63 -0
- package/src/components/collapsible/index.ts +6 -0
- package/src/components/collapsible/meta.ts +16 -0
- package/src/components/combobox/combobox.tsx +2 -2
- package/src/components/command/command.tsx +1 -1
- package/src/components/confirm-typed/confirm-typed.tsx +191 -0
- package/src/components/confirm-typed/index.ts +1 -0
- package/src/components/confirm-typed/meta.ts +22 -0
- package/src/components/cron-editor/cron-editor.tsx +676 -0
- package/src/components/cron-editor/cron-expression.ts +552 -0
- package/src/components/cron-editor/index.ts +23 -0
- package/src/components/cron-editor/meta.ts +23 -0
- package/src/components/data-table/data-table.tsx +14 -3
- package/src/components/dialog/dialog.tsx +2 -2
- package/src/components/diff-viewer/diff-viewer.tsx +4 -4
- package/src/components/direction/direction.tsx +38 -0
- package/src/components/direction/index.ts +1 -0
- package/src/components/direction/meta.ts +19 -0
- package/src/components/dns-record/dns-record.tsx +315 -0
- package/src/components/dns-record/index.ts +7 -0
- package/src/components/dns-record/meta.ts +21 -0
- package/src/components/drawer/drawer.tsx +1 -1
- package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
- package/src/components/input/input.tsx +1 -1
- package/src/components/log-viewer/log-viewer.tsx +2 -2
- package/src/components/meter/meter.tsx +1 -1
- package/src/components/pagination/pagination.tsx +13 -2
- package/src/components/permission-matrix/index.ts +14 -0
- package/src/components/permission-matrix/meta.ts +23 -0
- package/src/components/permission-matrix/permission-matrix.tsx +368 -0
- package/src/components/permission-matrix/permission-model.ts +120 -0
- package/src/components/record-diff/record-diff.tsx +4 -4
- package/src/components/secret-field/index.ts +6 -0
- package/src/components/secret-field/meta.ts +21 -0
- package/src/components/secret-field/secret-field.tsx +284 -0
- package/src/components/select/select.tsx +2 -2
- package/src/components/session-expiry/index.ts +9 -0
- package/src/components/session-expiry/meta.ts +23 -0
- package/src/components/session-expiry/session-expiry.tsx +321 -0
- package/src/components/sheet/sheet.tsx +8 -4
- package/src/components/shortcut-recorder/index.ts +19 -0
- package/src/components/shortcut-recorder/meta.ts +22 -0
- package/src/components/shortcut-recorder/shortcut-model.ts +204 -0
- package/src/components/shortcut-recorder/shortcut-recorder.tsx +336 -0
- package/src/components/sidebar/index.ts +19 -0
- package/src/components/sidebar/meta.ts +25 -0
- package/src/components/sidebar/sidebar.tsx +374 -0
- package/src/components/sync-status/index.ts +9 -0
- package/src/components/sync-status/meta.ts +21 -0
- package/src/components/sync-status/sync-status.tsx +217 -0
- package/src/components/table/table.tsx +3 -3
- package/src/components/tags-input/tags-input.tsx +1 -1
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/meta.ts +21 -0
- package/src/components/textarea/textarea.tsx +210 -0
- package/src/components/time-range-picker/time-range-picker.tsx +3 -3
- package/src/components/toast/toast.tsx +14 -6
- package/src/index.ts +15 -0
- package/src/lib/styles.ts +16 -0
- package/src/registry/blocks.ts +18 -0
- package/src/registry/components.ts +30 -0
- package/src/registry/schema.ts +8 -0
- package/src/blocks/admin-users/admin-users.tsx +0 -251
- package/src/blocks/admin-users/index.ts +0 -1
- package/src/blocks/admin-users/meta.ts +0 -27
- package/src/blocks/ai-chat/ai-chat.tsx +0 -280
- package/src/blocks/ai-chat/index.ts +0 -7
- package/src/blocks/ai-chat/meta.ts +0 -32
- package/src/blocks/dashboard/dashboard.tsx +0 -192
- package/src/blocks/dashboard/index.ts +0 -6
- package/src/blocks/dashboard/meta.ts +0 -18
- package/src/blocks/forgot-password/forgot-password.tsx +0 -158
- package/src/blocks/forgot-password/index.ts +0 -1
- package/src/blocks/forgot-password/meta.ts +0 -18
- package/src/blocks/login/index.ts +0 -1
- package/src/blocks/login/login.tsx +0 -178
- package/src/blocks/login/meta.ts +0 -18
- package/src/blocks/pricing/index.ts +0 -1
- package/src/blocks/pricing/meta.ts +0 -18
- package/src/blocks/pricing/pricing.tsx +0 -176
- package/src/blocks/settings/index.ts +0 -6
- package/src/blocks/settings/meta.ts +0 -29
- package/src/blocks/settings/settings.tsx +0 -317
- package/src/blocks/signup/index.ts +0 -1
- package/src/blocks/signup/meta.ts +0 -18
- package/src/blocks/signup/signup.tsx +0 -240
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-reasoning.js","names":["CollapsiblePrimitive"],"sources":["../../../src/components/ai-reasoning/ai-reasoning.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The model's reasoning, tucked away.\n *\n * Collapsed by default and deliberately understated. Reasoning is supporting\n * material — surfacing it at the same weight as the answer buries the answer,\n * and most readers never want it. It stays one keystroke away for the people\n * who do.\n */\nexport type ReasoningProps = ComponentPropsWithRef<typeof CollapsiblePrimitive.Root>;\n\nexport function Reasoning({ className, ...props }: ReasoningProps) {\n return (\n <CollapsiblePrimitive.Root\n data-slot=\"reasoning\"\n className={cn(\"text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport interface ReasoningTriggerProps extends ComponentPropsWithRef<\n typeof CollapsiblePrimitive.Trigger\n> {\n /** Shown while reasoning is still arriving. */\n streaming?: boolean;\n label?: string;\n streamingLabel?: string;\n}\n\nexport function ReasoningTrigger({\n className,\n streaming,\n label = \"Reasoning\",\n streamingLabel = \"Thinking…\",\n children,\n ...props\n}: ReasoningTriggerProps) {\n return (\n <CollapsiblePrimitive.Trigger\n data-slot=\"reasoning-trigger\"\n className={cn(\n \"flex items-center gap-1.5 rounded-md text-xs text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:text-foreground\",\n \"[&[data-state=open]>svg:last-child]:rotate-180\",\n focusRing,\n className,\n )}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-3.5 shrink-0\">\n <path\n d=\"M9.5 21h5M12 3a6 6 0 0 1 3.6 10.8c-.6.5-.9 1.1-1 1.7l-.1.5h-5l-.1-.5c-.1-.6-.4-1.2-1-1.7A6 6 0 0 1 12 3Z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n {children ?? (streaming ? streamingLabel : label)}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className=\"size-3.5 shrink-0 transition-transform duration-[var(--duration-normal)]\"\n >\n <path\n d=\"m6 9 6 6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </CollapsiblePrimitive.Trigger>\n );\n}\n\nexport function ReasoningContent({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<typeof CollapsiblePrimitive.Content>) {\n return (\n <CollapsiblePrimitive.Content\n data-slot=\"reasoning-content\"\n className={cn(\n \"overflow-hidden\",\n \"data-[state=closed]:animate-accordion-close data-[state=open]:animate-accordion-open\",\n className,\n )}\n {...props}\n >\n <div className=\"mt-2 border-
|
|
1
|
+
{"version":3,"file":"ai-reasoning.js","names":["CollapsiblePrimitive"],"sources":["../../../src/components/ai-reasoning/ai-reasoning.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The model's reasoning, tucked away.\n *\n * Collapsed by default and deliberately understated. Reasoning is supporting\n * material — surfacing it at the same weight as the answer buries the answer,\n * and most readers never want it. It stays one keystroke away for the people\n * who do.\n */\nexport type ReasoningProps = ComponentPropsWithRef<typeof CollapsiblePrimitive.Root>;\n\nexport function Reasoning({ className, ...props }: ReasoningProps) {\n return (\n <CollapsiblePrimitive.Root\n data-slot=\"reasoning\"\n className={cn(\"text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport interface ReasoningTriggerProps extends ComponentPropsWithRef<\n typeof CollapsiblePrimitive.Trigger\n> {\n /** Shown while reasoning is still arriving. */\n streaming?: boolean;\n label?: string;\n streamingLabel?: string;\n}\n\nexport function ReasoningTrigger({\n className,\n streaming,\n label = \"Reasoning\",\n streamingLabel = \"Thinking…\",\n children,\n ...props\n}: ReasoningTriggerProps) {\n return (\n <CollapsiblePrimitive.Trigger\n data-slot=\"reasoning-trigger\"\n className={cn(\n \"flex items-center gap-1.5 rounded-md text-xs text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:text-foreground\",\n \"[&[data-state=open]>svg:last-child]:rotate-180\",\n focusRing,\n className,\n )}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-3.5 shrink-0\">\n <path\n d=\"M9.5 21h5M12 3a6 6 0 0 1 3.6 10.8c-.6.5-.9 1.1-1 1.7l-.1.5h-5l-.1-.5c-.1-.6-.4-1.2-1-1.7A6 6 0 0 1 12 3Z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n {children ?? (streaming ? streamingLabel : label)}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className=\"size-3.5 shrink-0 transition-transform duration-[var(--duration-normal)]\"\n >\n <path\n d=\"m6 9 6 6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </CollapsiblePrimitive.Trigger>\n );\n}\n\nexport function ReasoningContent({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<typeof CollapsiblePrimitive.Content>) {\n return (\n <CollapsiblePrimitive.Content\n data-slot=\"reasoning-content\"\n className={cn(\n \"overflow-hidden\",\n \"data-[state=closed]:animate-accordion-close data-[state=open]:animate-accordion-open\",\n className,\n )}\n {...props}\n >\n <div className=\"mt-2 border-s-2 border-border ps-3 text-sm whitespace-pre-wrap text-muted-foreground\">\n {children}\n </div>\n </CollapsiblePrimitive.Content>\n );\n}\n"],"mappings":";;;;;;AAkBA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAyB;CACjE,OACE,oBAACA,YAAqB,MAAtB;EACE,aAAU;EACV,WAAW,GAAG,WAAW,SAAS;EAClC,GAAI;CACL,CAAA;AAEL;AAWA,SAAgB,iBAAiB,EAC/B,WACA,WACA,QAAQ,aACR,iBAAiB,aACjB,UACA,GAAG,SACqB;CACxB,OACE,qBAACA,YAAqB,SAAtB;EACE,aAAU;EACV,WAAW,GACT,sEACA,2EACA,kDACA,WACA,SACF;EACA,GAAI;EATN,UAAA;GAWE,oBAAC,OAAD;IAAK,SAAQ;IAAY,MAAK;IAAO,eAAY;IAAO,WAAU;IAChE,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;KACd,gBAAe;IAChB,CAAA;GACE,CAAA;GACJ,aAAa,YAAY,iBAAiB;GAC3C,oBAAC,OAAD;IACE,SAAQ;IACR,MAAK;IACL,eAAY;IACZ,WAAU;IAEV,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;KACd,gBAAe;IAChB,CAAA;GACE,CAAA;EACuB;;AAElC;AAEA,SAAgB,iBAAiB,EAC/B,WACA,UACA,GAAG,SAC0D;CAC7D,OACE,oBAACA,YAAqB,SAAtB;EACE,aAAU;EACV,WAAW,GACT,mBACA,wFACA,SACF;EACA,GAAI;EAEJ,UAAA,oBAAC,OAAD;GAAK,WAAU;GACZ;EACE,CAAA;CACuB,CAAA;AAElC"}
|
|
@@ -6,7 +6,7 @@ function Response({ className, streaming, children, ...props }) {
|
|
|
6
6
|
return /* @__PURE__ */ jsxs("div", {
|
|
7
7
|
"data-slot": "response",
|
|
8
8
|
"data-streaming": streaming || void 0,
|
|
9
|
-
className: cn("text-sm leading-relaxed whitespace-pre-wrap text-foreground", "[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4", "[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em]", "[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:
|
|
9
|
+
className: cn("text-sm leading-relaxed whitespace-pre-wrap text-foreground", "[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4", "[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em]", "[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:ps-5 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:ps-5", "[&_p+p]:mt-3", className),
|
|
10
10
|
...props,
|
|
11
11
|
children: [children, streaming ? /* @__PURE__ */ jsx(ResponseCaret, {}) : null]
|
|
12
12
|
});
|
|
@@ -18,7 +18,7 @@ function ResponseCaret({ className, ...props }) {
|
|
|
18
18
|
"data-slot": "response-caret",
|
|
19
19
|
"aria-hidden": "true",
|
|
20
20
|
"data-motion": "indicator",
|
|
21
|
-
className: cn("
|
|
21
|
+
className: cn("ms-0.5 inline-block h-[1em] w-[2px] translate-y-[0.15em] animate-caret bg-current align-baseline", className),
|
|
22
22
|
...props
|
|
23
23
|
});
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-response.js","names":[],"sources":["../../../src/components/ai-response/ai-response.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The text of an assistant response.\n *\n * Renders plain text with paragraph whitespace preserved. Markdown is\n * deliberately not parsed here: a Markdown renderer is a dependency, a security\n * decision about raw HTML, and a styling surface all at once, and every product\n * makes those differently. Pass already-rendered content as children when you\n * need it — the prose styling below applies either way.\n *\n * Not a live region, and not by omission. See the note on `Conversation`:\n * announcing streamed text token by token is unusable with a screen reader.\n */\nexport interface ResponseProps extends ComponentPropsWithRef<\"div\"> {\n /** Shows the caret. Purely visual — it carries no announcement. */\n streaming?: boolean;\n}\n\nexport function Response({ className, streaming, children, ...props }: ResponseProps) {\n return (\n <div\n data-slot=\"response\"\n data-streaming={streaming || undefined}\n className={cn(\n \"text-sm leading-relaxed whitespace-pre-wrap text-foreground\",\n // Minimal prose styling for consumers who pass rendered Markdown.\n \"[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4\",\n \"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em]\",\n \"[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:
|
|
1
|
+
{"version":3,"file":"ai-response.js","names":[],"sources":["../../../src/components/ai-response/ai-response.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The text of an assistant response.\n *\n * Renders plain text with paragraph whitespace preserved. Markdown is\n * deliberately not parsed here: a Markdown renderer is a dependency, a security\n * decision about raw HTML, and a styling surface all at once, and every product\n * makes those differently. Pass already-rendered content as children when you\n * need it — the prose styling below applies either way.\n *\n * Not a live region, and not by omission. See the note on `Conversation`:\n * announcing streamed text token by token is unusable with a screen reader.\n */\nexport interface ResponseProps extends ComponentPropsWithRef<\"div\"> {\n /** Shows the caret. Purely visual — it carries no announcement. */\n streaming?: boolean;\n}\n\nexport function Response({ className, streaming, children, ...props }: ResponseProps) {\n return (\n <div\n data-slot=\"response\"\n data-streaming={streaming || undefined}\n className={cn(\n \"text-sm leading-relaxed whitespace-pre-wrap text-foreground\",\n // Minimal prose styling for consumers who pass rendered Markdown.\n \"[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4\",\n \"[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em]\",\n \"[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:ps-5 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:ps-5\",\n \"[&_p+p]:mt-3\",\n className,\n )}\n {...props}\n >\n {children}\n {streaming ? <ResponseCaret /> : null}\n </div>\n );\n}\n\n/** The trailing cursor. Decorative — the streaming state is announced, if at\n * all, through ConversationStatus. */\nexport function ResponseCaret({ className, ...props }: ComponentPropsWithRef<\"span\">) {\n return (\n <span\n data-slot=\"response-caret\"\n aria-hidden=\"true\"\n data-motion=\"indicator\"\n className={cn(\n \"ms-0.5 inline-block h-[1em] w-[2px] translate-y-[0.15em] animate-caret bg-current align-baseline\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Shown while waiting for the first token.\n *\n * A distinct state from streaming: nothing has arrived yet, so there is no text\n * to show a caret after. Labelled for assistive technology because, unlike the\n * caret, this is the only thing on screen.\n */\nexport interface ThinkingIndicatorProps extends ComponentPropsWithRef<\"div\"> {\n label?: string;\n}\n\nexport function ThinkingIndicator({\n className,\n label = \"Thinking\",\n ...props\n}: ThinkingIndicatorProps) {\n return (\n <div\n data-slot=\"thinking-indicator\"\n className={cn(\"flex items-center gap-1.5 text-sm text-muted-foreground\", className)}\n {...props}\n >\n <span className=\"sr-only\">{label}</span>\n {[0, 1, 2].map((index) => (\n <span\n key={index}\n aria-hidden=\"true\"\n className=\"size-1.5 animate-pulse-soft rounded-full bg-current\"\n style={{ animationDelay: `${String(index * 160)}ms` }}\n />\n ))}\n </div>\n );\n}\n"],"mappings":";;;;AAuBA,SAAgB,SAAS,EAAE,WAAW,WAAW,UAAU,GAAG,SAAwB;CACpF,OACE,qBAAC,OAAD;EACE,aAAU;EACV,kBAAgB,aAAa,KAAA;EAC7B,WAAW,GACT,+DAEA,+DACA,6GACA,wFACA,gBACA,SACF;EACA,GAAI;EAZN,UAAA,CAcG,UACA,YAAY,oBAAC,eAAD,CAAgB,CAAA,IAAI,IAC9B;;AAET;;;AAIA,SAAgB,cAAc,EAAE,WAAW,GAAG,SAAwC;CACpF,OACE,oBAAC,QAAD;EACE,aAAU;EACV,eAAY;EACZ,eAAY;EACZ,WAAW,GACT,oGACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAaA,SAAgB,kBAAkB,EAChC,WACA,QAAQ,YACR,GAAG,SACsB;CACzB,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,2DAA2D,SAAS;EAClF,GAAI;EAHN,UAAA,CAKE,oBAAC,QAAD;GAAM,WAAU;GAAW,UAAA;EAAY,CAAA,GACtC;GAAC;GAAG;GAAG;EAAC,CAAC,CAAC,KAAK,UACd,oBAAC,QAAD;GAEE,eAAY;GACZ,WAAU;GACV,OAAO,EAAE,gBAAgB,GAAG,OAAO,QAAQ,GAAG,EAAE,IAAI;EACrD,GAJM,KAIN,CACF,CACE;;AAET"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ComponentPropsWithRef, ReactElement } from "react";
|
|
2
|
+
//#region src/components/ai-suggested-value/ai-suggested-value.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* An AI-proposed value for a form control, offered beside it rather than
|
|
5
|
+
* written into it.
|
|
6
|
+
*
|
|
7
|
+
* Autofill is the most common shape AI takes inside ordinary software —
|
|
8
|
+
* enrich this contact, fill this form from the upload, guess the category —
|
|
9
|
+
* and nearly every implementation writes the value straight into the field
|
|
10
|
+
* as if the person had typed it. Two things go wrong. A plausible, wrong
|
|
11
|
+
* value rides through on the person's own Submit, because nothing marked it
|
|
12
|
+
* as needing a look. And once submitted, the record cannot tell a value the
|
|
13
|
+
* model supplied from one a human typed, which is the one fact an audit later
|
|
14
|
+
* needs.
|
|
15
|
+
*
|
|
16
|
+
* So the suggestion stays pending until it is accepted, and acceptance is
|
|
17
|
+
* reported rather than performed: the component hands the value to `onAccept`
|
|
18
|
+
* and never touches the control. That is also what lets it wrap any control —
|
|
19
|
+
* a select, a date, a number, a combobox — where ghost text can only complete
|
|
20
|
+
* a string. Afterwards the field says it was filled by AI, says if it was
|
|
21
|
+
* edited since, and offers to put back what was there.
|
|
22
|
+
*
|
|
23
|
+
* Deliberately no "accept all". A button that takes every suggestion at once
|
|
24
|
+
* is the review deleting itself, and every product that ships one watches its
|
|
25
|
+
* users press it without reading.
|
|
26
|
+
*/
|
|
27
|
+
interface Suggestion<T = string> {
|
|
28
|
+
value: T;
|
|
29
|
+
/** How the value reads — a select's option text, a formatted date. Defaults to the value. */
|
|
30
|
+
label?: string;
|
|
31
|
+
/** Where it came from, in the reader's language: "from the invoice header". */
|
|
32
|
+
source?: string;
|
|
33
|
+
/** 0 to 1. Shown as a number, never only as a colour. */
|
|
34
|
+
confidence?: number;
|
|
35
|
+
}
|
|
36
|
+
type SuggestionStatus = "pending" | "accepted" | "dismissed";
|
|
37
|
+
interface SuggestedValueProps<T = string> extends Omit<ComponentPropsWithRef<"div">, "children" | "id" | "aria-describedby" | "aria-invalid"> {
|
|
38
|
+
/** The field, for the buttons' names: "Accept suggestion — Company". */
|
|
39
|
+
label: string;
|
|
40
|
+
/** Null or undefined offers nothing. A different suggestion starts pending again. */
|
|
41
|
+
suggestion?: Suggestion<T> | null;
|
|
42
|
+
/**
|
|
43
|
+
* The control's current value. Lets the marker say the fill was edited
|
|
44
|
+
* since, and lets Undo restore what the field held before.
|
|
45
|
+
*/
|
|
46
|
+
value?: T;
|
|
47
|
+
/** Supply to own the status; otherwise it is kept here. */
|
|
48
|
+
status?: SuggestionStatus;
|
|
49
|
+
onAccept: (value: T, suggestion: Suggestion<T>) => void;
|
|
50
|
+
onDismiss?: (suggestion: Suggestion<T>) => void;
|
|
51
|
+
/** Called with the value the field held before the fill, so it can be put back. */
|
|
52
|
+
onRevert?: (previous: T | undefined) => void;
|
|
53
|
+
/** How to compare values. Object.is by default. */
|
|
54
|
+
equals?: (a: T, b: T) => boolean;
|
|
55
|
+
/** Confidence below this is called low, in words. */
|
|
56
|
+
lowConfidenceBelow?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Announce a suggestion when it arrives. Off by default: a form that fills
|
|
59
|
+
* twenty fields at once would narrate all twenty, and the description on
|
|
60
|
+
* the control already tells a reader when they reach the field.
|
|
61
|
+
*/
|
|
62
|
+
announce?: boolean;
|
|
63
|
+
/** The control. Receives the description, and any id or ARIA a FormControl passes down. */
|
|
64
|
+
children: ReactElement<Record<string, unknown>>;
|
|
65
|
+
id?: string;
|
|
66
|
+
"aria-describedby"?: string;
|
|
67
|
+
"aria-invalid"?: ComponentPropsWithRef<"input">["aria-invalid"];
|
|
68
|
+
}
|
|
69
|
+
declare function SuggestedValue<T = string>({ className, label, suggestion, value, status: controlledStatus, onAccept, onDismiss, onRevert, equals, lowConfidenceBelow, announce, children, id, "aria-describedby": describedByProp, "aria-invalid": ariaInvalid, ...props }: SuggestedValueProps<T>): import("react").JSX.Element;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { SuggestedValue, SuggestedValueProps, Suggestion, SuggestionStatus };
|
|
72
|
+
//# sourceMappingURL=ai-suggested-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-suggested-value.d.ts","names":[],"sources":["../../../src/components/ai-suggested-value/ai-suggested-value.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;UAwCiB,WAAW;EAC1B,OAAO;;EAEP;;EAEA;;EAEA;;KAGU;UAEK,oBAAoB,oBAAoB,KACvD;;EAIA;;EAEA,aAAa,WAAW;;;;;EAKxB,QAAQ;;EAER,SAAS;EACT,WAAW,OAAO,GAAG,YAAY,WAAW;EAC5C,aAAa,YAAY,WAAW;;EAEpC,YAAY,UAAU;;EAEtB,UAAU,GAAG,GAAG,GAAG;;EAEnB;;;;;;EAMA;;EAEA,UAAU,aAAa;EAEvB;EACA;EACA,iBAAiB;;iBAqBH,eAAe,cAC7B,WACA,OACA,YACA,OACA,QAAQ,kBACR,UACA,WACA,UACA,QACA,oBACA,UACA,UACA,wBACoB,iCACJ,gBACb,SACF,oBAAoB,qBAAE,IAAA"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { disabledStyles, focusRing } from "../../lib/styles.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cloneElement, isValidElement, useId, useState } from "react";
|
|
6
|
+
//#region src/components/ai-suggested-value/ai-suggested-value.tsx
|
|
7
|
+
function defaultLabel(value) {
|
|
8
|
+
if (value === null || value === void 0) return "";
|
|
9
|
+
if (typeof value === "string") return value;
|
|
10
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
11
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
12
|
+
if (value instanceof Date) return value.toLocaleDateString();
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
function sameSuggestion(a, b, equals) {
|
|
16
|
+
if (!a || !b) return a === b;
|
|
17
|
+
return equals(a.value, b.value) && a.label === b.label && a.source === b.source;
|
|
18
|
+
}
|
|
19
|
+
function SuggestedValue({ className, label, suggestion, value, status: controlledStatus, onAccept, onDismiss, onRevert, equals = Object.is, lowConfidenceBelow = .7, announce = false, children, id, "aria-describedby": describedByProp, "aria-invalid": ariaInvalid, ...props }) {
|
|
20
|
+
const descriptionId = useId();
|
|
21
|
+
const [internalStatus, setInternalStatus] = useState("pending");
|
|
22
|
+
const [previous, setPrevious] = useState(void 0);
|
|
23
|
+
const [seen, setSeen] = useState(suggestion);
|
|
24
|
+
if (!sameSuggestion(seen, suggestion, equals)) {
|
|
25
|
+
setSeen(suggestion);
|
|
26
|
+
setInternalStatus("pending");
|
|
27
|
+
}
|
|
28
|
+
const status = controlledStatus ?? internalStatus;
|
|
29
|
+
const offered = suggestion ?? null;
|
|
30
|
+
const pending = offered !== null && status === "pending";
|
|
31
|
+
const accepted = offered !== null && status === "accepted";
|
|
32
|
+
const edited = accepted && value !== void 0 && !equals(value, offered.value);
|
|
33
|
+
const low = offered?.confidence !== void 0 && offered.confidence < lowConfidenceBelow;
|
|
34
|
+
const percent = offered?.confidence === void 0 ? null : new Intl.NumberFormat(void 0, {
|
|
35
|
+
style: "percent",
|
|
36
|
+
maximumFractionDigits: 0
|
|
37
|
+
}).format(offered.confidence);
|
|
38
|
+
const source = offered?.source ? ` ${offered.source}` : "";
|
|
39
|
+
let description = null;
|
|
40
|
+
if (pending) description = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
41
|
+
"Suggested:",
|
|
42
|
+
" ",
|
|
43
|
+
/* @__PURE__ */ jsx("strong", {
|
|
44
|
+
className: "font-medium text-foreground",
|
|
45
|
+
children: offered.label ?? defaultLabel(offered.value)
|
|
46
|
+
}),
|
|
47
|
+
source ? `,${source}` : null,
|
|
48
|
+
percent !== null ? /* @__PURE__ */ jsx("span", {
|
|
49
|
+
className: cn(low && "text-warning"),
|
|
50
|
+
children: low ? `. Low confidence, ${percent} — worth checking` : `. ${percent} confidence`
|
|
51
|
+
}) : null
|
|
52
|
+
] });
|
|
53
|
+
else if (accepted) description = edited ? `Filled by AI${source ? `,${source}` : ""}, then edited` : `Filled by AI${source ? `,${source}` : ""}`;
|
|
54
|
+
const describedBy = [
|
|
55
|
+
isValidElement(children) ? children.props["aria-describedby"] : void 0,
|
|
56
|
+
describedByProp,
|
|
57
|
+
description !== null ? descriptionId : null
|
|
58
|
+
].filter(Boolean).join(" ") || void 0;
|
|
59
|
+
const control = isValidElement(children) ? cloneElement(children, {
|
|
60
|
+
id: id ?? children.props.id,
|
|
61
|
+
"aria-describedby": describedBy,
|
|
62
|
+
"aria-invalid": ariaInvalid ?? children.props["aria-invalid"]
|
|
63
|
+
}) : children;
|
|
64
|
+
const accept = () => {
|
|
65
|
+
if (!offered) return;
|
|
66
|
+
setPrevious(value);
|
|
67
|
+
setInternalStatus("accepted");
|
|
68
|
+
onAccept(offered.value, offered);
|
|
69
|
+
};
|
|
70
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
71
|
+
"data-slot": "suggested-value",
|
|
72
|
+
"data-status": offered ? status : void 0,
|
|
73
|
+
"data-filled-by": accepted ? "ai" : void 0,
|
|
74
|
+
"data-edited": edited || void 0,
|
|
75
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
76
|
+
...props,
|
|
77
|
+
children: [control, /* @__PURE__ */ jsxs("div", {
|
|
78
|
+
"data-slot": "suggested-value-row",
|
|
79
|
+
"aria-live": announce ? "polite" : void 0,
|
|
80
|
+
className: cn("flex flex-wrap items-center gap-x-3 gap-y-1 text-xs", pending && "rounded-md border border-primary/40 bg-primary/5 px-2.5 py-1.5"),
|
|
81
|
+
children: [
|
|
82
|
+
description !== null ? /* @__PURE__ */ jsx("p", {
|
|
83
|
+
id: descriptionId,
|
|
84
|
+
"data-slot": "suggested-value-description",
|
|
85
|
+
className: "m-0 flex-1 text-muted-foreground",
|
|
86
|
+
children: description
|
|
87
|
+
}) : null,
|
|
88
|
+
pending ? /* @__PURE__ */ jsxs("span", {
|
|
89
|
+
className: "flex items-center gap-2",
|
|
90
|
+
children: [/* @__PURE__ */ jsx(RowButton, {
|
|
91
|
+
variant: "primary",
|
|
92
|
+
"aria-label": `Accept suggestion — ${label}`,
|
|
93
|
+
onClick: accept,
|
|
94
|
+
children: "Accept"
|
|
95
|
+
}), /* @__PURE__ */ jsx(RowButton, {
|
|
96
|
+
"aria-label": `Dismiss suggestion — ${label}`,
|
|
97
|
+
onClick: () => {
|
|
98
|
+
setInternalStatus("dismissed");
|
|
99
|
+
onDismiss?.(offered);
|
|
100
|
+
},
|
|
101
|
+
children: "Dismiss"
|
|
102
|
+
})]
|
|
103
|
+
}) : null,
|
|
104
|
+
accepted && onRevert ? /* @__PURE__ */ jsx(RowButton, {
|
|
105
|
+
"aria-label": `Undo — ${label}`,
|
|
106
|
+
onClick: () => {
|
|
107
|
+
setInternalStatus("pending");
|
|
108
|
+
onRevert(previous);
|
|
109
|
+
},
|
|
110
|
+
children: "Undo"
|
|
111
|
+
}) : null
|
|
112
|
+
]
|
|
113
|
+
})]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function RowButton({ className, variant = "default", ...props }) {
|
|
117
|
+
return /* @__PURE__ */ jsx("button", {
|
|
118
|
+
type: "button",
|
|
119
|
+
className: cn("rounded-md border px-2 py-0.5 text-xs font-medium transition-colors", variant === "primary" ? "border-primary bg-primary text-primary-foreground hover:bg-primary-hover" : "border-input bg-background hover:bg-accent hover:text-accent-foreground", focusRing, disabledStyles, className),
|
|
120
|
+
...props
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { SuggestedValue };
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=ai-suggested-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-suggested-value.js","names":[],"sources":["../../../src/components/ai-suggested-value/ai-suggested-value.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n cloneElement,\n isValidElement,\n useId,\n useState,\n type ComponentPropsWithRef,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * An AI-proposed value for a form control, offered beside it rather than\n * written into it.\n *\n * Autofill is the most common shape AI takes inside ordinary software —\n * enrich this contact, fill this form from the upload, guess the category —\n * and nearly every implementation writes the value straight into the field\n * as if the person had typed it. Two things go wrong. A plausible, wrong\n * value rides through on the person's own Submit, because nothing marked it\n * as needing a look. And once submitted, the record cannot tell a value the\n * model supplied from one a human typed, which is the one fact an audit later\n * needs.\n *\n * So the suggestion stays pending until it is accepted, and acceptance is\n * reported rather than performed: the component hands the value to `onAccept`\n * and never touches the control. That is also what lets it wrap any control —\n * a select, a date, a number, a combobox — where ghost text can only complete\n * a string. Afterwards the field says it was filled by AI, says if it was\n * edited since, and offers to put back what was there.\n *\n * Deliberately no \"accept all\". A button that takes every suggestion at once\n * is the review deleting itself, and every product that ships one watches its\n * users press it without reading.\n */\n\nexport interface Suggestion<T = string> {\n value: T;\n /** How the value reads — a select's option text, a formatted date. Defaults to the value. */\n label?: string;\n /** Where it came from, in the reader's language: \"from the invoice header\". */\n source?: string;\n /** 0 to 1. Shown as a number, never only as a colour. */\n confidence?: number;\n}\n\nexport type SuggestionStatus = \"pending\" | \"accepted\" | \"dismissed\";\n\nexport interface SuggestedValueProps<T = string> extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"children\" | \"id\" | \"aria-describedby\" | \"aria-invalid\"\n> {\n /** The field, for the buttons' names: \"Accept suggestion — Company\". */\n label: string;\n /** Null or undefined offers nothing. A different suggestion starts pending again. */\n suggestion?: Suggestion<T> | null;\n /**\n * The control's current value. Lets the marker say the fill was edited\n * since, and lets Undo restore what the field held before.\n */\n value?: T;\n /** Supply to own the status; otherwise it is kept here. */\n status?: SuggestionStatus;\n onAccept: (value: T, suggestion: Suggestion<T>) => void;\n onDismiss?: (suggestion: Suggestion<T>) => void;\n /** Called with the value the field held before the fill, so it can be put back. */\n onRevert?: (previous: T | undefined) => void;\n /** How to compare values. Object.is by default. */\n equals?: (a: T, b: T) => boolean;\n /** Confidence below this is called low, in words. */\n lowConfidenceBelow?: number;\n /**\n * Announce a suggestion when it arrives. Off by default: a form that fills\n * twenty fields at once would narrate all twenty, and the description on\n * the control already tells a reader when they reach the field.\n */\n announce?: boolean;\n /** The control. Receives the description, and any id or ARIA a FormControl passes down. */\n children: ReactElement<Record<string, unknown>>;\n /* Passed through to the control, so this can sit inside a FormControl. */\n id?: string;\n \"aria-describedby\"?: string;\n \"aria-invalid\"?: ComponentPropsWithRef<\"input\">[\"aria-invalid\"];\n}\n\nfunction defaultLabel(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"bigint\") return String(value);\n if (typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n if (value instanceof Date) return value.toLocaleDateString();\n return JSON.stringify(value);\n}\n\nfunction sameSuggestion<T>(\n a: Suggestion<T> | null | undefined,\n b: Suggestion<T> | null | undefined,\n equals: (x: T, y: T) => boolean,\n): boolean {\n if (!a || !b) return a === b;\n return equals(a.value, b.value) && a.label === b.label && a.source === b.source;\n}\n\nexport function SuggestedValue<T = string>({\n className,\n label,\n suggestion,\n value,\n status: controlledStatus,\n onAccept,\n onDismiss,\n onRevert,\n equals = Object.is,\n lowConfidenceBelow = 0.7,\n announce = false,\n children,\n id,\n \"aria-describedby\": describedByProp,\n \"aria-invalid\": ariaInvalid,\n ...props\n}: SuggestedValueProps<T>) {\n const descriptionId = useId();\n\n const [internalStatus, setInternalStatus] = useState<SuggestionStatus>(\"pending\");\n const [previous, setPrevious] = useState<T | undefined>(undefined);\n\n // A new suggestion starts pending, whatever happened to the last one.\n // Compared by content rather than identity, so a parent that rebuilds the\n // object on every keystroke does not reopen a suggestion already accepted.\n const [seen, setSeen] = useState(suggestion);\n if (!sameSuggestion(seen, suggestion, equals)) {\n setSeen(suggestion);\n setInternalStatus(\"pending\");\n }\n\n const status = controlledStatus ?? internalStatus;\n const offered = suggestion ?? null;\n const pending = offered !== null && status === \"pending\";\n const accepted = offered !== null && status === \"accepted\";\n const edited = accepted && value !== undefined && !equals(value, offered.value);\n\n const low = offered?.confidence !== undefined && offered.confidence < lowConfidenceBelow;\n const percent =\n offered?.confidence === undefined\n ? null\n : new Intl.NumberFormat(undefined, { style: \"percent\", maximumFractionDigits: 0 }).format(\n offered.confidence,\n );\n\n const source = offered?.source ? ` ${offered.source}` : \"\";\n\n // What the control's description says. This is how a reader who lands on\n // the field learns there is a suggestion, without any announcement.\n let description: ReactNode = null;\n if (pending) {\n description = (\n <>\n Suggested:{\" \"}\n <strong className=\"font-medium text-foreground\">\n {offered.label ?? defaultLabel(offered.value)}\n </strong>\n {source ? `,${source}` : null}\n {percent !== null ? (\n <span className={cn(low && \"text-warning\")}>\n {low ? `. Low confidence, ${percent} — worth checking` : `. ${percent} confidence`}\n </span>\n ) : null}\n </>\n );\n } else if (accepted) {\n description = edited\n ? `Filled by AI${source ? `,${source}` : \"\"}, then edited`\n : `Filled by AI${source ? `,${source}` : \"\"}`;\n }\n\n // Merged with whatever already describes the control — a FormControl's\n // description and message, or the child's own — rather than replacing it.\n const childDescribedBy = isValidElement(children)\n ? (children.props[\"aria-describedby\"] as string | undefined)\n : undefined;\n const describedBy =\n [childDescribedBy, describedByProp, description !== null ? descriptionId : null]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n const control = isValidElement(children)\n ? cloneElement(children, {\n id: id ?? children.props.id,\n \"aria-describedby\": describedBy,\n \"aria-invalid\": ariaInvalid ?? children.props[\"aria-invalid\"],\n })\n : children;\n\n const accept = () => {\n if (!offered) return;\n setPrevious(value);\n setInternalStatus(\"accepted\");\n onAccept(offered.value, offered);\n };\n\n return (\n <div\n data-slot=\"suggested-value\"\n data-status={offered ? status : undefined}\n data-filled-by={accepted ? \"ai\" : undefined}\n data-edited={edited || undefined}\n className={cn(\"flex flex-col gap-1.5\", className)}\n {...props}\n >\n {control}\n\n {/* Present from the start and never display:none, so that with `announce`\n a suggestion arriving after mount is heard — a region that enters the\n accessibility tree at the same moment as its content announces\n nothing. Empty, it has no height. */}\n <div\n data-slot=\"suggested-value-row\"\n aria-live={announce ? \"polite\" : undefined}\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs\",\n pending && \"rounded-md border border-primary/40 bg-primary/5 px-2.5 py-1.5\",\n )}\n >\n {description !== null ? (\n <p\n id={descriptionId}\n data-slot=\"suggested-value-description\"\n className=\"m-0 flex-1 text-muted-foreground\"\n >\n {description}\n </p>\n ) : null}\n\n {pending ? (\n <span className=\"flex items-center gap-2\">\n <RowButton\n variant=\"primary\"\n aria-label={`Accept suggestion — ${label}`}\n onClick={accept}\n >\n Accept\n </RowButton>\n <RowButton\n aria-label={`Dismiss suggestion — ${label}`}\n onClick={() => {\n setInternalStatus(\"dismissed\");\n onDismiss?.(offered);\n }}\n >\n Dismiss\n </RowButton>\n </span>\n ) : null}\n\n {accepted && onRevert ? (\n <RowButton\n aria-label={`Undo — ${label}`}\n onClick={() => {\n setInternalStatus(\"pending\");\n onRevert(previous);\n }}\n >\n Undo\n </RowButton>\n ) : null}\n </div>\n </div>\n );\n}\n\nfunction RowButton({\n className,\n variant = \"default\",\n ...props\n}: ComponentPropsWithRef<\"button\"> & { variant?: \"default\" | \"primary\" }) {\n return (\n <button\n type=\"button\"\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium transition-colors\",\n variant === \"primary\"\n ? \"border-primary bg-primary text-primary-foreground hover:bg-primary-hover\"\n : \"border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n disabledStyles,\n className,\n )}\n {...props}\n />\n );\n}\n"],"mappings":";;;;;;AAyFA,SAAS,aAAa,OAAwB;CAC5C,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CAC/E,IAAI,OAAO,UAAU,WAAW,OAAO,QAAQ,QAAQ;CACvD,IAAI,iBAAiB,MAAM,OAAO,MAAM,mBAAmB;CAC3D,OAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,eACP,GACA,GACA,QACS;CACT,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,MAAM;CAC3B,OAAO,OAAO,EAAE,OAAO,EAAE,KAAK,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE;AAC3E;AAEA,SAAgB,eAA2B,EACzC,WACA,OACA,YACA,OACA,QAAQ,kBACR,UACA,WACA,UACA,SAAS,OAAO,IAChB,qBAAqB,IACrB,WAAW,OACX,UACA,IACA,oBAAoB,iBACpB,gBAAgB,aAChB,GAAG,SACsB;CACzB,MAAM,gBAAgB,MAAM;CAE5B,MAAM,CAAC,gBAAgB,qBAAqB,SAA2B,SAAS;CAChF,MAAM,CAAC,UAAU,eAAe,SAAwB,KAAA,CAAS;CAKjE,MAAM,CAAC,MAAM,WAAW,SAAS,UAAU;CAC3C,IAAI,CAAC,eAAe,MAAM,YAAY,MAAM,GAAG;EAC7C,QAAQ,UAAU;EAClB,kBAAkB,SAAS;CAC7B;CAEA,MAAM,SAAS,oBAAoB;CACnC,MAAM,UAAU,cAAc;CAC9B,MAAM,UAAU,YAAY,QAAQ,WAAW;CAC/C,MAAM,WAAW,YAAY,QAAQ,WAAW;CAChD,MAAM,SAAS,YAAY,UAAU,KAAA,KAAa,CAAC,OAAO,OAAO,QAAQ,KAAK;CAE9E,MAAM,MAAM,SAAS,eAAe,KAAA,KAAa,QAAQ,aAAa;CACtE,MAAM,UACJ,SAAS,eAAe,KAAA,IACpB,OACA,IAAI,KAAK,aAAa,KAAA,GAAW;EAAE,OAAO;EAAW,uBAAuB;CAAE,CAAC,CAAC,CAAC,OAC/E,QAAQ,UACV;CAEN,MAAM,SAAS,SAAS,SAAS,IAAI,QAAQ,WAAW;CAIxD,IAAI,cAAyB;CAC7B,IAAI,SACF,cACE,qBAAA,UAAA,EAAA,UAAA;EAAE;EACW;EACX,oBAAC,UAAD;GAAQ,WAAU;GACf,UAAA,QAAQ,SAAS,aAAa,QAAQ,KAAK;EACtC,CAAA;EACP,SAAS,IAAI,WAAW;EACxB,YAAY,OACX,oBAAC,QAAD;GAAM,WAAW,GAAG,OAAO,cAAc;GACtC,UAAA,MAAM,qBAAqB,QAAQ,qBAAqB,KAAK,QAAQ;EAClE,CAAA,IACJ;CACJ,EAAA,CAAA;MAEC,IAAI,UACT,cAAc,SACV,eAAe,SAAS,IAAI,WAAW,GAAG,iBAC1C,eAAe,SAAS,IAAI,WAAW;CAQ7C,MAAM,cACJ;EAJuB,eAAe,QAAQ,IAC3C,SAAS,MAAM,sBAChB,KAAA;EAEiB;EAAiB,gBAAgB,OAAO,gBAAgB;CAAI,CAAC,CAC7E,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,KAAK,KAAA;CAElB,MAAM,UAAU,eAAe,QAAQ,IACnC,aAAa,UAAU;EACrB,IAAI,MAAM,SAAS,MAAM;EACzB,oBAAoB;EACpB,gBAAgB,eAAe,SAAS,MAAM;CAChD,CAAC,IACD;CAEJ,MAAM,eAAe;EACnB,IAAI,CAAC,SAAS;EACd,YAAY,KAAK;EACjB,kBAAkB,UAAU;EAC5B,SAAS,QAAQ,OAAO,OAAO;CACjC;CAEA,OACE,qBAAC,OAAD;EACE,aAAU;EACV,eAAa,UAAU,SAAS,KAAA;EAChC,kBAAgB,WAAW,OAAO,KAAA;EAClC,eAAa,UAAU,KAAA;EACvB,WAAW,GAAG,yBAAyB,SAAS;EAChD,GAAI;EANN,UAAA,CAQG,SAMD,qBAAC,OAAD;GACE,aAAU;GACV,aAAW,WAAW,WAAW,KAAA;GACjC,WAAW,GACT,uDACA,WAAW,gEACb;GANF,UAAA;IAQG,gBAAgB,OACf,oBAAC,KAAD;KACE,IAAI;KACJ,aAAU;KACV,WAAU;KAET,UAAA;IACA,CAAA,IACD;IAEH,UACC,qBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CACE,oBAAC,WAAD;MACE,SAAQ;MACR,cAAY,uBAAuB;MACnC,SAAS;MACV,UAAA;KAEU,CAAA,GACX,oBAAC,WAAD;MACE,cAAY,wBAAwB;MACpC,eAAe;OACb,kBAAkB,WAAW;OAC7B,YAAY,OAAO;MACrB;MACD,UAAA;KAEU,CAAA,CACP;IACJ,CAAA,IAAA;IAEH,YAAY,WACX,oBAAC,WAAD;KACE,cAAY,UAAU;KACtB,eAAe;MACb,kBAAkB,SAAS;MAC3B,SAAS,QAAQ;KACnB;KACD,UAAA;IAEU,CAAA,IACT;GACD;EACF,CAAA,CAAA;;AAET;AAEA,SAAS,UAAU,EACjB,WACA,UAAU,WACV,GAAG,SACqE;CACxE,OACE,oBAAC,UAAD;EACE,MAAK;EACL,WAAW,GACT,uEACA,YAAY,YACR,6EACA,2EACJ,WACA,gBACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineMeta } from "../../registry/schema.js";
|
|
2
|
+
//#region src/components/ai-suggested-value/meta.ts
|
|
3
|
+
const meta = defineMeta({
|
|
4
|
+
name: "ai-suggested-value",
|
|
5
|
+
title: "AI Suggested Value",
|
|
6
|
+
description: "An AI-proposed value for any form control, pending until accepted and marked afterwards.",
|
|
7
|
+
category: "ai",
|
|
8
|
+
status: "stable",
|
|
9
|
+
dependencies: [],
|
|
10
|
+
registryDependencies: [],
|
|
11
|
+
files: ["ai-suggested-value.tsx"],
|
|
12
|
+
a11y: "The suggestion is the control's description, so a reader who lands on the field hears it without any announcement — and announcing is opt-in, because a form filling twenty fields at once would narrate all twenty. The row that holds it is rendered from the start and never display:none, so when announcing is on, a suggestion arriving after mount is actually heard. Each button's name carries the field it belongs to, so a page of Accepts is a page of different buttons. Provenance and confidence are words with a number, never a colour. Acceptance is reported, not performed: the control is the consumer's, so its value, its label and its own keyboard behaviour are untouched — there is no shortcut hijacked from a select or a date field. The id and ARIA a FormControl passes down are forwarded to the control, and an existing aria-describedby is merged rather than replaced."
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { meta };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.js","names":[],"sources":["../../../src/components/ai-suggested-value/meta.ts"],"sourcesContent":["import { defineMeta } from \"@/registry/schema\";\n\nexport const meta = defineMeta({\n name: \"ai-suggested-value\",\n title: \"AI Suggested Value\",\n description:\n \"An AI-proposed value for any form control, pending until accepted and marked afterwards.\",\n category: \"ai\",\n status: \"stable\",\n dependencies: [],\n registryDependencies: [],\n files: [\"ai-suggested-value.tsx\"],\n a11y:\n \"The suggestion is the control's description, so a reader who lands on the field hears it \" +\n \"without any announcement — and announcing is opt-in, because a form filling twenty fields at \" +\n \"once would narrate all twenty. The row that holds it is rendered from the start and never \" +\n \"display:none, so when announcing is on, a suggestion arriving after mount is actually heard. \" +\n \"Each button's name carries the field it belongs to, so a page of Accepts is a page of \" +\n \"different buttons. Provenance and confidence are words with a number, never a colour. \" +\n \"Acceptance is reported, not performed: the control is the consumer's, so its value, its \" +\n \"label and its own keyboard behaviour are untouched — there is no shortcut hijacked from a \" +\n \"select or a date field. The id and ARIA a FormControl passes down are forwarded to the \" +\n \"control, and an existing aria-describedby is merged rather than replaced.\",\n});\n"],"mappings":";;AAEA,MAAa,OAAO,WAAW;CAC7B,MAAM;CACN,OAAO;CACP,aACE;CACF,UAAU;CACV,QAAQ;CACR,cAAc,CAAC;CACf,sBAAsB,CAAC;CACvB,OAAO,CAAC,wBAAwB;CAChC,MACE;AAUJ,CAAC"}
|
|
@@ -31,7 +31,7 @@ function Tool({ className, status = "pending", ...props }) {
|
|
|
31
31
|
function ToolHeader({ className, name, status = "pending", statusLabel, icon, ...props }) {
|
|
32
32
|
return /* @__PURE__ */ jsxs(Collapsible.Trigger, {
|
|
33
33
|
"data-slot": "tool-header",
|
|
34
|
-
className: cn("flex w-full items-center gap-2 px-3 py-2 text-
|
|
34
|
+
className: cn("flex w-full items-center gap-2 px-3 py-2 text-start", "transition-colors duration-[var(--duration-fast)] hover:bg-accent/50", "[&[data-state=open]>svg:last-child]:rotate-180", focusRing, className),
|
|
35
35
|
...props,
|
|
36
36
|
children: [
|
|
37
37
|
icon ?? /* @__PURE__ */ jsx(ToolIcon, { status }),
|
|
@@ -41,7 +41,7 @@ function ToolHeader({ className, name, status = "pending", statusLabel, icon, ..
|
|
|
41
41
|
}),
|
|
42
42
|
/* @__PURE__ */ jsx("span", {
|
|
43
43
|
"data-slot": "tool-status",
|
|
44
|
-
className: cn("
|
|
44
|
+
className: cn("ms-auto text-xs", status === "error" ? "text-destructive" : "text-muted-foreground"),
|
|
45
45
|
children: statusLabel ?? STATUS_LABEL[status]
|
|
46
46
|
}),
|
|
47
47
|
/* @__PURE__ */ jsx("svg", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-tool.js","names":["CollapsiblePrimitive"],"sources":["../../../src/components/ai-tool/ai-tool.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef, ReactNode } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A tool the model called, and what came back.\n *\n * Collapsed by default: a tool call is provenance, not the answer. Someone\n * checking why a response says what it does needs to open it; everybody else\n * needs it to stay out of the way.\n *\n * The status is the whole point of the header, so it is text, never a coloured\n * dot alone.\n */\n\nexport type ToolStatus = \"pending\" | \"running\" | \"success\" | \"error\";\n\nconst STATUS_LABEL: Record<ToolStatus, string> = {\n pending: \"Queued\",\n running: \"Running\",\n success: \"Completed\",\n error: \"Failed\",\n};\n\nconst toolVariants = cva(\"overflow-hidden rounded-lg border text-sm\", {\n variants: {\n status: {\n pending: \"border-border bg-muted/30\",\n running: \"border-info/30 bg-info/5\",\n success: \"border-border bg-muted/30\",\n error: \"border-destructive/30 bg-destructive/5\",\n },\n },\n defaultVariants: {\n status: \"pending\",\n },\n});\n\nexport interface ToolProps\n extends\n ComponentPropsWithRef<typeof CollapsiblePrimitive.Root>,\n VariantProps<typeof toolVariants> {\n status?: ToolStatus;\n}\n\nexport function Tool({ className, status = \"pending\", ...props }: ToolProps) {\n return (\n <CollapsiblePrimitive.Root\n data-slot=\"tool\"\n data-status={status}\n className={cn(toolVariants({ status }), className)}\n {...props}\n />\n );\n}\n\nexport interface ToolHeaderProps extends ComponentPropsWithRef<\n typeof CollapsiblePrimitive.Trigger\n> {\n /** The tool's name, as the model called it. */\n name: string;\n status?: ToolStatus;\n /** Overrides the status wording. */\n statusLabel?: string;\n icon?: ReactNode;\n}\n\nexport function ToolHeader({\n className,\n name,\n status = \"pending\",\n statusLabel,\n icon,\n ...props\n}: ToolHeaderProps) {\n return (\n <CollapsiblePrimitive.Trigger\n data-slot=\"tool-header\"\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-
|
|
1
|
+
{"version":3,"file":"ai-tool.js","names":["CollapsiblePrimitive"],"sources":["../../../src/components/ai-tool/ai-tool.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef, ReactNode } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A tool the model called, and what came back.\n *\n * Collapsed by default: a tool call is provenance, not the answer. Someone\n * checking why a response says what it does needs to open it; everybody else\n * needs it to stay out of the way.\n *\n * The status is the whole point of the header, so it is text, never a coloured\n * dot alone.\n */\n\nexport type ToolStatus = \"pending\" | \"running\" | \"success\" | \"error\";\n\nconst STATUS_LABEL: Record<ToolStatus, string> = {\n pending: \"Queued\",\n running: \"Running\",\n success: \"Completed\",\n error: \"Failed\",\n};\n\nconst toolVariants = cva(\"overflow-hidden rounded-lg border text-sm\", {\n variants: {\n status: {\n pending: \"border-border bg-muted/30\",\n running: \"border-info/30 bg-info/5\",\n success: \"border-border bg-muted/30\",\n error: \"border-destructive/30 bg-destructive/5\",\n },\n },\n defaultVariants: {\n status: \"pending\",\n },\n});\n\nexport interface ToolProps\n extends\n ComponentPropsWithRef<typeof CollapsiblePrimitive.Root>,\n VariantProps<typeof toolVariants> {\n status?: ToolStatus;\n}\n\nexport function Tool({ className, status = \"pending\", ...props }: ToolProps) {\n return (\n <CollapsiblePrimitive.Root\n data-slot=\"tool\"\n data-status={status}\n className={cn(toolVariants({ status }), className)}\n {...props}\n />\n );\n}\n\nexport interface ToolHeaderProps extends ComponentPropsWithRef<\n typeof CollapsiblePrimitive.Trigger\n> {\n /** The tool's name, as the model called it. */\n name: string;\n status?: ToolStatus;\n /** Overrides the status wording. */\n statusLabel?: string;\n icon?: ReactNode;\n}\n\nexport function ToolHeader({\n className,\n name,\n status = \"pending\",\n statusLabel,\n icon,\n ...props\n}: ToolHeaderProps) {\n return (\n <CollapsiblePrimitive.Trigger\n data-slot=\"tool-header\"\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-start\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent/50\",\n \"[&[data-state=open]>svg:last-child]:rotate-180\",\n focusRing,\n className,\n )}\n {...props}\n >\n {icon ?? <ToolIcon status={status} />}\n <span className=\"font-mono text-xs font-medium\">{name}</span>\n {/* Status in words. A coloured dot alone says nothing to a screen reader\n and nothing to anyone who cannot distinguish the colours. */}\n <span\n data-slot=\"tool-status\"\n className={cn(\n \"ms-auto text-xs\",\n status === \"error\" ? \"text-destructive\" : \"text-muted-foreground\",\n )}\n >\n {statusLabel ?? STATUS_LABEL[status]}\n </span>\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className=\"size-3.5 shrink-0 text-muted-foreground transition-transform duration-[var(--duration-normal)]\"\n >\n <path\n d=\"m6 9 6 6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </CollapsiblePrimitive.Trigger>\n );\n}\n\nfunction ToolIcon({ status }: { status: ToolStatus }) {\n if (status === \"running\") {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className=\"size-3.5 shrink-0 animate-spin text-info\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeWidth=\"2.5\" opacity=\"0.25\" />\n <path\n d=\"M12 3a9 9 0 0 1 9 9\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n }\n\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(\n \"size-3.5 shrink-0\",\n status === \"error\" ? \"text-destructive\" : \"text-muted-foreground\",\n )}\n >\n <path\n d={\n status === \"success\"\n ? \"m5 13 4 4L19 7\"\n : status === \"error\"\n ? \"M12 8v5m0 3h.01M10.3 4.3 2.6 18a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0Z\"\n : \"M14.7 6.3a4 4 0 0 1-5 5L5 16v3h3l4.7-4.7a4 4 0 0 1 5-5l-2.3 2.3\"\n }\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n\nexport function ToolContent({\n className,\n ...props\n}: ComponentPropsWithRef<typeof CollapsiblePrimitive.Content>) {\n return (\n <CollapsiblePrimitive.Content\n data-slot=\"tool-content\"\n className={cn(\n \"overflow-hidden border-t border-border\",\n \"data-[state=closed]:animate-accordion-close data-[state=open]:animate-accordion-open\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface ToolSectionProps extends ComponentPropsWithRef<\"div\"> {\n label: string;\n}\n\n/** A labelled block inside the disclosure: the arguments, or the result. */\nexport function ToolSection({ className, label, children, ...props }: ToolSectionProps) {\n return (\n <div\n data-slot=\"tool-section\"\n className={cn(\"border-b border-border/60 px-3 py-2 last:border-b-0\", className)}\n {...props}\n >\n <p className=\"mb-1 text-2xs font-medium tracking-wide text-muted-foreground uppercase\">\n {label}\n </p>\n {children}\n </div>\n );\n}\n\n/**\n * Serialised arguments or output.\n *\n * A `<pre>` with a tabindex, because JSON of any size scrolls and a scrollable\n * box that cannot be focused is unreachable by keyboard.\n */\nexport interface ToolPayloadProps extends ComponentPropsWithRef<\"pre\"> {\n label?: string;\n}\n\nexport function ToolPayload({ className, label = \"Payload\", ...props }: ToolPayloadProps) {\n return (\n <pre\n data-slot=\"tool-payload\"\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n className={cn(\n \"max-h-56 overflow-auto rounded-md bg-muted/60 p-2 font-mono text-xs leading-relaxed\",\n focusRing,\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { toolVariants };\n"],"mappings":";;;;;;;AAsBA,MAAM,eAA2C;CAC/C,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACT;AAEA,MAAM,eAAe,IAAI,6CAA6C;CACpE,UAAU,EACR,QAAQ;EACN,SAAS;EACT,SAAS;EACT,SAAS;EACT,OAAO;CACT,EACF;CACA,iBAAiB,EACf,QAAQ,UACV;AACF,CAAC;AASD,SAAgB,KAAK,EAAE,WAAW,SAAS,WAAW,GAAG,SAAoB;CAC3E,OACE,oBAACA,YAAqB,MAAtB;EACE,aAAU;EACV,eAAa;EACb,WAAW,GAAG,aAAa,EAAE,OAAO,CAAC,GAAG,SAAS;EACjD,GAAI;CACL,CAAA;AAEL;AAaA,SAAgB,WAAW,EACzB,WACA,MACA,SAAS,WACT,aACA,MACA,GAAG,SACe;CAClB,OACE,qBAACA,YAAqB,SAAtB;EACE,aAAU;EACV,WAAW,GACT,uDACA,wEACA,kDACA,WACA,SACF;EACA,GAAI;EATN,UAAA;GAWG,QAAQ,oBAAC,UAAD,EAAkB,OAAS,CAAA;GACpC,oBAAC,QAAD;IAAM,WAAU;IAAiC,UAAA;GAAW,CAAA;GAG5D,oBAAC,QAAD;IACE,aAAU;IACV,WAAW,GACT,mBACA,WAAW,UAAU,qBAAqB,uBAC5C;IAEC,UAAA,eAAe,aAAa;GACzB,CAAA;GACN,oBAAC,OAAD;IACE,SAAQ;IACR,MAAK;IACL,eAAY;IACZ,WAAU;IAEV,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;KACd,gBAAe;IAChB,CAAA;GACE,CAAA;EACuB;;AAElC;AAEA,SAAS,SAAS,EAAE,UAAkC;CACpD,IAAI,WAAW,WACb,OACE,qBAAC,OAAD;EACE,SAAQ;EACR,MAAK;EACL,eAAY;EACZ,WAAU;EAJZ,UAAA,CAME,oBAAC,UAAD;GAAQ,IAAG;GAAK,IAAG;GAAK,GAAE;GAAI,QAAO;GAAe,aAAY;GAAM,SAAQ;EAAQ,CAAA,GACtF,oBAAC,QAAD;GACE,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACf,CAAA,CACE;;CAIT,OACE,oBAAC,OAAD;EACE,SAAQ;EACR,MAAK;EACL,eAAY;EACZ,WAAW,GACT,qBACA,WAAW,UAAU,qBAAqB,uBAC5C;EAEA,UAAA,oBAAC,QAAD;GACE,GACE,WAAW,YACP,mBACA,WAAW,UACT,mGACA;GAER,QAAO;GACP,aAAY;GACZ,eAAc;GACd,gBAAe;EAChB,CAAA;CACE,CAAA;AAET;AAEA,SAAgB,YAAY,EAC1B,WACA,GAAG,SAC0D;CAC7D,OACE,oBAACA,YAAqB,SAAtB;EACE,aAAU;EACV,WAAW,GACT,0CACA,wFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;;AAOA,SAAgB,YAAY,EAAE,WAAW,OAAO,UAAU,GAAG,SAA2B;CACtF,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,uDAAuD,SAAS;EAC9E,GAAI;EAHN,UAAA,CAKE,oBAAC,KAAD;GAAG,WAAU;GACV,UAAA;EACA,CAAA,GACF,QACE;;AAET;AAYA,SAAgB,YAAY,EAAE,WAAW,QAAQ,WAAW,GAAG,SAA2B;CACxF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,UAAU;EACV,MAAK;EACL,cAAY;EACZ,WAAW,GACT,uFACA,WACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ComponentPropsWithRef } from "react";
|
|
2
|
+
//#region src/components/breadcrumb/breadcrumb.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Where this page sits in the hierarchy.
|
|
5
|
+
*
|
|
6
|
+
* Three things separate a breadcrumb that works from one that only looks like
|
|
7
|
+
* one, and all three are about the last item.
|
|
8
|
+
*
|
|
9
|
+
* It is not a link. A link to the page you are already on is a control that
|
|
10
|
+
* does nothing, and in a screen reader's list of links it is indistinguishable
|
|
11
|
+
* from the ones that go somewhere.
|
|
12
|
+
*
|
|
13
|
+
* It carries `aria-current="page"`. Bolding it says "you are here" to people
|
|
14
|
+
* who can see it and to nobody else.
|
|
15
|
+
*
|
|
16
|
+
* The separators are hidden. A slash between every item is read aloud as
|
|
17
|
+
* "slash" — "Home slash Projects slash Settings" — which is the punctuation of
|
|
18
|
+
* the design leaking into the content.
|
|
19
|
+
*/
|
|
20
|
+
declare function Breadcrumb({ className, ...props }: ComponentPropsWithRef<"nav">): import("react").JSX.Element;
|
|
21
|
+
declare function BreadcrumbList({ className, ...props }: ComponentPropsWithRef<"ol">): import("react").JSX.Element;
|
|
22
|
+
declare function BreadcrumbItem({ className, ...props }: ComponentPropsWithRef<"li">): import("react").JSX.Element;
|
|
23
|
+
interface BreadcrumbLinkProps extends ComponentPropsWithRef<"a"> {
|
|
24
|
+
/** Render the child as the link, for a router's own component. */
|
|
25
|
+
asChild?: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare function BreadcrumbLink({ className, asChild, ...props }: BreadcrumbLinkProps): import("react").JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* The page you are on.
|
|
30
|
+
*
|
|
31
|
+
* A span, not a link: there is nowhere to go. `aria-current="page"` is what
|
|
32
|
+
* says "you are here" to anything that is not looking at the font weight.
|
|
33
|
+
*/
|
|
34
|
+
declare function BreadcrumbPage({ className, ...props }: ComponentPropsWithRef<"span">): import("react").JSX.Element;
|
|
35
|
+
/**
|
|
36
|
+
* The mark between items.
|
|
37
|
+
*
|
|
38
|
+
* Hidden from assistive technology and given no accessible name: the list
|
|
39
|
+
* structure already conveys the sequence, and reading "slash" between every
|
|
40
|
+
* item is the design's punctuation leaking into the content.
|
|
41
|
+
*/
|
|
42
|
+
declare function BreadcrumbSeparator({ className, children, ...props }: ComponentPropsWithRef<"li">): import("react").JSX.Element;
|
|
43
|
+
/**
|
|
44
|
+
* Stands in for the middle of a long trail.
|
|
45
|
+
*
|
|
46
|
+
* Named rather than hidden, because unlike a separator it is *content*: it
|
|
47
|
+
* says levels have been left out. A bare "…" announces as nothing at all.
|
|
48
|
+
*/
|
|
49
|
+
declare function BreadcrumbEllipsis({ className, ...props }: ComponentPropsWithRef<"span">): import("react").JSX.Element;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbLinkProps, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator };
|
|
52
|
+
//# sourceMappingURL=breadcrumb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breadcrumb.d.ts","names":[],"sources":["../../../src/components/breadcrumb/breadcrumb.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;iBAwBgB,aAAa,cAAc,SAAS,+CAA4B,IAAA;iBAahE,iBAAiB,cAAc,SAAS,8CAA2B,IAAA;iBAanE,iBAAiB,cAAc,SAAS,8CAA2B,IAAA;UAUlE,4BAA4B;;EAE3C;;iBAGc,iBAAiB,WAAW,YAAoB,SAAS,sCAAmB,IAAA;;;;;;;iBAuB5E,iBAAiB,cAAc,SAAS,gDAA6B,IAAA;;;;;;;;iBAkBrE,sBACd,WACA,aACG,SACF,8CAA2B,IAAA;;;;;;;iBA8Bd,qBAAqB,cAAc,SAAS,gDAA6B,IAAA"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { focusRing } from "../../lib/styles.js";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
import { Slot } from "radix-ui";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/breadcrumb/breadcrumb.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Where this page sits in the hierarchy.
|
|
8
|
+
*
|
|
9
|
+
* Three things separate a breadcrumb that works from one that only looks like
|
|
10
|
+
* one, and all three are about the last item.
|
|
11
|
+
*
|
|
12
|
+
* It is not a link. A link to the page you are already on is a control that
|
|
13
|
+
* does nothing, and in a screen reader's list of links it is indistinguishable
|
|
14
|
+
* from the ones that go somewhere.
|
|
15
|
+
*
|
|
16
|
+
* It carries `aria-current="page"`. Bolding it says "you are here" to people
|
|
17
|
+
* who can see it and to nobody else.
|
|
18
|
+
*
|
|
19
|
+
* The separators are hidden. A slash between every item is read aloud as
|
|
20
|
+
* "slash" — "Home slash Projects slash Settings" — which is the punctuation of
|
|
21
|
+
* the design leaking into the content.
|
|
22
|
+
*/
|
|
23
|
+
function Breadcrumb({ className, ...props }) {
|
|
24
|
+
return /* @__PURE__ */ jsx("nav", {
|
|
25
|
+
"aria-label": "Breadcrumb",
|
|
26
|
+
"data-slot": "breadcrumb",
|
|
27
|
+
className: cn("min-w-0", className),
|
|
28
|
+
...props
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function BreadcrumbList({ className, ...props }) {
|
|
32
|
+
return /* @__PURE__ */ jsx("ol", {
|
|
33
|
+
"data-slot": "breadcrumb-list",
|
|
34
|
+
className: cn("flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground", className),
|
|
35
|
+
...props
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function BreadcrumbItem({ className, ...props }) {
|
|
39
|
+
return /* @__PURE__ */ jsx("li", {
|
|
40
|
+
"data-slot": "breadcrumb-item",
|
|
41
|
+
className: cn("inline-flex items-center gap-1.5", className),
|
|
42
|
+
...props
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function BreadcrumbLink({ className, asChild = false, ...props }) {
|
|
46
|
+
const Component = asChild ? Slot.Root : "a";
|
|
47
|
+
return /* @__PURE__ */ jsx(Component, {
|
|
48
|
+
"data-slot": "breadcrumb-link",
|
|
49
|
+
className: cn("rounded-sm transition-colors duration-[var(--duration-fast)]", "hover:text-foreground", focusRing, className),
|
|
50
|
+
...props
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The page you are on.
|
|
55
|
+
*
|
|
56
|
+
* A span, not a link: there is nowhere to go. `aria-current="page"` is what
|
|
57
|
+
* says "you are here" to anything that is not looking at the font weight.
|
|
58
|
+
*/
|
|
59
|
+
function BreadcrumbPage({ className, ...props }) {
|
|
60
|
+
return /* @__PURE__ */ jsx("span", {
|
|
61
|
+
"data-slot": "breadcrumb-page",
|
|
62
|
+
"aria-current": "page",
|
|
63
|
+
className: cn("font-medium text-foreground", className),
|
|
64
|
+
...props
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The mark between items.
|
|
69
|
+
*
|
|
70
|
+
* Hidden from assistive technology and given no accessible name: the list
|
|
71
|
+
* structure already conveys the sequence, and reading "slash" between every
|
|
72
|
+
* item is the design's punctuation leaking into the content.
|
|
73
|
+
*/
|
|
74
|
+
function BreadcrumbSeparator({ className, children, ...props }) {
|
|
75
|
+
return /* @__PURE__ */ jsx("li", {
|
|
76
|
+
"data-slot": "breadcrumb-separator",
|
|
77
|
+
role: "presentation",
|
|
78
|
+
"aria-hidden": true,
|
|
79
|
+
className: cn("[&>svg]:size-3.5", className),
|
|
80
|
+
...props,
|
|
81
|
+
children: children ?? /* @__PURE__ */ jsx("svg", {
|
|
82
|
+
viewBox: "0 0 24 24",
|
|
83
|
+
fill: "none",
|
|
84
|
+
className: "rtl:-scale-x-100",
|
|
85
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
86
|
+
d: "m9 18 6-6-6-6",
|
|
87
|
+
stroke: "currentColor",
|
|
88
|
+
strokeWidth: "2",
|
|
89
|
+
strokeLinecap: "round",
|
|
90
|
+
strokeLinejoin: "round"
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Stands in for the middle of a long trail.
|
|
97
|
+
*
|
|
98
|
+
* Named rather than hidden, because unlike a separator it is *content*: it
|
|
99
|
+
* says levels have been left out. A bare "…" announces as nothing at all.
|
|
100
|
+
*/
|
|
101
|
+
function BreadcrumbEllipsis({ className, ...props }) {
|
|
102
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
103
|
+
"data-slot": "breadcrumb-ellipsis",
|
|
104
|
+
role: "presentation",
|
|
105
|
+
className: cn("flex size-5 items-center justify-center", className),
|
|
106
|
+
...props,
|
|
107
|
+
children: [/* @__PURE__ */ jsxs("svg", {
|
|
108
|
+
viewBox: "0 0 24 24",
|
|
109
|
+
fill: "none",
|
|
110
|
+
"aria-hidden": true,
|
|
111
|
+
className: "size-4",
|
|
112
|
+
children: [
|
|
113
|
+
/* @__PURE__ */ jsx("circle", {
|
|
114
|
+
cx: "5",
|
|
115
|
+
cy: "12",
|
|
116
|
+
r: "1.5",
|
|
117
|
+
fill: "currentColor"
|
|
118
|
+
}),
|
|
119
|
+
/* @__PURE__ */ jsx("circle", {
|
|
120
|
+
cx: "12",
|
|
121
|
+
cy: "12",
|
|
122
|
+
r: "1.5",
|
|
123
|
+
fill: "currentColor"
|
|
124
|
+
}),
|
|
125
|
+
/* @__PURE__ */ jsx("circle", {
|
|
126
|
+
cx: "19",
|
|
127
|
+
cy: "12",
|
|
128
|
+
r: "1.5",
|
|
129
|
+
fill: "currentColor"
|
|
130
|
+
})
|
|
131
|
+
]
|
|
132
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
133
|
+
className: "sr-only",
|
|
134
|
+
children: "Intermediate levels"
|
|
135
|
+
})]
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator };
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=breadcrumb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"breadcrumb.js","names":[],"sources":["../../../src/components/breadcrumb/breadcrumb.tsx"],"sourcesContent":["import { Slot } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing, mirrorForDirection } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Where this page sits in the hierarchy.\n *\n * Three things separate a breadcrumb that works from one that only looks like\n * one, and all three are about the last item.\n *\n * It is not a link. A link to the page you are already on is a control that\n * does nothing, and in a screen reader's list of links it is indistinguishable\n * from the ones that go somewhere.\n *\n * It carries `aria-current=\"page\"`. Bolding it says \"you are here\" to people\n * who can see it and to nobody else.\n *\n * The separators are hidden. A slash between every item is read aloud as\n * \"slash\" — \"Home slash Projects slash Settings\" — which is the punctuation of\n * the design leaking into the content.\n */\n\nexport function Breadcrumb({ className, ...props }: ComponentPropsWithRef<\"nav\">) {\n return (\n <nav\n // Named, because a page can have more than one navigation landmark and\n // \"navigation\" on its own does not say which this is.\n aria-label=\"Breadcrumb\"\n data-slot=\"breadcrumb\"\n className={cn(\"min-w-0\", className)}\n {...props}\n />\n );\n}\n\nexport function BreadcrumbList({ className, ...props }: ComponentPropsWithRef<\"ol\">) {\n return (\n <ol\n data-slot=\"breadcrumb-list\"\n className={cn(\n \"flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function BreadcrumbItem({ className, ...props }: ComponentPropsWithRef<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-item\"\n className={cn(\"inline-flex items-center gap-1.5\", className)}\n {...props}\n />\n );\n}\n\nexport interface BreadcrumbLinkProps extends ComponentPropsWithRef<\"a\"> {\n /** Render the child as the link, for a router's own component. */\n asChild?: boolean;\n}\n\nexport function BreadcrumbLink({ className, asChild = false, ...props }: BreadcrumbLinkProps) {\n const Component = asChild ? Slot.Root : \"a\";\n\n return (\n <Component\n data-slot=\"breadcrumb-link\"\n className={cn(\n \"rounded-sm transition-colors duration-[var(--duration-fast)]\",\n \"hover:text-foreground\",\n focusRing,\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * The page you are on.\n *\n * A span, not a link: there is nowhere to go. `aria-current=\"page\"` is what\n * says \"you are here\" to anything that is not looking at the font weight.\n */\nexport function BreadcrumbPage({ className, ...props }: ComponentPropsWithRef<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-page\"\n aria-current=\"page\"\n className={cn(\"font-medium text-foreground\", className)}\n {...props}\n />\n );\n}\n\n/**\n * The mark between items.\n *\n * Hidden from assistive technology and given no accessible name: the list\n * structure already conveys the sequence, and reading \"slash\" between every\n * item is the design's punctuation leaking into the content.\n */\nexport function BreadcrumbSeparator({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<\"li\">) {\n return (\n <li\n data-slot=\"breadcrumb-separator\"\n role=\"presentation\"\n aria-hidden\n className={cn(\"[&>svg]:size-3.5\", className)}\n {...props}\n >\n {children ?? (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" className={mirrorForDirection}>\n <path\n d=\"m9 18 6-6-6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </li>\n );\n}\n\n/**\n * Stands in for the middle of a long trail.\n *\n * Named rather than hidden, because unlike a separator it is *content*: it\n * says levels have been left out. A bare \"…\" announces as nothing at all.\n */\nexport function BreadcrumbEllipsis({ className, ...props }: ComponentPropsWithRef<\"span\">) {\n return (\n <span\n data-slot=\"breadcrumb-ellipsis\"\n role=\"presentation\"\n className={cn(\"flex size-5 items-center justify-center\", className)}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden className=\"size-4\">\n <circle cx=\"5\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"19\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n </svg>\n <span className=\"sr-only\">Intermediate levels</span>\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,WAAW,EAAE,WAAW,GAAG,SAAuC;CAChF,OACE,oBAAC,OAAD;EAGE,cAAW;EACX,aAAU;EACV,WAAW,GAAG,WAAW,SAAS;EAClC,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,eAAe,EAAE,WAAW,GAAG,SAAsC;CACnF,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,qEACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,eAAe,EAAE,WAAW,GAAG,SAAsC;CACnF,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GAAG,oCAAoC,SAAS;EAC3D,GAAI;CACL,CAAA;AAEL;AAOA,SAAgB,eAAe,EAAE,WAAW,UAAU,OAAO,GAAG,SAA8B;CAC5F,MAAM,YAAY,UAAU,KAAK,OAAO;CAExC,OACE,oBAAC,WAAD;EACE,aAAU;EACV,WAAW,GACT,gEACA,yBACA,WACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;;;;;;;AAQA,SAAgB,eAAe,EAAE,WAAW,GAAG,SAAwC;CACrF,OACE,oBAAC,QAAD;EACE,aAAU;EACV,gBAAa;EACb,WAAW,GAAG,+BAA+B,SAAS;EACtD,GAAI;CACL,CAAA;AAEL;;;;;;;;AASA,SAAgB,oBAAoB,EAClC,WACA,UACA,GAAG,SAC2B;CAC9B,OACE,oBAAC,MAAD;EACE,aAAU;EACV,MAAK;EACL,eAAA;EACA,WAAW,GAAG,oBAAoB,SAAS;EAC3C,GAAI;EAEH,UAAA,YACC,oBAAC,OAAD;GAAK,SAAQ;GAAY,MAAK;GAAO,WAAA;GACnC,UAAA,oBAAC,QAAD;IACE,GAAE;IACF,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;GAChB,CAAA;EACE,CAAA;CAEL,CAAA;AAER;;;;;;;AAQA,SAAgB,mBAAmB,EAAE,WAAW,GAAG,SAAwC;CACzF,OACE,qBAAC,QAAD;EACE,aAAU;EACV,MAAK;EACL,WAAW,GAAG,2CAA2C,SAAS;EAClE,GAAI;EAJN,UAAA,CAME,qBAAC,OAAD;GAAK,SAAQ;GAAY,MAAK;GAAO,eAAA;GAAY,WAAU;GAA3D,UAAA;IACE,oBAAC,UAAD;KAAQ,IAAG;KAAI,IAAG;KAAK,GAAE;KAAM,MAAK;IAAgB,CAAA;IACpD,oBAAC,UAAD;KAAQ,IAAG;KAAK,IAAG;KAAK,GAAE;KAAM,MAAK;IAAgB,CAAA;IACrD,oBAAC,UAAD;KAAQ,IAAG;KAAK,IAAG;KAAK,GAAE;KAAM,MAAK;IAAgB,CAAA;GAClD;EACL,CAAA,GAAA,oBAAC,QAAD;GAAM,WAAU;GAAU,UAAA;EAAyB,CAAA,CAC/C;;AAEV"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbLinkProps, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "./breadcrumb.js";
|
|
2
|
+
export { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, type BreadcrumbLinkProps, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator };
|