@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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { Settings2 } from 'lucide-react';
|
|
6
|
+
import { Button } from '../../components/button.js';
|
|
7
|
+
import { useDebouncedValue } from '../use-debounced-value.js';
|
|
8
|
+
import { useDagEditor } from './use-dag-editor.js';
|
|
9
|
+
import { FlowDagCanvas } from './dag-canvas.js';
|
|
10
|
+
import { FlowLibraryRail } from './library-rail.js';
|
|
11
|
+
import { SavePublishBar } from './save-publish-bar.js';
|
|
12
|
+
import { IssuesPopover, isBlocked } from './issues-popover.js';
|
|
13
|
+
import { SettingsDialog } from './settings-dialog.js';
|
|
14
|
+
function useDefaultCanvasPresentation(_context) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
export function FlowEditor({ adapter, workspaceId, initialNodes, initialEdges, settingsTitle, isNew = false, externalDirty = false, initiallyUnpublished = false, onSaved, onPublished, onFirstSave, onError, renderInsertPicker, renderDropPicker, dropPickerWidth, buildEdge, isSingleHandle, handleOrderOf, clipboardKind, headingFont, testId = 'flow-editor', renderHeaderStart, useCanvasPresentation = useDefaultCanvasPresentation, renderSettings, }) {
|
|
18
|
+
const t = useTranslations('flowEditor');
|
|
19
|
+
const adapterCtx = useMemo(() => ({ workspaceId }), [workspaceId]);
|
|
20
|
+
const groups = useMemo(() => adapter.nodeGroups(adapterCtx), [adapter, adapterCtx]);
|
|
21
|
+
const dag = useDagEditor({
|
|
22
|
+
makeNodeData: adapter.makeNodeData,
|
|
23
|
+
...(adapter.cloneNodeData ? { cloneNodeData: adapter.cloneNodeData } : {}),
|
|
24
|
+
isProtected: adapter.isProtected,
|
|
25
|
+
...(adapter.isValidConnection ? { isValidConnection: adapter.isValidConnection } : {}),
|
|
26
|
+
initialNodes,
|
|
27
|
+
initialEdges,
|
|
28
|
+
...(buildEdge ? { buildEdge } : {}),
|
|
29
|
+
...(isSingleHandle ? { isSingleHandle } : {}),
|
|
30
|
+
...(handleOrderOf ? { handleOrderOf } : {}),
|
|
31
|
+
...(clipboardKind ? { clipboardKind } : {}),
|
|
32
|
+
});
|
|
33
|
+
const save = adapter.useSave();
|
|
34
|
+
const publish = adapter.usePublish();
|
|
35
|
+
const stickyNotes = adapter.useStickyNotes?.() ?? null;
|
|
36
|
+
const isSaving = save.isPending || publish.isPending;
|
|
37
|
+
const isDirty = dag.isDirty || externalDirty;
|
|
38
|
+
const [hasSavedUnpublishedDraft, setHasSavedUnpublishedDraft] = useState(initiallyUnpublished);
|
|
39
|
+
const publishReady = isDirty || hasSavedUnpublishedDraft;
|
|
40
|
+
const issues = useMemo(() => adapter.validate(dag.nodes, dag.edges), [adapter, dag.nodes, dag.edges]);
|
|
41
|
+
const invalidNodeIds = useMemo(() => new Set(issues.map((i) => i.nodeId)), [issues]);
|
|
42
|
+
const publishBlocked = isBlocked(issues);
|
|
43
|
+
const issuesKey = useMemo(() => issues.map((i) => `${i.nodeId}:${i.severity ?? 'error'}:${i.message}`).join('|'), [issues]);
|
|
44
|
+
const [issuesOpenKey, setIssuesOpenKey] = useState(null);
|
|
45
|
+
const issuesPopoverOpen = issues.length > 0 && issuesOpenKey === issuesKey;
|
|
46
|
+
const jumpToNode = useCallback((nodeId) => {
|
|
47
|
+
dag.selectNode(nodeId);
|
|
48
|
+
dag.requestFocus(nodeId);
|
|
49
|
+
}, [dag]);
|
|
50
|
+
const surfaceIssues = useCallback(() => {
|
|
51
|
+
if (issues.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
setIssuesOpenKey(issuesKey);
|
|
54
|
+
const first = issues[0];
|
|
55
|
+
if (first)
|
|
56
|
+
jumpToNode(first.nodeId);
|
|
57
|
+
}, [issues, issuesKey, jumpToNode]);
|
|
58
|
+
const setIssuesPopoverOpen = useCallback((open) => {
|
|
59
|
+
if (!open || issues.length === 0) {
|
|
60
|
+
setIssuesOpenKey(null);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
surfaceIssues();
|
|
64
|
+
}, [issues.length, surfaceIssues]);
|
|
65
|
+
const nodeLabelFor = useCallback((nodeId) => {
|
|
66
|
+
const node = dag.nodes.find((n) => n.id === nodeId);
|
|
67
|
+
if (node && adapter.nodeLabel)
|
|
68
|
+
return adapter.nodeLabel(node);
|
|
69
|
+
const data = node?.data;
|
|
70
|
+
const title = typeof data?.title === 'string' ? data.title.trim() : '';
|
|
71
|
+
return title || adapter.titleForKind(nodeId);
|
|
72
|
+
}, [adapter, dag.nodes]);
|
|
73
|
+
const [justSaved, setJustSaved] = useState(false);
|
|
74
|
+
const savedCueTimer = useRef(null);
|
|
75
|
+
const flashSaved = useCallback(() => {
|
|
76
|
+
setJustSaved(true);
|
|
77
|
+
if (savedCueTimer.current)
|
|
78
|
+
clearTimeout(savedCueTimer.current);
|
|
79
|
+
savedCueTimer.current = setTimeout(() => setJustSaved(false), 1800);
|
|
80
|
+
}, []);
|
|
81
|
+
useEffect(() => () => {
|
|
82
|
+
if (savedCueTimer.current)
|
|
83
|
+
clearTimeout(savedCueTimer.current);
|
|
84
|
+
}, []);
|
|
85
|
+
const wasNewRef = useRef(isNew);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!isNew)
|
|
88
|
+
wasNewRef.current = false;
|
|
89
|
+
}, [isNew]);
|
|
90
|
+
const persist = useCallback(async (positionsOnly) => {
|
|
91
|
+
// Freeze node identity only on content saves — never on the positions
|
|
92
|
+
// autosave, which would freeze a freshly-dropped, still-untitled node to a
|
|
93
|
+
// generic key. Stamp through a quiet commit so it leaves no undo checkpoint.
|
|
94
|
+
const stamped = positionsOnly ? null : (adapter.stampIdentity?.(dag.nodes) ?? null);
|
|
95
|
+
const nodesForSave = stamped ?? dag.nodes;
|
|
96
|
+
if (stamped)
|
|
97
|
+
dag.commitNodesQuiet(() => stamped);
|
|
98
|
+
const input = adapter.buildSaveInput({
|
|
99
|
+
nodes: nodesForSave,
|
|
100
|
+
edges: dag.edges,
|
|
101
|
+
positionsOnly,
|
|
102
|
+
});
|
|
103
|
+
const result = await save.mutateAsync(input);
|
|
104
|
+
setHasSavedUnpublishedDraft(true);
|
|
105
|
+
return result;
|
|
106
|
+
}, [adapter, dag, save]);
|
|
107
|
+
const handleSave = useCallback(async () => {
|
|
108
|
+
try {
|
|
109
|
+
const result = await persist(false);
|
|
110
|
+
dag.markSaved();
|
|
111
|
+
flashSaved();
|
|
112
|
+
onSaved?.(result);
|
|
113
|
+
if (wasNewRef.current) {
|
|
114
|
+
wasNewRef.current = false;
|
|
115
|
+
onFirstSave?.(result);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (issues.length > 0)
|
|
120
|
+
surfaceIssues();
|
|
121
|
+
onError?.(err, 'save');
|
|
122
|
+
}
|
|
123
|
+
}, [persist, dag, flashSaved, onSaved, onFirstSave, issues.length, surfaceIssues, onError]);
|
|
124
|
+
const handlePublish = useCallback(async () => {
|
|
125
|
+
if (publishBlocked) {
|
|
126
|
+
surfaceIssues();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const result = await persist(false);
|
|
131
|
+
await publish.mutateAsync(adapter.buildPublishInput(result));
|
|
132
|
+
dag.markSaved();
|
|
133
|
+
setHasSavedUnpublishedDraft(false);
|
|
134
|
+
onSaved?.(result);
|
|
135
|
+
onPublished?.(result);
|
|
136
|
+
if (wasNewRef.current) {
|
|
137
|
+
wasNewRef.current = false;
|
|
138
|
+
onFirstSave?.(result);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
if (issues.length > 0)
|
|
143
|
+
surfaceIssues();
|
|
144
|
+
onError?.(err, 'publish');
|
|
145
|
+
}
|
|
146
|
+
}, [
|
|
147
|
+
publishBlocked,
|
|
148
|
+
surfaceIssues,
|
|
149
|
+
persist,
|
|
150
|
+
publish,
|
|
151
|
+
adapter,
|
|
152
|
+
dag,
|
|
153
|
+
onSaved,
|
|
154
|
+
onPublished,
|
|
155
|
+
onFirstSave,
|
|
156
|
+
issues.length,
|
|
157
|
+
onError,
|
|
158
|
+
]);
|
|
159
|
+
const positionSig = useMemo(() => dag.nodes
|
|
160
|
+
.map((n) => `${n.id}:${Math.round(n.position.x)},${Math.round(n.position.y)}`)
|
|
161
|
+
.join('|'), [dag.nodes]);
|
|
162
|
+
const annotationSig = useMemo(() => (stickyNotes?.annotations ?? [])
|
|
163
|
+
.map((a) => `${a.id}:${Math.round(a.x)},${Math.round(a.y)}:${a.color ?? ''}:${a.text}`)
|
|
164
|
+
.join('|'), [stickyNotes]);
|
|
165
|
+
const debouncedSaveSig = useDebouncedValue(`${positionSig}\n${annotationSig}`, 600);
|
|
166
|
+
const lastSavedSigRef = useRef(null);
|
|
167
|
+
const persistRef = useRef(persist);
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
persistRef.current = persist;
|
|
170
|
+
}, [persist]);
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (lastSavedSigRef.current === null) {
|
|
173
|
+
lastSavedSigRef.current = debouncedSaveSig;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (isNew || !workspaceId)
|
|
177
|
+
return;
|
|
178
|
+
if (debouncedSaveSig === lastSavedSigRef.current)
|
|
179
|
+
return;
|
|
180
|
+
lastSavedSigRef.current = debouncedSaveSig;
|
|
181
|
+
void persistRef.current(true).catch(() => {
|
|
182
|
+
lastSavedSigRef.current = null;
|
|
183
|
+
});
|
|
184
|
+
}, [debouncedSaveSig, isNew, workspaceId]);
|
|
185
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
186
|
+
const toolbarExtras = adapter.toolbarExtras?.(adapterCtx);
|
|
187
|
+
const canvasPresentation = useCanvasPresentation({ dag, groups, invalidNodeIds });
|
|
188
|
+
const { afterCanvas, alternateContent, rightPanel: contributedRightPanel, toolbarExtras: contributedToolbarExtras, renderInsertPicker: contributedInsertPicker, renderDropPicker: contributedDropPicker, ...canvasProps } = canvasPresentation;
|
|
189
|
+
const canvasToolbarExtras = contributedToolbarExtras ?? toolbarExtras;
|
|
190
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", "data-testid": testId, children: [_jsxs("div", { className: "border-border/50 flex shrink-0 items-center gap-3 border-b px-6 py-2.5", children: [renderHeaderStart?.(), _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [_jsx(IssuesPopover, { issues: issues, nodeLabelFor: nodeLabelFor, onJump: jumpToNode, open: issuesPopoverOpen, onOpenChange: setIssuesPopoverOpen }), _jsxs(Button, { type: "button", size: "sm", variant: "ghost", onClick: () => setSettingsOpen(true), className: "gap-1.5", "data-testid": "flow-editor-settings", children: [_jsx(Settings2, { className: "size-3.5", strokeWidth: 1.8, "aria-hidden": true }), t('settings.open')] }), _jsx(SavePublishBar, { isDirty: isDirty, publishReady: publishReady, justSaved: justSaved, isSaving: isSaving, savePending: save.isPending, publishPending: publish.isPending, publishBlocked: publishBlocked, onSave: () => void handleSave(), onPublish: () => void handlePublish(), className: "flex items-center gap-1.5" })] })] }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col xl:flex-row", children: alternateContent ? (alternateContent) : (_jsxs(_Fragment, { children: [_jsx(FlowDagCanvas, { dag: dag, NodeComponent: adapter.NodeComponent, EdgeComponent: adapter.EdgeComponent, titleForKind: adapter.titleForKind, invalidNodeIds: invalidNodeIds, stickyNotes: adapter.supportsStickyNotes ? stickyNotes : null, showStickyNotes: adapter.supportsStickyNotes ?? false, renderInsertPicker: contributedInsertPicker ?? renderInsertPicker, renderDropPicker: contributedDropPicker ?? renderDropPicker, presentation: {
|
|
191
|
+
...canvasProps,
|
|
192
|
+
...(canvasToolbarExtras ? { toolbarExtras: canvasToolbarExtras } : {}),
|
|
193
|
+
...(dropPickerWidth ? { dropPickerWidth } : {}),
|
|
194
|
+
rightPanel: contributedRightPanel ?? (_jsx(FlowLibraryRail, { testId: "flow-editor-rail", selectedNode: dag.selectedNode, groups: groups, onAddKind: (kind) => dag.addNodeAtEnd(kind, adapter.titleForKind(kind)), onDeselect: dag.deselectAll, renderConfig: (node) => {
|
|
195
|
+
const Panel = adapter.NodeConfigPanel;
|
|
196
|
+
return (_jsx(Panel, { node: node, onChange: (patch) => dag.updateNodeData(node.id, patch), onClose: dag.deselectAll }, node.id));
|
|
197
|
+
}, ...(headingFont ? { headingFont } : {}) })),
|
|
198
|
+
} }), afterCanvas] })) }), renderSettings ? (renderSettings({ open: settingsOpen, onOpenChange: setSettingsOpen })) : (_jsx(SettingsDialog, { open: settingsOpen, onOpenChange: setSettingsOpen, title: settingsTitle, Content: adapter.SettingsDialogContent }))] }));
|
|
199
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FlowEditorIssue } from './adapter.js';
|
|
2
|
+
export declare function isBlocked(issues: readonly FlowEditorIssue[]): boolean;
|
|
3
|
+
export interface IssuesPopoverProps {
|
|
4
|
+
issues: readonly FlowEditorIssue[];
|
|
5
|
+
nodeLabelFor: (nodeId: string) => string;
|
|
6
|
+
onJump: (nodeId: string) => void;
|
|
7
|
+
open?: boolean;
|
|
8
|
+
onOpenChange?: (open: boolean) => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function IssuesPopover({ issues, nodeLabelFor, onJump, open, onOpenChange, className, }: IssuesPopoverProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { AlertTriangle, ChevronRight, Info } from 'lucide-react';
|
|
6
|
+
import { Button } from '../../components/button.js';
|
|
7
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../components/popover.js';
|
|
8
|
+
import { cn } from '../../lib/cn.js';
|
|
9
|
+
export function isBlocked(issues) {
|
|
10
|
+
return issues.some((issue) => issue.severity === 'error');
|
|
11
|
+
}
|
|
12
|
+
function severityRank(severity) {
|
|
13
|
+
return severity === 'error' ? 0 : severity === 'warning' ? 1 : 2;
|
|
14
|
+
}
|
|
15
|
+
function highestSeverity(issues) {
|
|
16
|
+
return issues.reduce((worst, issue) => {
|
|
17
|
+
const s = issue.severity ?? 'error';
|
|
18
|
+
return severityRank(s) < severityRank(worst) ? s : worst;
|
|
19
|
+
}, 'info');
|
|
20
|
+
}
|
|
21
|
+
function severityClass(severity) {
|
|
22
|
+
switch (severity ?? 'error') {
|
|
23
|
+
case 'error':
|
|
24
|
+
return 'text-destructive';
|
|
25
|
+
case 'warning':
|
|
26
|
+
return 'text-amber-600 dark:text-amber-400';
|
|
27
|
+
default:
|
|
28
|
+
return 'text-muted-foreground';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function SeverityIcon({ severity, label, className, }) {
|
|
32
|
+
const Icon = (severity ?? 'error') === 'info' ? Info : AlertTriangle;
|
|
33
|
+
return (_jsx(Icon, { role: "img", "aria-label": label, className: cn('size-3.5 shrink-0', severityClass(severity), className), strokeWidth: 1.8 }));
|
|
34
|
+
}
|
|
35
|
+
function IssuesList({ issues, nodeLabelFor, onJump, autoFocusFirst, className }) {
|
|
36
|
+
const t = useTranslations('flowEditor.issues');
|
|
37
|
+
const listRef = useRef(null);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!autoFocusFirst)
|
|
40
|
+
return;
|
|
41
|
+
const first = listRef.current?.querySelector('[data-issue-row]');
|
|
42
|
+
first?.focus();
|
|
43
|
+
}, [autoFocusFirst]);
|
|
44
|
+
const onListKeyDown = (e) => {
|
|
45
|
+
if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp')
|
|
46
|
+
return;
|
|
47
|
+
const rows = Array.from(listRef.current?.querySelectorAll('[data-issue-row]') ?? []);
|
|
48
|
+
const index = rows.indexOf(document.activeElement);
|
|
49
|
+
if (index === -1)
|
|
50
|
+
return;
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
const next = e.key === 'ArrowDown' ? index + 1 : index - 1;
|
|
53
|
+
rows[(next + rows.length) % rows.length]?.focus();
|
|
54
|
+
};
|
|
55
|
+
return (
|
|
56
|
+
// Roving arrow-key focus across the already-focusable issue rows (each row is
|
|
57
|
+
// a real <button>); the list element only relays arrow keys to them.
|
|
58
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
|
|
59
|
+
_jsx("ul", { ref: listRef, onKeyDown: onListKeyDown, className: cn('max-h-56 overflow-auto', className), children: issues.map((issue, i) => {
|
|
60
|
+
const severity = issue.severity ?? 'error';
|
|
61
|
+
return (_jsx("li", { children: _jsxs("button", { type: "button", "data-issue-row": true, "data-severity": severity, onClick: () => onJump(issue.nodeId), className: cn('group flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-left text-[12px]', 'hover:bg-muted focus-visible:bg-muted focus-visible:ring-ring/40', 'outline-none focus-visible:ring-2'), children: [_jsx(SeverityIcon, { severity: severity, label: t(`severity.${severity}`) }), _jsx("span", { className: "text-foreground/90 shrink-0 font-medium", children: nodeLabelFor(issue.nodeId) }), _jsx("span", { className: "text-muted-foreground min-w-0 flex-1 truncate", children: issue.message }), _jsx(ChevronRight, { className: "text-muted-foreground/60 group-hover:text-foreground size-3.5 shrink-0", strokeWidth: 1.8, "aria-hidden": true })] }) }, `${issue.nodeId}:${i}`));
|
|
62
|
+
}) }));
|
|
63
|
+
}
|
|
64
|
+
export function IssuesPopover({ issues, nodeLabelFor, onJump, open, onOpenChange, className, }) {
|
|
65
|
+
const t = useTranslations('flowEditor.issues');
|
|
66
|
+
if (issues.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
const worst = highestSeverity(issues);
|
|
69
|
+
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { type: "button", size: "sm", variant: "ghost", className: cn('gap-1.5', severityClass(worst), 'hover:bg-muted', className), "data-testid": "flow-editor-issues-toggle", children: [_jsx(SeverityIcon, { severity: worst, label: t(`severity.${worst}`) }), t('count', { count: issues.length })] }) }), _jsxs(PopoverContent, { align: "end", sideOffset: 8, className: "w-[360px] max-w-[calc(100vw-2rem)] gap-2 p-2", role: "alert", "aria-label": t('title'), "data-testid": "flow-editor-issues", children: [_jsxs("div", { className: "flex items-center gap-2 px-1 py-0.5", children: [_jsx(SeverityIcon, { severity: worst, label: t(`severity.${worst}`), "aria-hidden": true }), _jsx("span", { className: cn('text-[12px] font-medium', severityClass(worst)), children: t('title') })] }), _jsx(IssuesList, { issues: issues, nodeLabelFor: nodeLabelFor, onJump: onJump, className: "pr-1", autoFocusFirst: open !== false })] })] }));
|
|
70
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Node as RfNode } from 'reactflow';
|
|
3
|
+
import { type NodeLibraryGroup } from '../canvas/node-library.js';
|
|
4
|
+
export declare function FlowLibraryRail<TData>({ selectedNode, groups, onAddKind, onDeselect, renderConfig, headingFont, testId, draggable, libraryHeader, libraryFooter, libraryTestIdPrefix, searchPlaceholder, emptyText, resultsHeading, }: {
|
|
5
|
+
selectedNode: RfNode<TData> | null;
|
|
6
|
+
groups: NodeLibraryGroup[];
|
|
7
|
+
onAddKind: (kind: string) => void;
|
|
8
|
+
onDeselect: () => void;
|
|
9
|
+
renderConfig: (node: RfNode<TData>) => ReactNode;
|
|
10
|
+
headingFont?: string;
|
|
11
|
+
testId?: string;
|
|
12
|
+
draggable?: boolean;
|
|
13
|
+
libraryHeader?: ReactNode;
|
|
14
|
+
libraryFooter?: ReactNode;
|
|
15
|
+
libraryTestIdPrefix?: string;
|
|
16
|
+
searchPlaceholder?: string;
|
|
17
|
+
emptyText?: string;
|
|
18
|
+
resultsHeading?: string;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslations } from 'use-intl';
|
|
4
|
+
import { FLOW_NODE_DND_MIME } from '../canvas/canvas-frame.js';
|
|
5
|
+
import { NodeSearchList } from '../canvas/node-library.js';
|
|
6
|
+
import { cn } from '../../lib/cn.js';
|
|
7
|
+
import { FlowSideRail } from './side-rail.js';
|
|
8
|
+
export function FlowLibraryRail({ selectedNode, groups, onAddKind, onDeselect, renderConfig, headingFont, testId = 'flow-library-rail', draggable = true, libraryHeader, libraryFooter, libraryTestIdPrefix = 'flow-lib', searchPlaceholder, emptyText, resultsHeading, }) {
|
|
9
|
+
const t = useTranslations('flowEditor');
|
|
10
|
+
return (_jsx(FlowSideRail, { testId: testId, backHeader: selectedNode
|
|
11
|
+
? {
|
|
12
|
+
onBack: onDeselect,
|
|
13
|
+
backLabel: t('rail.back'),
|
|
14
|
+
title: t('rail.editStep'),
|
|
15
|
+
...(headingFont ? { headingFont } : {}),
|
|
16
|
+
}
|
|
17
|
+
: null, children: selectedNode ? (_jsx("div", { className: "min-h-0 flex-1 overflow-auto px-5 py-5", children: renderConfig(selectedNode) })) : (_jsxs(_Fragment, { children: [libraryHeader ?? (_jsxs("div", { className: "border-border/50 border-b px-4 pt-4 pb-3", children: [_jsx("h2", { className: cn('text-[14px]', headingFont ?? 'font-semibold'), children: t('rail.libraryTitle') }), _jsx("p", { className: "text-muted-foreground mt-1 text-[11px] leading-snug", children: t('rail.libraryHint') })] })), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(NodeSearchList, { groups: groups, onPick: onAddKind, emptyText: emptyText ?? t('rail.noSteps'), searchPlaceholder: searchPlaceholder ?? t('rail.searchSteps'), resultsHeading: resultsHeading ?? t('rail.searchResults'), testIdPrefix: libraryTestIdPrefix, fill: true, draggable: draggable, ...(draggable
|
|
18
|
+
? {
|
|
19
|
+
onItemDragStart: (kind, event) => {
|
|
20
|
+
event.dataTransfer.setData(FLOW_NODE_DND_MIME, kind);
|
|
21
|
+
event.dataTransfer.effectAllowed = 'copy';
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
: {}) }) }), libraryFooter] })) }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FlowCanvasController } from '../canvas/canvas-frame.js';
|
|
2
|
+
import type { DagEditor } from './use-dag-editor.js';
|
|
3
|
+
import type { FlowStickyNotes } from './adapter.js';
|
|
4
|
+
/**
|
|
5
|
+
* Extras the shell layers onto the controller beyond the plain `useDagEditor`
|
|
6
|
+
* pass-through: the optional sticky-notes slice (wired only when the domain
|
|
7
|
+
* supports it).
|
|
8
|
+
*/
|
|
9
|
+
export interface MakeControllerExtras {
|
|
10
|
+
readonly stickyNotes?: FlowStickyNotes | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build the `FlowCanvasController` the shared canvas frame consumes from a
|
|
14
|
+
* `useDagEditor` instance. Pure field pass-through — every mutating operation
|
|
15
|
+
* the canvas triggers (connect, insert-on-edge, delete, duplicate, undo/redo,
|
|
16
|
+
* tidy) routes back into the hook — plus the optional annotation slice.
|
|
17
|
+
*/
|
|
18
|
+
export declare function makeFlowCanvasController<TData, TEdge>(dag: DagEditor<TData, TEdge>, extras?: MakeControllerExtras): FlowCanvasController<TData, TEdge>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the `FlowCanvasController` the shared canvas frame consumes from a
|
|
3
|
+
* `useDagEditor` instance. Pure field pass-through — every mutating operation
|
|
4
|
+
* the canvas triggers (connect, insert-on-edge, delete, duplicate, undo/redo,
|
|
5
|
+
* tidy) routes back into the hook — plus the optional annotation slice.
|
|
6
|
+
*/
|
|
7
|
+
export function makeFlowCanvasController(dag, extras) {
|
|
8
|
+
const sticky = extras?.stickyNotes;
|
|
9
|
+
return {
|
|
10
|
+
nodes: dag.nodes,
|
|
11
|
+
edges: dag.edges,
|
|
12
|
+
onNodesChange: dag.onNodesChange,
|
|
13
|
+
onEdgesChange: dag.onEdgesChange,
|
|
14
|
+
onConnect: dag.onConnect,
|
|
15
|
+
isValidConnection: dag.isValidConnection,
|
|
16
|
+
setNodes: dag.setNodes,
|
|
17
|
+
setEdges: dag.setEdges,
|
|
18
|
+
insertNodeOnEdge: (edgeId, kind, title) => {
|
|
19
|
+
dag.insertNodeOnEdge(edgeId, kind, title);
|
|
20
|
+
},
|
|
21
|
+
addConnectedNodeAt: (args) => {
|
|
22
|
+
dag.addConnectedNodeAt(args);
|
|
23
|
+
},
|
|
24
|
+
removeNode: dag.removeNode,
|
|
25
|
+
removeNodesAndEdges: dag.removeNodesAndEdges,
|
|
26
|
+
duplicateNode: dag.duplicateNode,
|
|
27
|
+
undo: dag.undo,
|
|
28
|
+
redo: dag.redo,
|
|
29
|
+
canUndo: dag.canUndo,
|
|
30
|
+
canRedo: dag.canRedo,
|
|
31
|
+
tidy: dag.tidy,
|
|
32
|
+
deselectAll: dag.deselectAll,
|
|
33
|
+
focusRequest: dag.focusRequest,
|
|
34
|
+
...(sticky
|
|
35
|
+
? {
|
|
36
|
+
annotations: sticky.annotations,
|
|
37
|
+
addAnnotation: sticky.addAnnotation,
|
|
38
|
+
updateAnnotation: sticky.updateAnnotation,
|
|
39
|
+
commitAnnotation: sticky.commitAnnotation,
|
|
40
|
+
removeAnnotation: sticky.removeAnnotation,
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const CHIP_PALETTE = {
|
|
2
|
+
strong: 'bg-foreground text-background',
|
|
3
|
+
neutral: 'bg-muted text-muted-foreground',
|
|
4
|
+
primary: 'bg-primary/12 text-primary',
|
|
5
|
+
blue: 'bg-blue-500/12 text-blue-700 dark:text-blue-300',
|
|
6
|
+
violet: 'bg-violet-500/12 text-violet-700 dark:text-violet-300',
|
|
7
|
+
emerald: 'bg-emerald-500/12 text-emerald-700 dark:text-emerald-300',
|
|
8
|
+
amber: 'bg-amber-500/12 text-amber-700 dark:text-amber-300',
|
|
9
|
+
indigo: 'bg-indigo-500/12 text-indigo-700 dark:text-indigo-300',
|
|
10
|
+
rose: 'bg-rose-500/12 text-rose-700 dark:text-rose-300',
|
|
11
|
+
cyan: 'bg-cyan-500/12 text-cyan-700 dark:text-cyan-300',
|
|
12
|
+
teal: 'bg-teal-500/12 text-teal-700 dark:text-teal-300',
|
|
13
|
+
};
|
|
14
|
+
export function chipClassForKey(key) {
|
|
15
|
+
return CHIP_PALETTE[key];
|
|
16
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface SavePublishBarProps {
|
|
2
|
+
readonly isDirty: boolean;
|
|
3
|
+
readonly justSaved: boolean;
|
|
4
|
+
readonly isSaving: boolean;
|
|
5
|
+
readonly publishBlocked: boolean;
|
|
6
|
+
readonly canSaveEmpty?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Whether there is anything to publish — unsaved canvas edits OR a
|
|
9
|
+
* saved-but-unpublished draft. Save clears `isDirty` but not this, so Publish
|
|
10
|
+
* stays available after a plain Save. Defaults to `isDirty` (edit-then-publish)
|
|
11
|
+
* when a caller doesn't track draft-vs-published separately.
|
|
12
|
+
*/
|
|
13
|
+
readonly publishReady?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Per-action in-flight flags for the spinner. The button label never changes
|
|
16
|
+
* (that shifts width and flickers on every autosave); instead the leading icon
|
|
17
|
+
* becomes a spinner. Both default to `isSaving` so callers that don't
|
|
18
|
+
* distinguish still get a spinner on the acting button.
|
|
19
|
+
*/
|
|
20
|
+
readonly savePending?: boolean;
|
|
21
|
+
readonly publishPending?: boolean;
|
|
22
|
+
readonly onSave: () => void;
|
|
23
|
+
readonly onPublish: () => void;
|
|
24
|
+
readonly publishTitle?: string;
|
|
25
|
+
readonly className?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function SavePublishBar({ isDirty, justSaved, isSaving, publishBlocked, canSaveEmpty, publishReady, savePending, publishPending, onSave, onPublish, publishTitle, className, }: SavePublishBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Check, Loader2, UploadCloud } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { Button } from '../../components/button.js';
|
|
6
|
+
export function SavePublishBar({ isDirty, justSaved, isSaving, publishBlocked, canSaveEmpty = false, publishReady, savePending, publishPending, onSave, onPublish, publishTitle, className, }) {
|
|
7
|
+
const t = useTranslations('flowEditor.actions');
|
|
8
|
+
const canSave = (isDirty || canSaveEmpty) && !isSaving;
|
|
9
|
+
const hasUnpublished = publishReady ?? isDirty;
|
|
10
|
+
const canPublish = hasUnpublished && !publishBlocked && !isSaving;
|
|
11
|
+
const saving = savePending ?? isSaving;
|
|
12
|
+
const publishing = publishPending ?? isSaving;
|
|
13
|
+
return (_jsxs("div", { className: className, children: [_jsxs(Button, { type: "button", size: "sm", variant: "outline", disabled: !canSave, "aria-busy": saving, onClick: onSave, className: "gap-1.5", "data-testid": "flow-editor-save", children: [saving ? (_jsx(Loader2, { className: "size-3.5 shrink-0 animate-spin", strokeWidth: 1.8, "aria-hidden": true })) : justSaved ? (_jsx(Check, { className: "size-3.5 shrink-0", strokeWidth: 2, "aria-hidden": true })) : isDirty ? (_jsx("span", { "aria-hidden": true, className: "bg-foreground/50 size-1.5 shrink-0 rounded-full" })) : null, t('save')] }), _jsxs(Button, { type: "button", size: "sm", disabled: !canPublish, "aria-busy": publishing, onClick: onPublish, title: publishTitle, className: "gap-1.5", "data-testid": "flow-editor-publish", children: [publishing ? (_jsx(Loader2, { className: "size-3.5 shrink-0 animate-spin", strokeWidth: 1.8, "aria-hidden": true })) : (_jsx(UploadCloud, { className: "size-3.5 shrink-0", strokeWidth: 1.8, "aria-hidden": true })), t('publish')] })] }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import type { SettingsDialogContentProps } from './adapter.js';
|
|
3
|
+
export interface SettingsDialogProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onOpenChange: (open: boolean) => void;
|
|
6
|
+
title: string;
|
|
7
|
+
Content?: ComponentType<SettingsDialogContentProps>;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
testId?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function SettingsDialog({ open, onOpenChange, title, Content, children, testId, }: SettingsDialogProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'use-intl';
|
|
5
|
+
import { cn } from '../../lib/cn.js';
|
|
6
|
+
import { Dialog, DialogClose, DialogContent, DialogTitle } from '../../components/dialog.js';
|
|
7
|
+
export function SettingsDialog({ open, onOpenChange, title, Content, children, testId = 'flow-editor-settings-dialog', }) {
|
|
8
|
+
const t = useTranslations('flowEditor');
|
|
9
|
+
const close = () => onOpenChange(false);
|
|
10
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: open ? (_jsxs(DialogContent, { "data-testid": testId, showCloseButton: false, className: cn('gap-0 overflow-hidden p-0', 'h-[min(680px,90vh)] sm:max-w-[min(880px,92vw)]'), children: [_jsx(DialogTitle, { className: "sr-only", children: title }), _jsx(DialogClose, { "aria-label": t('settings.close'), className: "hover:bg-muted focus-visible:ring-ring/50 text-muted-foreground hover:text-foreground absolute top-3 right-3 z-10 inline-flex size-7 items-center justify-center rounded-md transition-colors outline-none focus-visible:ring-1", children: _jsx(X, { className: "size-4", strokeWidth: 1.5, "aria-hidden": true }) }), _jsx("div", { className: "bg-background flex min-h-0 flex-col overflow-y-auto", children: Content ? _jsx(Content, { onClose: close }) : children })] })) : null }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { Info } from 'lucide-react';
|
|
3
|
+
export declare function SettingsDialogShell({ title, nav, children, }: {
|
|
4
|
+
title: string;
|
|
5
|
+
nav: ReactNode;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
export declare function NavItem({ icon: Icon, label, active, onClick, }: {
|
|
9
|
+
icon: typeof Info;
|
|
10
|
+
label: string;
|
|
11
|
+
active: boolean;
|
|
12
|
+
onClick: () => void;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
export declare function SettingsNotice({ children }: {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export declare function SettingsSection({ title, description, children, }: {
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {} from 'react';
|
|
4
|
+
import { Info } from 'lucide-react';
|
|
5
|
+
import { DialogContent, DialogTitle } from '../../components/dialog.js';
|
|
6
|
+
import { cn } from '../../lib/cn.js';
|
|
7
|
+
export function SettingsDialogShell({ title, nav, children, }) {
|
|
8
|
+
return (_jsxs(DialogContent, { showCloseButton: true, className: cn('gap-0 overflow-hidden p-0', 'h-[min(680px,90vh)] sm:max-w-[min(880px,92vw)]', 'grid grid-cols-[220px_1fr] grid-rows-1'), children: [_jsx(DialogTitle, { className: "sr-only", children: title }), _jsxs("aside", { className: "border-border bg-sidebar flex min-h-0 flex-col border-r", children: [_jsx("div", { className: "text-muted-foreground/70 px-4 pt-4 pb-1 font-mono text-[10.5px] tracking-tight", children: title }), _jsx("ul", { className: "flex flex-1 flex-col gap-px overflow-y-auto px-2 py-1", children: nav })] }), _jsx("div", { className: "bg-background min-w-0 overflow-y-auto", children: _jsx("div", { className: "mx-auto max-w-2xl px-8 py-7", children: children }) })] }));
|
|
9
|
+
}
|
|
10
|
+
export function NavItem({ icon: Icon, label, active, onClick, }) {
|
|
11
|
+
return (_jsx("li", { children: _jsxs("button", { type: "button", onClick: onClick, "data-active": active || undefined, className: cn('flex h-7 w-full cursor-pointer items-center gap-2 rounded-md px-2 text-[12.5px] transition-colors', active
|
|
12
|
+
? 'bg-sidebar-accent text-sidebar-accent-foreground'
|
|
13
|
+
: 'text-sidebar-foreground/85 hover:bg-sidebar-accent/60 hover:text-sidebar-foreground'), children: [_jsx("span", { className: cn('shrink-0', active ? 'text-primary' : 'text-muted-foreground'), children: _jsx(Icon, { className: "size-3.5", strokeWidth: 1.8, "aria-hidden": true }) }), _jsx("span", { className: "truncate", children: label })] }) }));
|
|
14
|
+
}
|
|
15
|
+
export function SettingsNotice({ children }) {
|
|
16
|
+
return (_jsxs("div", { role: "status", className: "border-border/60 text-muted-foreground mb-4 flex items-start gap-2.5 border-b pb-4 text-[12.5px] leading-relaxed", children: [_jsx(Info, { className: "text-foreground/70 mt-0.5 size-4 shrink-0", strokeWidth: 1.8, "aria-hidden": true }), _jsx("span", { children: children })] }));
|
|
17
|
+
}
|
|
18
|
+
export function SettingsSection({ title, description, children, }) {
|
|
19
|
+
return (_jsxs("section", { className: "flex flex-col gap-4", children: [_jsxs("header", { children: [_jsx("h2", { className: "text-foreground text-[18px] font-semibold tracking-tight", children: title }), _jsx("p", { className: "text-muted-foreground mt-1 text-[12.5px] leading-relaxed", children: description })] }), children] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface FlowSideRailBackHeader {
|
|
3
|
+
onBack: () => void;
|
|
4
|
+
/** Accessible label for the back button. */
|
|
5
|
+
backLabel: string;
|
|
6
|
+
/** Header title shown next to the back button. */
|
|
7
|
+
title: ReactNode;
|
|
8
|
+
headingFont?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The responsive detail rail shell shared by the flow editors and the run
|
|
12
|
+
* viewer: a bottom strip below `xl`, a 360px right sidebar at `xl`. Optionally
|
|
13
|
+
* renders a back-button header (owned by the rail); the body is whatever the
|
|
14
|
+
* caller composes as children. Domain content (node library / config /
|
|
15
|
+
* run overview / step detail) lives in the caller — this owns only the chrome.
|
|
16
|
+
*/
|
|
17
|
+
export declare function FlowSideRail({ backHeader, children, testId, className, }: {
|
|
18
|
+
backHeader?: FlowSideRailBackHeader | null;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
testId?: string;
|
|
21
|
+
className?: string;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ArrowLeft } from 'lucide-react';
|
|
4
|
+
import { Button } from '../../components/button.js';
|
|
5
|
+
import { cn } from '../../lib/cn.js';
|
|
6
|
+
/**
|
|
7
|
+
* The responsive detail rail shell shared by the flow editors and the run
|
|
8
|
+
* viewer: a bottom strip below `xl`, a 360px right sidebar at `xl`. Optionally
|
|
9
|
+
* renders a back-button header (owned by the rail); the body is whatever the
|
|
10
|
+
* caller composes as children. Domain content (node library / config /
|
|
11
|
+
* run overview / step detail) lives in the caller — this owns only the chrome.
|
|
12
|
+
*/
|
|
13
|
+
export function FlowSideRail({ backHeader, children, testId = 'flow-side-rail', className, }) {
|
|
14
|
+
return (_jsxs("aside", { "data-testid": testId, className: cn('border-border/60 bg-muted/15 flex min-h-0 w-full flex-1 flex-col border-t', 'xl:w-[360px] xl:flex-none xl:self-stretch xl:border-t-0 xl:border-l', className), children: [backHeader ? (_jsxs("div", { className: "border-border/50 flex shrink-0 items-center gap-1.5 border-b px-3 py-2.5", children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: backHeader.onBack, "aria-label": backHeader.backLabel, className: "text-muted-foreground hover:text-foreground -ml-1", children: _jsx(ArrowLeft, { className: "size-4", strokeWidth: 1.8 }) }), _jsx("span", { className: cn('text-[13px] font-medium', backHeader.headingFont), children: backHeader.title })] })) : null, children] }));
|
|
15
|
+
}
|