@devalok/shilp-sutra 0.56.0 → 0.57.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/MIGRATION.md +107 -0
- package/dist/_chunks/badge-group.js +48 -34
- package/dist/_chunks/badge-group.js.map +1 -1
- package/dist/_chunks/chat.js +91 -75
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/document-preview.js +1 -1
- package/dist/_chunks/document-preview.js.map +1 -1
- package/dist/_chunks/emoji-suggestion.js +1 -1
- package/dist/_chunks/emoji-suggestion.js.map +1 -1
- package/dist/_chunks/mention-suggestion.js +2 -2
- package/dist/_chunks/mention-suggestion.js.map +1 -1
- package/dist/_chunks/oauth-button.js +1 -1
- package/dist/_chunks/oauth-button.js.map +1 -1
- package/dist/_chunks/success.js +2 -2
- package/dist/_chunks/success.js.map +1 -1
- package/dist/_chunks/tree-view.js +1 -1
- package/dist/_chunks/tree-view.js.map +1 -1
- package/dist/_chunks/use-calendar.js +4 -4
- package/dist/_chunks/use-calendar.js.map +1 -1
- package/dist/ai/blocks/text.js +1 -1
- package/dist/ai/blocks/text.js.map +1 -1
- package/dist/ai/command-bar.js +9 -9
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/ai/conversation.js +2 -2
- package/dist/ai/conversation.js.map +1 -1
- package/dist/composed/activity-feed.js +1 -1
- package/dist/composed/activity-feed.js.map +1 -1
- package/dist/composed/avatar-group.d.ts +7 -2
- package/dist/composed/avatar-group.d.ts.map +1 -1
- package/dist/composed/avatar-group.js +3 -3
- package/dist/composed/avatar-group.js.map +1 -1
- package/dist/composed/command-palette.js +7 -7
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/content-card.js +2 -2
- package/dist/composed/content-card.js.map +1 -1
- package/dist/composed/date-picker/index.js +4 -4
- package/dist/composed/date-picker/index.js.map +1 -1
- package/dist/composed/diff.d.ts +23 -2
- package/dist/composed/diff.d.ts.map +1 -1
- package/dist/composed/diff.js +341 -260
- package/dist/composed/diff.js.map +1 -1
- package/dist/composed/emoji-picker.js +3 -3
- package/dist/composed/emoji-picker.js.map +1 -1
- package/dist/composed/error-boundary.js +2 -2
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/file-preview.js +1 -1
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/filter-bar.js +1 -1
- package/dist/composed/filter-bar.js.map +1 -1
- package/dist/composed/inline-edit.js +1 -1
- package/dist/composed/inline-edit.js.map +1 -1
- package/dist/composed/loading-skeleton.js +3 -3
- package/dist/composed/loading-skeleton.js.map +1 -1
- package/dist/composed/master-detail.js +1 -1
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/member-picker.js +1 -1
- package/dist/composed/member-picker.js.map +1 -1
- package/dist/composed/multi-select-popover.js +1 -1
- package/dist/composed/multi-select-popover.js.map +1 -1
- package/dist/composed/page-skeletons.js +4 -4
- package/dist/composed/page-skeletons.js.map +1 -1
- package/dist/composed/responsive-modal.js +1 -1
- package/dist/composed/responsive-modal.js.map +1 -1
- package/dist/composed/rich-chat-input.js +17 -17
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.js +5 -5
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/composed/schedule-view.js +5 -5
- package/dist/composed/schedule-view.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -3
- package/dist/hooks/use-container-size.d.ts +18 -0
- package/dist/hooks/use-container-size.d.ts.map +1 -0
- package/dist/hooks/use-container-size.js +28 -0
- package/dist/hooks/use-container-size.js.map +1 -0
- package/dist/shell/app-shell.d.ts +101 -0
- package/dist/shell/app-shell.d.ts.map +1 -0
- package/dist/shell/app-shell.js +76 -0
- package/dist/shell/app-shell.js.map +1 -0
- package/dist/shell/bottom-navbar.js +2 -2
- package/dist/shell/bottom-navbar.js.map +1 -1
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +6 -5
- package/dist/shell/notification-center.js +4 -4
- package/dist/shell/notification-center.js.map +1 -1
- package/dist/shell/notification-preferences.js +1 -1
- package/dist/shell/notification-preferences.js.map +1 -1
- package/dist/shell/top-bar.js +7 -7
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/tokens/generate-scale.d.ts +49 -0
- package/dist/tokens/generate-scale.d.ts.map +1 -1
- package/dist/tokens/primitives.css +29 -26
- package/dist/tokens/semantic.css +98 -39
- package/dist/tokens/variants.css +1 -1
- package/dist/ui/accordion.js +1 -1
- package/dist/ui/accordion.js.map +1 -1
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/alert-dialog.js.map +1 -1
- package/dist/ui/alert.d.ts.map +1 -1
- package/dist/ui/alert.js +7 -7
- package/dist/ui/alert.js.map +1 -1
- package/dist/ui/avatar.js +3 -3
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/badge-indicator.js +1 -1
- package/dist/ui/badge-indicator.js.map +1 -1
- package/dist/ui/badge.d.ts +47 -33
- package/dist/ui/badge.d.ts.map +1 -1
- package/dist/ui/banner.js +1 -1
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/button.js +4 -4
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/card.js +4 -4
- package/dist/ui/card.js.map +1 -1
- package/dist/ui/chat/date-separator.d.ts +8 -1
- package/dist/ui/chat/date-separator.d.ts.map +1 -1
- package/dist/ui/checkbox.js +1 -1
- package/dist/ui/checkbox.js.map +1 -1
- package/dist/ui/code.js +2 -2
- package/dist/ui/code.js.map +1 -1
- package/dist/ui/color-input.js +3 -3
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/combobox.js +4 -4
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/context-menu.js +4 -4
- package/dist/ui/context-menu.js.map +1 -1
- package/dist/ui/data-table-body.d.ts +3 -1
- package/dist/ui/data-table-body.d.ts.map +1 -1
- package/dist/ui/data-table-body.js +93 -92
- package/dist/ui/data-table-body.js.map +1 -1
- package/dist/ui/data-table-bulk-actions.d.ts +7 -1
- package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
- package/dist/ui/data-table-bulk-actions.js +14 -8
- package/dist/ui/data-table-bulk-actions.js.map +1 -1
- package/dist/ui/data-table-card.d.ts.map +1 -1
- package/dist/ui/data-table-card.js +66 -47
- package/dist/ui/data-table-card.js.map +1 -1
- package/dist/ui/data-table-context.d.ts +4 -0
- package/dist/ui/data-table-context.d.ts.map +1 -1
- package/dist/ui/data-table-context.js.map +1 -1
- package/dist/ui/data-table-header.d.ts.map +1 -1
- package/dist/ui/data-table-header.js +18 -15
- package/dist/ui/data-table-header.js.map +1 -1
- package/dist/ui/data-table-pagination.js +3 -3
- package/dist/ui/data-table-pagination.js.map +1 -1
- package/dist/ui/data-table-toolbar.d.ts +11 -1
- package/dist/ui/data-table-toolbar.d.ts.map +1 -1
- package/dist/ui/data-table-toolbar.js +7 -7
- package/dist/ui/data-table-toolbar.js.map +1 -1
- package/dist/ui/data-table.d.ts +52 -5
- package/dist/ui/data-table.d.ts.map +1 -1
- package/dist/ui/data-table.js +181 -169
- package/dist/ui/data-table.js.map +1 -1
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/dot.js +1 -1
- package/dist/ui/dot.js.map +1 -1
- package/dist/ui/dropdown-menu.js +4 -4
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/file-upload.js +3 -3
- package/dist/ui/file-upload.js.map +1 -1
- package/dist/ui/input-otp.js +1 -1
- package/dist/ui/input-otp.js.map +1 -1
- package/dist/ui/input.js +8 -8
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/menubar.js +5 -5
- package/dist/ui/menubar.js.map +1 -1
- package/dist/ui/navigation-menu.js +1 -1
- package/dist/ui/navigation-menu.js.map +1 -1
- package/dist/ui/number-input.js +3 -3
- package/dist/ui/number-input.js.map +1 -1
- package/dist/ui/pagination.js +1 -1
- package/dist/ui/pagination.js.map +1 -1
- package/dist/ui/progress-ring.js +2 -2
- package/dist/ui/progress-ring.js.map +1 -1
- package/dist/ui/progress.d.ts.map +1 -1
- package/dist/ui/progress.js +16 -12
- package/dist/ui/progress.js.map +1 -1
- package/dist/ui/radio.js +1 -1
- package/dist/ui/radio.js.map +1 -1
- package/dist/ui/select.js +5 -5
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/sheet.js +1 -1
- package/dist/ui/sheet.js.map +1 -1
- package/dist/ui/sidebar.js +12 -12
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/slider.js +1 -1
- package/dist/ui/slider.js.map +1 -1
- package/dist/ui/split-button.js +3 -3
- package/dist/ui/split-button.js.map +1 -1
- package/dist/ui/stat-card.js +1 -1
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/stepper.js +2 -2
- package/dist/ui/stepper.js.map +1 -1
- package/dist/ui/surface.d.ts.map +1 -1
- package/dist/ui/surface.js +2 -2
- package/dist/ui/surface.js.map +1 -1
- package/dist/ui/switch.js +1 -1
- package/dist/ui/switch.js.map +1 -1
- package/dist/ui/table.js +1 -1
- package/dist/ui/table.js.map +1 -1
- package/dist/ui/tabs.d.ts.map +1 -1
- package/dist/ui/tabs.js +1 -1
- package/dist/ui/tabs.js.map +1 -1
- package/dist/ui/textarea.js +5 -5
- package/dist/ui/textarea.js.map +1 -1
- package/dist/ui/toast.js +1 -1
- package/dist/ui/toast.js.map +1 -1
- package/dist/ui/toggle.js +3 -3
- package/dist/ui/toggle.js.map +1 -1
- package/docs/components/composed/avatar-group.md +4 -4
- package/docs/components/composed/diff.md +10 -2
- package/docs/components/composed/schedule-view.md +1 -1
- package/docs/components/shell/app-shell.md +132 -0
- package/docs/components/ui/avatar.md +1 -1
- package/docs/components/ui/button.md +1 -1
- package/docs/components/ui/chat.md +7 -1
- package/docs/components/ui/color-swatch.md +1 -1
- package/docs/components/ui/data-table-toolbar.md +6 -2
- package/docs/components/ui/data-table.md +41 -7
- package/docs/components/ui/dot.md +1 -1
- package/docs/components/ui/progress.md +1 -0
- package/docs/components/ui/surface.md +3 -3
- package/docs/components/ui/table.md +2 -2
- package/docs/recipes/install-astro.md +1 -0
- package/docs/recipes/install-next-app-router.md +1 -0
- package/docs/recipes/install-remix.md +1 -0
- package/docs/recipes/install-tanstack-start.md +1 -0
- package/docs/recipes/install-vite.md +1 -0
- package/llms.txt +2 -1
- package/make-kit/Guidelines.md +2 -2
- package/make-kit/components/button.md +1 -1
- package/make-kit/components/card.md +6 -6
- package/make-kit/components/surface.md +4 -4
- package/make-kit/foundations/color.md +4 -4
- package/make-kit/foundations/dark-mode.md +1 -1
- package/make-kit/foundations/radius.md +1 -1
- package/make-kit/foundations/surfaces.md +9 -9
- package/mcp-manifest.json +350 -32
- package/package.json +6 -3
- package/skill/SKILL.md +12 -6
- package/skill/references/components.md +2 -1
- package/skill/references/setup-astro.md +1 -0
- package/skill/references/setup-next-app-router.md +1 -0
- package/skill/references/setup-remix.md +1 -0
- package/skill/references/setup-tanstack-start.md +1 -0
- package/skill/references/setup-vite.md +1 -0
- package/dist/primitives/react-toast.d.ts +0 -120
- package/dist/ui/charts/_internal/scales.d.ts +0 -5
- package/dist/ui/charts/_internal/scales.d.ts.map +0 -1
- package/dist/ui/lib/slot.d.ts +0 -8
- package/dist/ui/lib/slot.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-preview.js","names":[],"sources":["../../src/composed/file-preview/document-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback, Toolbar } from './shared'\n\n// ============================================================\n// PDF Preview — Adobe/Google Drive style\n// ============================================================\n\nexport default function DocumentPreview({ url, initialPage, onError }: { url: string; initialPage: number; onError?: (msg: string) => void }) {\n React.useEffect(() => {\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`\n }, [])\n\n const [numPages, setNumPages] = React.useState(0)\n const [page, setPage] = React.useState(initialPage)\n const [pageInput, setPageInput] = React.useState(String(initialPage))\n const [error, setError] = React.useState(false)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [hovered, setHovered] = React.useState(false)\n\n // Sync page input when page changes via buttons\n React.useEffect(() => { setPageInput(String(page)) }, [page])\n\n // Keyboard nav — scoped to container focus/hover\n React.useEffect(() => {\n function handleKey(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el) return\n const hasFocus = el.contains(document.activeElement)\n if (!hasFocus && !hovered) return\n\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault()\n setPage((p) => Math.min(numPages, p + 1))\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault()\n setPage((p) => Math.max(1, p - 1))\n }\n }\n document.addEventListener('keydown', handleKey)\n return () => document.removeEventListener('keydown', handleKey)\n }, [numPages, hovered])\n\n function handlePageInputSubmit(e: React.KeyboardEvent) {\n if (e.key === 'Enter') {\n const n = parseInt(pageInput, 10)\n if (n >= 1 && n <= numPages) setPage(n)\n else setPageInput(String(page))\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load PDF\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"flex flex-col items-center gap-ds-03\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n tabIndex={-1}\n >\n <div className=\"overflow-auto max-h-[70vh] rounded-control bg-surface-sunken border border-surface-border\">\n <Document\n file={url}\n onLoadSuccess={({ numPages: n }) => setNumPages(n)}\n onLoadError={() => { setError(true); onError?.('PDF failed to load') }}\n loading={<Skeleton className=\"h-[500px] w-[400px]\" />}\n error={<ErrorFallback message=\"Failed to load PDF\" url={url} />}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={page}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Page pageNumber={page} renderTextLayer={false} renderAnnotationLayer={false} />\n </motion.div>\n </AnimatePresence>\n </Document>\n </div>\n\n {numPages > 0 && (\n <Toolbar>\n <Button variant=\"ghost\" size=\"icon-xs\" onClick={() => setPage((p) => Math.max(1, p - 1))} disabled={page <= 1} aria-label=\"Previous page (←)\" title=\"Previous page\">\n <Icon icon={IconChevronLeft} size=\"sm\" />\n </Button>\n <div className=\"flex items-center gap-ds-01 text-caption text-surface-fg-muted\">\n <input\n type=\"text\"\n value={pageInput}\n onChange={(e) => setPageInput(e.target.value)}\n onKeyDown={handlePageInputSubmit}\n onBlur={() => setPageInput(String(page))}\n className=\"w-8 bg-transparent text-center font-mono text-surface-fg outline-hidden focus:bg-surface-
|
|
1
|
+
{"version":3,"file":"document-preview.js","names":[],"sources":["../../src/composed/file-preview/document-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronLeft, IconChevronRight } from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback, Toolbar } from './shared'\n\n// ============================================================\n// PDF Preview — Adobe/Google Drive style\n// ============================================================\n\nexport default function DocumentPreview({ url, initialPage, onError }: { url: string; initialPage: number; onError?: (msg: string) => void }) {\n React.useEffect(() => {\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`\n }, [])\n\n const [numPages, setNumPages] = React.useState(0)\n const [page, setPage] = React.useState(initialPage)\n const [pageInput, setPageInput] = React.useState(String(initialPage))\n const [error, setError] = React.useState(false)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [hovered, setHovered] = React.useState(false)\n\n // Sync page input when page changes via buttons\n React.useEffect(() => { setPageInput(String(page)) }, [page])\n\n // Keyboard nav — scoped to container focus/hover\n React.useEffect(() => {\n function handleKey(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el) return\n const hasFocus = el.contains(document.activeElement)\n if (!hasFocus && !hovered) return\n\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault()\n setPage((p) => Math.min(numPages, p + 1))\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault()\n setPage((p) => Math.max(1, p - 1))\n }\n }\n document.addEventListener('keydown', handleKey)\n return () => document.removeEventListener('keydown', handleKey)\n }, [numPages, hovered])\n\n function handlePageInputSubmit(e: React.KeyboardEvent) {\n if (e.key === 'Enter') {\n const n = parseInt(pageInput, 10)\n if (n >= 1 && n <= numPages) setPage(n)\n else setPageInput(String(page))\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load PDF\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"flex flex-col items-center gap-ds-03\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n tabIndex={-1}\n >\n <div className=\"overflow-auto max-h-[70vh] rounded-control bg-surface-sunken border border-surface-border\">\n <Document\n file={url}\n onLoadSuccess={({ numPages: n }) => setNumPages(n)}\n onLoadError={() => { setError(true); onError?.('PDF failed to load') }}\n loading={<Skeleton className=\"h-[500px] w-[400px]\" />}\n error={<ErrorFallback message=\"Failed to load PDF\" url={url} />}\n >\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={page}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Page pageNumber={page} renderTextLayer={false} renderAnnotationLayer={false} />\n </motion.div>\n </AnimatePresence>\n </Document>\n </div>\n\n {numPages > 0 && (\n <Toolbar>\n <Button variant=\"ghost\" size=\"icon-xs\" onClick={() => setPage((p) => Math.max(1, p - 1))} disabled={page <= 1} aria-label=\"Previous page (←)\" title=\"Previous page\">\n <Icon icon={IconChevronLeft} size=\"sm\" />\n </Button>\n <div className=\"flex items-center gap-ds-01 text-caption text-surface-fg-muted\">\n <input\n type=\"text\"\n value={pageInput}\n onChange={(e) => setPageInput(e.target.value)}\n onKeyDown={handlePageInputSubmit}\n onBlur={() => setPageInput(String(page))}\n className=\"w-8 bg-transparent text-center font-mono text-surface-fg outline-hidden focus:bg-surface-panel-hover rounded-control-inner\"\n aria-label=\"Page number\"\n />\n <span>/ {numPages}</span>\n </div>\n <Button variant=\"ghost\" size=\"icon-xs\" onClick={() => setPage((p) => Math.min(numPages, p + 1))} disabled={page >= numPages} aria-label=\"Next page (→)\" title=\"Next page\">\n <Icon icon={IconChevronRight} size=\"sm\" />\n </Button>\n </Toolbar>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -49,7 +49,7 @@ var p = t.forwardRef(({ items: i, command: a }, o) => {
|
|
|
49
49
|
role: "option",
|
|
50
50
|
"aria-selected": i === s,
|
|
51
51
|
onClick: () => a(t),
|
|
52
|
-
className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", i === s ? "bg-surface-
|
|
52
|
+
className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", i === s ? "bg-surface-panel-hover text-surface-fg" : "text-surface-fg-muted hover:bg-surface-panel-hover"),
|
|
53
53
|
children: [/* @__PURE__ */ n(f, {
|
|
54
54
|
emoji: t,
|
|
55
55
|
size: "1.25em"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emoji-suggestion.js","names":[],"sources":["../../src/composed/extensions/emoji-data.ts","../../src/composed/extensions/emoji-suggestion.tsx"],"sourcesContent":["// Emoji dataset for the editors' `:shortcode:` autocomplete. Sources\n// `@emoji-mart/data` — a pure-JSON package with NO react peer, so it is\n// unaffected by the React-19 peer issue that drove the picker off\n// `@emoji-mart/react` (→ frimousse). Native-only since that migration: no\n// spritesheet coordinates, no per-art-style loaders.\n\ninterface EmojiSkin {\n native: string\n}\n\ninterface EmojiEntry {\n id: string\n name: string\n keywords?: string[]\n skins: EmojiSkin[]\n}\n\nexport interface EmojiDataset {\n emojis: Record<string, EmojiEntry>\n}\n\nexport interface ResolvedEmoji {\n id: string\n name: string\n native: string\n}\n\nlet cachedData: EmojiDataset | null = null\n\nexport async function loadEmojiData(): Promise<EmojiDataset> {\n if (cachedData) return cachedData\n const mod = await import('@emoji-mart/data')\n cachedData = ((mod as { default?: EmojiDataset }).default ?? mod) as EmojiDataset\n return cachedData\n}\n\nfunction resolve(entry: EmojiEntry): ResolvedEmoji {\n return { id: entry.id, name: entry.name, native: entry.skins[0]?.native ?? '' }\n}\n\nexport function searchEmoji(data: EmojiDataset, query: string, limit = 8): ResolvedEmoji[] {\n const entries = Object.values(data.emojis)\n if (!query) return entries.slice(0, limit).map(resolve)\n const q = query.toLowerCase()\n const results: ResolvedEmoji[] = []\n for (const e of entries) {\n if (results.length >= limit) break\n if (\n e.id.includes(q) ||\n e.name.toLowerCase().includes(q) ||\n e.keywords?.some((k) => k.includes(q))\n ) {\n results.push(resolve(e))\n }\n }\n return results\n}\n","import { Extension } from '@tiptap/core'\nimport Suggestion, { type SuggestionKeyDownProps,type SuggestionProps } from '@tiptap/suggestion'\nimport { PluginKey } from '@tiptap/pm/state'\nimport * as React from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\n\nimport { cn } from '../../ui/lib/utils'\nimport { loadEmojiData, type ResolvedEmoji, searchEmoji } from './emoji-data'\n\n// Re-export for external consumers\nexport type { ResolvedEmoji as EmojiSuggestionItem }\n\n// Native-only since the frimousse migration.\nfunction EmojiImage({ emoji, size = '1.2em' }: { emoji: ResolvedEmoji; size?: string }) {\n return <span style={{ fontSize: size }}>{emoji.native}</span>\n}\n\ninterface EmojiListRef {\n onKeyDown: (props: SuggestionKeyDownProps) => boolean\n}\n\ninterface EmojiListProps {\n items: ResolvedEmoji[]\n command: (item: ResolvedEmoji) => void\n}\n\nconst EmojiList = React.forwardRef<EmojiListRef, EmojiListProps>(\n ({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n\n React.useEffect(() => setSelectedIndex(0), [items])\n\n React.useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'ArrowUp') {\n setSelectedIndex((i) => (i + items.length - 1) % items.length)\n return true\n }\n if (event.key === 'ArrowDown') {\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n }\n if (event.key === 'Enter') {\n if (items[selectedIndex]) command(items[selectedIndex])\n return true\n }\n return false\n },\n }))\n\n if (!items.length) return null\n\n return (\n <div role=\"listbox\" aria-label=\"Emoji suggestions\" className=\"z-popover max-h-[200px] overflow-x-hidden overflow-y-auto rounded-control bg-surface-overlay shadow-raised-hover\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm',\n index === selectedIndex ? 'bg-surface-raised text-surface-fg' : 'text-surface-fg-muted hover:bg-surface-raised',\n )}\n >\n <EmojiImage emoji={item} size=\"1.25em\" />\n <span className=\"truncate\">:{item.id}:</span>\n </button>\n ))}\n </div>\n )\n },\n)\nEmojiList.displayName = 'EmojiList'\n\nfunction createEmojiSuggestionRenderer() {\n return () => {\n let root: Root | null = null\n let container: HTMLDivElement | null = null\n let componentRef: EmojiListRef | null = null\n\n return {\n onStart: (props: SuggestionProps<ResolvedEmoji>) => {\n container = document.createElement('div')\n container.style.position = 'absolute'\n container.style.zIndex = '1400'\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n document.body.appendChild(container)\n root = createRoot(container)\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onUpdate: (props: SuggestionProps<ResolvedEmoji>) => {\n if (!root || !container) return\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n return true\n }\n return componentRef?.onKeyDown(props) ?? false\n },\n\n onExit: () => {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n },\n }\n }\n}\n\n/** Factory: returns the configured `:shortcode:` emoji suggestion extension. */\nexport function createEmojiSuggestion() {\n return Extension.create({\n name: 'emojiSuggestion',\n\n addProseMirrorPlugins() {\n return [\n Suggestion({\n pluginKey: new PluginKey('emojiSuggestion'),\n editor: this.editor,\n char: ':',\n items: async ({ query }) => {\n const data = await loadEmojiData()\n return searchEmoji(data, query, 8)\n },\n command: ({ editor, range, props: item }) => {\n const emoji = item as ResolvedEmoji\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({\n type: 'emojiNode',\n attrs: { id: emoji.id, native: emoji.native },\n })\n .run()\n },\n render: createEmojiSuggestionRenderer(),\n }),\n ]\n },\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"emoji-suggestion.js","names":[],"sources":["../../src/composed/extensions/emoji-data.ts","../../src/composed/extensions/emoji-suggestion.tsx"],"sourcesContent":["// Emoji dataset for the editors' `:shortcode:` autocomplete. Sources\n// `@emoji-mart/data` — a pure-JSON package with NO react peer, so it is\n// unaffected by the React-19 peer issue that drove the picker off\n// `@emoji-mart/react` (→ frimousse). Native-only since that migration: no\n// spritesheet coordinates, no per-art-style loaders.\n\ninterface EmojiSkin {\n native: string\n}\n\ninterface EmojiEntry {\n id: string\n name: string\n keywords?: string[]\n skins: EmojiSkin[]\n}\n\nexport interface EmojiDataset {\n emojis: Record<string, EmojiEntry>\n}\n\nexport interface ResolvedEmoji {\n id: string\n name: string\n native: string\n}\n\nlet cachedData: EmojiDataset | null = null\n\nexport async function loadEmojiData(): Promise<EmojiDataset> {\n if (cachedData) return cachedData\n const mod = await import('@emoji-mart/data')\n cachedData = ((mod as { default?: EmojiDataset }).default ?? mod) as EmojiDataset\n return cachedData\n}\n\nfunction resolve(entry: EmojiEntry): ResolvedEmoji {\n return { id: entry.id, name: entry.name, native: entry.skins[0]?.native ?? '' }\n}\n\nexport function searchEmoji(data: EmojiDataset, query: string, limit = 8): ResolvedEmoji[] {\n const entries = Object.values(data.emojis)\n if (!query) return entries.slice(0, limit).map(resolve)\n const q = query.toLowerCase()\n const results: ResolvedEmoji[] = []\n for (const e of entries) {\n if (results.length >= limit) break\n if (\n e.id.includes(q) ||\n e.name.toLowerCase().includes(q) ||\n e.keywords?.some((k) => k.includes(q))\n ) {\n results.push(resolve(e))\n }\n }\n return results\n}\n","import { Extension } from '@tiptap/core'\nimport Suggestion, { type SuggestionKeyDownProps,type SuggestionProps } from '@tiptap/suggestion'\nimport { PluginKey } from '@tiptap/pm/state'\nimport * as React from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\n\nimport { cn } from '../../ui/lib/utils'\nimport { loadEmojiData, type ResolvedEmoji, searchEmoji } from './emoji-data'\n\n// Re-export for external consumers\nexport type { ResolvedEmoji as EmojiSuggestionItem }\n\n// Native-only since the frimousse migration.\nfunction EmojiImage({ emoji, size = '1.2em' }: { emoji: ResolvedEmoji; size?: string }) {\n return <span style={{ fontSize: size }}>{emoji.native}</span>\n}\n\ninterface EmojiListRef {\n onKeyDown: (props: SuggestionKeyDownProps) => boolean\n}\n\ninterface EmojiListProps {\n items: ResolvedEmoji[]\n command: (item: ResolvedEmoji) => void\n}\n\nconst EmojiList = React.forwardRef<EmojiListRef, EmojiListProps>(\n ({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n\n React.useEffect(() => setSelectedIndex(0), [items])\n\n React.useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'ArrowUp') {\n setSelectedIndex((i) => (i + items.length - 1) % items.length)\n return true\n }\n if (event.key === 'ArrowDown') {\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n }\n if (event.key === 'Enter') {\n if (items[selectedIndex]) command(items[selectedIndex])\n return true\n }\n return false\n },\n }))\n\n if (!items.length) return null\n\n return (\n <div role=\"listbox\" aria-label=\"Emoji suggestions\" className=\"z-popover max-h-[200px] overflow-x-hidden overflow-y-auto rounded-control bg-surface-overlay shadow-raised-hover\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm',\n index === selectedIndex ? 'bg-surface-panel-hover text-surface-fg' : 'text-surface-fg-muted hover:bg-surface-panel-hover',\n )}\n >\n <EmojiImage emoji={item} size=\"1.25em\" />\n <span className=\"truncate\">:{item.id}:</span>\n </button>\n ))}\n </div>\n )\n },\n)\nEmojiList.displayName = 'EmojiList'\n\nfunction createEmojiSuggestionRenderer() {\n return () => {\n let root: Root | null = null\n let container: HTMLDivElement | null = null\n let componentRef: EmojiListRef | null = null\n\n return {\n onStart: (props: SuggestionProps<ResolvedEmoji>) => {\n container = document.createElement('div')\n container.style.position = 'absolute'\n container.style.zIndex = '1400'\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n document.body.appendChild(container)\n root = createRoot(container)\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onUpdate: (props: SuggestionProps<ResolvedEmoji>) => {\n if (!root || !container) return\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n return true\n }\n return componentRef?.onKeyDown(props) ?? false\n },\n\n onExit: () => {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n },\n }\n }\n}\n\n/** Factory: returns the configured `:shortcode:` emoji suggestion extension. */\nexport function createEmojiSuggestion() {\n return Extension.create({\n name: 'emojiSuggestion',\n\n addProseMirrorPlugins() {\n return [\n Suggestion({\n pluginKey: new PluginKey('emojiSuggestion'),\n editor: this.editor,\n char: ':',\n items: async ({ query }) => {\n const data = await loadEmojiData()\n return searchEmoji(data, query, 8)\n },\n command: ({ editor, range, props: item }) => {\n const emoji = item as ResolvedEmoji\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({\n type: 'emojiNode',\n attrs: { id: emoji.id, native: emoji.native },\n })\n .run()\n },\n render: createEmojiSuggestionRenderer(),\n }),\n ]\n },\n })\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ function m({ node: e }) {
|
|
|
19
19
|
href: /^https?:\/\//.test(e.attrs.url) ? e.attrs.url : "#",
|
|
20
20
|
target: "_blank",
|
|
21
21
|
rel: "noopener noreferrer",
|
|
22
|
-
className: "inline-flex items-center gap-ds-03 rounded-control border border-surface-border-strong bg-surface-
|
|
22
|
+
className: "inline-flex items-center gap-ds-03 rounded-control border border-surface-border-strong bg-surface-panel px-ds-04 py-ds-03 text-body-sm text-surface-fg transition-colors hover:border-surface-border-strong hover:bg-surface-panel-hover",
|
|
23
23
|
contentEditable: !1,
|
|
24
24
|
children: [
|
|
25
25
|
/* @__PURE__ */ i(t, {
|
|
@@ -80,7 +80,7 @@ var h = u.create({
|
|
|
80
80
|
role: "option",
|
|
81
81
|
"aria-selected": r === s,
|
|
82
82
|
onClick: () => n(t),
|
|
83
|
-
className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", r === s ? "bg-surface-
|
|
83
|
+
className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", r === s ? "bg-surface-panel-hover text-surface-fg" : "text-surface-fg-muted hover:bg-surface-panel-hover"),
|
|
84
84
|
children: [t.avatar ? /* @__PURE__ */ i("img", {
|
|
85
85
|
src: t.avatar,
|
|
86
86
|
alt: "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mention-suggestion.js","names":[],"sources":["../../src/composed/extensions/file-attachment.tsx","../../src/composed/extensions/mention-suggestion.tsx"],"sourcesContent":["import { IconDownload,IconFile } from '@tabler/icons-react'\nimport { mergeAttributes,Node } from '@tiptap/core'\nimport { type NodeViewProps,NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react'\n\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction FileAttachmentView({ node }: NodeViewProps) {\n const safeUrl = /^https?:\\/\\//.test(node.attrs.url) ? node.attrs.url : '#'\n return (\n <NodeViewWrapper className=\"my-ds-02\">\n <a\n href={safeUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-ds-03 rounded-control border border-surface-border-strong bg-surface-raised px-ds-04 py-ds-03 text-body-sm text-surface-fg transition-colors hover:border-surface-border-strong hover:bg-surface-raised-hover\"\n contentEditable={false}\n >\n <Icon icon={IconFile} size=\"sm\" className=\"shrink-0 text-surface-fg-subtle\" />\n <TruncatedText mode=\"middle\" className=\"min-w-0\">{node.attrs.name}</TruncatedText>\n <span className=\"shrink-0 text-surface-fg-subtle\">({formatFileSize(node.attrs.size)})</span>\n <Icon icon={IconDownload} size=\"sm\" className=\"shrink-0 text-surface-fg-subtle\" />\n </a>\n </NodeViewWrapper>\n )\n}\n\nexport const FileAttachment = Node.create({\n name: 'fileAttachment',\n group: 'block',\n atom: true,\n\n addAttributes() {\n return {\n url: { default: null },\n name: { default: 'Untitled' },\n size: { default: 0 },\n }\n },\n\n parseHTML() {\n return [{ tag: 'div[data-file-attachment]' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['div', mergeAttributes({ 'data-file-attachment': '' }, HTMLAttributes)]\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(FileAttachmentView)\n },\n})\n","import type { SuggestionKeyDownProps,SuggestionProps } from '@tiptap/suggestion'\nimport * as React from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\n\nimport { cn } from '../../ui/lib/utils'\nimport type { MentionItem } from '../rich-text-editor'\n\ninterface MentionListRef {\n onKeyDown: (props: SuggestionKeyDownProps) => boolean\n}\n\ninterface MentionListProps {\n items: MentionItem[]\n command: (item: MentionItem) => void\n}\n\nconst MentionList = React.forwardRef<MentionListRef, MentionListProps>(\n ({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n\n React.useEffect(() => setSelectedIndex(0), [items])\n\n React.useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'ArrowUp') {\n setSelectedIndex((i) => (i + items.length - 1) % items.length)\n return true\n }\n if (event.key === 'ArrowDown') {\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n }\n if (event.key === 'Enter') {\n if (items[selectedIndex]) command(items[selectedIndex])\n return true\n }\n return false\n },\n }))\n\n if (!items.length) return null\n\n return (\n <div role=\"listbox\" aria-label=\"Mention suggestions\" className=\"z-popover overflow-hidden rounded-control bg-surface-overlay shadow-raised-hover\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm',\n index === selectedIndex ? 'bg-surface-raised text-surface-fg' : 'text-surface-fg-muted hover:bg-surface-raised',\n )}\n >\n {item.avatar ? (\n <img src={item.avatar} alt=\"\" className=\"h-ico-md w-ico-md rounded-pill object-cover\" />\n ) : (\n <span className=\"flex h-ico-md w-ico-md items-center justify-center rounded-pill bg-accent-2 text-[10px] font-semibold text-accent-11\">\n {item.label.charAt(0).toUpperCase()}\n </span>\n )}\n <span>{item.label}</span>\n </button>\n ))}\n </div>\n )\n },\n)\nMentionList.displayName = 'MentionList'\n\nexport function createSuggestionRenderer(onMentionSelect?: (item: MentionItem) => void) {\n return () => {\n let root: Root | null = null\n let container: HTMLDivElement | null = null\n let componentRef: MentionListRef | null = null\n\n return {\n onStart: (props: SuggestionProps<MentionItem>) => {\n container = document.createElement('div')\n container.style.position = 'absolute'\n container.style.zIndex = '1400' // z-popover\n\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n document.body.appendChild(container)\n\n root = createRoot(container)\n root.render(\n <MentionList\n ref={(r) => { componentRef = r }}\n items={props.items as MentionItem[]}\n command={(item) => {\n props.command({ id: item.id, label: item.label })\n onMentionSelect?.(item)\n }}\n />,\n )\n },\n\n onUpdate: (props: SuggestionProps<MentionItem>) => {\n if (!root || !container) return\n\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n\n root.render(\n <MentionList\n ref={(r) => { componentRef = r }}\n items={props.items as MentionItem[]}\n command={(item) => {\n props.command({ id: item.id, label: item.label })\n onMentionSelect?.(item)\n }}\n />,\n )\n },\n\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n return true\n }\n return componentRef?.onKeyDown(props) ?? false\n },\n\n onExit: () => {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n },\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mention-suggestion.js","names":[],"sources":["../../src/composed/extensions/file-attachment.tsx","../../src/composed/extensions/mention-suggestion.tsx"],"sourcesContent":["import { IconDownload,IconFile } from '@tabler/icons-react'\nimport { mergeAttributes,Node } from '@tiptap/core'\nimport { type NodeViewProps,NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react'\n\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction FileAttachmentView({ node }: NodeViewProps) {\n const safeUrl = /^https?:\\/\\//.test(node.attrs.url) ? node.attrs.url : '#'\n return (\n <NodeViewWrapper className=\"my-ds-02\">\n <a\n href={safeUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-ds-03 rounded-control border border-surface-border-strong bg-surface-panel px-ds-04 py-ds-03 text-body-sm text-surface-fg transition-colors hover:border-surface-border-strong hover:bg-surface-panel-hover\"\n contentEditable={false}\n >\n <Icon icon={IconFile} size=\"sm\" className=\"shrink-0 text-surface-fg-subtle\" />\n <TruncatedText mode=\"middle\" className=\"min-w-0\">{node.attrs.name}</TruncatedText>\n <span className=\"shrink-0 text-surface-fg-subtle\">({formatFileSize(node.attrs.size)})</span>\n <Icon icon={IconDownload} size=\"sm\" className=\"shrink-0 text-surface-fg-subtle\" />\n </a>\n </NodeViewWrapper>\n )\n}\n\nexport const FileAttachment = Node.create({\n name: 'fileAttachment',\n group: 'block',\n atom: true,\n\n addAttributes() {\n return {\n url: { default: null },\n name: { default: 'Untitled' },\n size: { default: 0 },\n }\n },\n\n parseHTML() {\n return [{ tag: 'div[data-file-attachment]' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['div', mergeAttributes({ 'data-file-attachment': '' }, HTMLAttributes)]\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(FileAttachmentView)\n },\n})\n","import type { SuggestionKeyDownProps,SuggestionProps } from '@tiptap/suggestion'\nimport * as React from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\n\nimport { cn } from '../../ui/lib/utils'\nimport type { MentionItem } from '../rich-text-editor'\n\ninterface MentionListRef {\n onKeyDown: (props: SuggestionKeyDownProps) => boolean\n}\n\ninterface MentionListProps {\n items: MentionItem[]\n command: (item: MentionItem) => void\n}\n\nconst MentionList = React.forwardRef<MentionListRef, MentionListProps>(\n ({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n\n React.useEffect(() => setSelectedIndex(0), [items])\n\n React.useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'ArrowUp') {\n setSelectedIndex((i) => (i + items.length - 1) % items.length)\n return true\n }\n if (event.key === 'ArrowDown') {\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n }\n if (event.key === 'Enter') {\n if (items[selectedIndex]) command(items[selectedIndex])\n return true\n }\n return false\n },\n }))\n\n if (!items.length) return null\n\n return (\n <div role=\"listbox\" aria-label=\"Mention suggestions\" className=\"z-popover overflow-hidden rounded-control bg-surface-overlay shadow-raised-hover\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm',\n index === selectedIndex ? 'bg-surface-panel-hover text-surface-fg' : 'text-surface-fg-muted hover:bg-surface-panel-hover',\n )}\n >\n {item.avatar ? (\n <img src={item.avatar} alt=\"\" className=\"h-ico-md w-ico-md rounded-pill object-cover\" />\n ) : (\n <span className=\"flex h-ico-md w-ico-md items-center justify-center rounded-pill bg-accent-2 text-[10px] font-semibold text-accent-11\">\n {item.label.charAt(0).toUpperCase()}\n </span>\n )}\n <span>{item.label}</span>\n </button>\n ))}\n </div>\n )\n },\n)\nMentionList.displayName = 'MentionList'\n\nexport function createSuggestionRenderer(onMentionSelect?: (item: MentionItem) => void) {\n return () => {\n let root: Root | null = null\n let container: HTMLDivElement | null = null\n let componentRef: MentionListRef | null = null\n\n return {\n onStart: (props: SuggestionProps<MentionItem>) => {\n container = document.createElement('div')\n container.style.position = 'absolute'\n container.style.zIndex = '1400' // z-popover\n\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n document.body.appendChild(container)\n\n root = createRoot(container)\n root.render(\n <MentionList\n ref={(r) => { componentRef = r }}\n items={props.items as MentionItem[]}\n command={(item) => {\n props.command({ id: item.id, label: item.label })\n onMentionSelect?.(item)\n }}\n />,\n )\n },\n\n onUpdate: (props: SuggestionProps<MentionItem>) => {\n if (!root || !container) return\n\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n\n root.render(\n <MentionList\n ref={(r) => { componentRef = r }}\n items={props.items as MentionItem[]}\n command={(item) => {\n props.command({ id: item.id, label: item.label })\n onMentionSelect?.(item)\n }}\n />,\n )\n },\n\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n return true\n }\n return componentRef?.onKeyDown(props) ?? false\n },\n\n onExit: () => {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n },\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -162,7 +162,7 @@ var M = i.forwardRef(({ provider: i, accountLabel: s, connected: c, onAction: l,
|
|
|
162
162
|
let _ = x[i], v = b[i], y = d ?? (c ? "Disconnect" : `Connect ${v}`);
|
|
163
163
|
return /* @__PURE__ */ o("div", {
|
|
164
164
|
ref: g,
|
|
165
|
-
className: e("flex items-center justify-between gap-ds-04 py-ds-04 px-ds-04 rounded-surface border border-surface-border bg-surface-
|
|
165
|
+
className: e("flex items-center justify-between gap-ds-04 py-ds-04 px-ds-04 rounded-surface border border-surface-border bg-surface-panel", m),
|
|
166
166
|
"data-provider": i,
|
|
167
167
|
"data-connected": c ? "true" : "false",
|
|
168
168
|
...h,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-button.js","names":[],"sources":["../../src/ui/oauth-button/oauth-button.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconBrandApple,\n IconBrandDiscord,\n IconBrandFacebook,\n IconBrandGithub,\n IconBrandGitlab,\n IconBrandGoogleFilled,\n IconBrandLinkedin,\n IconBrandSlack,\n IconBrandWindows,\n IconBrandXFilled,\n IconFingerprint,\n IconKey,\n IconMail,\n} from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button, type ButtonProps } from '../button'\nimport type { IconInput } from '../lib/icon-input'\nimport { normalizeIcon } from '../lib/normalize-icon'\nimport { cn } from '../lib/utils'\nimport { TruncatedText } from '../truncated-text'\n\n// ── Types ───────────────────────────────────────────────────────\n\nexport type OAuthProvider =\n | 'google'\n | 'apple'\n | 'github'\n | 'microsoft'\n | 'x'\n | 'linkedin'\n | 'facebook'\n | 'discord'\n | 'slack'\n | 'gitlab'\n | 'sso'\n | 'email'\n | 'passkey'\n\nexport type OAuthIntent = 'continue' | 'signin' | 'signup'\n\n/**\n * Visual treatment — 1:1 with Button's variant axis. OAuthButton is a thin\n * wrapper over Button, so every variant renders pixel-for-pixel identical\n * to a regular Button at the same variant + size. Brand identity lives in\n * the glyph (override via `icon` for a brand-multicolour SVG), not the\n * button surface.\n *\n * - `solid` (default) | `soft` | `outline` | `ghost`\n */\nexport type OAuthVariant = 'solid' | 'soft' | 'outline' | 'ghost'\n\n/** @deprecated Use `OAuthVariant`. brand/dark map to solid; outline passes through. */\nexport type OAuthAppearance = 'brand' | 'outline' | 'dark'\n\n// ── Static metadata ─────────────────────────────────────────────\n\nconst providerDisplay: Record<OAuthProvider, string> = {\n google: 'Google',\n apple: 'Apple',\n github: 'GitHub',\n microsoft: 'Microsoft',\n x: 'X',\n linkedin: 'LinkedIn',\n facebook: 'Facebook',\n discord: 'Discord',\n slack: 'Slack',\n gitlab: 'GitLab',\n sso: 'SSO',\n email: 'email',\n passkey: 'a passkey',\n}\n\n// Default glyph per provider — sourced from Tabler (already a peer dep).\n// Consumer can override with the `icon` prop to drop in a brand-multicolour SVG.\nconst providerIcon: Record<OAuthProvider, React.ComponentType<{ size?: number | string; 'aria-hidden'?: boolean }>> = {\n google: IconBrandGoogleFilled,\n apple: IconBrandApple,\n github: IconBrandGithub,\n microsoft: IconBrandWindows,\n x: IconBrandXFilled,\n linkedin: IconBrandLinkedin,\n facebook: IconBrandFacebook,\n discord: IconBrandDiscord,\n slack: IconBrandSlack,\n gitlab: IconBrandGitlab,\n sso: IconKey,\n email: IconMail,\n passkey: IconFingerprint,\n}\n\nfunction providerName(provider: OAuthProvider): string {\n // Display name for `compact` mode — drops the \"a\" article on passkey.\n return provider === 'passkey' ? 'Passkey' : providerDisplay[provider]\n}\n\nfunction defaultLabel(provider: OAuthProvider, intent: OAuthIntent, compact: boolean): string {\n if (compact) return providerName(provider)\n const name = providerDisplay[provider]\n switch (intent) {\n case 'signin':\n return `Sign in with ${name}`\n case 'signup':\n return `Sign up with ${name}`\n case 'continue':\n default:\n return `Continue with ${name}`\n }\n}\n\nconst providerCopyOverride: Partial<Record<OAuthProvider, Partial<Record<OAuthIntent, string>>>> = {\n passkey: {\n continue: 'Continue with a passkey',\n signin: 'Sign in with a passkey',\n signup: 'Create a passkey',\n },\n}\n\nfunction resolveLabel(provider: OAuthProvider, intent: OAuthIntent, compact: boolean): string {\n if (compact) return providerName(provider)\n return providerCopyOverride[provider]?.[intent] ?? defaultLabel(provider, intent, compact)\n}\n\n/** Long-form name used for aria-label even when compact label is rendered. */\nfunction resolveAriaName(provider: OAuthProvider, intent: OAuthIntent): string {\n return providerCopyOverride[provider]?.[intent] ?? defaultLabel(provider, intent, false)\n}\n\n// **Visual treatment:** OAuthButton is a thin wrapper over `<Button>` — every\n// height, padding, radius, border, hover, focus, active, and disabled state is\n// the DS Button's. We do NOT inject per-provider hex backgrounds: brand\n// identity lives in the glyph (Tabler default; consumer can pass a real\n// multicolour SVG via `icon`). This guarantees pixel-for-pixel consistency\n// with every other button in the system at every variant.\n\nconst ICON_PX: Record<string, number> = {\n xs: 14,\n sm: 16,\n md: 18,\n lg: 20,\n 'compact-xs': 14,\n 'compact-sm': 14,\n 'compact-md': 16,\n icon: 18,\n 'icon-xs': 14,\n 'icon-sm': 16,\n 'icon-md': 18,\n 'icon-lg': 20,\n}\n\nconst ICON_ONLY_SIZE: Record<string, NonNullable<ButtonProps['size']>> = {\n xs: 'icon-xs',\n sm: 'icon-sm',\n md: 'icon-md',\n lg: 'icon-lg',\n 'compact-xs': 'icon-xs',\n 'compact-sm': 'icon-sm',\n 'compact-md': 'icon-md',\n}\n\n// ── Component ───────────────────────────────────────────────────\n\n/**\n * Props for `OAuthButton` — a brand-aware sign-in button for OAuth providers.\n *\n * Composes on top of `<Button>`, inheriting `size`, `loading`, `onClickAsync`,\n * `fullWidth`, and async state machinery. The provider's icon, label, and\n * brand-coloured styling are derived from the `provider` prop.\n *\n * **Provider** (required): includes 10 social providers + 3 generic\n * identifiers (`sso` enterprise, `email` magic-link, `passkey` WebAuthn).\n *\n * **Intent**: drives the verb in the label.\n * - `continue` (default) — \"Continue with Google\" — works on login and signup.\n * - `signin` — \"Sign in with Google\".\n * - `signup` — \"Sign up with Google\".\n *\n * **Appearance**: visual treatment.\n * - `brand` (default) — provider's brand background colour.\n * - `outline` — DS-neutral outline with monochrome glyph. Best for uniform rows.\n * - `dark` — unified Apple-style across all providers for visual consistency.\n *\n * **icon**: override the default Tabler glyph (e.g. drop in the provider's\n * official multicolour SVG downloaded from their brand page).\n *\n * **iconOnly**: render a square icon button. Provider name kept in `aria-label`.\n *\n * **lastUsed**: shows a small \"Last used\" pill above the button — high-converting\n * UX hint. Consumer tracks which provider the user picked previously.\n *\n * **helperText**: optional reassurance copy rendered below the button.\n *\n * @example\n * <OAuthButton provider=\"google\" intent=\"continue\" fullWidth onClickAsync={signInWithGoogle} />\n *\n * @example\n * // True brand-multicolour glyph:\n * <OAuthButton provider=\"google\" icon={<GoogleGSvg />} />\n *\n * @example\n * <OAuthGroup>\n * <OAuthButton provider=\"google\" appearance=\"dark\" />\n * <OAuthButton provider=\"apple\" appearance=\"dark\" />\n * <OAuthButton provider=\"github\" appearance=\"dark\" />\n * </OAuthGroup>\n */\nexport interface OAuthButtonProps\n extends Omit<\n ButtonProps,\n 'variant' | 'color' | 'startIcon' | 'endIcon' | 'shape' | 'children'\n > {\n /** OAuth provider this button represents */\n provider: OAuthProvider\n /** Verb in the default label. Default: `continue` */\n intent?: OAuthIntent\n /**\n * Visual treatment. Default: `solid`. Mirrors Button's variant axis with\n * an extra `dark` for row coherence across providers.\n */\n variant?: OAuthVariant\n /** @deprecated Use `variant`. `appearance=\"brand\"` maps to `variant=\"solid\"`. */\n appearance?: OAuthAppearance\n /** Override the default glyph (e.g. brand-multicolour SVG). Accepts any `IconInput`. */\n icon?: IconInput\n /** Render a compact icon-only button (provider name kept in aria-label) */\n iconOnly?: boolean\n /**\n * Short label — renders just the provider name (\"Google\") instead of the\n * full \"Continue with Google\". Use under an explicit \"Or sign in with\"\n * divider where the verb is already established. aria-label still gets the\n * full long form for screen readers.\n */\n compact?: boolean\n /**\n * Show \"Last used\" hint — a small pill anchored to the top-right corner of\n * the button (sits ON the corner, half outside the button bounds).\n */\n lastUsed?: boolean\n /**\n * Customise the \"Last used\" badge. Pass a ReactNode to replace the entire\n * pill (use this for custom copy, icons, or styling), or a render fn that\n * receives the default props for tweaks. Only rendered when `lastUsed` is\n * also set so the wrapper / aria augmentation logic stays consistent.\n *\n * @example\n * <OAuthButton provider=\"google\" lastUsed lastUsedSlot={<MyBadge />} />\n */\n lastUsedSlot?: React.ReactNode | ((defaults: { label: string }) => React.ReactNode)\n /** Override the visible badge label text. Default: \"Last used\". */\n lastUsedLabel?: string\n /** Reassurance copy rendered below the button */\n helperText?: React.ReactNode\n /** Override the default label entirely (i18n / custom copy) */\n children?: React.ReactNode\n}\n\nconst OAuthButton = React.forwardRef<HTMLButtonElement, OAuthButtonProps>(\n (\n {\n provider,\n intent = 'continue',\n variant: variantProp,\n appearance,\n icon,\n iconOnly = false,\n compact = false,\n lastUsed = false,\n lastUsedSlot,\n lastUsedLabel = 'Last used',\n helperText,\n children,\n className,\n size: sizeProp,\n fullWidth = false,\n ...buttonProps\n },\n ref,\n ) => {\n const size: NonNullable<ButtonProps['size']> = sizeProp ?? 'md'\n const DefaultGlyph = providerIcon[provider]\n const iconPx = ICON_PX[size] ?? 18\n const glyphNode = normalizeIcon(icon) ?? <DefaultGlyph size={iconPx} aria-hidden />\n const label = children ?? resolveLabel(provider, intent, compact)\n\n // Resolve final variant. `appearance` is a deprecated alias mapped to\n // the closest DS variant — brand/dark both collapse to solid since\n // OAuthButton no longer injects per-provider brand colours.\n const variant: OAuthVariant =\n variantProp ??\n (appearance === 'outline' ? 'outline' : 'solid')\n\n // aria-label always carries the long-form name so screen readers stay\n // informative even when the visual label is compact or icon-only.\n const longName = resolveAriaName(provider, intent)\n const ariaLabelForButton = iconOnly || compact || lastUsed\n ? `${longName}${lastUsed ? ' (last used)' : ''}`\n : undefined\n\n // \"Last used\" badge — sits ON the top-right corner of the button,\n // overlapping the button edge for the classic \"notification badge\"\n // look. Rendered as a SIBLING to Button (not a child) because Button's\n // `overflow-hidden` would clip a child poking outside the corner.\n //\n // Composable: pass `lastUsedSlot` (ReactNode or render fn) to replace\n // the default pill. `lastUsedLabel` swaps just the text inside it.\n // aria-label augments the accessible name with \"(last used)\".\n // intentional: a 9px corner pill, smaller than Badge's smallest size (xs).\n // Not worth a sub-xs Badge size for one call site — kept as a documented exception.\n const defaultBadge = (\n <span className=\"pointer-events-none inline-flex items-center rounded-pill bg-accent-9 text-accent-fg px-2 py-1 text-label-xs leading-none font-semibold uppercase tracking-wide shadow-overlay\">\n {lastUsedLabel}\n </span>\n )\n const customBadge =\n typeof lastUsedSlot === 'function' ? lastUsedSlot({ label: lastUsedLabel }) : lastUsedSlot\n const renderedBadge = customBadge ?? defaultBadge\n\n const buttonEl = (\n <Button\n ref={ref}\n variant={variant}\n color=\"neutral\"\n size={iconOnly ? ICON_ONLY_SIZE[size] ?? 'icon-md' : size}\n fullWidth={fullWidth && !iconOnly}\n startIcon={iconOnly ? undefined : (glyphNode as React.ReactElement)}\n aria-label={ariaLabelForButton}\n data-provider={provider}\n data-oauth-variant={variant}\n data-last-used={lastUsed || undefined}\n className={className}\n {...buttonProps}\n >\n {iconOnly ? glyphNode : label}\n </Button>\n )\n\n const showBadge = lastUsed && !iconOnly\n if (!showBadge && !helperText) return buttonEl\n\n return (\n <div className={cn('relative inline-flex flex-col', fullWidth && 'w-full')}>\n {buttonEl}\n {showBadge ? (\n <span\n aria-hidden=\"true\"\n data-last-used-badge=\"\"\n className=\"pointer-events-none absolute -top-[10px] -right-2 z-10\"\n >\n {renderedBadge}\n </span>\n ) : null}\n {helperText ? (\n <span className=\"mt-ds-02 text-body-sm text-surface-fg-subtle\">{helperText}</span>\n ) : null}\n </div>\n )\n },\n)\nOAuthButton.displayName = 'OAuthButton'\n\nexport { OAuthButton }\n\n// ── OAuthGroup ─────────────────────────────────────────────────\n\nexport interface OAuthGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout. `vertical` (default) stacks full-width buttons. */\n orientation?: 'vertical' | 'horizontal'\n /** Gap token between buttons. Default: `ds-03` */\n gap?: 'ds-02' | 'ds-03' | 'ds-04'\n /** Stretch children to fill width (vertical only). Default: true */\n fullWidth?: boolean\n /**\n * Move any child with `lastUsed` to position 0. Stripe-style ordering — the\n * provider the user picked last time becomes the top option, which gives a\n * stronger conversion lift than a \"Last used\" badge does on its own.\n * Default: false (preserve source order).\n */\n reorderLastUsedFirst?: boolean\n}\n\n/**\n * Stacked layout wrapper for OAuth buttons with consistent spacing. Defaults to\n * vertical, full-width children — the dominant sign-in pattern. For an icon-only\n * row, set `orientation=\"horizontal\"` and use `<OAuthButton iconOnly />`.\n */\nconst OAuthGroup = React.forwardRef<HTMLDivElement, OAuthGroupProps>(\n (\n {\n orientation = 'vertical',\n gap = 'ds-03',\n fullWidth = true,\n reorderLastUsedFirst = false,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const gapClass =\n gap === 'ds-02' ? 'gap-ds-02' : gap === 'ds-04' ? 'gap-ds-04' : 'gap-ds-03'\n const stretch = orientation === 'vertical' && fullWidth\n\n const ordered = React.useMemo(() => {\n if (!reorderLastUsedFirst) return children\n const arr = React.Children.toArray(children)\n const lastUsedIdx = arr.findIndex(\n (c) => React.isValidElement(c) && (c.props as { lastUsed?: boolean })?.lastUsed,\n )\n if (lastUsedIdx <= 0) return children\n const next = [...arr]\n const [pulled] = next.splice(lastUsedIdx, 1)\n next.unshift(pulled)\n return next\n }, [children, reorderLastUsedFirst])\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex',\n orientation === 'vertical' ? 'flex-col' : 'flex-row flex-wrap items-center',\n gapClass,\n // Force both the direct wrapper AND the leaf <button> to stretch.\n // OAuthButton's outer wrapper picks up [&>*]:w-full; the button itself\n // sits two layers deep when a helperText wrapper exists, so\n // [&_button]:w-full covers both cases (wrapped + unwrapped) without\n // affecting unrelated descendants — only Buttons live inside.\n stretch && 'w-full [&>*]:w-full [&_button]:w-full',\n className,\n )}\n {...props}\n >\n {ordered}\n </div>\n )\n },\n)\nOAuthGroup.displayName = 'OAuthGroup'\n\nexport { OAuthGroup }\n\n// ── OAuthDivider ───────────────────────────────────────────────\n\nexport interface OAuthDividerProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Text between the rules. Default: \"or\" */\n label?: React.ReactNode\n}\n\nconst OAuthDivider = React.forwardRef<HTMLDivElement, OAuthDividerProps>(\n ({ label = 'or', className, ...props }, ref) => (\n <div\n ref={ref}\n role=\"separator\"\n aria-orientation=\"horizontal\"\n className={cn('flex items-center gap-ds-03', className)}\n {...props}\n >\n <hr aria-hidden=\"true\" className=\"flex-1 border-t border-surface-border-subtle\" />\n <span className=\"text-label-sm text-surface-fg-subtle uppercase tracking-wide\">{label}</span>\n <hr aria-hidden=\"true\" className=\"flex-1 border-t border-surface-border-subtle\" />\n </div>\n ),\n)\nOAuthDivider.displayName = 'OAuthDivider'\n\nexport { OAuthDivider }\n\n// ── OAuthConnectionRow (settings page — linked state) ──────────\n\nexport interface OAuthConnectionRowProps extends React.HTMLAttributes<HTMLDivElement> {\n provider: OAuthProvider\n /** Optional account identifier shown next to the provider name (e.g. email) */\n accountLabel?: React.ReactNode\n /** Whether the provider is currently linked */\n connected: boolean\n /** Click handler for the connect/disconnect action */\n onAction?: (e: React.MouseEvent<HTMLButtonElement>) => void\n /** Promise-returning click handler — uses Button's async state machine */\n onActionAsync?: (e: React.MouseEvent<HTMLButtonElement>) => Promise<void>\n /** Override the action label */\n actionLabel?: React.ReactNode\n /** Disable the action button */\n disabled?: boolean\n /** Override the default glyph. Accepts any `IconInput`. */\n icon?: IconInput\n}\n\n/**\n * Settings-page row representing a linked OAuth provider. Different surface\n * from sign-in: this is an existing connection the user can disconnect or\n * (re-)connect. Renders glyph + provider name + optional account label + a\n * connect/disconnect button.\n */\nconst OAuthConnectionRow = React.forwardRef<HTMLDivElement, OAuthConnectionRowProps>(\n (\n {\n provider,\n accountLabel,\n connected,\n onAction,\n onActionAsync,\n actionLabel,\n disabled,\n icon,\n className,\n ...props\n },\n ref,\n ) => {\n const DefaultGlyph = providerIcon[provider]\n const name = providerDisplay[provider]\n const resolvedLabel = actionLabel ?? (connected ? 'Disconnect' : `Connect ${name}`)\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex items-center justify-between gap-ds-04 py-ds-04 px-ds-04 rounded-surface border border-surface-border bg-surface-raised',\n className,\n )}\n data-provider={provider}\n data-connected={connected ? 'true' : 'false'}\n {...props}\n >\n <div className=\"flex items-center gap-ds-03 min-w-0\">\n {normalizeIcon(icon) ?? <DefaultGlyph size={24} aria-hidden />}\n <div className=\"flex flex-col min-w-0\">\n <TruncatedText className=\"text-body-md font-semibold text-surface-fg\">{name}</TruncatedText>\n {accountLabel ? (\n // Account labels are usually an email — middle-truncate to keep the domain.\n // Only a string can be measured/truncated; fall back to a plain clip otherwise.\n typeof accountLabel === 'string' ? (\n <TruncatedText mode=\"middle\" className=\"text-body-sm text-surface-fg-muted\">\n {accountLabel}\n </TruncatedText>\n ) : (\n <span className=\"text-body-sm text-surface-fg-muted truncate\">{accountLabel}</span>\n )\n ) : connected ? (\n <span className=\"text-body-sm text-success-11\">Connected</span>\n ) : (\n <span className=\"text-body-sm text-surface-fg-subtle\">Not connected</span>\n )}\n </div>\n </div>\n <Button\n size=\"sm\"\n variant={connected ? 'soft' : 'solid'}\n color={connected ? 'neutral' : 'accent'}\n disabled={disabled}\n onClick={onAction}\n onClickAsync={onActionAsync}\n >\n {resolvedLabel}\n </Button>\n </div>\n )\n },\n)\nOAuthConnectionRow.displayName = 'OAuthConnectionRow'\n\nexport { OAuthConnectionRow }\n\n// ── OAuthGlyph (atomic glyph render) ───────────────────────────\n\nexport interface OAuthGlyphProps extends Omit<React.SVGAttributes<SVGElement>, 'children'> {\n provider: OAuthProvider\n /** Glyph pixel size. Default 18. */\n size?: number\n}\n\n/**\n * Render just the provider glyph — useful when building a custom OAuth\n * trigger (e.g. an icon-button inside a Tooltip, or a provider chip on a\n * profile card). Defaults to the Tabler glyph; the icon prop on\n * `OAuthButton` is the right escape hatch for the brand multi-colour mark.\n */\nconst OAuthGlyph = React.forwardRef<SVGSVGElement, OAuthGlyphProps>(\n ({ provider, size = 18, ...props }, ref) => {\n const Cmp = providerIcon[provider] as React.ComponentType<{\n size?: number\n 'aria-hidden'?: boolean\n ref?: React.Ref<SVGSVGElement>\n }>\n return <Cmp ref={ref} size={size} aria-hidden {...(props as Record<string, unknown>)} />\n },\n)\nOAuthGlyph.displayName = 'OAuthGlyph'\n\nexport { OAuthGlyph }\n\n// ── Static helpers (composable building blocks) ────────────────\n\n/**\n * Resolve the visible label for a provider × intent combo. Useful when\n * rendering OAuth UIs outside of `OAuthButton` (e.g. a Confirm dialog\n * \"Disconnect from Google?\" title).\n */\nexport function getOAuthLabel(provider: OAuthProvider, intent: OAuthIntent = 'continue', compact = false): string {\n return resolveLabel(provider, intent, compact)\n}\n\n/** Display name for a provider, ready for sentence interpolation. */\nexport function getOAuthName(provider: OAuthProvider): string {\n return providerName(provider)\n}\n\n// ── Compound namespace ─────────────────────────────────────────\n\n/**\n * Namespaced API — drop-in alternative to importing each piece separately.\n *\n * @example\n * import { OAuth } from '@devalok/shilp-sutra/ui/oauth-button'\n *\n * <OAuth.Group reorderLastUsedFirst>\n * <OAuth.Button provider=\"google\" lastUsed />\n * <OAuth.Button provider=\"passkey\" />\n * <OAuth.Divider />\n * <OAuth.Button provider=\"sso\" variant=\"ghost\" />\n * </OAuth.Group>\n */\nexport const OAuth = {\n Button: OAuthButton,\n Group: OAuthGroup,\n Divider: OAuthDivider,\n ConnectionRow: OAuthConnectionRow,\n Glyph: OAuthGlyph,\n getLabel: getOAuthLabel,\n getName: getOAuthName,\n} as const\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oauth-button.js","names":[],"sources":["../../src/ui/oauth-button/oauth-button.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconBrandApple,\n IconBrandDiscord,\n IconBrandFacebook,\n IconBrandGithub,\n IconBrandGitlab,\n IconBrandGoogleFilled,\n IconBrandLinkedin,\n IconBrandSlack,\n IconBrandWindows,\n IconBrandXFilled,\n IconFingerprint,\n IconKey,\n IconMail,\n} from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button, type ButtonProps } from '../button'\nimport type { IconInput } from '../lib/icon-input'\nimport { normalizeIcon } from '../lib/normalize-icon'\nimport { cn } from '../lib/utils'\nimport { TruncatedText } from '../truncated-text'\n\n// ── Types ───────────────────────────────────────────────────────\n\nexport type OAuthProvider =\n | 'google'\n | 'apple'\n | 'github'\n | 'microsoft'\n | 'x'\n | 'linkedin'\n | 'facebook'\n | 'discord'\n | 'slack'\n | 'gitlab'\n | 'sso'\n | 'email'\n | 'passkey'\n\nexport type OAuthIntent = 'continue' | 'signin' | 'signup'\n\n/**\n * Visual treatment — 1:1 with Button's variant axis. OAuthButton is a thin\n * wrapper over Button, so every variant renders pixel-for-pixel identical\n * to a regular Button at the same variant + size. Brand identity lives in\n * the glyph (override via `icon` for a brand-multicolour SVG), not the\n * button surface.\n *\n * - `solid` (default) | `soft` | `outline` | `ghost`\n */\nexport type OAuthVariant = 'solid' | 'soft' | 'outline' | 'ghost'\n\n/** @deprecated Use `OAuthVariant`. brand/dark map to solid; outline passes through. */\nexport type OAuthAppearance = 'brand' | 'outline' | 'dark'\n\n// ── Static metadata ─────────────────────────────────────────────\n\nconst providerDisplay: Record<OAuthProvider, string> = {\n google: 'Google',\n apple: 'Apple',\n github: 'GitHub',\n microsoft: 'Microsoft',\n x: 'X',\n linkedin: 'LinkedIn',\n facebook: 'Facebook',\n discord: 'Discord',\n slack: 'Slack',\n gitlab: 'GitLab',\n sso: 'SSO',\n email: 'email',\n passkey: 'a passkey',\n}\n\n// Default glyph per provider — sourced from Tabler (already a peer dep).\n// Consumer can override with the `icon` prop to drop in a brand-multicolour SVG.\nconst providerIcon: Record<OAuthProvider, React.ComponentType<{ size?: number | string; 'aria-hidden'?: boolean }>> = {\n google: IconBrandGoogleFilled,\n apple: IconBrandApple,\n github: IconBrandGithub,\n microsoft: IconBrandWindows,\n x: IconBrandXFilled,\n linkedin: IconBrandLinkedin,\n facebook: IconBrandFacebook,\n discord: IconBrandDiscord,\n slack: IconBrandSlack,\n gitlab: IconBrandGitlab,\n sso: IconKey,\n email: IconMail,\n passkey: IconFingerprint,\n}\n\nfunction providerName(provider: OAuthProvider): string {\n // Display name for `compact` mode — drops the \"a\" article on passkey.\n return provider === 'passkey' ? 'Passkey' : providerDisplay[provider]\n}\n\nfunction defaultLabel(provider: OAuthProvider, intent: OAuthIntent, compact: boolean): string {\n if (compact) return providerName(provider)\n const name = providerDisplay[provider]\n switch (intent) {\n case 'signin':\n return `Sign in with ${name}`\n case 'signup':\n return `Sign up with ${name}`\n case 'continue':\n default:\n return `Continue with ${name}`\n }\n}\n\nconst providerCopyOverride: Partial<Record<OAuthProvider, Partial<Record<OAuthIntent, string>>>> = {\n passkey: {\n continue: 'Continue with a passkey',\n signin: 'Sign in with a passkey',\n signup: 'Create a passkey',\n },\n}\n\nfunction resolveLabel(provider: OAuthProvider, intent: OAuthIntent, compact: boolean): string {\n if (compact) return providerName(provider)\n return providerCopyOverride[provider]?.[intent] ?? defaultLabel(provider, intent, compact)\n}\n\n/** Long-form name used for aria-label even when compact label is rendered. */\nfunction resolveAriaName(provider: OAuthProvider, intent: OAuthIntent): string {\n return providerCopyOverride[provider]?.[intent] ?? defaultLabel(provider, intent, false)\n}\n\n// **Visual treatment:** OAuthButton is a thin wrapper over `<Button>` — every\n// height, padding, radius, border, hover, focus, active, and disabled state is\n// the DS Button's. We do NOT inject per-provider hex backgrounds: brand\n// identity lives in the glyph (Tabler default; consumer can pass a real\n// multicolour SVG via `icon`). This guarantees pixel-for-pixel consistency\n// with every other button in the system at every variant.\n\nconst ICON_PX: Record<string, number> = {\n xs: 14,\n sm: 16,\n md: 18,\n lg: 20,\n 'compact-xs': 14,\n 'compact-sm': 14,\n 'compact-md': 16,\n icon: 18,\n 'icon-xs': 14,\n 'icon-sm': 16,\n 'icon-md': 18,\n 'icon-lg': 20,\n}\n\nconst ICON_ONLY_SIZE: Record<string, NonNullable<ButtonProps['size']>> = {\n xs: 'icon-xs',\n sm: 'icon-sm',\n md: 'icon-md',\n lg: 'icon-lg',\n 'compact-xs': 'icon-xs',\n 'compact-sm': 'icon-sm',\n 'compact-md': 'icon-md',\n}\n\n// ── Component ───────────────────────────────────────────────────\n\n/**\n * Props for `OAuthButton` — a brand-aware sign-in button for OAuth providers.\n *\n * Composes on top of `<Button>`, inheriting `size`, `loading`, `onClickAsync`,\n * `fullWidth`, and async state machinery. The provider's icon, label, and\n * brand-coloured styling are derived from the `provider` prop.\n *\n * **Provider** (required): includes 10 social providers + 3 generic\n * identifiers (`sso` enterprise, `email` magic-link, `passkey` WebAuthn).\n *\n * **Intent**: drives the verb in the label.\n * - `continue` (default) — \"Continue with Google\" — works on login and signup.\n * - `signin` — \"Sign in with Google\".\n * - `signup` — \"Sign up with Google\".\n *\n * **Appearance**: visual treatment.\n * - `brand` (default) — provider's brand background colour.\n * - `outline` — DS-neutral outline with monochrome glyph. Best for uniform rows.\n * - `dark` — unified Apple-style across all providers for visual consistency.\n *\n * **icon**: override the default Tabler glyph (e.g. drop in the provider's\n * official multicolour SVG downloaded from their brand page).\n *\n * **iconOnly**: render a square icon button. Provider name kept in `aria-label`.\n *\n * **lastUsed**: shows a small \"Last used\" pill above the button — high-converting\n * UX hint. Consumer tracks which provider the user picked previously.\n *\n * **helperText**: optional reassurance copy rendered below the button.\n *\n * @example\n * <OAuthButton provider=\"google\" intent=\"continue\" fullWidth onClickAsync={signInWithGoogle} />\n *\n * @example\n * // True brand-multicolour glyph:\n * <OAuthButton provider=\"google\" icon={<GoogleGSvg />} />\n *\n * @example\n * <OAuthGroup>\n * <OAuthButton provider=\"google\" appearance=\"dark\" />\n * <OAuthButton provider=\"apple\" appearance=\"dark\" />\n * <OAuthButton provider=\"github\" appearance=\"dark\" />\n * </OAuthGroup>\n */\nexport interface OAuthButtonProps\n extends Omit<\n ButtonProps,\n 'variant' | 'color' | 'startIcon' | 'endIcon' | 'shape' | 'children'\n > {\n /** OAuth provider this button represents */\n provider: OAuthProvider\n /** Verb in the default label. Default: `continue` */\n intent?: OAuthIntent\n /**\n * Visual treatment. Default: `solid`. Mirrors Button's variant axis with\n * an extra `dark` for row coherence across providers.\n */\n variant?: OAuthVariant\n /** @deprecated Use `variant`. `appearance=\"brand\"` maps to `variant=\"solid\"`. */\n appearance?: OAuthAppearance\n /** Override the default glyph (e.g. brand-multicolour SVG). Accepts any `IconInput`. */\n icon?: IconInput\n /** Render a compact icon-only button (provider name kept in aria-label) */\n iconOnly?: boolean\n /**\n * Short label — renders just the provider name (\"Google\") instead of the\n * full \"Continue with Google\". Use under an explicit \"Or sign in with\"\n * divider where the verb is already established. aria-label still gets the\n * full long form for screen readers.\n */\n compact?: boolean\n /**\n * Show \"Last used\" hint — a small pill anchored to the top-right corner of\n * the button (sits ON the corner, half outside the button bounds).\n */\n lastUsed?: boolean\n /**\n * Customise the \"Last used\" badge. Pass a ReactNode to replace the entire\n * pill (use this for custom copy, icons, or styling), or a render fn that\n * receives the default props for tweaks. Only rendered when `lastUsed` is\n * also set so the wrapper / aria augmentation logic stays consistent.\n *\n * @example\n * <OAuthButton provider=\"google\" lastUsed lastUsedSlot={<MyBadge />} />\n */\n lastUsedSlot?: React.ReactNode | ((defaults: { label: string }) => React.ReactNode)\n /** Override the visible badge label text. Default: \"Last used\". */\n lastUsedLabel?: string\n /** Reassurance copy rendered below the button */\n helperText?: React.ReactNode\n /** Override the default label entirely (i18n / custom copy) */\n children?: React.ReactNode\n}\n\nconst OAuthButton = React.forwardRef<HTMLButtonElement, OAuthButtonProps>(\n (\n {\n provider,\n intent = 'continue',\n variant: variantProp,\n appearance,\n icon,\n iconOnly = false,\n compact = false,\n lastUsed = false,\n lastUsedSlot,\n lastUsedLabel = 'Last used',\n helperText,\n children,\n className,\n size: sizeProp,\n fullWidth = false,\n ...buttonProps\n },\n ref,\n ) => {\n const size: NonNullable<ButtonProps['size']> = sizeProp ?? 'md'\n const DefaultGlyph = providerIcon[provider]\n const iconPx = ICON_PX[size] ?? 18\n const glyphNode = normalizeIcon(icon) ?? <DefaultGlyph size={iconPx} aria-hidden />\n const label = children ?? resolveLabel(provider, intent, compact)\n\n // Resolve final variant. `appearance` is a deprecated alias mapped to\n // the closest DS variant — brand/dark both collapse to solid since\n // OAuthButton no longer injects per-provider brand colours.\n const variant: OAuthVariant =\n variantProp ??\n (appearance === 'outline' ? 'outline' : 'solid')\n\n // aria-label always carries the long-form name so screen readers stay\n // informative even when the visual label is compact or icon-only.\n const longName = resolveAriaName(provider, intent)\n const ariaLabelForButton = iconOnly || compact || lastUsed\n ? `${longName}${lastUsed ? ' (last used)' : ''}`\n : undefined\n\n // \"Last used\" badge — sits ON the top-right corner of the button,\n // overlapping the button edge for the classic \"notification badge\"\n // look. Rendered as a SIBLING to Button (not a child) because Button's\n // `overflow-hidden` would clip a child poking outside the corner.\n //\n // Composable: pass `lastUsedSlot` (ReactNode or render fn) to replace\n // the default pill. `lastUsedLabel` swaps just the text inside it.\n // aria-label augments the accessible name with \"(last used)\".\n // intentional: a 9px corner pill, smaller than Badge's smallest size (xs).\n // Not worth a sub-xs Badge size for one call site — kept as a documented exception.\n const defaultBadge = (\n <span className=\"pointer-events-none inline-flex items-center rounded-pill bg-accent-9 text-accent-fg px-2 py-1 text-label-xs leading-none font-semibold uppercase tracking-wide shadow-overlay\">\n {lastUsedLabel}\n </span>\n )\n const customBadge =\n typeof lastUsedSlot === 'function' ? lastUsedSlot({ label: lastUsedLabel }) : lastUsedSlot\n const renderedBadge = customBadge ?? defaultBadge\n\n const buttonEl = (\n <Button\n ref={ref}\n variant={variant}\n color=\"neutral\"\n size={iconOnly ? ICON_ONLY_SIZE[size] ?? 'icon-md' : size}\n fullWidth={fullWidth && !iconOnly}\n startIcon={iconOnly ? undefined : (glyphNode as React.ReactElement)}\n aria-label={ariaLabelForButton}\n data-provider={provider}\n data-oauth-variant={variant}\n data-last-used={lastUsed || undefined}\n className={className}\n {...buttonProps}\n >\n {iconOnly ? glyphNode : label}\n </Button>\n )\n\n const showBadge = lastUsed && !iconOnly\n if (!showBadge && !helperText) return buttonEl\n\n return (\n <div className={cn('relative inline-flex flex-col', fullWidth && 'w-full')}>\n {buttonEl}\n {showBadge ? (\n <span\n aria-hidden=\"true\"\n data-last-used-badge=\"\"\n className=\"pointer-events-none absolute -top-[10px] -right-2 z-10\"\n >\n {renderedBadge}\n </span>\n ) : null}\n {helperText ? (\n <span className=\"mt-ds-02 text-body-sm text-surface-fg-subtle\">{helperText}</span>\n ) : null}\n </div>\n )\n },\n)\nOAuthButton.displayName = 'OAuthButton'\n\nexport { OAuthButton }\n\n// ── OAuthGroup ─────────────────────────────────────────────────\n\nexport interface OAuthGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Layout. `vertical` (default) stacks full-width buttons. */\n orientation?: 'vertical' | 'horizontal'\n /** Gap token between buttons. Default: `ds-03` */\n gap?: 'ds-02' | 'ds-03' | 'ds-04'\n /** Stretch children to fill width (vertical only). Default: true */\n fullWidth?: boolean\n /**\n * Move any child with `lastUsed` to position 0. Stripe-style ordering — the\n * provider the user picked last time becomes the top option, which gives a\n * stronger conversion lift than a \"Last used\" badge does on its own.\n * Default: false (preserve source order).\n */\n reorderLastUsedFirst?: boolean\n}\n\n/**\n * Stacked layout wrapper for OAuth buttons with consistent spacing. Defaults to\n * vertical, full-width children — the dominant sign-in pattern. For an icon-only\n * row, set `orientation=\"horizontal\"` and use `<OAuthButton iconOnly />`.\n */\nconst OAuthGroup = React.forwardRef<HTMLDivElement, OAuthGroupProps>(\n (\n {\n orientation = 'vertical',\n gap = 'ds-03',\n fullWidth = true,\n reorderLastUsedFirst = false,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const gapClass =\n gap === 'ds-02' ? 'gap-ds-02' : gap === 'ds-04' ? 'gap-ds-04' : 'gap-ds-03'\n const stretch = orientation === 'vertical' && fullWidth\n\n const ordered = React.useMemo(() => {\n if (!reorderLastUsedFirst) return children\n const arr = React.Children.toArray(children)\n const lastUsedIdx = arr.findIndex(\n (c) => React.isValidElement(c) && (c.props as { lastUsed?: boolean })?.lastUsed,\n )\n if (lastUsedIdx <= 0) return children\n const next = [...arr]\n const [pulled] = next.splice(lastUsedIdx, 1)\n next.unshift(pulled)\n return next\n }, [children, reorderLastUsedFirst])\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex',\n orientation === 'vertical' ? 'flex-col' : 'flex-row flex-wrap items-center',\n gapClass,\n // Force both the direct wrapper AND the leaf <button> to stretch.\n // OAuthButton's outer wrapper picks up [&>*]:w-full; the button itself\n // sits two layers deep when a helperText wrapper exists, so\n // [&_button]:w-full covers both cases (wrapped + unwrapped) without\n // affecting unrelated descendants — only Buttons live inside.\n stretch && 'w-full [&>*]:w-full [&_button]:w-full',\n className,\n )}\n {...props}\n >\n {ordered}\n </div>\n )\n },\n)\nOAuthGroup.displayName = 'OAuthGroup'\n\nexport { OAuthGroup }\n\n// ── OAuthDivider ───────────────────────────────────────────────\n\nexport interface OAuthDividerProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Text between the rules. Default: \"or\" */\n label?: React.ReactNode\n}\n\nconst OAuthDivider = React.forwardRef<HTMLDivElement, OAuthDividerProps>(\n ({ label = 'or', className, ...props }, ref) => (\n <div\n ref={ref}\n role=\"separator\"\n aria-orientation=\"horizontal\"\n className={cn('flex items-center gap-ds-03', className)}\n {...props}\n >\n <hr aria-hidden=\"true\" className=\"flex-1 border-t border-surface-border-subtle\" />\n <span className=\"text-label-sm text-surface-fg-subtle uppercase tracking-wide\">{label}</span>\n <hr aria-hidden=\"true\" className=\"flex-1 border-t border-surface-border-subtle\" />\n </div>\n ),\n)\nOAuthDivider.displayName = 'OAuthDivider'\n\nexport { OAuthDivider }\n\n// ── OAuthConnectionRow (settings page — linked state) ──────────\n\nexport interface OAuthConnectionRowProps extends React.HTMLAttributes<HTMLDivElement> {\n provider: OAuthProvider\n /** Optional account identifier shown next to the provider name (e.g. email) */\n accountLabel?: React.ReactNode\n /** Whether the provider is currently linked */\n connected: boolean\n /** Click handler for the connect/disconnect action */\n onAction?: (e: React.MouseEvent<HTMLButtonElement>) => void\n /** Promise-returning click handler — uses Button's async state machine */\n onActionAsync?: (e: React.MouseEvent<HTMLButtonElement>) => Promise<void>\n /** Override the action label */\n actionLabel?: React.ReactNode\n /** Disable the action button */\n disabled?: boolean\n /** Override the default glyph. Accepts any `IconInput`. */\n icon?: IconInput\n}\n\n/**\n * Settings-page row representing a linked OAuth provider. Different surface\n * from sign-in: this is an existing connection the user can disconnect or\n * (re-)connect. Renders glyph + provider name + optional account label + a\n * connect/disconnect button.\n */\nconst OAuthConnectionRow = React.forwardRef<HTMLDivElement, OAuthConnectionRowProps>(\n (\n {\n provider,\n accountLabel,\n connected,\n onAction,\n onActionAsync,\n actionLabel,\n disabled,\n icon,\n className,\n ...props\n },\n ref,\n ) => {\n const DefaultGlyph = providerIcon[provider]\n const name = providerDisplay[provider]\n const resolvedLabel = actionLabel ?? (connected ? 'Disconnect' : `Connect ${name}`)\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex items-center justify-between gap-ds-04 py-ds-04 px-ds-04 rounded-surface border border-surface-border bg-surface-panel',\n className,\n )}\n data-provider={provider}\n data-connected={connected ? 'true' : 'false'}\n {...props}\n >\n <div className=\"flex items-center gap-ds-03 min-w-0\">\n {normalizeIcon(icon) ?? <DefaultGlyph size={24} aria-hidden />}\n <div className=\"flex flex-col min-w-0\">\n <TruncatedText className=\"text-body-md font-semibold text-surface-fg\">{name}</TruncatedText>\n {accountLabel ? (\n // Account labels are usually an email — middle-truncate to keep the domain.\n // Only a string can be measured/truncated; fall back to a plain clip otherwise.\n typeof accountLabel === 'string' ? (\n <TruncatedText mode=\"middle\" className=\"text-body-sm text-surface-fg-muted\">\n {accountLabel}\n </TruncatedText>\n ) : (\n <span className=\"text-body-sm text-surface-fg-muted truncate\">{accountLabel}</span>\n )\n ) : connected ? (\n <span className=\"text-body-sm text-success-11\">Connected</span>\n ) : (\n <span className=\"text-body-sm text-surface-fg-subtle\">Not connected</span>\n )}\n </div>\n </div>\n <Button\n size=\"sm\"\n variant={connected ? 'soft' : 'solid'}\n color={connected ? 'neutral' : 'accent'}\n disabled={disabled}\n onClick={onAction}\n onClickAsync={onActionAsync}\n >\n {resolvedLabel}\n </Button>\n </div>\n )\n },\n)\nOAuthConnectionRow.displayName = 'OAuthConnectionRow'\n\nexport { OAuthConnectionRow }\n\n// ── OAuthGlyph (atomic glyph render) ───────────────────────────\n\nexport interface OAuthGlyphProps extends Omit<React.SVGAttributes<SVGElement>, 'children'> {\n provider: OAuthProvider\n /** Glyph pixel size. Default 18. */\n size?: number\n}\n\n/**\n * Render just the provider glyph — useful when building a custom OAuth\n * trigger (e.g. an icon-button inside a Tooltip, or a provider chip on a\n * profile card). Defaults to the Tabler glyph; the icon prop on\n * `OAuthButton` is the right escape hatch for the brand multi-colour mark.\n */\nconst OAuthGlyph = React.forwardRef<SVGSVGElement, OAuthGlyphProps>(\n ({ provider, size = 18, ...props }, ref) => {\n const Cmp = providerIcon[provider] as React.ComponentType<{\n size?: number\n 'aria-hidden'?: boolean\n ref?: React.Ref<SVGSVGElement>\n }>\n return <Cmp ref={ref} size={size} aria-hidden {...(props as Record<string, unknown>)} />\n },\n)\nOAuthGlyph.displayName = 'OAuthGlyph'\n\nexport { OAuthGlyph }\n\n// ── Static helpers (composable building blocks) ────────────────\n\n/**\n * Resolve the visible label for a provider × intent combo. Useful when\n * rendering OAuth UIs outside of `OAuthButton` (e.g. a Confirm dialog\n * \"Disconnect from Google?\" title).\n */\nexport function getOAuthLabel(provider: OAuthProvider, intent: OAuthIntent = 'continue', compact = false): string {\n return resolveLabel(provider, intent, compact)\n}\n\n/** Display name for a provider, ready for sentence interpolation. */\nexport function getOAuthName(provider: OAuthProvider): string {\n return providerName(provider)\n}\n\n// ── Compound namespace ─────────────────────────────────────────\n\n/**\n * Namespaced API — drop-in alternative to importing each piece separately.\n *\n * @example\n * import { OAuth } from '@devalok/shilp-sutra/ui/oauth-button'\n *\n * <OAuth.Group reorderLastUsedFirst>\n * <OAuth.Button provider=\"google\" lastUsed />\n * <OAuth.Button provider=\"passkey\" />\n * <OAuth.Divider />\n * <OAuth.Button provider=\"sso\" variant=\"ghost\" />\n * </OAuth.Group>\n */\nexport const OAuth = {\n Button: OAuthButton,\n Group: OAuthGroup,\n Divider: OAuthDivider,\n ConnectionRow: OAuthConnectionRow,\n Glyph: OAuthGlyph,\n getLabel: getOAuthLabel,\n getName: getOAuthName,\n} as const\n"],"mappings":"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"}
|
package/dist/_chunks/success.js
CHANGED
|
@@ -87,7 +87,7 @@ var M = C.memo(function({ data: e, confidence: n }) {
|
|
|
87
87
|
/* @__PURE__ */ w(m, { children: f.map((e, n) => {
|
|
88
88
|
let a = typeof e.id == "string" || typeof e.id == "number" ? String(e.id) : `row-${n}`;
|
|
89
89
|
return /* @__PURE__ */ w(i ? "tr" : k.tr, {
|
|
90
|
-
className: "hover:bg-surface-
|
|
90
|
+
className: "hover:bg-surface-panel-hover transition-colors",
|
|
91
91
|
...i ? {} : {
|
|
92
92
|
initial: {
|
|
93
93
|
opacity: 0,
|
|
@@ -150,7 +150,7 @@ var N = C.memo(function({ data: e, confidence: t, onAction: n }) {
|
|
|
150
150
|
children: "Why this action?"
|
|
151
151
|
})
|
|
152
152
|
}), /* @__PURE__ */ w(d, { children: /* @__PURE__ */ w("p", {
|
|
153
|
-
className: "text-body-sm text-surface-fg-muted mt-2 p-3 bg-surface-
|
|
153
|
+
className: "text-body-sm text-surface-fg-muted mt-2 p-3 bg-surface-panel rounded-surface",
|
|
154
154
|
children: e.rationale
|
|
155
155
|
}) })]
|
|
156
156
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"success.js","names":[],"sources":["../../src/ai/blocks/block-table.tsx","../../src/ai/blocks/confirm.tsx","../../src/ai/blocks/divider.tsx","../../src/ai/blocks/info.tsx","../../src/ai/blocks/loading.tsx","../../src/ai/blocks/stat-row.tsx","../../src/ai/blocks/success.tsx"],"sourcesContent":["'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { Badge } from '../../ui/badge'\nimport { springs } from '../../ui/lib/motion'\nimport { cn } from '../../ui/lib/utils'\nimport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '../../ui/table'\nimport type { BlockComponentProps, BlockTableColumn,BlockTableData } from '../types'\nimport { BlockShell } from './block-shell'\n\ntype SortDir = 'asc' | 'desc' | null\n\nfunction renderCellValue(value: unknown, column: BlockTableColumn) {\n if (column.variant === 'badge') {\n if (value && typeof value === 'object' && 'label' in value) {\n const badgeObj = value as { label: string; color: string }\n return (\n <Badge variant=\"subtle\" size=\"sm\" color={badgeObj.color as 'default'}>\n {badgeObj.label}\n </Badge>\n )\n }\n return (\n <Badge variant=\"subtle\" size=\"sm\">\n {String(value ?? '')}\n </Badge>\n )\n }\n\n return String(value ?? '')\n}\n\nfunction sortRows(\n rows: Record<string, unknown>[],\n sortKey: string | null,\n sortDir: SortDir,\n): Record<string, unknown>[] {\n if (!sortKey || !sortDir) return rows\n return [...rows].sort((a, b) => {\n const aVal = a[sortKey]\n const bVal = b[sortKey]\n const aStr = String(aVal ?? '')\n const bStr = String(bVal ?? '')\n const aNum = Number(aVal)\n const bNum = Number(bVal)\n\n // If both are numeric, sort numerically\n if (!Number.isNaN(aNum) && !Number.isNaN(bNum)) {\n return sortDir === 'asc' ? aNum - bNum : bNum - aNum\n }\n\n // Otherwise sort lexicographically\n const cmp = aStr.localeCompare(bStr)\n return sortDir === 'asc' ? cmp : -cmp\n })\n}\n\nconst BlockTable = React.memo(function BlockTable({\n data,\n confidence,\n}: BlockComponentProps<BlockTableData>) {\n const { reducedMotion } = useMotion()\n const [sortKey, setSortKey] = React.useState<string | null>(null)\n const [sortDir, setSortDir] = React.useState<SortDir>(null)\n\n const handleHeaderClick = React.useCallback(\n (key: string) => {\n if (!data.sortable) return\n if (sortKey !== key) {\n setSortKey(key)\n setSortDir('asc')\n } else if (sortDir === 'asc') {\n setSortDir('desc')\n } else if (sortDir === 'desc') {\n setSortKey(null)\n setSortDir(null)\n }\n },\n [data.sortable, sortKey, sortDir],\n )\n\n const columns = data.columns ?? []\n const sortedRows = sortRows(data.rows ?? [], sortKey, sortDir)\n\n return (\n <BlockShell confidence={confidence} className=\"overflow-x-auto\">\n <Table>\n {data.caption && <TableCaption>{data.caption}</TableCaption>}\n <TableHeader>\n <TableRow>\n {columns.map((col) => {\n const isSorted = sortKey === col.key\n const ariaSort = data.sortable\n ? isSorted\n ? sortDir === 'asc'\n ? ('ascending' as const)\n : ('descending' as const)\n : ('none' as const)\n : undefined\n\n return (\n <TableHead\n key={col.key}\n role=\"columnheader\"\n className={cn(\n col.variant === 'number' && 'text-right',\n data.sortable && 'cursor-pointer select-none',\n )}\n tabIndex={data.sortable ? 0 : undefined}\n onClick={\n data.sortable\n ? () => handleHeaderClick(col.key)\n : undefined\n }\n onKeyDown={\n data.sortable\n ? (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleHeaderClick(col.key)\n }\n }\n : undefined\n }\n aria-sort={ariaSort}\n >\n <span className=\"inline-flex items-center gap-1\">\n {col.label}\n {isSorted && sortDir === 'asc' && (\n <span aria-hidden=\"true\">▲</span>\n )}\n {isSorted && sortDir === 'desc' && (\n <span aria-hidden=\"true\">▼</span>\n )}\n </span>\n </TableHead>\n )\n })}\n </TableRow>\n </TableHeader>\n <TableBody>\n {sortedRows.map((row, rowIndex) => {\n const rowKey =\n typeof row.id === 'string' || typeof row.id === 'number'\n ? String(row.id)\n : `row-${rowIndex}`\n\n const RowWrapper = reducedMotion ? 'tr' : motion.tr\n\n const motionProps = reducedMotion\n ? {}\n : {\n initial: { opacity: 0, y: 6 } as const,\n animate: { opacity: 1, y: 0 } as const,\n transition: { ...springs.responsive, delay: rowIndex * 0.03 },\n }\n\n return (\n <RowWrapper\n key={rowKey}\n className=\"hover:bg-surface-raised-hover transition-colors\"\n {...motionProps}\n >\n {columns.map((col) => (\n <TableCell\n key={col.key}\n className={cn(\n 'text-body-sm py-ds-02b px-ds-04',\n col.variant === 'number' &&\n 'text-right tabular-nums font-medium',\n )}\n >\n {renderCellValue(row[col.key], col)}\n </TableCell>\n ))}\n </RowWrapper>\n )\n })}\n </TableBody>\n </Table>\n </BlockShell>\n )\n})\n\nBlockTable.displayName = 'BlockTable'\n\nexport { BlockTable }\n","'use client'\n\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../../ui/collapsible'\nimport type { BlockComponentProps, ConfirmBlockData } from '../types'\nimport { BlockShell } from './block-shell'\n\nconst ConfirmBlock = React.memo(function ConfirmBlock({\n data,\n confidence,\n onAction,\n}: BlockComponentProps<ConfirmBlockData>) {\n const handleConfirm = React.useCallback(() => {\n onAction?.(data.actionId, 'confirm')\n }, [data.actionId, onAction])\n\n const handleCancel = React.useCallback(() => {\n onAction?.(data.actionId, 'cancel')\n }, [data.actionId, onAction])\n\n return (\n <BlockShell confidence={confidence}>\n {data.description && (\n <p className=\"text-body-sm text-surface-fg-muted mb-3\">\n {data.description}\n </p>\n )}\n\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"solid\"\n color={data.destructive ? 'error' : undefined}\n onClick={handleConfirm}\n >\n {data.label}\n </Button>\n <Button variant=\"ghost\" onClick={handleCancel}>\n Cancel\n </Button>\n </div>\n\n {data.rationale && (\n <Collapsible className=\"mt-3\">\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"text-caption text-surface-fg-subtle underline cursor-pointer\"\n >\n Why this action?\n </button>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <p className=\"text-body-sm text-surface-fg-muted mt-2 p-3 bg-surface-raised rounded-surface\">\n {data.rationale}\n </p>\n </CollapsibleContent>\n </Collapsible>\n )}\n </BlockShell>\n )\n})\n\nConfirmBlock.displayName = 'ConfirmBlock'\n\nexport { ConfirmBlock }\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { tweens } from '../../ui/lib/motion'\nimport { Separator } from '../../ui/separator'\nimport type { BlockComponentProps } from '../types'\n\nconst DividerBlock = React.memo(function DividerBlock(\n _props: BlockComponentProps<Record<string, never>>,\n) {\n const { reducedMotion } = useMotion()\n\n if (reducedMotion) {\n return <Separator />\n }\n\n return (\n <motion.div\n initial={{ scaleX: 0 }}\n animate={{ scaleX: 1 }}\n style={{ originX: 0.5 }}\n transition={tweens.elegant}\n >\n <Separator />\n </motion.div>\n )\n})\n\nDividerBlock.displayName = 'DividerBlock'\n\nexport { DividerBlock }\n","'use client'\n\nimport * as React from 'react'\n\nimport { Alert } from '../../ui/alert'\nimport type { BlockComponentProps } from '../types'\n\nconst InfoBlock = React.memo(function InfoBlock({\n data,\n}: BlockComponentProps<{ message: string }>) {\n return (\n <Alert color=\"info\" variant=\"subtle\" role=\"status\">\n {data.message}\n </Alert>\n )\n})\n\nInfoBlock.displayName = 'InfoBlock'\n\nexport { InfoBlock }\n","'use client'\n\nimport {\n IconCircle,\n IconCircleCheck,\n IconCircleX,\n} from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { Icon } from '../../ui/icon'\nimport { durations } from '../../ui/lib/motion'\nimport { cn } from '../../ui/lib/utils'\nimport { Skeleton } from '../../ui/skeleton'\nimport type { BlockComponentProps, LoadingBlockData, ProcessingStep } from '../types'\n\n/** Spinning indicator for active steps */\nfunction StepSpinner() {\n return (\n <motion.span\n className=\"inline-block h-4 w-4 rounded-pill border-2 border-current border-t-transparent text-accent-9\"\n animate={{ rotate: 360 }}\n transition={{ repeat: Infinity, duration: 1, ease: 'linear' }}\n aria-hidden=\"true\"\n data-testid=\"step-spinner\"\n />\n )\n}\n\n/** Icon for a processing step based on its status */\nfunction StepIcon({ status }: { status: ProcessingStep['status'] }) {\n switch (status) {\n case 'done':\n return (\n <span data-testid=\"step-icon-done\">\n <Icon icon={IconCircleCheck} size=\"sm\" className=\"text-success-11\" />\n </span>\n )\n case 'active':\n return <StepSpinner />\n case 'error':\n return (\n <span data-testid=\"step-icon-error\">\n <Icon icon={IconCircleX} size=\"sm\" className=\"text-error-11\" />\n </span>\n )\n case 'pending':\n default:\n return (\n <span data-testid=\"step-icon-pending\">\n <Icon icon={IconCircle} size=\"sm\" className=\"text-surface-fg-subtle opacity-50\" />\n </span>\n )\n }\n}\n\nconst LoadingBlock = React.memo(function LoadingBlock({\n data,\n}: BlockComponentProps<LoadingBlockData>) {\n const { reducedMotion } = useMotion()\n const isReduced = reducedMotion\n\n // Mode A: Skeleton lines\n if (data.lines != null) {\n const lineCount = Math.max(1, data.lines)\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-label=\"Loading\"\n className=\"flex flex-col gap-2\"\n >\n {Array.from({ length: lineCount }).map((_, i) => (\n <Skeleton\n key={i}\n animation=\"shimmer\"\n className={cn(\n 'h-4',\n i === lineCount - 1 ? 'w-3/5' : 'w-full',\n )}\n />\n ))}\n <span className=\"sr-only\">Loading</span>\n </div>\n )\n }\n\n // Mode B: Processing steps\n if (data.steps) {\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-label=\"Processing\"\n className=\"flex flex-col gap-2\"\n >\n {data.steps.map((step, index) => (\n <motion.div\n key={step.id}\n className=\"flex items-center gap-2\"\n initial={isReduced ? undefined : { opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={\n isReduced\n ? { duration: 0 }\n : { duration: durations.moderate01b, delay: index * 0.06 }\n }\n >\n <StepIcon status={step.status} />\n <span\n className={cn(\n 'text-body-sm',\n step.status === 'done' || step.status === 'active'\n ? 'text-surface-fg'\n : 'text-surface-fg-subtle',\n )}\n >\n {step.label}\n </span>\n </motion.div>\n ))}\n <span className=\"sr-only\">Processing</span>\n </div>\n )\n }\n\n // Fallback: empty status container\n return (\n <div role=\"status\" aria-busy=\"true\" aria-label=\"Loading\">\n <span className=\"sr-only\">Loading</span>\n </div>\n )\n})\n\nLoadingBlock.displayName = 'LoadingBlock'\n\nexport { LoadingBlock }\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { springs } from '../../ui/lib/motion'\nimport { StatCard } from '../../ui/stat-card'\nimport type { BlockComponentProps, StatRowBlockData } from '../types'\n\nconst StatRowBlock = React.memo(function StatRowBlock({\n data,\n}: BlockComponentProps<StatRowBlockData>) {\n const { reducedMotion } = useMotion()\n const stats = data.stats ?? []\n\n if (stats.length === 0) return null\n\n return (\n <div className=\"flex flex-wrap gap-ds-05\">\n {stats.map((stat, index) => {\n const delta = stat.change\n ? {\n value: stat.change.value,\n direction: stat.change.direction,\n }\n : undefined\n\n if (reducedMotion) {\n return (\n <div key={stat.label} className=\"flex-1 min-w-[140px]\">\n <StatCard\n label={stat.label}\n value={stat.value}\n delta={delta}\n />\n </div>\n )\n }\n\n return (\n <motion.div\n key={stat.label}\n className=\"flex-1 min-w-[140px]\"\n initial={{ y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ ...springs.responsive, delay: index * 0.08 }}\n >\n <StatCard\n label={stat.label}\n value={stat.value}\n delta={delta}\n />\n </motion.div>\n )\n })}\n </div>\n )\n})\n\nStatRowBlock.displayName = 'StatRowBlock'\n\nexport { StatRowBlock }\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { Alert } from '../../ui/alert'\nimport { Button } from '../../ui/button'\nimport type { BlockComponentProps, SuccessBlockData } from '../types'\nimport { BlockShell } from './block-shell'\n\nconst DEFAULT_UNDO_TIMEOUT = 5000\n\nconst SuccessBlock = React.memo(function SuccessBlock({\n data,\n blockId,\n confidence,\n onAction,\n}: BlockComponentProps<SuccessBlockData>) {\n const { reducedMotion } = useMotion()\n const isReduced = reducedMotion\n const undoTimeout = data.undoTimeout ?? DEFAULT_UNDO_TIMEOUT\n const [remaining, setRemaining] = React.useState(undoTimeout)\n const [showUndo, setShowUndo] = React.useState(!!data.undoable)\n\n React.useEffect(() => {\n if (!data.undoable) return\n\n const interval = setInterval(() => {\n setRemaining((prev) => {\n const next = prev - 100\n if (next <= 0) {\n clearInterval(interval)\n setShowUndo(false)\n return 0\n }\n return next\n })\n }, 100)\n\n return () => clearInterval(interval)\n }, [data.undoable, undoTimeout])\n\n const handleUndo = React.useCallback(() => {\n onAction?.(blockId || 'unknown', 'undo')\n }, [blockId, onAction])\n\n const remainingSeconds = Math.ceil(remaining / 1000)\n\n // Countdown ring dimensions\n const ringSize = 20\n const ringStroke = 2\n const ringRadius = (ringSize - ringStroke) / 2\n const ringCircumference = 2 * Math.PI * ringRadius\n\n return (\n <BlockShell confidence={confidence}>\n <Alert\n color=\"success\"\n variant=\"subtle\"\n title={data.title}\n >\n {data.message}\n </Alert>\n\n {showUndo && data.undoable && (\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleUndo}\n aria-label={`Undo action, ${remainingSeconds} seconds remaining`}\n >\n <span className=\"flex items-center gap-1.5\">\n <svg\n width={ringSize}\n height={ringSize}\n viewBox={`0 0 ${ringSize} ${ringSize}`}\n className=\"shrink-0 -rotate-90\"\n aria-hidden=\"true\"\n >\n <circle\n cx={ringSize / 2}\n cy={ringSize / 2}\n r={ringRadius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={ringStroke}\n opacity={0.2}\n />\n <motion.circle\n cx={ringSize / 2}\n cy={ringSize / 2}\n r={ringRadius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={ringStroke}\n strokeDasharray={ringCircumference}\n initial={isReduced ? undefined : { strokeDashoffset: 0 }}\n animate={{\n strokeDashoffset: ringCircumference,\n }}\n transition={\n isReduced\n ? { duration: 0 }\n : {\n duration: undoTimeout / 1000,\n ease: 'linear',\n }\n }\n />\n </svg>\n Undo\n </span>\n </Button>\n </div>\n )}\n\n </BlockShell>\n )\n})\n\nSuccessBlock.displayName = 'SuccessBlock'\n\nexport { SuccessBlock }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"success.js","names":[],"sources":["../../src/ai/blocks/block-table.tsx","../../src/ai/blocks/confirm.tsx","../../src/ai/blocks/divider.tsx","../../src/ai/blocks/info.tsx","../../src/ai/blocks/loading.tsx","../../src/ai/blocks/stat-row.tsx","../../src/ai/blocks/success.tsx"],"sourcesContent":["'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { Badge } from '../../ui/badge'\nimport { springs } from '../../ui/lib/motion'\nimport { cn } from '../../ui/lib/utils'\nimport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '../../ui/table'\nimport type { BlockComponentProps, BlockTableColumn,BlockTableData } from '../types'\nimport { BlockShell } from './block-shell'\n\ntype SortDir = 'asc' | 'desc' | null\n\nfunction renderCellValue(value: unknown, column: BlockTableColumn) {\n if (column.variant === 'badge') {\n if (value && typeof value === 'object' && 'label' in value) {\n const badgeObj = value as { label: string; color: string }\n return (\n <Badge variant=\"subtle\" size=\"sm\" color={badgeObj.color as 'default'}>\n {badgeObj.label}\n </Badge>\n )\n }\n return (\n <Badge variant=\"subtle\" size=\"sm\">\n {String(value ?? '')}\n </Badge>\n )\n }\n\n return String(value ?? '')\n}\n\nfunction sortRows(\n rows: Record<string, unknown>[],\n sortKey: string | null,\n sortDir: SortDir,\n): Record<string, unknown>[] {\n if (!sortKey || !sortDir) return rows\n return [...rows].sort((a, b) => {\n const aVal = a[sortKey]\n const bVal = b[sortKey]\n const aStr = String(aVal ?? '')\n const bStr = String(bVal ?? '')\n const aNum = Number(aVal)\n const bNum = Number(bVal)\n\n // If both are numeric, sort numerically\n if (!Number.isNaN(aNum) && !Number.isNaN(bNum)) {\n return sortDir === 'asc' ? aNum - bNum : bNum - aNum\n }\n\n // Otherwise sort lexicographically\n const cmp = aStr.localeCompare(bStr)\n return sortDir === 'asc' ? cmp : -cmp\n })\n}\n\nconst BlockTable = React.memo(function BlockTable({\n data,\n confidence,\n}: BlockComponentProps<BlockTableData>) {\n const { reducedMotion } = useMotion()\n const [sortKey, setSortKey] = React.useState<string | null>(null)\n const [sortDir, setSortDir] = React.useState<SortDir>(null)\n\n const handleHeaderClick = React.useCallback(\n (key: string) => {\n if (!data.sortable) return\n if (sortKey !== key) {\n setSortKey(key)\n setSortDir('asc')\n } else if (sortDir === 'asc') {\n setSortDir('desc')\n } else if (sortDir === 'desc') {\n setSortKey(null)\n setSortDir(null)\n }\n },\n [data.sortable, sortKey, sortDir],\n )\n\n const columns = data.columns ?? []\n const sortedRows = sortRows(data.rows ?? [], sortKey, sortDir)\n\n return (\n <BlockShell confidence={confidence} className=\"overflow-x-auto\">\n <Table>\n {data.caption && <TableCaption>{data.caption}</TableCaption>}\n <TableHeader>\n <TableRow>\n {columns.map((col) => {\n const isSorted = sortKey === col.key\n const ariaSort = data.sortable\n ? isSorted\n ? sortDir === 'asc'\n ? ('ascending' as const)\n : ('descending' as const)\n : ('none' as const)\n : undefined\n\n return (\n <TableHead\n key={col.key}\n role=\"columnheader\"\n className={cn(\n col.variant === 'number' && 'text-right',\n data.sortable && 'cursor-pointer select-none',\n )}\n tabIndex={data.sortable ? 0 : undefined}\n onClick={\n data.sortable\n ? () => handleHeaderClick(col.key)\n : undefined\n }\n onKeyDown={\n data.sortable\n ? (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleHeaderClick(col.key)\n }\n }\n : undefined\n }\n aria-sort={ariaSort}\n >\n <span className=\"inline-flex items-center gap-1\">\n {col.label}\n {isSorted && sortDir === 'asc' && (\n <span aria-hidden=\"true\">▲</span>\n )}\n {isSorted && sortDir === 'desc' && (\n <span aria-hidden=\"true\">▼</span>\n )}\n </span>\n </TableHead>\n )\n })}\n </TableRow>\n </TableHeader>\n <TableBody>\n {sortedRows.map((row, rowIndex) => {\n const rowKey =\n typeof row.id === 'string' || typeof row.id === 'number'\n ? String(row.id)\n : `row-${rowIndex}`\n\n const RowWrapper = reducedMotion ? 'tr' : motion.tr\n\n const motionProps = reducedMotion\n ? {}\n : {\n initial: { opacity: 0, y: 6 } as const,\n animate: { opacity: 1, y: 0 } as const,\n transition: { ...springs.responsive, delay: rowIndex * 0.03 },\n }\n\n return (\n <RowWrapper\n key={rowKey}\n className=\"hover:bg-surface-panel-hover transition-colors\"\n {...motionProps}\n >\n {columns.map((col) => (\n <TableCell\n key={col.key}\n className={cn(\n 'text-body-sm py-ds-02b px-ds-04',\n col.variant === 'number' &&\n 'text-right tabular-nums font-medium',\n )}\n >\n {renderCellValue(row[col.key], col)}\n </TableCell>\n ))}\n </RowWrapper>\n )\n })}\n </TableBody>\n </Table>\n </BlockShell>\n )\n})\n\nBlockTable.displayName = 'BlockTable'\n\nexport { BlockTable }\n","'use client'\n\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../../ui/collapsible'\nimport type { BlockComponentProps, ConfirmBlockData } from '../types'\nimport { BlockShell } from './block-shell'\n\nconst ConfirmBlock = React.memo(function ConfirmBlock({\n data,\n confidence,\n onAction,\n}: BlockComponentProps<ConfirmBlockData>) {\n const handleConfirm = React.useCallback(() => {\n onAction?.(data.actionId, 'confirm')\n }, [data.actionId, onAction])\n\n const handleCancel = React.useCallback(() => {\n onAction?.(data.actionId, 'cancel')\n }, [data.actionId, onAction])\n\n return (\n <BlockShell confidence={confidence}>\n {data.description && (\n <p className=\"text-body-sm text-surface-fg-muted mb-3\">\n {data.description}\n </p>\n )}\n\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"solid\"\n color={data.destructive ? 'error' : undefined}\n onClick={handleConfirm}\n >\n {data.label}\n </Button>\n <Button variant=\"ghost\" onClick={handleCancel}>\n Cancel\n </Button>\n </div>\n\n {data.rationale && (\n <Collapsible className=\"mt-3\">\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"text-caption text-surface-fg-subtle underline cursor-pointer\"\n >\n Why this action?\n </button>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <p className=\"text-body-sm text-surface-fg-muted mt-2 p-3 bg-surface-panel rounded-surface\">\n {data.rationale}\n </p>\n </CollapsibleContent>\n </Collapsible>\n )}\n </BlockShell>\n )\n})\n\nConfirmBlock.displayName = 'ConfirmBlock'\n\nexport { ConfirmBlock }\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { tweens } from '../../ui/lib/motion'\nimport { Separator } from '../../ui/separator'\nimport type { BlockComponentProps } from '../types'\n\nconst DividerBlock = React.memo(function DividerBlock(\n _props: BlockComponentProps<Record<string, never>>,\n) {\n const { reducedMotion } = useMotion()\n\n if (reducedMotion) {\n return <Separator />\n }\n\n return (\n <motion.div\n initial={{ scaleX: 0 }}\n animate={{ scaleX: 1 }}\n style={{ originX: 0.5 }}\n transition={tweens.elegant}\n >\n <Separator />\n </motion.div>\n )\n})\n\nDividerBlock.displayName = 'DividerBlock'\n\nexport { DividerBlock }\n","'use client'\n\nimport * as React from 'react'\n\nimport { Alert } from '../../ui/alert'\nimport type { BlockComponentProps } from '../types'\n\nconst InfoBlock = React.memo(function InfoBlock({\n data,\n}: BlockComponentProps<{ message: string }>) {\n return (\n <Alert color=\"info\" variant=\"subtle\" role=\"status\">\n {data.message}\n </Alert>\n )\n})\n\nInfoBlock.displayName = 'InfoBlock'\n\nexport { InfoBlock }\n","'use client'\n\nimport {\n IconCircle,\n IconCircleCheck,\n IconCircleX,\n} from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { Icon } from '../../ui/icon'\nimport { durations } from '../../ui/lib/motion'\nimport { cn } from '../../ui/lib/utils'\nimport { Skeleton } from '../../ui/skeleton'\nimport type { BlockComponentProps, LoadingBlockData, ProcessingStep } from '../types'\n\n/** Spinning indicator for active steps */\nfunction StepSpinner() {\n return (\n <motion.span\n className=\"inline-block h-4 w-4 rounded-pill border-2 border-current border-t-transparent text-accent-9\"\n animate={{ rotate: 360 }}\n transition={{ repeat: Infinity, duration: 1, ease: 'linear' }}\n aria-hidden=\"true\"\n data-testid=\"step-spinner\"\n />\n )\n}\n\n/** Icon for a processing step based on its status */\nfunction StepIcon({ status }: { status: ProcessingStep['status'] }) {\n switch (status) {\n case 'done':\n return (\n <span data-testid=\"step-icon-done\">\n <Icon icon={IconCircleCheck} size=\"sm\" className=\"text-success-11\" />\n </span>\n )\n case 'active':\n return <StepSpinner />\n case 'error':\n return (\n <span data-testid=\"step-icon-error\">\n <Icon icon={IconCircleX} size=\"sm\" className=\"text-error-11\" />\n </span>\n )\n case 'pending':\n default:\n return (\n <span data-testid=\"step-icon-pending\">\n <Icon icon={IconCircle} size=\"sm\" className=\"text-surface-fg-subtle opacity-50\" />\n </span>\n )\n }\n}\n\nconst LoadingBlock = React.memo(function LoadingBlock({\n data,\n}: BlockComponentProps<LoadingBlockData>) {\n const { reducedMotion } = useMotion()\n const isReduced = reducedMotion\n\n // Mode A: Skeleton lines\n if (data.lines != null) {\n const lineCount = Math.max(1, data.lines)\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-label=\"Loading\"\n className=\"flex flex-col gap-2\"\n >\n {Array.from({ length: lineCount }).map((_, i) => (\n <Skeleton\n key={i}\n animation=\"shimmer\"\n className={cn(\n 'h-4',\n i === lineCount - 1 ? 'w-3/5' : 'w-full',\n )}\n />\n ))}\n <span className=\"sr-only\">Loading</span>\n </div>\n )\n }\n\n // Mode B: Processing steps\n if (data.steps) {\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-label=\"Processing\"\n className=\"flex flex-col gap-2\"\n >\n {data.steps.map((step, index) => (\n <motion.div\n key={step.id}\n className=\"flex items-center gap-2\"\n initial={isReduced ? undefined : { opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={\n isReduced\n ? { duration: 0 }\n : { duration: durations.moderate01b, delay: index * 0.06 }\n }\n >\n <StepIcon status={step.status} />\n <span\n className={cn(\n 'text-body-sm',\n step.status === 'done' || step.status === 'active'\n ? 'text-surface-fg'\n : 'text-surface-fg-subtle',\n )}\n >\n {step.label}\n </span>\n </motion.div>\n ))}\n <span className=\"sr-only\">Processing</span>\n </div>\n )\n }\n\n // Fallback: empty status container\n return (\n <div role=\"status\" aria-busy=\"true\" aria-label=\"Loading\">\n <span className=\"sr-only\">Loading</span>\n </div>\n )\n})\n\nLoadingBlock.displayName = 'LoadingBlock'\n\nexport { LoadingBlock }\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { springs } from '../../ui/lib/motion'\nimport { StatCard } from '../../ui/stat-card'\nimport type { BlockComponentProps, StatRowBlockData } from '../types'\n\nconst StatRowBlock = React.memo(function StatRowBlock({\n data,\n}: BlockComponentProps<StatRowBlockData>) {\n const { reducedMotion } = useMotion()\n const stats = data.stats ?? []\n\n if (stats.length === 0) return null\n\n return (\n <div className=\"flex flex-wrap gap-ds-05\">\n {stats.map((stat, index) => {\n const delta = stat.change\n ? {\n value: stat.change.value,\n direction: stat.change.direction,\n }\n : undefined\n\n if (reducedMotion) {\n return (\n <div key={stat.label} className=\"flex-1 min-w-[140px]\">\n <StatCard\n label={stat.label}\n value={stat.value}\n delta={delta}\n />\n </div>\n )\n }\n\n return (\n <motion.div\n key={stat.label}\n className=\"flex-1 min-w-[140px]\"\n initial={{ y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ ...springs.responsive, delay: index * 0.08 }}\n >\n <StatCard\n label={stat.label}\n value={stat.value}\n delta={delta}\n />\n </motion.div>\n )\n })}\n </div>\n )\n})\n\nStatRowBlock.displayName = 'StatRowBlock'\n\nexport { StatRowBlock }\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../../motion/motion-provider'\nimport { Alert } from '../../ui/alert'\nimport { Button } from '../../ui/button'\nimport type { BlockComponentProps, SuccessBlockData } from '../types'\nimport { BlockShell } from './block-shell'\n\nconst DEFAULT_UNDO_TIMEOUT = 5000\n\nconst SuccessBlock = React.memo(function SuccessBlock({\n data,\n blockId,\n confidence,\n onAction,\n}: BlockComponentProps<SuccessBlockData>) {\n const { reducedMotion } = useMotion()\n const isReduced = reducedMotion\n const undoTimeout = data.undoTimeout ?? DEFAULT_UNDO_TIMEOUT\n const [remaining, setRemaining] = React.useState(undoTimeout)\n const [showUndo, setShowUndo] = React.useState(!!data.undoable)\n\n React.useEffect(() => {\n if (!data.undoable) return\n\n const interval = setInterval(() => {\n setRemaining((prev) => {\n const next = prev - 100\n if (next <= 0) {\n clearInterval(interval)\n setShowUndo(false)\n return 0\n }\n return next\n })\n }, 100)\n\n return () => clearInterval(interval)\n }, [data.undoable, undoTimeout])\n\n const handleUndo = React.useCallback(() => {\n onAction?.(blockId || 'unknown', 'undo')\n }, [blockId, onAction])\n\n const remainingSeconds = Math.ceil(remaining / 1000)\n\n // Countdown ring dimensions\n const ringSize = 20\n const ringStroke = 2\n const ringRadius = (ringSize - ringStroke) / 2\n const ringCircumference = 2 * Math.PI * ringRadius\n\n return (\n <BlockShell confidence={confidence}>\n <Alert\n color=\"success\"\n variant=\"subtle\"\n title={data.title}\n >\n {data.message}\n </Alert>\n\n {showUndo && data.undoable && (\n <div className=\"mt-2 flex items-center gap-2\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleUndo}\n aria-label={`Undo action, ${remainingSeconds} seconds remaining`}\n >\n <span className=\"flex items-center gap-1.5\">\n <svg\n width={ringSize}\n height={ringSize}\n viewBox={`0 0 ${ringSize} ${ringSize}`}\n className=\"shrink-0 -rotate-90\"\n aria-hidden=\"true\"\n >\n <circle\n cx={ringSize / 2}\n cy={ringSize / 2}\n r={ringRadius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={ringStroke}\n opacity={0.2}\n />\n <motion.circle\n cx={ringSize / 2}\n cy={ringSize / 2}\n r={ringRadius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={ringStroke}\n strokeDasharray={ringCircumference}\n initial={isReduced ? undefined : { strokeDashoffset: 0 }}\n animate={{\n strokeDashoffset: ringCircumference,\n }}\n transition={\n isReduced\n ? { duration: 0 }\n : {\n duration: undoTimeout / 1000,\n ease: 'linear',\n }\n }\n />\n </svg>\n Undo\n </span>\n </Button>\n </div>\n )}\n\n </BlockShell>\n )\n})\n\nSuccessBlock.displayName = 'SuccessBlock'\n\nexport { SuccessBlock }\n"],"mappings":"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"}
|
|
@@ -175,7 +175,7 @@ var _ = a.forwardRef(({ itemId: o, label: s, icon: d, secondaryLabel: f, actions
|
|
|
175
175
|
onClick: w,
|
|
176
176
|
onKeyDown: E,
|
|
177
177
|
style: { paddingLeft: `calc(${_} * var(--spacing-ds-05b) + var(--spacing-ds-03))` },
|
|
178
|
-
className: t("flex items-center gap-ds-02 py-ds-02 px-ds-02 rounded-control cursor-pointer transition-colors duration-fast-01", "hover:bg-surface-
|
|
178
|
+
className: t("flex items-center gap-ds-02 py-ds-02 px-ds-02 rounded-control cursor-pointer transition-colors duration-fast-01", "hover:bg-surface-panel-hover", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9", C && "bg-accent-3 text-accent-11", h && "opacity-action-disabled cursor-not-allowed pointer-events-none", v),
|
|
179
179
|
children: [
|
|
180
180
|
x ? /* @__PURE__ */ c("button", {
|
|
181
181
|
type: "button",
|