@kortix/ui 0.0.0-stage → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -2
- package/dist/_vendor/kortix-shared.js +341 -0
- package/dist/chat/agent-picker.d.ts +8 -0
- package/dist/chat/agent-picker.js +18 -0
- package/dist/chat/composer.d.ts +31 -0
- package/dist/chat/composer.js +114 -0
- package/dist/chat/index.d.ts +22 -0
- package/dist/chat/index.js +22 -0
- package/dist/chat/markdown.d.ts +10 -0
- package/dist/chat/markdown.js +36 -0
- package/dist/chat/message-list.d.ts +25 -0
- package/dist/chat/message-list.js +73 -0
- package/dist/chat/message-view.d.ts +17 -0
- package/dist/chat/message-view.js +86 -0
- package/dist/chat/model-picker.d.ts +10 -0
- package/dist/chat/model-picker.js +68 -0
- package/dist/chat/permission-prompt.d.ts +10 -0
- package/dist/chat/permission-prompt.js +40 -0
- package/dist/chat/question-prompt.d.ts +8 -0
- package/dist/chat/question-prompt.js +60 -0
- package/dist/chat/send-failure.d.ts +9 -0
- package/dist/chat/send-failure.js +21 -0
- package/dist/chat/session-thread.d.ts +28 -0
- package/dist/chat/session-thread.js +47 -0
- package/dist/chat/streaming-markdown.d.ts +21 -0
- package/dist/chat/streaming-markdown.js +59 -0
- package/dist/chat/tool-call.d.ts +4 -0
- package/dist/chat/tool-call.js +150 -0
- package/dist/chat/user-message-parts.d.ts +6 -0
- package/dist/chat/user-message-parts.js +11 -0
- package/dist/components/ui/accordion.d.ts +7 -0
- package/dist/components/ui/accordion.js +18 -0
- package/dist/components/ui/alert-dialog.d.ts +19 -0
- package/dist/components/ui/alert-dialog.js +48 -0
- package/dist/components/ui/alert.d.ts +13 -0
- package/dist/components/ui/alert.js +67 -0
- package/dist/components/ui/avatar.d.ts +11 -0
- package/dist/components/ui/avatar.js +23 -0
- package/dist/components/ui/back-pill.d.ts +13 -0
- package/dist/components/ui/back-pill.js +18 -0
- package/dist/components/ui/badge.d.ts +10 -0
- package/dist/components/ui/badge.js +42 -0
- package/dist/components/ui/breadcrumb.d.ts +11 -0
- package/dist/components/ui/breadcrumb.js +28 -0
- package/dist/components/ui/bubble.d.ts +20 -0
- package/dist/components/ui/bubble.js +38 -0
- package/dist/components/ui/button-group.d.ts +9 -0
- package/dist/components/ui/button-group.js +32 -0
- package/dist/components/ui/button.d.ts +13 -0
- package/dist/components/ui/button.js +67 -0
- package/dist/components/ui/calendar.d.ts +4 -0
- package/dist/components/ui/calendar.js +39 -0
- package/dist/components/ui/card.d.ts +96 -0
- package/dist/components/ui/card.js +291 -0
- package/dist/components/ui/chain-of-thought.d.ts +75 -0
- package/dist/components/ui/chain-of-thought.js +66 -0
- package/dist/components/ui/chart.d.ts +65 -0
- package/dist/components/ui/chart.js +118 -0
- package/dist/components/ui/checkbox-group.d.ts +19 -0
- package/dist/components/ui/checkbox-group.js +41 -0
- package/dist/components/ui/checkbox.d.ts +14 -0
- package/dist/components/ui/checkbox.js +27 -0
- package/dist/components/ui/collapsible.d.ts +5 -0
- package/dist/components/ui/collapsible.js +14 -0
- package/dist/components/ui/command.d.ts +58 -0
- package/dist/components/ui/command.js +135 -0
- package/dist/components/ui/confirm-dialog.d.ts +19 -0
- package/dist/components/ui/confirm-dialog.js +30 -0
- package/dist/components/ui/context-menu.d.ts +45 -0
- package/dist/components/ui/context-menu.js +93 -0
- package/dist/components/ui/count-badge.d.ts +26 -0
- package/dist/components/ui/count-badge.js +26 -0
- package/dist/components/ui/data-grid.d.ts +24 -0
- package/dist/components/ui/data-grid.js +107 -0
- package/dist/components/ui/date-range-picker.d.ts +119 -0
- package/dist/components/ui/date-range-picker.js +190 -0
- package/dist/components/ui/definition-list.d.ts +15 -0
- package/dist/components/ui/definition-list.js +26 -0
- package/dist/components/ui/dialog.d.ts +29 -0
- package/dist/components/ui/dialog.js +52 -0
- package/dist/components/ui/disclosure.d.ts +121 -0
- package/dist/components/ui/disclosure.js +138 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-10.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-10.js +29 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-12.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-12.js +19 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-13.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-13.js +19 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-15.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-15.js +22 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-16.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-16.js +5 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-18.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-18.js +5 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-19.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-19.js +5 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-2.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-2.js +3 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-20.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-20.js +5 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-21.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-21.js +5 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-3.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-3.js +3 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-4.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-4.js +3 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-5.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-5.js +3 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-6.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-6.js +19 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-7.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-7.js +19 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-8.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-8.js +19 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-9.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-3x3-9.js +23 -0
- package/dist/components/ui/dot-matrix/dotm-circular-1.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-1.js +28 -0
- package/dist/components/ui/dot-matrix/dotm-circular-10.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-10.js +40 -0
- package/dist/components/ui/dot-matrix/dotm-circular-11.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-11.js +37 -0
- package/dist/components/ui/dot-matrix/dotm-circular-12.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-12.js +42 -0
- package/dist/components/ui/dot-matrix/dotm-circular-14.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-14.js +37 -0
- package/dist/components/ui/dot-matrix/dotm-circular-15.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-15.js +49 -0
- package/dist/components/ui/dot-matrix/dotm-circular-17.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-17.js +31 -0
- package/dist/components/ui/dot-matrix/dotm-circular-2.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-2.js +37 -0
- package/dist/components/ui/dot-matrix/dotm-circular-3.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-3.js +54 -0
- package/dist/components/ui/dot-matrix/dotm-circular-4.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-4.js +35 -0
- package/dist/components/ui/dot-matrix/dotm-circular-5.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-5.js +30 -0
- package/dist/components/ui/dot-matrix/dotm-circular-6.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-6.js +37 -0
- package/dist/components/ui/dot-matrix/dotm-circular-7.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-7.js +25 -0
- package/dist/components/ui/dot-matrix/dotm-circular-8.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-8.js +26 -0
- package/dist/components/ui/dot-matrix/dotm-circular-9.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-circular-9.js +41 -0
- package/dist/components/ui/dot-matrix/dotm-square-1.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-1.js +27 -0
- package/dist/components/ui/dot-matrix/dotm-square-10.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-10.js +27 -0
- package/dist/components/ui/dot-matrix/dotm-square-11.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-11.js +25 -0
- package/dist/components/ui/dot-matrix/dotm-square-12.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-12.js +28 -0
- package/dist/components/ui/dot-matrix/dotm-square-13.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-13.js +45 -0
- package/dist/components/ui/dot-matrix/dotm-square-14.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-14.js +38 -0
- package/dist/components/ui/dot-matrix/dotm-square-15.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-15.js +31 -0
- package/dist/components/ui/dot-matrix/dotm-square-16.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-16.js +33 -0
- package/dist/components/ui/dot-matrix/dotm-square-17.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-17.js +27 -0
- package/dist/components/ui/dot-matrix/dotm-square-18.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-18.js +29 -0
- package/dist/components/ui/dot-matrix/dotm-square-19.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-19.js +74 -0
- package/dist/components/ui/dot-matrix/dotm-square-2.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-2.js +72 -0
- package/dist/components/ui/dot-matrix/dotm-square-20.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-20.js +79 -0
- package/dist/components/ui/dot-matrix/dotm-square-3.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-3.js +23 -0
- package/dist/components/ui/dot-matrix/dotm-square-4.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-4.js +41 -0
- package/dist/components/ui/dot-matrix/dotm-square-5.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-5.js +23 -0
- package/dist/components/ui/dot-matrix/dotm-square-6.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-6.js +20 -0
- package/dist/components/ui/dot-matrix/dotm-square-7.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-7.js +43 -0
- package/dist/components/ui/dot-matrix/dotm-square-8.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-8.js +57 -0
- package/dist/components/ui/dot-matrix/dotm-square-9.d.ts +4 -0
- package/dist/components/ui/dot-matrix/dotm-square-9.js +89 -0
- package/dist/components/ui/dot-matrix/session-dot-matrix.d.ts +46 -0
- package/dist/components/ui/dot-matrix/session-dot-matrix.js +153 -0
- package/dist/components/ui/drawer.d.ts +15 -0
- package/dist/components/ui/drawer.js +35 -0
- package/dist/components/ui/dropdown-menu.d.ts +49 -0
- package/dist/components/ui/dropdown-menu.js +113 -0
- package/dist/components/ui/emoji-picker.d.ts +15 -0
- package/dist/components/ui/emoji-picker.js +137 -0
- package/dist/components/ui/emoji-tint.d.ts +41 -0
- package/dist/components/ui/emoji-tint.js +131 -0
- package/dist/components/ui/empty.d.ts +11 -0
- package/dist/components/ui/empty.js +33 -0
- package/dist/components/ui/entity-avatar.d.ts +84 -0
- package/dist/components/ui/entity-avatar.js +106 -0
- package/dist/components/ui/faded-scroll-area.d.ts +8 -0
- package/dist/components/ui/faded-scroll-area.js +80 -0
- package/dist/components/ui/favicon-avatar.d.ts +32 -0
- package/dist/components/ui/favicon-avatar.js +18 -0
- package/dist/components/ui/field.d.ts +15 -0
- package/dist/components/ui/field.js +48 -0
- package/dist/components/ui/file-tree.d.ts +32 -0
- package/dist/components/ui/file-tree.js +30 -0
- package/dist/components/ui/glyph-face.d.ts +18 -0
- package/dist/components/ui/glyph-face.js +37 -0
- package/dist/components/ui/glyph-paths-source.d.ts +2 -0
- package/dist/components/ui/glyph-paths-source.js +40 -0
- package/dist/components/ui/glyph-paths.generated.d.ts +2 -0
- package/dist/components/ui/glyph-paths.generated.js +207 -0
- package/dist/components/ui/glyph-picker.d.ts +22 -0
- package/dist/components/ui/glyph-picker.js +118 -0
- package/dist/components/ui/glyph-registry.d.ts +36 -0
- package/dist/components/ui/glyph-registry.js +479 -0
- package/dist/components/ui/glyph-tint.d.ts +6 -0
- package/dist/components/ui/glyph-tint.js +93 -0
- package/dist/components/ui/hint.d.ts +14 -0
- package/dist/components/ui/hint.js +12 -0
- package/dist/components/ui/histogram-bars.d.ts +38 -0
- package/dist/components/ui/histogram-bars.js +93 -0
- package/dist/components/ui/hover-card.d.ts +28 -0
- package/dist/components/ui/hover-card.js +35 -0
- package/dist/components/ui/icon-provider.d.ts +23 -0
- package/dist/components/ui/icon-provider.js +30 -0
- package/dist/components/ui/info-banner.d.ts +14 -0
- package/dist/components/ui/info-banner.js +72 -0
- package/dist/components/ui/inline-meta.d.ts +6 -0
- package/dist/components/ui/inline-meta.js +8 -0
- package/dist/components/ui/input-group.d.ts +19 -0
- package/dist/components/ui/input-group.js +73 -0
- package/dist/components/ui/input.d.ts +26 -0
- package/dist/components/ui/input.js +23 -0
- package/dist/components/ui/item.d.ts +23 -0
- package/dist/components/ui/item.js +67 -0
- package/dist/components/ui/kbd.d.ts +3 -0
- package/dist/components/ui/kbd.js +20 -0
- package/dist/components/ui/kortix-asterisk.d.ts +6 -0
- package/dist/components/ui/kortix-asterisk.js +17 -0
- package/dist/components/ui/kortix-icons.d.ts +2 -0
- package/dist/components/ui/kortix-icons.js +17 -0
- package/dist/components/ui/kortix-loader.d.ts +76 -0
- package/dist/components/ui/kortix-loader.js +74 -0
- package/dist/components/ui/kortix-logo.d.ts +30 -0
- package/dist/components/ui/kortix-logo.js +47 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.js +9 -0
- package/dist/components/ui/list.d.ts +15 -0
- package/dist/components/ui/list.js +25 -0
- package/dist/components/ui/loading.d.ts +19 -0
- package/dist/components/ui/loading.js +31 -0
- package/dist/components/ui/local-time.d.ts +34 -0
- package/dist/components/ui/local-time.js +60 -0
- package/dist/components/ui/map.d.ts +101 -0
- package/dist/components/ui/map.js +257 -0
- package/dist/components/ui/marker.d.ts +14 -0
- package/dist/components/ui/marker.js +28 -0
- package/dist/components/ui/menu-recipe.d.ts +102 -0
- package/dist/components/ui/menu-recipe.js +129 -0
- package/dist/components/ui/mermaid-render.d.ts +51 -0
- package/dist/components/ui/mermaid-render.js +65 -0
- package/dist/components/ui/mermaid-renderer.d.ts +13 -0
- package/dist/components/ui/mermaid-renderer.js +340 -0
- package/dist/components/ui/message.d.ts +15 -0
- package/dist/components/ui/message.js +26 -0
- package/dist/components/ui/modal.d.ts +104 -0
- package/dist/components/ui/modal.js +180 -0
- package/dist/components/ui/navigation-menu.d.ts +16 -0
- package/dist/components/ui/navigation-menu.js +35 -0
- package/dist/components/ui/page-search-bar.d.ts +11 -0
- package/dist/components/ui/page-search-bar.js +13 -0
- package/dist/components/ui/phone-input.d.ts +7 -0
- package/dist/components/ui/phone-input.js +59 -0
- package/dist/components/ui/pixel-kortix-mark.d.ts +13 -0
- package/dist/components/ui/pixel-kortix-mark.js +40 -0
- package/dist/components/ui/popover.d.ts +10 -0
- package/dist/components/ui/popover.js +38 -0
- package/dist/components/ui/portal.d.ts +5 -0
- package/dist/components/ui/portal.js +15 -0
- package/dist/components/ui/preview-image.d.ts +14 -0
- package/dist/components/ui/preview-image.js +58 -0
- package/dist/components/ui/progress-ring.d.ts +6 -0
- package/dist/components/ui/progress-ring.js +9 -0
- package/dist/components/ui/progress.d.ts +6 -0
- package/dist/components/ui/progress.js +8 -0
- package/dist/components/ui/radio-group.d.ts +12 -0
- package/dist/components/ui/radio-group.js +20 -0
- package/dist/components/ui/resizable.d.ts +24 -0
- package/dist/components/ui/resizable.js +16 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.js +11 -0
- package/dist/components/ui/section-card.d.ts +37 -0
- package/dist/components/ui/section-card.js +14 -0
- package/dist/components/ui/section.d.ts +10 -0
- package/dist/components/ui/section.js +22 -0
- package/dist/components/ui/select.d.ts +25 -0
- package/dist/components/ui/select.js +59 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.js +8 -0
- package/dist/components/ui/shader-safe.d.ts +48 -0
- package/dist/components/ui/shader-safe.js +74 -0
- package/dist/components/ui/sheet.d.ts +32 -0
- package/dist/components/ui/sheet.js +55 -0
- package/dist/components/ui/sidebar-context.d.ts +72 -0
- package/dist/components/ui/sidebar-context.js +36 -0
- package/dist/components/ui/sidebar-menu-primitives.d.ts +38 -0
- package/dist/components/ui/sidebar-menu-primitives.js +97 -0
- package/dist/components/ui/sidebar-panel.d.ts +36 -0
- package/dist/components/ui/sidebar-panel.js +265 -0
- package/dist/components/ui/sidebar-peek.d.ts +27 -0
- package/dist/components/ui/sidebar-peek.js +70 -0
- package/dist/components/ui/sidebar-width.d.ts +48 -0
- package/dist/components/ui/sidebar-width.js +62 -0
- package/dist/components/ui/sidebar.d.ts +18 -0
- package/dist/components/ui/sidebar.js +194 -0
- package/dist/components/ui/skeleton.d.ts +3 -0
- package/dist/components/ui/skeleton.js +6 -0
- package/dist/components/ui/slider-native.d.ts +7 -0
- package/dist/components/ui/slider-native.js +16 -0
- package/dist/components/ui/slider.d.ts +14 -0
- package/dist/components/ui/slider.js +48 -0
- package/dist/components/ui/sliding-tab-indicator.d.ts +61 -0
- package/dist/components/ui/sliding-tab-indicator.js +128 -0
- package/dist/components/ui/split-sheet.d.ts +61 -0
- package/dist/components/ui/split-sheet.js +239 -0
- package/dist/components/ui/spotlight-card.d.ts +8 -0
- package/dist/components/ui/spotlight-card.js +43 -0
- package/dist/components/ui/status-ring.d.ts +38 -0
- package/dist/components/ui/status-ring.js +38 -0
- package/dist/components/ui/status.d.ts +58 -0
- package/dist/components/ui/status.js +76 -0
- package/dist/components/ui/stepper.d.ts +28 -0
- package/dist/components/ui/stepper.js +73 -0
- package/dist/components/ui/switch.d.ts +8 -0
- package/dist/components/ui/switch.js +131 -0
- package/dist/components/ui/system-message.d.ts +23 -0
- package/dist/components/ui/system-message.js +51 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/components/ui/table.js +28 -0
- package/dist/components/ui/tabs.d.ts +73 -0
- package/dist/components/ui/tabs.js +207 -0
- package/dist/components/ui/tag.d.ts +7 -0
- package/dist/components/ui/tag.js +22 -0
- package/dist/components/ui/text-shimmer.d.ts +13 -0
- package/dist/components/ui/text-shimmer.js +91 -0
- package/dist/components/ui/textarea.d.ts +33 -0
- package/dist/components/ui/textarea.js +87 -0
- package/dist/components/ui/toast.d.ts +105 -0
- package/dist/components/ui/toast.js +233 -0
- package/dist/components/ui/toggle.d.ts +12 -0
- package/dist/components/ui/toggle.js +26 -0
- package/dist/components/ui/tooltip.d.ts +9 -0
- package/dist/components/ui/tooltip.js +16 -0
- package/dist/components/ui/trigger-variants.d.ts +14 -0
- package/dist/components/ui/trigger-variants.js +67 -0
- package/dist/components/ui/type-to-confirm-dialog.d.ts +88 -0
- package/dist/components/ui/type-to-confirm-dialog.js +101 -0
- package/dist/components/ui/typed-title-logic.d.ts +22 -0
- package/dist/components/ui/typed-title-logic.js +33 -0
- package/dist/components/ui/typed-title.d.ts +38 -0
- package/dist/components/ui/typed-title.js +66 -0
- package/dist/components/ui/typing-animation.d.ts +36 -0
- package/dist/components/ui/typing-animation.js +141 -0
- package/dist/components/ui/user-avatar.d.ts +47 -0
- package/dist/components/ui/user-avatar.js +46 -0
- package/dist/hooks/use-debounce.d.ts +4 -0
- package/dist/hooks/use-debounce.js +17 -0
- package/dist/hooks/use-is-dark.d.ts +2 -0
- package/dist/hooks/use-is-dark.js +27 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.js +15 -0
- package/dist/hooks/use-proximity-hover.d.ts +52 -0
- package/dist/hooks/use-proximity-hover.js +232 -0
- package/dist/icons/close.d.ts +2 -0
- package/dist/icons/close.js +7 -0
- package/dist/icons/copy.d.ts +3 -0
- package/dist/icons/copy.js +6 -0
- package/dist/icons/sidebar-toggle.d.ts +13 -0
- package/dist/icons/sidebar-toggle.js +13 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +33 -0
- package/dist/lib/dotmatrix-core.d.ts +113 -0
- package/dist/lib/dotmatrix-core.js +920 -0
- package/dist/lib/dotmatrix-hooks.d.ts +28 -0
- package/dist/lib/dotmatrix-hooks.js +151 -0
- package/dist/lib/favicon.d.ts +4 -0
- package/dist/lib/favicon.js +21 -0
- package/dist/lib/font-weight.d.ts +6 -0
- package/dist/lib/font-weight.js +6 -0
- package/dist/lib/icon-config.d.ts +33 -0
- package/dist/lib/icon-config.js +22 -0
- package/dist/lib/icon-context.d.ts +34 -0
- package/dist/lib/icon-context.js +81 -0
- package/dist/lib/portal-root.d.ts +8 -0
- package/dist/lib/portal-root.js +26 -0
- package/dist/lib/settle.d.ts +16 -0
- package/dist/lib/settle.js +29 -0
- package/dist/lib/springs.d.ts +31 -0
- package/dist/lib/springs.js +29 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lib/z-stack.d.ts +55 -0
- package/dist/lib/z-stack.js +130 -0
- package/dist/providers/ui-provider.d.ts +141 -0
- package/dist/providers/ui-provider.js +93 -0
- package/dist/styles/dotmatrix-loader.css +1239 -0
- package/dist/styles/fonts.css +46 -0
- package/dist/styles/tailwind.css +10 -0
- package/dist/styles/theme.css +1019 -0
- package/dist/styles/tokens.css +146 -0
- package/dist/styles.css +2 -0
- package/package.json +881 -4
- package/skills/kortix-ui/SKILL.md +150 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { cva } from 'class-variance-authority';
|
|
2
|
+
/**
|
|
3
|
+
* The shared look for every disclosure trigger in the system — Select, Dropdown,
|
|
4
|
+
* Popover, Sheet, Modal, Dialog. One definition so a bare trigger renders the
|
|
5
|
+
* same box regardless of which overlay it opens.
|
|
6
|
+
*
|
|
7
|
+
* `secondary` (default) and `outline` draw the input field surface; `transparent`
|
|
8
|
+
* keeps `border-transparent` from the base, so the box metrics stay identical
|
|
9
|
+
* and a row of mixed variants aligns to the same baseline.
|
|
10
|
+
*
|
|
11
|
+
* Triggers rendered with `asChild` opt out entirely — the child owns its styling.
|
|
12
|
+
*/
|
|
13
|
+
/** Literal classes, not composed from input.tsx, so Tailwind can see every prefixed one. */
|
|
14
|
+
const fieldTriggerClasses = [
|
|
15
|
+
'bg-popover border-border',
|
|
16
|
+
'focus-visible:border-ring focus-visible:ring-ring/15 focus-visible:ring-3',
|
|
17
|
+
'data-[state=open]:border-ring data-[state=open]:ring-ring/15 data-[state=open]:ring-3',
|
|
18
|
+
].join(' ');
|
|
19
|
+
export const triggerVariants = cva([
|
|
20
|
+
'group/ui-trigger flex w-fit shrink-0 cursor-pointer items-center justify-between gap-2 rounded-md border border-transparent font-normal outline-none',
|
|
21
|
+
'text-foreground data-placeholder:text-muted-foreground',
|
|
22
|
+
// Never `transition-all`: only the properties that actually change on hover/press.
|
|
23
|
+
'transition-[background-color,border-color,color,box-shadow,transform] duration-150 ease-out',
|
|
24
|
+
'focus-visible:ring-kortix-base focus-visible:ring-[0.6px] focus-visible:outline-none data-[state=open]:ring-0',
|
|
25
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
26
|
+
// One line, ellipsised. `truncate`, not `line-clamp-1`: line-clamp sets
|
|
27
|
+
// `display: -webkit-box` on every direct <span>, which out-ranks a child's
|
|
28
|
+
// own `flex` — an icon + label wrapper then stacked the icon ABOVE the
|
|
29
|
+
// label and lost its gap. `truncate` changes overflow only, not display.
|
|
30
|
+
'[&>span]:min-w-0 [&>span]:truncate [&>span]:text-left',
|
|
31
|
+
], {
|
|
32
|
+
variants: {
|
|
33
|
+
variant: {
|
|
34
|
+
// `secondary` and `outline` are form-field triggers: they draw the exact
|
|
35
|
+
// surface `Input` draws (`inputSurfaceClasses` in input.tsx), and an OPEN
|
|
36
|
+
// trigger shows the same border + 3px halo as a focused input, so a
|
|
37
|
+
// Select beside a text field reads as one form.
|
|
38
|
+
secondary: fieldTriggerClasses,
|
|
39
|
+
outline: fieldTriggerClasses,
|
|
40
|
+
// Only the borderless toolbar trigger takes a press scale. A form field
|
|
41
|
+
// does not shrink under the pointer — an input never does, and a
|
|
42
|
+
// Select beside it should behave the same.
|
|
43
|
+
transparent: 'bg-transparent hover:bg-foreground/5 motion-safe:active:scale-[0.98]',
|
|
44
|
+
},
|
|
45
|
+
size: {
|
|
46
|
+
sm: 'h-8 px-2.5 text-sm',
|
|
47
|
+
md: 'h-10 px-3.5 text-sm',
|
|
48
|
+
lg: 'h-11 px-4 text-base',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
defaultVariants: {
|
|
52
|
+
variant: 'secondary',
|
|
53
|
+
size: 'sm',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
/** Caret sizing per trigger size — matches the text step of each size. */
|
|
57
|
+
export const TRIGGER_ICON_SIZE = {
|
|
58
|
+
sm: 'size-4',
|
|
59
|
+
md: 'size-4',
|
|
60
|
+
lg: 'size-4.5',
|
|
61
|
+
};
|
|
62
|
+
/** The caret classes: muted, shrink-proof, and flipped while the overlay is open. */
|
|
63
|
+
export const TRIGGER_CARET_CLASS = [
|
|
64
|
+
'text-muted-foreground shrink-0',
|
|
65
|
+
'motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-out',
|
|
66
|
+
'group-data-[state=open]/ui-trigger:rotate-180',
|
|
67
|
+
].join(' ');
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A destructive confirmation that only arms once the user has TYPED an exact
|
|
3
|
+
* phrase — the workspace name, the account name, whatever uniquely identifies
|
|
4
|
+
* the thing being destroyed.
|
|
5
|
+
*
|
|
6
|
+
* **When to use this instead of `ConfirmDialog`.** `ConfirmDialog`
|
|
7
|
+
* (`confirm-dialog.tsx`) stays the default for every destructive mutation:
|
|
8
|
+
* revoke a key, delete a secret, disconnect a channel. Reach for THIS one only
|
|
9
|
+
* when the action is both irreversible AND ambiguous — i.e. the user could
|
|
10
|
+
* plausibly be looking at the wrong entity. A single click, even a confirmed
|
|
11
|
+
* one, does not distinguish "delete this workspace" from "delete the workspace
|
|
12
|
+
* I meant". Typing the name does.
|
|
13
|
+
*
|
|
14
|
+
* **Why `Modal` and not `AlertDialog`.** The moment a confirmation grows an
|
|
15
|
+
* input it is a form, and `ConfirmDialog`'s `AlertDialog` shell has no room for
|
|
16
|
+
* one. `profile-tab.tsx`'s Delete-account flow already made this call (it
|
|
17
|
+
* hand-rolls a `Modal` with a "type delete to confirm" field); this component
|
|
18
|
+
* is that same pattern promoted to a primitive so the next destructive flow
|
|
19
|
+
* composes it instead of copying it. Profile's own dialog is deliberately left
|
|
20
|
+
* alone here — it carries an extra grace-period radio group, so adopting this
|
|
21
|
+
* is its own change, not a drive-by.
|
|
22
|
+
*
|
|
23
|
+
* **Why the phrase is the entity's name, not a fixed word.** A fixed word
|
|
24
|
+
* ("delete", "DELETE") proves the user read a prompt. The entity's name proves
|
|
25
|
+
* the user knows WHICH entity they are destroying. The expensive mistake in
|
|
26
|
+
* practice is not clicking too fast — it is clicking correctly on the wrong
|
|
27
|
+
* workspace.
|
|
28
|
+
*
|
|
29
|
+
* The matcher is `confirmationPhraseMatches` below, exported and tested
|
|
30
|
+
* separately: this dialog renders through a Radix portal, which emits nothing
|
|
31
|
+
* under `renderToStaticMarkup`, so a test that reached for the button's
|
|
32
|
+
* `disabled` attribute in rendered output would silently be unable to fail.
|
|
33
|
+
* See `type-to-confirm-dialog.test.ts`.
|
|
34
|
+
*/
|
|
35
|
+
import { type ReactNode } from 'react';
|
|
36
|
+
/**
|
|
37
|
+
* Does what the user typed authorize the destructive action?
|
|
38
|
+
*
|
|
39
|
+
* Three deliberate rules, each guarding a specific failure:
|
|
40
|
+
*
|
|
41
|
+
* 1. **An empty/blank `phrase` NEVER arms.** This is the important one. The
|
|
42
|
+
* phrase is usually a name read out of a query (`project?.name`), which is
|
|
43
|
+
* `undefined` while that query is still loading. A naive
|
|
44
|
+
* `typed === phrase` would then compare `'' === ''` and arm the destroy
|
|
45
|
+
* button on an untouched dialog — the precise opposite of this component's
|
|
46
|
+
* reason to exist.
|
|
47
|
+
* 2. **Both sides are trimmed.** Names get copied out of the surrounding UI and
|
|
48
|
+
* arrive with a trailing space. Leading/trailing whitespace is a paste
|
|
49
|
+
* artifact, never intent, and rejecting it only teaches users to distrust
|
|
50
|
+
* the field.
|
|
51
|
+
* 3. **The comparison is case-insensitive.** Case adds friction without adding
|
|
52
|
+
* safety: the guarantee being bought is "the user knows which entity this
|
|
53
|
+
* is", and typing `acme prod` for `Acme Prod` demonstrates that fully. A
|
|
54
|
+
* case-sensitive match would reject it and buy nothing.
|
|
55
|
+
*/
|
|
56
|
+
export declare function confirmationPhraseMatches(typed: string, phrase: string): boolean;
|
|
57
|
+
export interface TypeToConfirmDialogProps {
|
|
58
|
+
open: boolean;
|
|
59
|
+
onOpenChange: (open: boolean) => void;
|
|
60
|
+
/** Stable across entities — "Delete workspace?", not "Delete Acme?". The
|
|
61
|
+
* entity's name belongs in `description`, where it does not make the
|
|
62
|
+
* heading jump around between openings. */
|
|
63
|
+
title: string;
|
|
64
|
+
/** One or two sentences naming the entity and the irreversibility. */
|
|
65
|
+
description: ReactNode;
|
|
66
|
+
/** What the action destroys or stops, one item per line. Rendered as a
|
|
67
|
+
* bulleted list inside a bordered panel. Static per call site, so index
|
|
68
|
+
* keys are safe. */
|
|
69
|
+
consequences?: readonly ReactNode[];
|
|
70
|
+
/** Heading above `consequences`. */
|
|
71
|
+
consequencesTitle?: string;
|
|
72
|
+
/** What SURVIVES the action. Optional, but include it whenever something
|
|
73
|
+
* meaningful does — a user who has just been told "this is permanent"
|
|
74
|
+
* reads an unqualified warning as "my code is gone too". */
|
|
75
|
+
reassurance?: ReactNode;
|
|
76
|
+
/** The exact string the user must type. While this is blank the confirm
|
|
77
|
+
* button never arms — see `confirmationPhraseMatches`. */
|
|
78
|
+
confirmPhrase: string;
|
|
79
|
+
/** Verb phrase for the armed button, e.g. "Delete workspace". */
|
|
80
|
+
confirmLabel: string;
|
|
81
|
+
/** Non-destructive dismissal. Phrase it as the safe outcome ("Keep
|
|
82
|
+
* workspace"), not as a neutral "Cancel" — at the moment of decision the
|
|
83
|
+
* escape hatch should read as a choice, not an abort. */
|
|
84
|
+
cancelLabel?: string;
|
|
85
|
+
onConfirm: () => void;
|
|
86
|
+
isPending?: boolean;
|
|
87
|
+
}
|
|
88
|
+
export declare function TypeToConfirmDialog({ open, onOpenChange, ...content }: TypeToConfirmDialogProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* A destructive confirmation that only arms once the user has TYPED an exact
|
|
5
|
+
* phrase — the workspace name, the account name, whatever uniquely identifies
|
|
6
|
+
* the thing being destroyed.
|
|
7
|
+
*
|
|
8
|
+
* **When to use this instead of `ConfirmDialog`.** `ConfirmDialog`
|
|
9
|
+
* (`confirm-dialog.tsx`) stays the default for every destructive mutation:
|
|
10
|
+
* revoke a key, delete a secret, disconnect a channel. Reach for THIS one only
|
|
11
|
+
* when the action is both irreversible AND ambiguous — i.e. the user could
|
|
12
|
+
* plausibly be looking at the wrong entity. A single click, even a confirmed
|
|
13
|
+
* one, does not distinguish "delete this workspace" from "delete the workspace
|
|
14
|
+
* I meant". Typing the name does.
|
|
15
|
+
*
|
|
16
|
+
* **Why `Modal` and not `AlertDialog`.** The moment a confirmation grows an
|
|
17
|
+
* input it is a form, and `ConfirmDialog`'s `AlertDialog` shell has no room for
|
|
18
|
+
* one. `profile-tab.tsx`'s Delete-account flow already made this call (it
|
|
19
|
+
* hand-rolls a `Modal` with a "type delete to confirm" field); this component
|
|
20
|
+
* is that same pattern promoted to a primitive so the next destructive flow
|
|
21
|
+
* composes it instead of copying it. Profile's own dialog is deliberately left
|
|
22
|
+
* alone here — it carries an extra grace-period radio group, so adopting this
|
|
23
|
+
* is its own change, not a drive-by.
|
|
24
|
+
*
|
|
25
|
+
* **Why the phrase is the entity's name, not a fixed word.** A fixed word
|
|
26
|
+
* ("delete", "DELETE") proves the user read a prompt. The entity's name proves
|
|
27
|
+
* the user knows WHICH entity they are destroying. The expensive mistake in
|
|
28
|
+
* practice is not clicking too fast — it is clicking correctly on the wrong
|
|
29
|
+
* workspace.
|
|
30
|
+
*
|
|
31
|
+
* The matcher is `confirmationPhraseMatches` below, exported and tested
|
|
32
|
+
* separately: this dialog renders through a Radix portal, which emits nothing
|
|
33
|
+
* under `renderToStaticMarkup`, so a test that reached for the button's
|
|
34
|
+
* `disabled` attribute in rendered output would silently be unable to fail.
|
|
35
|
+
* See `type-to-confirm-dialog.test.ts`.
|
|
36
|
+
*/
|
|
37
|
+
import { useId, useState } from 'react';
|
|
38
|
+
import { useUiStrings } from '../../providers/ui-provider.js';
|
|
39
|
+
import { Button } from './button.js';
|
|
40
|
+
import { Input } from './input.js';
|
|
41
|
+
import { Label } from './label.js';
|
|
42
|
+
import Loading from './loading.js';
|
|
43
|
+
import { Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, } from './modal.js';
|
|
44
|
+
/**
|
|
45
|
+
* Does what the user typed authorize the destructive action?
|
|
46
|
+
*
|
|
47
|
+
* Three deliberate rules, each guarding a specific failure:
|
|
48
|
+
*
|
|
49
|
+
* 1. **An empty/blank `phrase` NEVER arms.** This is the important one. The
|
|
50
|
+
* phrase is usually a name read out of a query (`project?.name`), which is
|
|
51
|
+
* `undefined` while that query is still loading. A naive
|
|
52
|
+
* `typed === phrase` would then compare `'' === ''` and arm the destroy
|
|
53
|
+
* button on an untouched dialog — the precise opposite of this component's
|
|
54
|
+
* reason to exist.
|
|
55
|
+
* 2. **Both sides are trimmed.** Names get copied out of the surrounding UI and
|
|
56
|
+
* arrive with a trailing space. Leading/trailing whitespace is a paste
|
|
57
|
+
* artifact, never intent, and rejecting it only teaches users to distrust
|
|
58
|
+
* the field.
|
|
59
|
+
* 3. **The comparison is case-insensitive.** Case adds friction without adding
|
|
60
|
+
* safety: the guarantee being bought is "the user knows which entity this
|
|
61
|
+
* is", and typing `acme prod` for `Acme Prod` demonstrates that fully. A
|
|
62
|
+
* case-sensitive match would reject it and buy nothing.
|
|
63
|
+
*/
|
|
64
|
+
export function confirmationPhraseMatches(typed, phrase) {
|
|
65
|
+
const target = phrase.trim();
|
|
66
|
+
if (!target)
|
|
67
|
+
return false;
|
|
68
|
+
return typed.trim().toLowerCase() === target.toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
export function TypeToConfirmDialog({ open, onOpenChange, ...content }) {
|
|
71
|
+
return (_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsx(ModalContent, { className: "lg:max-w-md", children: _jsx(TypeToConfirmBody, { onOpenChange: onOpenChange, ...content }) }) }));
|
|
72
|
+
}
|
|
73
|
+
function TypeToConfirmBody({ onOpenChange, title, description, consequences, consequencesTitle = 'This removes:', reassurance, confirmPhrase, confirmLabel, cancelLabel, onConfirm, isPending = false, }) {
|
|
74
|
+
const strings = useUiStrings();
|
|
75
|
+
const [typed, setTyped] = useState('');
|
|
76
|
+
const inputId = useId();
|
|
77
|
+
// Derived, never stored: if `confirmPhrase` changes while the dialog is open
|
|
78
|
+
// (the entity is renamed in another tab), the button disarms on the next
|
|
79
|
+
// render instead of holding a stale "armed" flag.
|
|
80
|
+
const armed = confirmationPhraseMatches(typed, confirmPhrase);
|
|
81
|
+
const canSubmit = armed && !isPending;
|
|
82
|
+
return (_jsxs(_Fragment, { children: [_jsxs(ModalHeader, { className: "space-y-1.5", children: [_jsx(ModalTitle, { children: title }), _jsx(ModalDescription, { className: "text-pretty", children: description })] }), _jsxs("form", { onSubmit: (e) => {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
// Guarded, not just visually disabled: Enter in the text field
|
|
85
|
+
// submits the form, and a disabled button does not stop that.
|
|
86
|
+
if (canSubmit)
|
|
87
|
+
onConfirm();
|
|
88
|
+
}, children: [_jsxs(ModalBody, { children: [consequences?.length ? (_jsxs("div", { className: "bg-popover space-y-2 rounded-md border px-4 py-3", children: [_jsx("p", { className: "text-sm font-medium", children: consequencesTitle }), _jsx("ul", { className: "text-muted-foreground list-disc space-y-1.5 pl-5 text-sm text-pretty", children: (() => {
|
|
89
|
+
// Callers pass static string lists — key by content, with an
|
|
90
|
+
// occurrence counter for repeats and non-text nodes.
|
|
91
|
+
const seen = new Map();
|
|
92
|
+
return consequences.map((item) => {
|
|
93
|
+
const base = typeof item === 'string' || typeof item === 'number' ? String(item) : 'node';
|
|
94
|
+
const n = seen.get(base) ?? 0;
|
|
95
|
+
seen.set(base, n + 1);
|
|
96
|
+
return _jsx("li", { children: item }, n === 0 ? base : `${base}#${n}`);
|
|
97
|
+
});
|
|
98
|
+
})() })] })) : null, reassurance ? (_jsx("p", { className: "text-muted-foreground text-sm text-pretty", children: reassurance })) : null, _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: inputId, className: "text-sm font-normal", children: ["Type", ' ', _jsx("span", { className: "text-foreground font-medium break-all", children: confirmPhrase }), " to confirm"] }), _jsx(Input, { id: inputId, value: typed, onChange: (e) => setTyped(e.target.value), disabled: isPending, autoComplete: "off", autoCorrect: "off", autoCapitalize: "none", spellCheck: false, "aria-describedby": `${inputId}-hint` }), _jsx("p", { id: `${inputId}-hint`, className: "sr-only", "aria-live": "polite", children: armed
|
|
99
|
+
? `Confirmed. ${confirmLabel} is now enabled.`
|
|
100
|
+
: `Type the exact name to enable ${confirmLabel}.` })] })] }), _jsxs(ModalFooter, { className: "sm:justify-between", children: [_jsx(Button, { type: "button", variant: "outline-ghost", onClick: () => onOpenChange(false), disabled: isPending, className: "w-full sm:w-auto", children: cancelLabel ?? strings.cancel }), _jsxs(Button, { type: "submit", variant: "destructive", disabled: !canSubmit, className: "w-full gap-1.5 sm:w-auto", children: [isPending ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, confirmLabel] })] })] })] }));
|
|
101
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a title should type itself in.
|
|
3
|
+
*
|
|
4
|
+
* Pure, and separate from the component, because this is the whole decision —
|
|
5
|
+
* the rendering is a detail. A session title types when its VALUE CHANGES, not
|
|
6
|
+
* when it renders. That distinction is what keeps the effect off the frequency
|
|
7
|
+
* cliff: a page load, a scroll back up, a re-sort of the list and a re-render
|
|
8
|
+
* from unrelated state all paint titles instantly, because none of them change
|
|
9
|
+
* a title. Twenty rows typing at once on load would be noise; this cannot
|
|
10
|
+
* produce it.
|
|
11
|
+
*/
|
|
12
|
+
/** How long between characters. A fast, confident typist — slow enough to read
|
|
13
|
+
* as typing, quick enough that a 40-character title lands in ~1.4s. Shared by
|
|
14
|
+
* every surface so the effect is one thing, not per-list guesswork. */
|
|
15
|
+
export declare const TYPE_SPEED_MS = 36;
|
|
16
|
+
export declare function shouldTypeOnChange({ previous, next, reduceMotion, }: {
|
|
17
|
+
/** What this row last displayed. `null` means it has never displayed
|
|
18
|
+
* anything — the very first render. */
|
|
19
|
+
previous: string | null;
|
|
20
|
+
next: string;
|
|
21
|
+
reduceMotion: boolean;
|
|
22
|
+
}): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a title should type itself in.
|
|
3
|
+
*
|
|
4
|
+
* Pure, and separate from the component, because this is the whole decision —
|
|
5
|
+
* the rendering is a detail. A session title types when its VALUE CHANGES, not
|
|
6
|
+
* when it renders. That distinction is what keeps the effect off the frequency
|
|
7
|
+
* cliff: a page load, a scroll back up, a re-sort of the list and a re-render
|
|
8
|
+
* from unrelated state all paint titles instantly, because none of them change
|
|
9
|
+
* a title. Twenty rows typing at once on load would be noise; this cannot
|
|
10
|
+
* produce it.
|
|
11
|
+
*/
|
|
12
|
+
/** How long between characters. A fast, confident typist — slow enough to read
|
|
13
|
+
* as typing, quick enough that a 40-character title lands in ~1.4s. Shared by
|
|
14
|
+
* every surface so the effect is one thing, not per-list guesswork. */
|
|
15
|
+
export const TYPE_SPEED_MS = 36;
|
|
16
|
+
export function shouldTypeOnChange({ previous, next, reduceMotion, }) {
|
|
17
|
+
// First render is never an animation. The row is appearing with whatever the
|
|
18
|
+
// title already is; there was no change to show, and animating here is what
|
|
19
|
+
// would make a cold page load type twenty titles at once.
|
|
20
|
+
if (previous === null)
|
|
21
|
+
return false;
|
|
22
|
+
if (previous === next)
|
|
23
|
+
return false;
|
|
24
|
+
// Reduced motion still gets the new title — instantly. Typing is decoration
|
|
25
|
+
// over a value that is already correct, so removing it costs nothing; a fade
|
|
26
|
+
// would be the compromise if the motion carried meaning, and it does not.
|
|
27
|
+
if (reduceMotion)
|
|
28
|
+
return false;
|
|
29
|
+
// An empty title has nothing to type. Guarded because the animation would
|
|
30
|
+
// otherwise complete on an empty string and leave the cursor blinking on a
|
|
31
|
+
// row with no text.
|
|
32
|
+
return next.length > 0;
|
|
33
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A title that types itself in when it changes.
|
|
3
|
+
*
|
|
4
|
+
* The sessions list shows "New session" the moment a session is created, then
|
|
5
|
+
* swaps it for the real name seconds later when the agent has one. That swap
|
|
6
|
+
* used to be an instant text replacement — the row simply said something
|
|
7
|
+
* different on the next frame, with nothing to say a value had been written.
|
|
8
|
+
* Typing it says so.
|
|
9
|
+
*
|
|
10
|
+
* TWO PIECES on purpose. `useTypeOnChange` is the trigger and is shared, so
|
|
11
|
+
* every list agrees on WHEN a title types. `TypedTitle` is one rendering of it,
|
|
12
|
+
* for rows that show a title as plain inline text. The sidebar renders its own
|
|
13
|
+
* (it wraps the title in a hover-scrolling marquee) and takes only the hook —
|
|
14
|
+
* shared decision, surface-specific presentation.
|
|
15
|
+
*/
|
|
16
|
+
import { TYPE_SPEED_MS } from './typed-title-logic.js';
|
|
17
|
+
export { TYPE_SPEED_MS };
|
|
18
|
+
export declare function useTypeOnChange(text: string): {
|
|
19
|
+
isTyping: boolean;
|
|
20
|
+
/** Remount key for the typing element. A run must restart from character
|
|
21
|
+
* zero when the title changes again mid-type — without a changing key the
|
|
22
|
+
* animation keeps its old progress and types the tail of a string nobody
|
|
23
|
+
* asked for. */
|
|
24
|
+
runId: number;
|
|
25
|
+
/** Hand to the animation's `onComplete`. */
|
|
26
|
+
stop: () => void;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Inline typed title for a list row.
|
|
30
|
+
*
|
|
31
|
+
* Renders as plain text at rest — no wrapper, no extra element — so a row that
|
|
32
|
+
* is not animating costs exactly what it did before, and the parent's
|
|
33
|
+
* `truncate` keeps ellipsising long titles as it always has.
|
|
34
|
+
*/
|
|
35
|
+
export declare function TypedTitle({ text, className }: {
|
|
36
|
+
text: string;
|
|
37
|
+
className?: string;
|
|
38
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* A title that types itself in when it changes.
|
|
5
|
+
*
|
|
6
|
+
* The sessions list shows "New session" the moment a session is created, then
|
|
7
|
+
* swaps it for the real name seconds later when the agent has one. That swap
|
|
8
|
+
* used to be an instant text replacement — the row simply said something
|
|
9
|
+
* different on the next frame, with nothing to say a value had been written.
|
|
10
|
+
* Typing it says so.
|
|
11
|
+
*
|
|
12
|
+
* TWO PIECES on purpose. `useTypeOnChange` is the trigger and is shared, so
|
|
13
|
+
* every list agrees on WHEN a title types. `TypedTitle` is one rendering of it,
|
|
14
|
+
* for rows that show a title as plain inline text. The sidebar renders its own
|
|
15
|
+
* (it wraps the title in a hover-scrolling marquee) and takes only the hook —
|
|
16
|
+
* shared decision, surface-specific presentation.
|
|
17
|
+
*/
|
|
18
|
+
import { shouldTypeOnChange, TYPE_SPEED_MS } from './typed-title-logic.js';
|
|
19
|
+
import { TypingAnimation } from './typing-animation.js';
|
|
20
|
+
import { cn } from '../../lib/utils.js';
|
|
21
|
+
import { useReducedMotion } from 'motion/react';
|
|
22
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
23
|
+
export { TYPE_SPEED_MS };
|
|
24
|
+
export function useTypeOnChange(text) {
|
|
25
|
+
// The last value this row DISPLAYED, in a ref rather than state: it must not
|
|
26
|
+
// itself cause a render, and it has to update in the same pass that decides
|
|
27
|
+
// to animate, or a second change arriving mid-type compares against a stale
|
|
28
|
+
// value and is missed.
|
|
29
|
+
const previousRef = useRef(null);
|
|
30
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
31
|
+
const [runId, setRunId] = useState(0);
|
|
32
|
+
const reduceMotion = !!useReducedMotion();
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const previous = previousRef.current;
|
|
35
|
+
previousRef.current = text;
|
|
36
|
+
if (!shouldTypeOnChange({ previous, next: text, reduceMotion }))
|
|
37
|
+
return;
|
|
38
|
+
setIsTyping(true);
|
|
39
|
+
setRunId((id) => id + 1);
|
|
40
|
+
}, [text, reduceMotion]);
|
|
41
|
+
const stop = useCallback(() => setIsTyping(false), []);
|
|
42
|
+
return { isTyping, runId, stop };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Inline typed title for a list row.
|
|
46
|
+
*
|
|
47
|
+
* Renders as plain text at rest — no wrapper, no extra element — so a row that
|
|
48
|
+
* is not animating costs exactly what it did before, and the parent's
|
|
49
|
+
* `truncate` keeps ellipsising long titles as it always has.
|
|
50
|
+
*/
|
|
51
|
+
export function TypedTitle({ text, className }) {
|
|
52
|
+
const { isTyping, runId, stop } = useTypeOnChange(text);
|
|
53
|
+
if (!isTyping)
|
|
54
|
+
return _jsx("span", { className: className, children: text });
|
|
55
|
+
return (_jsx(TypingAnimation, {
|
|
56
|
+
// The row is already on screen and the title has already changed —
|
|
57
|
+
// waiting for an intersection observer would mean a visible row types
|
|
58
|
+
// late, or a row scrolled past types nothing at all.
|
|
59
|
+
startOnView: false, showCursor: true, blinkCursor: true, cursorStyle: "line", typeSpeed: TYPE_SPEED_MS, onComplete: stop,
|
|
60
|
+
// The component ships marketing-hero defaults (`leading-20`, negative
|
|
61
|
+
// tracking) that belong to a headline, not a 13px list row. Reset both
|
|
62
|
+
// so the typed text sits on the same baseline and rhythm as the plain
|
|
63
|
+
// text it replaces — any drift here reads as the row twitching when the
|
|
64
|
+
// animation starts and again when it ends.
|
|
65
|
+
className: cn('leading-none tracking-normal', className), children: text }, runId));
|
|
66
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DOMMotionComponents, type HTMLMotionProps, type MotionProps } from 'motion/react';
|
|
2
|
+
declare const motionElements: {
|
|
3
|
+
readonly article: import("motion/react").ForwardRefComponent<HTMLElement, HTMLMotionProps<"article">>;
|
|
4
|
+
readonly div: import("motion/react").ForwardRefComponent<HTMLDivElement, HTMLMotionProps<"div">>;
|
|
5
|
+
readonly h1: import("motion/react").ForwardRefComponent<HTMLHeadingElement, HTMLMotionProps<"h1">>;
|
|
6
|
+
readonly h2: import("motion/react").ForwardRefComponent<HTMLHeadingElement, HTMLMotionProps<"h2">>;
|
|
7
|
+
readonly h3: import("motion/react").ForwardRefComponent<HTMLHeadingElement, HTMLMotionProps<"h3">>;
|
|
8
|
+
readonly h4: import("motion/react").ForwardRefComponent<HTMLHeadingElement, HTMLMotionProps<"h4">>;
|
|
9
|
+
readonly h5: import("motion/react").ForwardRefComponent<HTMLHeadingElement, HTMLMotionProps<"h5">>;
|
|
10
|
+
readonly h6: import("motion/react").ForwardRefComponent<HTMLHeadingElement, HTMLMotionProps<"h6">>;
|
|
11
|
+
readonly li: import("motion/react").ForwardRefComponent<HTMLLIElement, HTMLMotionProps<"li">>;
|
|
12
|
+
readonly p: import("motion/react").ForwardRefComponent<HTMLParagraphElement, HTMLMotionProps<"p">>;
|
|
13
|
+
readonly section: import("motion/react").ForwardRefComponent<HTMLElement, HTMLMotionProps<"section">>;
|
|
14
|
+
readonly span: import("motion/react").ForwardRefComponent<HTMLSpanElement, HTMLMotionProps<"span">>;
|
|
15
|
+
};
|
|
16
|
+
type MotionElementType = Extract<keyof DOMMotionComponents, keyof typeof motionElements>;
|
|
17
|
+
interface TypingAnimationProps extends Omit<MotionProps, 'children'> {
|
|
18
|
+
children?: string;
|
|
19
|
+
words?: string[];
|
|
20
|
+
className?: string;
|
|
21
|
+
duration?: number;
|
|
22
|
+
typeSpeed?: number;
|
|
23
|
+
deleteSpeed?: number;
|
|
24
|
+
delay?: number;
|
|
25
|
+
pauseDelay?: number;
|
|
26
|
+
loop?: boolean;
|
|
27
|
+
as?: MotionElementType;
|
|
28
|
+
startOnView?: boolean;
|
|
29
|
+
showCursor?: boolean;
|
|
30
|
+
blinkCursor?: boolean;
|
|
31
|
+
cursorStyle?: 'line' | 'block' | 'underscore';
|
|
32
|
+
/** Fires once when a non-looping animation finishes typing the last word. */
|
|
33
|
+
onComplete?: () => void;
|
|
34
|
+
}
|
|
35
|
+
export declare function TypingAnimation({ children, words, className, duration, typeSpeed, deleteSpeed, delay, pauseDelay, loop, as: Component, startOnView, showCursor, blinkCursor, cursorStyle, onComplete, ...props }: TypingAnimationProps): import("react").JSX.Element;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { m, useInView, } from 'motion/react';
|
|
4
|
+
import { useEffect, useMemo, useRef, useState, } from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils.js';
|
|
6
|
+
const motionElements = {
|
|
7
|
+
article: m.article,
|
|
8
|
+
div: m.div,
|
|
9
|
+
h1: m.h1,
|
|
10
|
+
h2: m.h2,
|
|
11
|
+
h3: m.h3,
|
|
12
|
+
h4: m.h4,
|
|
13
|
+
h5: m.h5,
|
|
14
|
+
h6: m.h6,
|
|
15
|
+
li: m.li,
|
|
16
|
+
p: m.p,
|
|
17
|
+
section: m.section,
|
|
18
|
+
span: m.span,
|
|
19
|
+
};
|
|
20
|
+
export function TypingAnimation({ children, words, className, duration = 100, typeSpeed, deleteSpeed, delay = 0, pauseDelay = 1000, loop = false, as: Component = 'span', startOnView = true, showCursor = true, blinkCursor = true, cursorStyle = 'line', onComplete, ...props }) {
|
|
21
|
+
const MotionComponent = motionElements[Component];
|
|
22
|
+
const [displayedText, setDisplayedText] = useState('');
|
|
23
|
+
const [currentWordIndex, setCurrentWordIndex] = useState(0);
|
|
24
|
+
const [currentCharIndex, setCurrentCharIndex] = useState(0);
|
|
25
|
+
const [phase, setPhase] = useState('typing');
|
|
26
|
+
const elementRef = useRef(null);
|
|
27
|
+
const isInView = useInView(elementRef, {
|
|
28
|
+
amount: 0.3,
|
|
29
|
+
once: true,
|
|
30
|
+
});
|
|
31
|
+
const wordsToAnimate = useMemo(() => words ?? (children ? [children] : []), [words, children]);
|
|
32
|
+
const hasMultipleWords = wordsToAnimate.length > 1;
|
|
33
|
+
const typingSpeed = typeSpeed ?? duration;
|
|
34
|
+
const deletingSpeed = deleteSpeed ?? typingSpeed / 2;
|
|
35
|
+
const shouldStart = startOnView ? isInView : true;
|
|
36
|
+
const animationSourceKey = useMemo(() => (words ? words.join('\u0000') : (children ?? '')), [words, children]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
setDisplayedText('');
|
|
39
|
+
setCurrentWordIndex(0);
|
|
40
|
+
setCurrentCharIndex(0);
|
|
41
|
+
setPhase('typing');
|
|
42
|
+
}, [animationSourceKey]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
let timeout = null;
|
|
45
|
+
if (shouldStart && wordsToAnimate.length > 0) {
|
|
46
|
+
const timeoutDelay = delay > 0 && displayedText === ''
|
|
47
|
+
? delay
|
|
48
|
+
: phase === 'typing'
|
|
49
|
+
? typingSpeed
|
|
50
|
+
: phase === 'deleting'
|
|
51
|
+
? deletingSpeed
|
|
52
|
+
: pauseDelay;
|
|
53
|
+
timeout = setTimeout(() => {
|
|
54
|
+
const currentWord = wordsToAnimate[currentWordIndex] || '';
|
|
55
|
+
const graphemes = Array.from(currentWord);
|
|
56
|
+
switch (phase) {
|
|
57
|
+
case 'typing':
|
|
58
|
+
if (currentCharIndex < graphemes.length) {
|
|
59
|
+
setDisplayedText(graphemes.slice(0, currentCharIndex + 1).join(''));
|
|
60
|
+
setCurrentCharIndex((prev) => prev + 1);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
if (hasMultipleWords || loop) {
|
|
64
|
+
const isLastWord = currentWordIndex === wordsToAnimate.length - 1;
|
|
65
|
+
if (!isLastWord || loop) {
|
|
66
|
+
setPhase('pause');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
case 'pause':
|
|
72
|
+
setPhase('deleting');
|
|
73
|
+
break;
|
|
74
|
+
case 'deleting':
|
|
75
|
+
if (currentCharIndex > 0) {
|
|
76
|
+
setDisplayedText(graphemes.slice(0, currentCharIndex - 1).join(''));
|
|
77
|
+
setCurrentCharIndex((prev) => prev - 1);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const nextIndex = (currentWordIndex + 1) % wordsToAnimate.length;
|
|
81
|
+
setCurrentWordIndex(nextIndex);
|
|
82
|
+
setPhase('typing');
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}, timeoutDelay);
|
|
87
|
+
}
|
|
88
|
+
return () => {
|
|
89
|
+
if (timeout !== null) {
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}, [
|
|
94
|
+
shouldStart,
|
|
95
|
+
phase,
|
|
96
|
+
currentCharIndex,
|
|
97
|
+
currentWordIndex,
|
|
98
|
+
displayedText,
|
|
99
|
+
wordsToAnimate,
|
|
100
|
+
hasMultipleWords,
|
|
101
|
+
loop,
|
|
102
|
+
typingSpeed,
|
|
103
|
+
deletingSpeed,
|
|
104
|
+
pauseDelay,
|
|
105
|
+
delay,
|
|
106
|
+
]);
|
|
107
|
+
const currentWordGraphemes = Array.from(wordsToAnimate[currentWordIndex] || '');
|
|
108
|
+
const isComplete = !loop &&
|
|
109
|
+
currentWordIndex === wordsToAnimate.length - 1 &&
|
|
110
|
+
currentCharIndex >= currentWordGraphemes.length &&
|
|
111
|
+
phase !== 'deleting';
|
|
112
|
+
const onCompleteRef = useRef(onComplete);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
onCompleteRef.current = onComplete;
|
|
115
|
+
}, [onComplete]);
|
|
116
|
+
const didFireCompleteRef = useRef(false);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
didFireCompleteRef.current = false;
|
|
119
|
+
}, [animationSourceKey]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!isComplete || didFireCompleteRef.current)
|
|
122
|
+
return;
|
|
123
|
+
didFireCompleteRef.current = true;
|
|
124
|
+
onCompleteRef.current?.();
|
|
125
|
+
}, [isComplete]);
|
|
126
|
+
const shouldShowCursor = showCursor &&
|
|
127
|
+
!isComplete &&
|
|
128
|
+
(hasMultipleWords || loop || currentCharIndex < currentWordGraphemes.length);
|
|
129
|
+
const getCursorChar = () => {
|
|
130
|
+
switch (cursorStyle) {
|
|
131
|
+
case 'block':
|
|
132
|
+
return '▌';
|
|
133
|
+
case 'underscore':
|
|
134
|
+
return '_';
|
|
135
|
+
case 'line':
|
|
136
|
+
default:
|
|
137
|
+
return '|';
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
return (_jsxs(MotionComponent, { ref: elementRef, className: cn('leading-20 tracking-[-0.02em]', Component === 'span' && 'inline-block', className), ...props, children: [displayedText, shouldShowCursor && (_jsx("span", { className: cn('inline-block', blinkCursor && 'animate-blink-cursor'), children: getCursorChar() }))] }));
|
|
141
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* `text` goes on the fallback itself: the primitive's fallback sets its own
|
|
4
|
+
* `text-sm`, which beat a size class on the root. Two initials at 14px
|
|
5
|
+
* semibold filled the 22px `sm` tile edge to edge and overflowed an 18px one.
|
|
6
|
+
* `xs` and `sm` hold ONE initial, as EntityAvatar does: `text-xs` (13px) is
|
|
7
|
+
* the floor of the type scale, and two letters at 13px still fill an 18px tile.
|
|
8
|
+
*/
|
|
9
|
+
declare const SIZE_MAP: {
|
|
10
|
+
readonly xs: {
|
|
11
|
+
readonly box: "size-5";
|
|
12
|
+
readonly text: "text-xs";
|
|
13
|
+
readonly initials: 1;
|
|
14
|
+
};
|
|
15
|
+
readonly sm: {
|
|
16
|
+
readonly box: "size-6";
|
|
17
|
+
readonly text: "text-xs";
|
|
18
|
+
readonly initials: 1;
|
|
19
|
+
};
|
|
20
|
+
readonly md: {
|
|
21
|
+
readonly box: "size-8";
|
|
22
|
+
readonly text: "text-xs";
|
|
23
|
+
readonly initials: 2;
|
|
24
|
+
};
|
|
25
|
+
readonly lg: {
|
|
26
|
+
readonly box: "size-10";
|
|
27
|
+
readonly text: "text-sm";
|
|
28
|
+
readonly initials: 2;
|
|
29
|
+
};
|
|
30
|
+
readonly xl: {
|
|
31
|
+
readonly box: "size-14";
|
|
32
|
+
readonly text: "text-base";
|
|
33
|
+
readonly initials: 2;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export type UserAvatarSize = keyof typeof SIZE_MAP;
|
|
37
|
+
export interface UserAvatarProps {
|
|
38
|
+
email: string;
|
|
39
|
+
name?: string | null;
|
|
40
|
+
avatarUrl?: string | null;
|
|
41
|
+
size?: UserAvatarSize;
|
|
42
|
+
className?: string;
|
|
43
|
+
ring?: boolean;
|
|
44
|
+
variant?: 'default' | 'primary';
|
|
45
|
+
}
|
|
46
|
+
export declare function UserAvatar({ email, name, avatarUrl, size, variant, className, ring, }: UserAvatarProps): React.JSX.Element;
|
|
47
|
+
export {};
|