@atmos.build/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/components/alert-dialog.d.ts +14 -0
- package/dist/components/alert-dialog.js +40 -0
- package/dist/components/at-search.d.ts +35 -0
- package/dist/components/at-search.js +57 -0
- package/dist/components/attachment-thumbnail-stack.d.ts +13 -0
- package/dist/components/attachment-thumbnail-stack.js +23 -0
- package/dist/components/audio-preview.d.ts +33 -0
- package/dist/components/audio-preview.js +176 -0
- package/dist/components/avatar-uploader.d.ts +29 -0
- package/dist/components/avatar-uploader.js +67 -0
- package/dist/components/avatar.d.ts +12 -0
- package/dist/components/avatar.js +38 -0
- package/dist/components/badge.d.ts +9 -0
- package/dist/components/badge.js +25 -0
- package/dist/components/button-group.d.ts +12 -0
- package/dist/components/button-group.js +29 -0
- package/dist/components/button.d.ts +10 -0
- package/dist/components/button.js +42 -0
- package/dist/components/calendar.d.ts +26 -0
- package/dist/components/calendar.js +154 -0
- package/dist/components/channel-list.d.ts +33 -0
- package/dist/components/channel-list.js +40 -0
- package/dist/components/charts/area-chart.d.ts +2 -0
- package/dist/components/charts/area-chart.js +5 -0
- package/dist/components/charts/bar-chart.d.ts +2 -0
- package/dist/components/charts/bar-chart.js +58 -0
- package/dist/components/charts/colors.d.ts +2 -0
- package/dist/components/charts/colors.js +42 -0
- package/dist/components/charts/hover.d.ts +20 -0
- package/dist/components/charts/hover.js +31 -0
- package/dist/components/charts/index.d.ts +10 -0
- package/dist/components/charts/index.js +9 -0
- package/dist/components/charts/layout.d.ts +35 -0
- package/dist/components/charts/layout.js +101 -0
- package/dist/components/charts/legend.d.ts +6 -0
- package/dist/components/charts/legend.js +7 -0
- package/dist/components/charts/line-area-chart.d.ts +4 -0
- package/dist/components/charts/line-area-chart.js +86 -0
- package/dist/components/charts/line-chart.d.ts +2 -0
- package/dist/components/charts/line-chart.js +5 -0
- package/dist/components/charts/pie-chart.d.ts +2 -0
- package/dist/components/charts/pie-chart.js +22 -0
- package/dist/components/charts/radar-chart.d.ts +2 -0
- package/dist/components/charts/radar-chart.js +29 -0
- package/dist/components/charts/radial-chart.d.ts +2 -0
- package/dist/components/charts/radial-chart.js +19 -0
- package/dist/components/charts/scatter-chart.d.ts +2 -0
- package/dist/components/charts/scatter-chart.js +16 -0
- package/dist/components/charts/story-data.d.ts +24 -0
- package/dist/components/charts/story-data.js +48 -0
- package/dist/components/charts/types.d.ts +43 -0
- package/dist/components/charts/types.js +1 -0
- package/dist/components/checkbox.d.ts +4 -0
- package/dist/components/checkbox.js +10 -0
- package/dist/components/chip.d.ts +25 -0
- package/dist/components/chip.js +36 -0
- package/dist/components/code-editor-theme.d.ts +19 -0
- package/dist/components/code-editor-theme.js +100 -0
- package/dist/components/collapsible-section-header.d.ts +82 -0
- package/dist/components/collapsible-section-header.js +82 -0
- package/dist/components/collection-view/collection-view-group-settings.d.ts +73 -0
- package/dist/components/collection-view/collection-view-group-settings.js +55 -0
- package/dist/components/collection-view/collection-view-settings.d.ts +115 -0
- package/dist/components/collection-view/collection-view-settings.js +146 -0
- package/dist/components/collection-view/collection-view.d.ts +87 -0
- package/dist/components/collection-view/collection-view.js +95 -0
- package/dist/components/collection-view/index.d.ts +6 -0
- package/dist/components/collection-view/index.js +6 -0
- package/dist/components/collection-view/use-collection-detail-navigation.d.ts +11 -0
- package/dist/components/collection-view/use-collection-detail-navigation.js +12 -0
- package/dist/components/collection-view/use-collection-escape.d.ts +9 -0
- package/dist/components/collection-view/use-collection-escape.js +20 -0
- package/dist/components/collection-view/use-collection-selection.d.ts +42 -0
- package/dist/components/collection-view/use-collection-selection.js +203 -0
- package/dist/components/color-picker.d.ts +18 -0
- package/dist/components/color-picker.js +77 -0
- package/dist/components/color-swatch-list.d.ts +16 -0
- package/dist/components/color-swatch-list.js +60 -0
- package/dist/components/combobox/combobox.d.ts +4 -0
- package/dist/components/combobox/combobox.js +70 -0
- package/dist/components/combobox/compound.d.ts +68 -0
- package/dist/components/combobox/compound.js +151 -0
- package/dist/components/combobox/context.d.ts +34 -0
- package/dist/components/combobox/context.js +123 -0
- package/dist/components/combobox/create-step.d.ts +12 -0
- package/dist/components/combobox/create-step.js +8 -0
- package/dist/components/combobox/index.d.ts +10 -0
- package/dist/components/combobox/index.js +5 -0
- package/dist/components/combobox/inline-create-form.d.ts +24 -0
- package/dist/components/combobox/inline-create-form.js +43 -0
- package/dist/components/combobox/types.d.ts +57 -0
- package/dist/components/combobox/types.js +10 -0
- package/dist/components/command.d.ts +35 -0
- package/dist/components/command.js +76 -0
- package/dist/components/comment-pin.d.ts +13 -0
- package/dist/components/comment-pin.js +33 -0
- package/dist/components/confidence-meter.d.ts +11 -0
- package/dist/components/confidence-meter.js +24 -0
- package/dist/components/context-menu.d.ts +31 -0
- package/dist/components/context-menu.js +54 -0
- package/dist/components/copy-button.d.ts +12 -0
- package/dist/components/copy-button.js +32 -0
- package/dist/components/date-picker.d.ts +29 -0
- package/dist/components/date-picker.js +86 -0
- package/dist/components/date-tile.d.ts +11 -0
- package/dist/components/date-tile.js +16 -0
- package/dist/components/date-time-picker.d.ts +25 -0
- package/dist/components/date-time-picker.js +153 -0
- package/dist/components/detail-breadcrumb-utils.d.ts +2 -0
- package/dist/components/detail-breadcrumb-utils.js +18 -0
- package/dist/components/detail-nav-bar.d.ts +28 -0
- package/dist/components/detail-nav-bar.js +28 -0
- package/dist/components/detail-top-bar.d.ts +49 -0
- package/dist/components/detail-top-bar.js +41 -0
- package/dist/components/dialog.d.ts +22 -0
- package/dist/components/dialog.js +51 -0
- package/dist/components/dropdown-menu.d.ts +29 -0
- package/dist/components/dropdown-menu.js +52 -0
- package/dist/components/editable-doc-title.d.ts +17 -0
- package/dist/components/editable-doc-title.js +147 -0
- package/dist/components/empty-state.d.ts +12 -0
- package/dist/components/empty-state.js +6 -0
- package/dist/components/filter-condition-editor.d.ts +18 -0
- package/dist/components/filter-condition-editor.js +102 -0
- package/dist/components/form-dialog.d.ts +30 -0
- package/dist/components/form-dialog.js +52 -0
- package/dist/components/formula-editor.d.ts +18 -0
- package/dist/components/formula-editor.js +154 -0
- package/dist/components/hover-card.d.ts +52 -0
- package/dist/components/hover-card.js +51 -0
- package/dist/components/icon-button.d.ts +15 -0
- package/dist/components/icon-button.js +18 -0
- package/dist/components/inline-alert.d.ts +11 -0
- package/dist/components/inline-alert.js +17 -0
- package/dist/components/input.d.ts +3 -0
- package/dist/components/input.js +7 -0
- package/dist/components/kanban-board.d.ts +27 -0
- package/dist/components/kanban-board.js +145 -0
- package/dist/components/kbd.d.ts +3 -0
- package/dist/components/kbd.js +8 -0
- package/dist/components/label.d.ts +4 -0
- package/dist/components/label.js +9 -0
- package/dist/components/markdown-editor/index.d.ts +4 -0
- package/dist/components/markdown-editor/index.js +2 -0
- package/dist/components/markdown-editor/markdown-editor.d.ts +16 -0
- package/dist/components/markdown-editor/markdown-editor.js +53 -0
- package/dist/components/markdown-editor/serialize.d.ts +12 -0
- package/dist/components/markdown-editor/serialize.js +122 -0
- package/dist/components/markdown-editor/vim.d.ts +8 -0
- package/dist/components/markdown-editor/vim.js +394 -0
- package/dist/components/metric-grid.d.ts +41 -0
- package/dist/components/metric-grid.js +39 -0
- package/dist/components/microphone-priority.d.ts +29 -0
- package/dist/components/microphone-priority.js +15 -0
- package/dist/components/middle-truncate.d.ts +6 -0
- package/dist/components/middle-truncate.js +15 -0
- package/dist/components/multi-combobox.d.ts +48 -0
- package/dist/components/multi-combobox.js +221 -0
- package/dist/components/naut-avatar-engine.d.ts +83 -0
- package/dist/components/naut-avatar-engine.js +803 -0
- package/dist/components/naut-avatar.d.ts +31 -0
- package/dist/components/naut-avatar.js +108 -0
- package/dist/components/navigable-list.d.ts +4 -0
- package/dist/components/navigable-list.js +20 -0
- package/dist/components/note-icon-picker.d.ts +20 -0
- package/dist/components/note-icon-picker.js +84 -0
- package/dist/components/note-icon.d.ts +690 -0
- package/dist/components/note-icon.js +353 -0
- package/dist/components/note-row.d.ts +33 -0
- package/dist/components/note-row.js +29 -0
- package/dist/components/overlapping-dots.d.ts +10 -0
- package/dist/components/overlapping-dots.js +13 -0
- package/dist/components/overview-card.d.ts +12 -0
- package/dist/components/overview-card.js +9 -0
- package/dist/components/panel-header.d.ts +8 -0
- package/dist/components/panel-header.js +6 -0
- package/dist/components/panel-section-states.d.ts +9 -0
- package/dist/components/panel-section-states.js +10 -0
- package/dist/components/person-avatar.d.ts +10 -0
- package/dist/components/person-avatar.js +23 -0
- package/dist/components/photo-avatar.d.ts +11 -0
- package/dist/components/photo-avatar.js +24 -0
- package/dist/components/picker-field.d.ts +23 -0
- package/dist/components/picker-field.js +24 -0
- package/dist/components/popover-stack.d.ts +26 -0
- package/dist/components/popover-stack.js +89 -0
- package/dist/components/popover.d.ts +12 -0
- package/dist/components/popover.js +31 -0
- package/dist/components/portal-login-barrier.d.ts +12 -0
- package/dist/components/portal-login-barrier.js +41 -0
- package/dist/components/presence-dot.d.ts +8 -0
- package/dist/components/presence-dot.js +15 -0
- package/dist/components/presence-overlay.d.ts +27 -0
- package/dist/components/presence-overlay.js +34 -0
- package/dist/components/property-list.d.ts +64 -0
- package/dist/components/property-list.js +313 -0
- package/dist/components/property-panel.d.ts +54 -0
- package/dist/components/property-panel.js +87 -0
- package/dist/components/property-selector.d.ts +36 -0
- package/dist/components/property-selector.js +141 -0
- package/dist/components/provenance-chip.d.ts +12 -0
- package/dist/components/provenance-chip.js +17 -0
- package/dist/components/qr-code.d.ts +13 -0
- package/dist/components/qr-code.js +42 -0
- package/dist/components/qr-encode.d.ts +2 -0
- package/dist/components/qr-encode.js +469 -0
- package/dist/components/quantity-stepper.d.ts +25 -0
- package/dist/components/quantity-stepper.js +43 -0
- package/dist/components/radio-group.d.ts +5 -0
- package/dist/components/radio-group.js +13 -0
- package/dist/components/rename-dialog.d.ts +19 -0
- package/dist/components/rename-dialog.js +35 -0
- package/dist/components/row-surface.d.ts +1 -0
- package/dist/components/row-surface.js +5 -0
- package/dist/components/row-trailing.d.ts +30 -0
- package/dist/components/row-trailing.js +59 -0
- package/dist/components/run-status-badge.d.ts +16 -0
- package/dist/components/run-status-badge.js +43 -0
- package/dist/components/scroll-area.d.ts +5 -0
- package/dist/components/scroll-area.js +12 -0
- package/dist/components/select.d.ts +15 -0
- package/dist/components/select.js +39 -0
- package/dist/components/selectable-card.d.ts +33 -0
- package/dist/components/selectable-card.js +66 -0
- package/dist/components/separator.d.ts +3 -0
- package/dist/components/separator.js +8 -0
- package/dist/components/settings-dialog-shell.d.ts +46 -0
- package/dist/components/settings-dialog-shell.js +48 -0
- package/dist/components/shimmer-text.d.ts +8 -0
- package/dist/components/shimmer-text.js +26 -0
- package/dist/components/side-panel-tokens.d.ts +27 -0
- package/dist/components/side-panel-tokens.js +26 -0
- package/dist/components/side-panel.d.ts +94 -0
- package/dist/components/side-panel.js +106 -0
- package/dist/components/skeleton.d.ts +2 -0
- package/dist/components/skeleton.js +6 -0
- package/dist/components/sortable.d.ts +65 -0
- package/dist/components/sortable.js +110 -0
- package/dist/components/spec-strip.d.ts +18 -0
- package/dist/components/spec-strip.js +15 -0
- package/dist/components/speech-playback.d.ts +25 -0
- package/dist/components/speech-playback.js +35 -0
- package/dist/components/sql-editor.d.ts +13 -0
- package/dist/components/sql-editor.js +228 -0
- package/dist/components/stat-tile.d.ts +7 -0
- package/dist/components/stat-tile.js +6 -0
- package/dist/components/status-badge.d.ts +17 -0
- package/dist/components/status-badge.js +8 -0
- package/dist/components/status-tone.d.ts +9 -0
- package/dist/components/status-tone.js +10 -0
- package/dist/components/sticky-header-fade.d.ts +8 -0
- package/dist/components/sticky-header-fade.js +11 -0
- package/dist/components/swatch-button.d.ts +14 -0
- package/dist/components/swatch-button.js +25 -0
- package/dist/components/switch.d.ts +6 -0
- package/dist/components/switch.js +9 -0
- package/dist/components/system-load/index.d.ts +6 -0
- package/dist/components/system-load/index.js +6 -0
- package/dist/components/system-load/load-block.d.ts +20 -0
- package/dist/components/system-load/load-block.js +23 -0
- package/dist/components/system-load/load-grid.d.ts +16 -0
- package/dist/components/system-load/load-grid.js +23 -0
- package/dist/components/system-load/load-meter.d.ts +25 -0
- package/dist/components/system-load/load-meter.js +29 -0
- package/dist/components/system-load/load-tone.d.ts +21 -0
- package/dist/components/system-load/load-tone.js +29 -0
- package/dist/components/system-load/meter-row.d.ts +31 -0
- package/dist/components/system-load/meter-row.js +29 -0
- package/dist/components/system-load/resource-meter.d.ts +19 -0
- package/dist/components/system-load/resource-meter.js +15 -0
- package/dist/components/tab-title-bar.d.ts +12 -0
- package/dist/components/tab-title-bar.js +9 -0
- package/dist/components/table.d.ts +10 -0
- package/dist/components/table.js +29 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +97 -0
- package/dist/components/template-chip-input.d.ts +36 -0
- package/dist/components/template-chip-input.js +271 -0
- package/dist/components/template-input.d.ts +24 -0
- package/dist/components/template-input.js +57 -0
- package/dist/components/terminal-spinner.d.ts +11 -0
- package/dist/components/terminal-spinner.js +48 -0
- package/dist/components/textarea.d.ts +3 -0
- package/dist/components/textarea.js +7 -0
- package/dist/components/time-column.d.ts +24 -0
- package/dist/components/time-column.js +60 -0
- package/dist/components/timestamped-transcript.d.ts +38 -0
- package/dist/components/timestamped-transcript.js +29 -0
- package/dist/components/timezone-picker.d.ts +22 -0
- package/dist/components/timezone-picker.js +60 -0
- package/dist/components/tooltip.d.ts +11 -0
- package/dist/components/tooltip.js +29 -0
- package/dist/components/transcription-overlay.d.ts +47 -0
- package/dist/components/transcription-overlay.js +30 -0
- package/dist/components/usage-bar.d.ts +17 -0
- package/dist/components/usage-bar.js +26 -0
- package/dist/components/view-query-bar.d.ts +13 -0
- package/dist/components/view-query-bar.js +13 -0
- package/dist/components/view-query-controls.d.ts +159 -0
- package/dist/components/view-query-controls.js +109 -0
- package/dist/components/view-save-menu.d.ts +20 -0
- package/dist/components/view-save-menu.js +13 -0
- package/dist/components/view-sort-editor.d.ts +40 -0
- package/dist/components/view-sort-editor.js +47 -0
- package/dist/components/voice-line.d.ts +37 -0
- package/dist/components/voice-line.js +293 -0
- package/dist/components/weekly-schedule-editor.d.ts +15 -0
- package/dist/components/weekly-schedule-editor.js +28 -0
- package/dist/flow/canvas/canvas-frame.d.ts +160 -0
- package/dist/flow/canvas/canvas-frame.js +521 -0
- package/dist/flow/canvas/collaborator-layer.d.ts +12 -0
- package/dist/flow/canvas/collaborator-layer.js +43 -0
- package/dist/flow/canvas/connector-edge.d.ts +22 -0
- package/dist/flow/canvas/connector-edge.js +61 -0
- package/dist/flow/canvas/editor-context.d.ts +23 -0
- package/dist/flow/canvas/editor-context.js +6 -0
- package/dist/flow/canvas/graph-core.d.ts +60 -0
- package/dist/flow/canvas/graph-core.js +296 -0
- package/dist/flow/canvas/minimap-sticky-overlay.d.ts +4 -0
- package/dist/flow/canvas/minimap-sticky-overlay.js +45 -0
- package/dist/flow/canvas/node-card.d.ts +46 -0
- package/dist/flow/canvas/node-card.js +56 -0
- package/dist/flow/canvas/node-library.d.ts +40 -0
- package/dist/flow/canvas/node-library.js +54 -0
- package/dist/flow/canvas/sticky-notes.d.ts +25 -0
- package/dist/flow/canvas/sticky-notes.js +101 -0
- package/dist/flow/collaborative-graph-presence.d.ts +28 -0
- package/dist/flow/collaborative-graph-presence.js +55 -0
- package/dist/flow/collaborative-graph-sync.d.ts +40 -0
- package/dist/flow/collaborative-graph-sync.js +127 -0
- package/dist/flow/editor/adapter.d.ts +64 -0
- package/dist/flow/editor/adapter.js +1 -0
- package/dist/flow/editor/command-registry.d.ts +10 -0
- package/dist/flow/editor/command-registry.js +35 -0
- package/dist/flow/editor/dag-canvas.d.ts +18 -0
- package/dist/flow/editor/dag-canvas.js +33 -0
- package/dist/flow/editor/flow-editor.d.ts +54 -0
- package/dist/flow/editor/flow-editor.js +199 -0
- package/dist/flow/editor/issues-popover.d.ts +11 -0
- package/dist/flow/editor/issues-popover.js +70 -0
- package/dist/flow/editor/library-rail.d.ts +19 -0
- package/dist/flow/editor/library-rail.js +25 -0
- package/dist/flow/editor/make-controller.d.ts +18 -0
- package/dist/flow/editor/make-controller.js +44 -0
- package/dist/flow/editor/node-chip.d.ts +2 -0
- package/dist/flow/editor/node-chip.js +16 -0
- package/dist/flow/editor/save-publish-bar.d.ts +27 -0
- package/dist/flow/editor/save-publish-bar.js +14 -0
- package/dist/flow/editor/settings-dialog.d.ts +11 -0
- package/dist/flow/editor/settings-dialog.js +11 -0
- package/dist/flow/editor/settings-shell.d.ts +21 -0
- package/dist/flow/editor/settings-shell.js +20 -0
- package/dist/flow/editor/side-rail.d.ts +22 -0
- package/dist/flow/editor/side-rail.js +15 -0
- package/dist/flow/editor/use-dag-editor.d.ts +94 -0
- package/dist/flow/editor/use-dag-editor.js +502 -0
- package/dist/flow/index.d.ts +25 -0
- package/dist/flow/index.js +25 -0
- package/dist/flow/presence-motion.d.ts +10 -0
- package/dist/flow/presence-motion.js +88 -0
- package/dist/flow/styles.css +1 -0
- package/dist/flow/use-collaborative-graph.d.ts +61 -0
- package/dist/flow/use-collaborative-graph.js +396 -0
- package/dist/flow/use-debounced-value.d.ts +1 -0
- package/dist/flow/use-debounced-value.js +10 -0
- package/dist/hooks/use-detail-nav.d.ts +11 -0
- package/dist/hooks/use-detail-nav.js +49 -0
- package/dist/hooks/use-flush-on-hide.d.ts +1 -0
- package/dist/hooks/use-flush-on-hide.js +21 -0
- package/dist/hooks/use-media-query.d.ts +1 -0
- package/dist/hooks/use-media-query.js +20 -0
- package/dist/hooks/use-virtualizer.d.ts +1 -0
- package/dist/hooks/use-virtualizer.js +1 -0
- package/dist/index.d.ts +139 -0
- package/dist/index.js +139 -0
- package/dist/lib/brand-style.d.ts +6 -0
- package/dist/lib/brand-style.js +16 -0
- package/dist/lib/cn.d.ts +2 -0
- package/dist/lib/cn.js +5 -0
- package/dist/lib/composited-glint.d.ts +7 -0
- package/dist/lib/composited-glint.js +98 -0
- package/dist/lib/date-time-format.d.ts +2 -0
- package/dist/lib/date-time-format.js +21 -0
- package/dist/lib/disclosure-state.d.ts +11 -0
- package/dist/lib/disclosure-state.js +22 -0
- package/dist/lib/label-swatches.d.ts +10 -0
- package/dist/lib/label-swatches.js +24 -0
- package/dist/lib/local-ui-state.d.ts +10 -0
- package/dist/lib/local-ui-state.js +95 -0
- package/dist/lib/parse-loose-datetime.d.ts +1 -0
- package/dist/lib/parse-loose-datetime.js +180 -0
- package/dist/lib/parse-loose-time.d.ts +6 -0
- package/dist/lib/parse-loose-time.js +53 -0
- package/dist/lib/parse-loose.d.ts +1 -0
- package/dist/lib/parse-loose.js +170 -0
- package/dist/lib/react-19-jsx.d.ts +8 -0
- package/dist/lib/react-19-jsx.js +1 -0
- package/dist/lib/view-chrome.d.ts +5 -0
- package/dist/lib/view-chrome.js +5 -0
- package/dist/lib/view-query/config.d.ts +26 -0
- package/dist/lib/view-query/config.js +162 -0
- package/dist/lib/view-query/evaluate.d.ts +11 -0
- package/dist/lib/view-query/evaluate.js +196 -0
- package/dist/lib/view-query/group-order.d.ts +25 -0
- package/dist/lib/view-query/group-order.js +49 -0
- package/dist/lib/view-query/index.d.ts +6 -0
- package/dist/lib/view-query/index.js +6 -0
- package/dist/lib/view-query/model.d.ts +34 -0
- package/dist/lib/view-query/model.js +88 -0
- package/dist/lib/view-query/sort.d.ts +7 -0
- package/dist/lib/view-query/sort.js +30 -0
- package/dist/lib/view-query/types.d.ts +70 -0
- package/dist/lib/view-query/types.js +12 -0
- package/dist/lib/weekly-schedule.d.ts +8 -0
- package/dist/lib/weekly-schedule.js +10 -0
- package/dist/mcp-app/index.d.ts +4 -0
- package/dist/mcp-app/index.js +3 -0
- package/dist/mcp-app/shadcn.css +629 -0
- package/dist/mcp-app/styles.css +72 -0
- package/dist/mcp-app/theme.d.ts +2 -0
- package/dist/mcp-app/theme.js +62 -0
- package/dist/mcp-app/use-atmos-mcp-app.d.ts +109 -0
- package/dist/mcp-app/use-atmos-mcp-app.js +30 -0
- package/dist/mcp-app/vite.d.ts +7 -0
- package/dist/mcp-app/vite.js +30 -0
- package/dist/tokens/index.d.ts +7 -0
- package/dist/tokens/index.js +7 -0
- package/dist/tokens/semantic.css +110 -0
- package/dist/tokens/tokens.css +195 -0
- package/package.json +98 -0
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# atmOS UI
|
|
2
|
+
|
|
3
|
+
`@atmos/ui` is the component package for atmOS product surfaces and MCP Apps. Its root export contains the complete base component catalog.
|
|
4
|
+
|
|
5
|
+
## MCP Apps
|
|
6
|
+
|
|
7
|
+
An extension imports components from the normal package entry:
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Button, Command, Dialog, DialogContent, Table } from '@atmos/ui'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
MCP-specific host integration is available from a subpath of the same package:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useAtmosMcpApp } from '@atmos/ui/mcp-app'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The build configuration and stylesheet use their own subpaths:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { atmosMcpAppViteConfig } from '@atmos/ui/mcp-app/vite'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```css
|
|
26
|
+
@import '@atmos/ui/mcp-app/styles.css';
|
|
27
|
+
|
|
28
|
+
@source './**/*.{ts,tsx}';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The stylesheet scans the complete component catalog, so any component exported by `@atmos/ui` receives its normal Tailwind styles without maintaining a second MCP-specific allowlist.
|
|
32
|
+
|
|
33
|
+
Inside this repository, extension UI packages resolve `@atmos/ui` through the pnpm workspace. External extensions install an exact public npm version during authoring:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install --save-exact @atmos/ui react@19 react-dom@19
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The compiled `dist/app.js` and `dist/app.css` are committed with the extension and inlined into its MCP App resource. Attaching or opening an extension therefore never installs packages or executes a package manager.
|
|
40
|
+
|
|
41
|
+
Flow surfaces import their stylesheet explicitly so the JavaScript entry remains server-rendering compatible:
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
@import '@atmos/ui/flow/styles.css';
|
|
45
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
|
|
3
|
+
declare function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>): React.JSX.Element;
|
|
5
|
+
declare function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>): React.JSX.Element;
|
|
6
|
+
declare function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>): React.JSX.Element;
|
|
7
|
+
declare function AlertDialogContent({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content>): React.JSX.Element;
|
|
8
|
+
declare function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
9
|
+
declare function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
10
|
+
declare function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>): React.JSX.Element;
|
|
11
|
+
declare function AlertDialogDescription({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Description>): React.JSX.Element;
|
|
12
|
+
declare function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action>): React.JSX.Element;
|
|
13
|
+
declare function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>): React.JSX.Element;
|
|
14
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { buttonVariants } from "./button.js";
|
|
7
|
+
function AlertDialog({ ...props }) {
|
|
8
|
+
return _jsx(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
|
|
9
|
+
}
|
|
10
|
+
function AlertDialogTrigger({ ...props }) {
|
|
11
|
+
return _jsx(AlertDialogPrimitive.Trigger, { "data-slot": "alert-dialog-trigger", ...props });
|
|
12
|
+
}
|
|
13
|
+
function AlertDialogPortal({ ...props }) {
|
|
14
|
+
return _jsx(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props });
|
|
15
|
+
}
|
|
16
|
+
function AlertDialogOverlay({ className, ...props }) {
|
|
17
|
+
return (_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "alert-dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs duration-100 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className), ...props }));
|
|
18
|
+
}
|
|
19
|
+
function AlertDialogContent({ className, ...props }) {
|
|
20
|
+
return (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Content, { "data-slot": "alert-dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props })] }));
|
|
21
|
+
}
|
|
22
|
+
function AlertDialogHeader({ className, ...props }) {
|
|
23
|
+
return (_jsx("div", { "data-slot": "alert-dialog-header", className: cn("flex flex-col gap-2", className), ...props }));
|
|
24
|
+
}
|
|
25
|
+
function AlertDialogFooter({ className, ...props }) {
|
|
26
|
+
return (_jsx("div", { "data-slot": "alert-dialog-footer", className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props }));
|
|
27
|
+
}
|
|
28
|
+
function AlertDialogTitle({ className, ...props }) {
|
|
29
|
+
return (_jsx(AlertDialogPrimitive.Title, { "data-slot": "alert-dialog-title", className: cn("font-heading text-base leading-none font-medium", className), ...props }));
|
|
30
|
+
}
|
|
31
|
+
function AlertDialogDescription({ className, ...props }) {
|
|
32
|
+
return (_jsx(AlertDialogPrimitive.Description, { "data-slot": "alert-dialog-description", className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
33
|
+
}
|
|
34
|
+
function AlertDialogAction({ className, ...props }) {
|
|
35
|
+
return (_jsx(AlertDialogPrimitive.Action, { className: cn(buttonVariants({ size: "sm" }), className), ...props }));
|
|
36
|
+
}
|
|
37
|
+
function AlertDialogCancel({ className, ...props }) {
|
|
38
|
+
return (_jsx(AlertDialogPrimitive.Cancel, { className: cn(buttonVariants({ variant: "outline", size: "sm" }), className), ...props }));
|
|
39
|
+
}
|
|
40
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { UseItemsResult } from './combobox/types.js';
|
|
3
|
+
export interface AtSearchLabels {
|
|
4
|
+
placeholder: string;
|
|
5
|
+
resultsLabel: string;
|
|
6
|
+
loadFailed: string;
|
|
7
|
+
typeToSearch: string;
|
|
8
|
+
noMatches: string;
|
|
9
|
+
teamMembers: (count: number) => string;
|
|
10
|
+
memberBadge: string;
|
|
11
|
+
}
|
|
12
|
+
export type AtSearchKind = 'member' | 'external' | 'team';
|
|
13
|
+
export type AtSearchItem = {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
sublabel?: string;
|
|
17
|
+
kind: AtSearchKind;
|
|
18
|
+
memberCount?: number | null;
|
|
19
|
+
};
|
|
20
|
+
export type AtSearchProps<V> = {
|
|
21
|
+
useItems: (query: string) => UseItemsResult<AtSearchItem>;
|
|
22
|
+
toValues: (item: AtSearchItem) => V[];
|
|
23
|
+
onAdd: (value: V) => void;
|
|
24
|
+
onCreateFromQuery?: (query: string) => V | null;
|
|
25
|
+
canCreateFromQuery?: (query: string) => boolean;
|
|
26
|
+
createLabel?: string;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
contentClassName?: string;
|
|
30
|
+
triggerClassName?: string;
|
|
31
|
+
open?: boolean;
|
|
32
|
+
onOpenChange?: (open: boolean) => void;
|
|
33
|
+
labels?: Partial<AtSearchLabels>;
|
|
34
|
+
};
|
|
35
|
+
export declare function AtSearch<V>(props: AtSearchProps<V>): ReactNode;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { Building2, Users } from 'lucide-react';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
import { Avatar } from './avatar.js';
|
|
7
|
+
import { ComboboxContent, ComboboxCreate, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxRoot, ComboboxTrigger, } from './combobox/compound.js';
|
|
8
|
+
import { useCombobox } from './combobox/context.js';
|
|
9
|
+
const DEFAULT_LABELS = {
|
|
10
|
+
placeholder: 'Search people and teams',
|
|
11
|
+
resultsLabel: 'Search results',
|
|
12
|
+
loadFailed: 'Could not load results',
|
|
13
|
+
typeToSearch: 'Type to search',
|
|
14
|
+
noMatches: 'No matches',
|
|
15
|
+
teamMembers: (count) => `${count} members`,
|
|
16
|
+
memberBadge: 'Member',
|
|
17
|
+
};
|
|
18
|
+
function avatarHue(key) {
|
|
19
|
+
let hash = 0;
|
|
20
|
+
for (let i = 0; i < key.length; i += 1) {
|
|
21
|
+
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
22
|
+
}
|
|
23
|
+
return `oklch(65% 0.12 ${hash % 360})`;
|
|
24
|
+
}
|
|
25
|
+
export function AtSearch(props) {
|
|
26
|
+
const { children, triggerClassName, contentClassName, open, onOpenChange, ...body } = props;
|
|
27
|
+
return (_jsxs(ComboboxRoot, { open: open, onOpenChange: onOpenChange, children: [_jsx(ComboboxTrigger, { className: triggerClassName, children: children }), _jsx(ComboboxContent, { className: contentClassName, children: _jsx(Body, { ...body }) })] }));
|
|
28
|
+
}
|
|
29
|
+
function Body({ useItems, toValues, onAdd, onCreateFromQuery, canCreateFromQuery, createLabel, placeholder, labels: labelsProp, }) {
|
|
30
|
+
const labels = { ...DEFAULT_LABELS, ...labelsProp };
|
|
31
|
+
const { query, debouncedQuery, setOpen } = useCombobox();
|
|
32
|
+
const result = useItems(debouncedQuery);
|
|
33
|
+
const items = useMemo(() => result.data ?? [], [result.data]);
|
|
34
|
+
const trimmedQuery = query.trim();
|
|
35
|
+
const isPending = result.isLoading || trimmedQuery !== debouncedQuery.trim();
|
|
36
|
+
const showCreate = !!onCreateFromQuery &&
|
|
37
|
+
trimmedQuery.length > 0 &&
|
|
38
|
+
(canCreateFromQuery ? canCreateFromQuery(trimmedQuery) : true) &&
|
|
39
|
+
!items.some((i) => i.label.toLowerCase() === trimmedQuery.toLowerCase());
|
|
40
|
+
const addItem = (item) => {
|
|
41
|
+
for (const value of toValues(item))
|
|
42
|
+
onAdd(value);
|
|
43
|
+
setOpen(false);
|
|
44
|
+
};
|
|
45
|
+
const create = () => {
|
|
46
|
+
const value = onCreateFromQuery?.(trimmedQuery);
|
|
47
|
+
if (value != null)
|
|
48
|
+
onAdd(value);
|
|
49
|
+
setOpen(false);
|
|
50
|
+
};
|
|
51
|
+
return (_jsxs(_Fragment, { children: [_jsx(ComboboxInput, { placeholder: placeholder ?? labels.placeholder, triggerKey: "@", loading: isPending }), _jsxs(ComboboxList, { label: labels.resultsLabel, children: [result.isError && items.length === 0 ? (_jsx(ComboboxEmpty, { children: labels.loadFailed })) : items.length === 0 && !showCreate ? (_jsx(ComboboxEmpty, { pending: isPending, children: trimmedQuery.length === 0 ? labels.typeToSearch : labels.noMatches })) : (items.map((item) => (_jsx(ComboboxItem, { value: item.id, onSelect: () => addItem(item), children: ({ active }) => _jsx(Row, { item: item, active: active, labels: labels }) }, item.id)))), showCreate && createLabel ? (_jsx(ComboboxCreate, { query: trimmedQuery, onCreate: create, label: createLabel })) : null] })] }));
|
|
52
|
+
}
|
|
53
|
+
function Row({ item, active, labels, }) {
|
|
54
|
+
const isTeam = item.kind === 'team';
|
|
55
|
+
const hue = avatarHue(item.sublabel ?? item.label);
|
|
56
|
+
return (_jsxs("span", { className: cn('flex w-full min-w-0 items-center gap-2', active && 'text-accent-foreground'), children: [_jsx(Avatar, { size: "sm", name: item.label, color: hue, "aria-label": item.label, className: "shrink-0", children: isTeam ? _jsx(Users, { className: "size-3", strokeWidth: 2, "aria-hidden": true }) : undefined }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "text-foreground truncate text-[12.5px]", children: item.label }), item.sublabel ? (_jsx("span", { className: "text-muted-foreground truncate font-mono text-[10.5px]", children: item.sublabel })) : null] }), isTeam ? (_jsxs("span", { className: "bg-muted text-muted-foreground ml-auto inline-flex items-center gap-1 rounded-sm px-1.5 py-0.5 text-[10.5px]", children: [_jsx(Building2, { className: "size-2.5", strokeWidth: 1.5, "aria-hidden": true }), labels.teamMembers(item.memberCount ?? 0)] })) : item.kind === 'member' ? (_jsx("span", { className: "bg-primary/10 text-primary ml-auto rounded-sm px-1.5 py-0.5 text-[10.5px]", children: labels.memberBadge })) : null] }));
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
declare const TILE_SIZE: {
|
|
3
|
+
readonly sm: "size-7";
|
|
4
|
+
readonly md: "size-9";
|
|
5
|
+
};
|
|
6
|
+
export interface AttachmentThumbnailStackProps extends React.ComponentProps<'span'> {
|
|
7
|
+
size?: keyof typeof TILE_SIZE;
|
|
8
|
+
/** Rendered as a trailing `+n` tile when the caller shows fewer thumbnails than it has items. */
|
|
9
|
+
overflowCount?: number;
|
|
10
|
+
label?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const AttachmentThumbnailStack: React.ForwardRefExoticComponent<Omit<AttachmentThumbnailStackProps, "ref"> & React.RefAttributes<HTMLSpanElement>>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../lib/cn.js';
|
|
5
|
+
const TILE_SIZE = { sm: 'size-7', md: 'size-9' };
|
|
6
|
+
const TILE_OVERLAP = { sm: '-ml-3', md: '-ml-4' };
|
|
7
|
+
// Cycled so a stack of any length keeps the hand-dropped look without a random seed.
|
|
8
|
+
const TILT_DEGREES = [-7, 5, -3, 8, -5, 4];
|
|
9
|
+
const SHIFT_PIXELS = [0, -1.5, 1.5, -1, 1, -2];
|
|
10
|
+
export const AttachmentThumbnailStack = React.forwardRef(function AttachmentThumbnailStack({ size = 'sm', overflowCount = 0, label, className, children, ...props }, ref) {
|
|
11
|
+
const tiles = React.Children.toArray(children).filter(React.isValidElement);
|
|
12
|
+
if (tiles.length === 0 && overflowCount <= 0)
|
|
13
|
+
return null;
|
|
14
|
+
const total = tiles.length + (overflowCount > 0 ? 1 : 0);
|
|
15
|
+
return (_jsxs("span", { ref: ref, "data-slot": "attachment-thumbnail-stack", role: "img", "aria-label": label, className: cn('group/thumbnail-stack flex shrink-0 items-center', className), ...props, children: [tiles.map((tile, index) => (_jsx(ThumbnailTile, { size: size, index: index, total: total, children: tile }, tile.key ?? index))), overflowCount > 0 ? (_jsx(ThumbnailTile, { size: size, index: tiles.length, total: total, children: _jsxs("span", { className: "text-muted-foreground flex size-full items-center justify-center text-[10px] tabular-nums", children: ["+", overflowCount] }) })) : null] }));
|
|
16
|
+
});
|
|
17
|
+
function ThumbnailTile({ size, index, total, children, }) {
|
|
18
|
+
return (_jsx("span", { "data-slot": "attachment-thumbnail", style: {
|
|
19
|
+
'--thumbnail-tilt': `${TILT_DEGREES[index % TILT_DEGREES.length]}deg`,
|
|
20
|
+
'--thumbnail-shift': `${SHIFT_PIXELS[index % SHIFT_PIXELS.length]}px`,
|
|
21
|
+
zIndex: total - index,
|
|
22
|
+
}, className: cn('border-border/60 bg-card relative block shrink-0 overflow-hidden rounded-md border shadow-sm', 'transition-transform duration-200 ease-out motion-reduce:transition-none', '[transform:rotate(var(--thumbnail-tilt))_translateY(var(--thumbnail-shift))]', 'group-hover/thumbnail-stack:[transform:rotate(0deg)]', TILE_SIZE[size], index > 0 && TILE_OVERLAP[size]), children: children }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type VariantProps } from 'class-variance-authority';
|
|
3
|
+
declare const root: (props?: ({
|
|
4
|
+
size?: "sm" | "md" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
export type AudioPreviewSourceResolver = () => Promise<string | null>;
|
|
7
|
+
export interface AudioPreviewLabels {
|
|
8
|
+
play: string;
|
|
9
|
+
pause: string;
|
|
10
|
+
download: string;
|
|
11
|
+
loading: string;
|
|
12
|
+
error: string;
|
|
13
|
+
empty: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AudioPreviewProps extends Omit<React.ComponentProps<'div'>, 'title' | 'onDurationChange' | 'onTimeUpdate'>, VariantProps<typeof root> {
|
|
16
|
+
src?: string | null;
|
|
17
|
+
loadSrc?: AudioPreviewSourceResolver;
|
|
18
|
+
label?: React.ReactNode;
|
|
19
|
+
meta?: React.ReactNode;
|
|
20
|
+
labels: AudioPreviewLabels;
|
|
21
|
+
state?: 'auto' | 'loading' | 'error' | 'empty';
|
|
22
|
+
peaks?: number[] | null;
|
|
23
|
+
showDownload?: boolean;
|
|
24
|
+
downloadName?: string;
|
|
25
|
+
monoClassName?: string;
|
|
26
|
+
preload?: 'none' | 'metadata' | 'auto';
|
|
27
|
+
mediaRef?: React.RefCallback<HTMLAudioElement | null>;
|
|
28
|
+
onTimeUpdate?: (currentTime: number) => void;
|
|
29
|
+
onDurationChange?: (duration: number) => void;
|
|
30
|
+
onPlayingChange?: (playing: boolean) => void;
|
|
31
|
+
}
|
|
32
|
+
declare function AudioPreview({ src, loadSrc, label, meta, labels, state, peaks, showDownload, downloadName, monoClassName, preload, mediaRef, onTimeUpdate, onDurationChange, onPlayingChange, size, className, ...props }: AudioPreviewProps): React.JSX.Element;
|
|
33
|
+
export { AudioPreview };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cva } from 'class-variance-authority';
|
|
5
|
+
import { AlertCircle, Download, Loader2, Pause, Play } from 'lucide-react';
|
|
6
|
+
import { cn } from '../lib/cn.js';
|
|
7
|
+
const root = cva('group/audio-preview flex items-center gap-2 rounded-md border border-border/60 bg-muted/[0.18] text-foreground', {
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
sm: 'px-2.5 py-2',
|
|
11
|
+
md: 'px-3 py-3',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: { size: 'md' },
|
|
15
|
+
});
|
|
16
|
+
function formatSeconds(seconds) {
|
|
17
|
+
const total = Math.max(0, Math.floor(seconds));
|
|
18
|
+
const min = Math.floor(total / 60);
|
|
19
|
+
const sec = total % 60;
|
|
20
|
+
return `${min}:${sec.toString().padStart(2, '0')}`;
|
|
21
|
+
}
|
|
22
|
+
function AudioPreview({ src, loadSrc, label, meta, labels, state = 'auto', peaks, showDownload, downloadName, monoClassName, preload = 'none', mediaRef, onTimeUpdate, onDurationChange, onPlayingChange, size, className, ...props }) {
|
|
23
|
+
const audioRef = React.useRef(null);
|
|
24
|
+
const setAudioRef = React.useCallback((node) => {
|
|
25
|
+
audioRef.current = node;
|
|
26
|
+
mediaRef?.(node);
|
|
27
|
+
}, [mediaRef]);
|
|
28
|
+
const [resolvedSrc, setResolvedSrc] = React.useState(src ?? null);
|
|
29
|
+
const [status, setStatus] = React.useState(src ? 'ready' : 'idle');
|
|
30
|
+
const [isPlaying, setIsPlaying] = React.useState(false);
|
|
31
|
+
const [currentTime, setCurrentTime] = React.useState(0);
|
|
32
|
+
const [duration, setDuration] = React.useState(0);
|
|
33
|
+
const [prevSrc, setPrevSrc] = React.useState(src);
|
|
34
|
+
if (src !== prevSrc) {
|
|
35
|
+
setPrevSrc(src);
|
|
36
|
+
setResolvedSrc(src ?? null);
|
|
37
|
+
setStatus(src ? 'ready' : 'idle');
|
|
38
|
+
setIsPlaying(false);
|
|
39
|
+
setCurrentTime(0);
|
|
40
|
+
setDuration(0);
|
|
41
|
+
}
|
|
42
|
+
const forced = state !== 'auto';
|
|
43
|
+
const empty = state === 'empty' || (!forced && !src && !loadSrc);
|
|
44
|
+
const loading = state === 'loading' || (!forced && status === 'loading');
|
|
45
|
+
const errored = state === 'error' || (!forced && status === 'error');
|
|
46
|
+
const placeholder = empty
|
|
47
|
+
? labels.empty
|
|
48
|
+
: loading
|
|
49
|
+
? labels.loading
|
|
50
|
+
: errored
|
|
51
|
+
? labels.error
|
|
52
|
+
: null;
|
|
53
|
+
const playable = !forced && (Boolean(resolvedSrc) || Boolean(loadSrc));
|
|
54
|
+
const totalDuration = Number.isFinite(duration) ? duration : 0;
|
|
55
|
+
const progress = totalDuration > 0 ? Math.min(1, Math.max(0, currentTime / totalDuration)) : 0;
|
|
56
|
+
const ensureSrc = React.useCallback(async () => {
|
|
57
|
+
if (resolvedSrc)
|
|
58
|
+
return resolvedSrc;
|
|
59
|
+
if (!loadSrc)
|
|
60
|
+
return null;
|
|
61
|
+
setStatus('loading');
|
|
62
|
+
try {
|
|
63
|
+
const next = await loadSrc();
|
|
64
|
+
if (!next) {
|
|
65
|
+
setStatus('error');
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
setResolvedSrc(next);
|
|
69
|
+
setStatus('ready');
|
|
70
|
+
return next;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
setStatus('error');
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}, [resolvedSrc, loadSrc]);
|
|
77
|
+
const togglePlay = React.useCallback(async () => {
|
|
78
|
+
const audio = audioRef.current;
|
|
79
|
+
if (!audio)
|
|
80
|
+
return;
|
|
81
|
+
if (!audio.paused) {
|
|
82
|
+
audio.pause();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!resolvedSrc) {
|
|
86
|
+
const next = await ensureSrc();
|
|
87
|
+
if (!next || !audioRef.current)
|
|
88
|
+
return;
|
|
89
|
+
if (audioRef.current.src !== next) {
|
|
90
|
+
audioRef.current.src = next;
|
|
91
|
+
audioRef.current.load();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
void audioRef.current?.play().catch(() => setStatus('error'));
|
|
95
|
+
}, [resolvedSrc, ensureSrc]);
|
|
96
|
+
const seekToFraction = React.useCallback((fraction) => {
|
|
97
|
+
const audio = audioRef.current;
|
|
98
|
+
if (!audio || !totalDuration)
|
|
99
|
+
return;
|
|
100
|
+
const clamped = Math.min(1, Math.max(0, fraction));
|
|
101
|
+
audio.currentTime = clamped * totalDuration;
|
|
102
|
+
setCurrentTime(audio.currentTime);
|
|
103
|
+
}, [totalDuration]);
|
|
104
|
+
if (placeholder) {
|
|
105
|
+
return (_jsxs("div", { className: cn(root({ size }), 'border-dashed text-muted-foreground gap-1.5 text-xs', errored && 'text-destructive border-destructive/40', className), "data-slot": "audio-preview", "data-state": empty ? 'empty' : loading ? 'loading' : 'error', ...props, children: [loading ? (_jsx(Loader2, { className: "size-3.5 animate-spin", strokeWidth: 1.6, "aria-hidden": true })) : errored ? (_jsx(AlertCircle, { className: "size-3.5", strokeWidth: 1.6, "aria-hidden": true })) : null, _jsx("span", { className: "truncate", children: placeholder })] }));
|
|
106
|
+
}
|
|
107
|
+
const wantsDownload = showDownload ?? Boolean(resolvedSrc);
|
|
108
|
+
return (_jsxs("div", { className: cn(root({ size }), className), "data-slot": "audio-preview", "data-state": isPlaying ? 'playing' : 'paused', ...props, children: [_jsx("button", { type: "button", onClick: () => void togglePlay(), disabled: !playable, "aria-label": isPlaying ? labels.pause : labels.play, className: cn('flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-full border border-border bg-background text-foreground transition-colors', 'hover:bg-muted focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 outline-none', 'disabled:pointer-events-none disabled:opacity-50'), children: isPlaying ? (_jsx(Pause, { className: "size-3.5", strokeWidth: 1.6, "aria-hidden": true })) : (_jsx(Play, { className: "size-3.5", strokeWidth: 1.6, "aria-hidden": true })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [(label || meta) && (_jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [label ? _jsx("span", { className: "truncate text-xs text-foreground/90", children: label }) : _jsx("span", {}), meta ? (_jsx("span", { className: cn('shrink-0 text-[10px] text-muted-foreground tabular-nums', monoClassName ?? 'font-mono'), children: meta })) : null] })), _jsx(ProgressTrack, { peaks: peaks, progress: progress, disabled: !totalDuration, onSeek: seekToFraction, className: cn((label || meta) && 'mt-1') }), _jsxs("div", { className: cn('mt-1 text-[10px] text-muted-foreground tabular-nums', monoClassName ?? 'font-mono'), children: [formatSeconds(Math.max(0, currentTime)), totalDuration > 0 ? ` / ${formatSeconds(totalDuration)}` : ''] })] }), wantsDownload && resolvedSrc ? (_jsx("a", { href: resolvedSrc, download: downloadName ?? true, "aria-label": labels.download, title: labels.download, className: cn('flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors', 'hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 outline-none'), children: _jsx(Download, { className: "size-3.5", strokeWidth: 1.6, "aria-hidden": true }) })) : null, _jsx("audio", { ref: setAudioRef, preload: preload, src: resolvedSrc ?? undefined, className: "hidden", onPlay: () => {
|
|
109
|
+
setIsPlaying(true);
|
|
110
|
+
onPlayingChange?.(true);
|
|
111
|
+
}, onPause: () => {
|
|
112
|
+
setIsPlaying(false);
|
|
113
|
+
onPlayingChange?.(false);
|
|
114
|
+
}, onEnded: () => {
|
|
115
|
+
setIsPlaying(false);
|
|
116
|
+
onPlayingChange?.(false);
|
|
117
|
+
}, onError: () => {
|
|
118
|
+
if (resolvedSrc)
|
|
119
|
+
setStatus('error');
|
|
120
|
+
}, onTimeUpdate: (event) => {
|
|
121
|
+
setCurrentTime(event.currentTarget.currentTime);
|
|
122
|
+
onTimeUpdate?.(event.currentTarget.currentTime);
|
|
123
|
+
}, onLoadedMetadata: (event) => {
|
|
124
|
+
setDuration(event.currentTarget.duration);
|
|
125
|
+
onDurationChange?.(event.currentTarget.duration);
|
|
126
|
+
}, children: _jsx("track", { kind: "captions" }) })] }));
|
|
127
|
+
}
|
|
128
|
+
function ProgressTrack({ peaks, progress, disabled, onSeek, className, }) {
|
|
129
|
+
const handle = (event) => {
|
|
130
|
+
if (disabled)
|
|
131
|
+
return;
|
|
132
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
133
|
+
if (rect.width <= 0)
|
|
134
|
+
return;
|
|
135
|
+
onSeek((event.clientX - rect.left) / rect.width);
|
|
136
|
+
};
|
|
137
|
+
const onKeyDown = (event) => {
|
|
138
|
+
if (disabled)
|
|
139
|
+
return;
|
|
140
|
+
if (event.key === 'ArrowRight') {
|
|
141
|
+
event.preventDefault();
|
|
142
|
+
onSeek(Math.min(1, progress + 0.05));
|
|
143
|
+
}
|
|
144
|
+
else if (event.key === 'ArrowLeft') {
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
onSeek(Math.max(0, progress - 0.05));
|
|
147
|
+
}
|
|
148
|
+
else if (event.key === 'Home') {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
onSeek(0);
|
|
151
|
+
}
|
|
152
|
+
else if (event.key === 'End') {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
onSeek(1);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const sharedProps = {
|
|
158
|
+
role: 'slider',
|
|
159
|
+
tabIndex: disabled ? -1 : 0,
|
|
160
|
+
'aria-valuemin': 0,
|
|
161
|
+
'aria-valuemax': 100,
|
|
162
|
+
'aria-valuenow': Math.round(progress * 100),
|
|
163
|
+
'aria-disabled': disabled || undefined,
|
|
164
|
+
onClick: handle,
|
|
165
|
+
onKeyDown,
|
|
166
|
+
className: cn('w-full cursor-pointer rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring/50', disabled && 'cursor-default', className),
|
|
167
|
+
};
|
|
168
|
+
if (peaks && peaks.length > 0) {
|
|
169
|
+
return (_jsx("div", { ...sharedProps, children: _jsx("span", { className: "flex h-5 w-full items-center gap-[2px]", children: peaks.map((p, i) => {
|
|
170
|
+
const played = i / peaks.length <= progress;
|
|
171
|
+
return (_jsx("span", { className: cn('w-full rounded-full transition-colors', played ? 'bg-primary' : 'bg-muted-foreground/30'), style: { height: `${Math.max(8, Math.round(p * 100))}%` } }, i));
|
|
172
|
+
}) }) }));
|
|
173
|
+
}
|
|
174
|
+
return (_jsx("div", { ...sharedProps, children: _jsx("span", { className: "bg-muted flex h-1.5 w-full overflow-hidden rounded-full", children: _jsx("span", { className: "bg-primary h-full rounded-full", style: { width: `${Math.round(progress * 100)}%` } }) }) }));
|
|
175
|
+
}
|
|
176
|
+
export { AudioPreview };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface AvatarUploaderLabels {
|
|
3
|
+
editAvatar: string;
|
|
4
|
+
uploadImage: string;
|
|
5
|
+
replaceImage: string;
|
|
6
|
+
removeImage: string;
|
|
7
|
+
removeHint: string;
|
|
8
|
+
mustBeImage: string;
|
|
9
|
+
tooLarge: (megabytes: number) => string;
|
|
10
|
+
}
|
|
11
|
+
export interface AvatarUploaderProps {
|
|
12
|
+
currentFileId: string | null | undefined;
|
|
13
|
+
currentUrl?: string | null;
|
|
14
|
+
fallbackInitials: string;
|
|
15
|
+
kind: 'avatar' | 'logo';
|
|
16
|
+
onChange: (fileId: string | null) => void | Promise<void>;
|
|
17
|
+
onUpload: (file: File, kind: 'avatar' | 'logo') => Promise<{
|
|
18
|
+
id: string;
|
|
19
|
+
}>;
|
|
20
|
+
labels: AvatarUploaderLabels;
|
|
21
|
+
size?: number;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
className?: string;
|
|
24
|
+
open?: boolean;
|
|
25
|
+
onOpenChange?: (open: boolean) => void;
|
|
26
|
+
menuExtra?: ReactNode;
|
|
27
|
+
frameClassName?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function AvatarUploader({ currentFileId, currentUrl, fallbackInitials, kind, onChange, onUpload, labels, size, disabled, className, open: openProp, onOpenChange, menuExtra, frameClassName, }: AvatarUploaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useState } from 'react';
|
|
4
|
+
import { Camera, Pencil, Trash2 } from 'lucide-react';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
import { Button } from './button.js';
|
|
7
|
+
import { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger } from './popover.js';
|
|
8
|
+
import { TerminalSpinner } from './terminal-spinner.js';
|
|
9
|
+
const MAX_BYTES = 5 * 1024 * 1024;
|
|
10
|
+
export function AvatarUploader({ currentFileId, currentUrl, fallbackInitials, kind, onChange, onUpload, labels, size = 56, disabled, className, open: openProp, onOpenChange, menuExtra, frameClassName, }) {
|
|
11
|
+
const inputRef = useRef(null);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const [uploading, setUploading] = useState(false);
|
|
14
|
+
const [openState, setOpenState] = useState(false);
|
|
15
|
+
const open = openProp ?? openState;
|
|
16
|
+
const setOpen = (next) => {
|
|
17
|
+
setOpenState(next);
|
|
18
|
+
onOpenChange?.(next);
|
|
19
|
+
};
|
|
20
|
+
const blocked = disabled || uploading;
|
|
21
|
+
const handlePick = async (file) => {
|
|
22
|
+
if (!file)
|
|
23
|
+
return;
|
|
24
|
+
setError(null);
|
|
25
|
+
if (!file.type.startsWith('image/')) {
|
|
26
|
+
setError(labels.mustBeImage);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (file.size > MAX_BYTES) {
|
|
30
|
+
setError(labels.tooLarge(MAX_BYTES / 1024 / 1024));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
setUploading(true);
|
|
34
|
+
try {
|
|
35
|
+
const uploaded = await onUpload(file, kind);
|
|
36
|
+
await onChange(uploaded.id);
|
|
37
|
+
}
|
|
38
|
+
catch (uploadError) {
|
|
39
|
+
setError(uploadError instanceof Error ? uploadError.message : String(uploadError));
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
setUploading(false);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const handleClear = async () => {
|
|
46
|
+
setError(null);
|
|
47
|
+
setOpen(false);
|
|
48
|
+
try {
|
|
49
|
+
await onChange(null);
|
|
50
|
+
}
|
|
51
|
+
catch (clearError) {
|
|
52
|
+
setError(clearError instanceof Error ? clearError.message : String(clearError));
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const openPicker = () => {
|
|
56
|
+
if (blocked)
|
|
57
|
+
return;
|
|
58
|
+
setError(null);
|
|
59
|
+
inputRef.current?.click();
|
|
60
|
+
setOpen(false);
|
|
61
|
+
};
|
|
62
|
+
return (_jsxs("div", { "data-slot": "avatar-uploader", className: cn('inline-flex flex-col items-start gap-1.5', className), children: [_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: blocked, "aria-label": labels.editAvatar, className: cn('group bg-primary text-primary-foreground ring-border focus-visible:ring-ring relative flex shrink-0 items-center justify-center overflow-hidden rounded-full font-medium shadow-sm ring-1 transition outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-70', frameClassName), style: { width: size, height: size, fontSize: Math.round(size * 0.32) }, children: [currentUrl ? (_jsx("img", { src: currentUrl, alt: "", className: "size-full object-cover" })) : (fallbackInitials), _jsx("span", { className: cn('bg-foreground/45 absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100', open && 'opacity-100'), "aria-hidden": true, children: _jsx("span", { className: "bg-background text-foreground ring-border grid size-7 place-items-center rounded-full shadow-sm ring-1", children: _jsx(Pencil, { className: "size-3.5", strokeWidth: 1.5 }) }) }), uploading ? (_jsx("span", { className: "bg-foreground/45 absolute inset-0 flex items-center justify-center", children: _jsx(TerminalSpinner, { className: "text-background text-sm" }) })) : null] }) }), _jsxs(PopoverContent, { align: "start", sideOffset: 8, className: "w-64 gap-1.5 p-2", children: [_jsx(PopoverHeader, { className: "px-2 py-1.5", children: _jsx(PopoverTitle, { className: "text-sm", children: labels.editAvatar }) }), _jsxs(Button, { type: "button", variant: "ghost", disabled: blocked, onClick: openPicker, className: "h-auto justify-start gap-3 px-2 py-2 text-left", children: [_jsx("span", { className: "bg-muted text-foreground grid size-8 shrink-0 place-items-center rounded-md", children: _jsx(Camera, { className: "size-4", strokeWidth: 1.5 }) }), _jsx("span", { className: "min-w-0 text-xs font-medium", children: currentFileId ? labels.replaceImage : labels.uploadImage })] }), currentFileId ? (_jsxs(Button, { type: "button", variant: "ghost", disabled: blocked, onClick: () => void handleClear(), className: "text-destructive hover:text-destructive h-auto justify-start gap-3 px-2 py-2 text-left", children: [_jsx("span", { className: "bg-destructive/10 text-destructive grid size-8 shrink-0 place-items-center rounded-md", children: _jsx(Trash2, { className: "size-4", strokeWidth: 1.5 }) }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block text-xs font-medium", children: labels.removeImage }), _jsx("span", { className: "text-destructive/75 mt-0.5 block text-xs font-normal", children: labels.removeHint })] })] })) : null, menuExtra ? (_jsx("div", { className: "border-border/60 mt-1 border-t px-1 pt-2.5 pb-1.5", children: menuExtra })) : null] })] }), _jsx("input", { ref: inputRef, type: "file", accept: "image/png,image/jpeg,image/webp,image/svg+xml,image/gif", className: "hidden", disabled: blocked, "aria-label": labels.uploadImage, onChange: (event) => {
|
|
63
|
+
const file = event.currentTarget.files?.[0];
|
|
64
|
+
event.currentTarget.value = '';
|
|
65
|
+
void handlePick(file);
|
|
66
|
+
} }), error ? _jsx("div", { className: "text-destructive text-xs", children: error }) : null] }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type VariantProps } from 'class-variance-authority';
|
|
3
|
+
declare const avatarVariants: (props?: ({
|
|
4
|
+
size?: "sm" | "default" | "xs" | "lg" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
export interface AvatarProps extends Omit<React.ComponentProps<'span'>, 'color'>, VariantProps<typeof avatarVariants> {
|
|
7
|
+
name?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function initialsFromName(name: string): string;
|
|
11
|
+
export declare function Avatar({ className, size, name, color, children, style, ...props }: AvatarProps): React.JSX.Element;
|
|
12
|
+
export { avatarVariants };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { cn } from '../lib/cn.js';
|
|
5
|
+
// A compact initials avatar — the small round marker that stands in for a person
|
|
6
|
+
// wherever a photo isn't shown (comment authors, presence cursors, member
|
|
7
|
+
// lists). Generalised as an atom so every "who did this" marker reads as one
|
|
8
|
+
// system. Purely presentational: pass a `name` (initials are derived) and an
|
|
9
|
+
// optional brand/presence `color`; with no colour it falls back to the muted
|
|
10
|
+
// token so it stays theme-correct.
|
|
11
|
+
const avatarVariants = cva('atmos-avatar inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-full font-medium leading-none', {
|
|
12
|
+
variants: {
|
|
13
|
+
size: {
|
|
14
|
+
xs: 'size-4 text-[8px]',
|
|
15
|
+
sm: 'size-5 text-[9px]',
|
|
16
|
+
default: 'size-6 text-[10px]',
|
|
17
|
+
lg: 'size-8 text-xs',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: { size: 'default' },
|
|
21
|
+
});
|
|
22
|
+
// Two-letter initials from a display name: first letter of the first and last
|
|
23
|
+
// word, else the first two letters of a single word.
|
|
24
|
+
export function initialsFromName(name) {
|
|
25
|
+
const parts = name.trim().split(/\s+/u).filter(Boolean);
|
|
26
|
+
const first = parts[0] ?? '';
|
|
27
|
+
if (!first)
|
|
28
|
+
return '?';
|
|
29
|
+
if (parts.length === 1)
|
|
30
|
+
return first.slice(0, 2).toUpperCase();
|
|
31
|
+
const last = parts[parts.length - 1] ?? '';
|
|
32
|
+
return `${first.charAt(0)}${last.charAt(0)}`.toUpperCase();
|
|
33
|
+
}
|
|
34
|
+
export function Avatar({ className, size, name, color, children, style, ...props }) {
|
|
35
|
+
const content = children ?? (name ? initialsFromName(name) : '?');
|
|
36
|
+
return (_jsx("span", { "data-slot": "avatar", "aria-hidden": props['aria-label'] ? undefined : true, className: cn(avatarVariants({ size }), color ? 'text-white' : 'bg-muted text-foreground/80', className), style: color ? { background: color, ...style } : style, ...props, children: content }));
|
|
37
|
+
}
|
|
38
|
+
export { avatarVariants };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type VariantProps } from 'class-variance-authority';
|
|
3
|
+
declare const badgeVariants: (props?: ({
|
|
4
|
+
variant?: "link" | "default" | "outline" | "secondary" | "ghost" | "destructive" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
|
|
7
|
+
asChild?: boolean;
|
|
8
|
+
}): React.JSX.Element;
|
|
9
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { Slot } from 'radix-ui';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
const badgeVariants = cva('group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!', {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
|
|
10
|
+
secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
|
|
11
|
+
destructive: 'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',
|
|
12
|
+
outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
|
|
13
|
+
ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
|
|
14
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: 'default',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
function Badge({ className, variant = 'default', asChild = false, ...props }) {
|
|
22
|
+
const Comp = asChild ? Slot.Root : 'span';
|
|
23
|
+
return (_jsx(Comp, { "data-slot": 'badge', "data-variant": variant, className: cn(badgeVariants({ variant }), className), ...props }));
|
|
24
|
+
}
|
|
25
|
+
export { Badge, badgeVariants };
|