@selvajs/ui 6.6.0-beta.8 → 7.0.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 +0 -3
- package/dist/components/ErrorScreen.svelte +1 -1
- package/dist/components/compute/AppLayout.svelte +95 -37
- package/dist/components/compute/AppLayout.svelte.d.ts +18 -1
- package/dist/components/compute/CollapsedPanelStrip.svelte +4 -2
- package/dist/components/compute/ComputeApp.svelte +6 -2
- package/dist/components/compute/ComputeApp.svelte.d.ts +1 -1
- package/dist/components/compute/ComputeMessagesDialog.svelte +3 -3
- package/dist/components/compute/ParameterPresetManager.svelte +9 -21
- package/dist/components/compute/SolveMessages.svelte +101 -87
- package/dist/components/compute/SolveMessages.svelte.d.ts +12 -0
- package/dist/components/layout/AppShell.svelte +2 -2
- package/dist/components/layout/FooterItemRenderer.svelte +2 -2
- package/dist/components/layout/FooterItemRenderer.svelte.d.ts +1 -1
- package/dist/components/layout/PageContent.svelte +3 -2
- package/dist/components/layout/PageFooter.svelte +8 -6
- package/dist/components/layout/PageHeader.svelte +3 -3
- package/dist/components/layout/SideNav.svelte +56 -15
- package/dist/components/layout/SubNav.svelte +2 -2
- package/dist/components/preview/ChartOutput.svelte +158 -62
- package/dist/components/preview/Group.svelte +14 -9
- package/dist/components/preview/HelpButton.svelte +1 -1
- package/dist/components/preview/ImageOutput.svelte +16 -11
- package/dist/components/preview/InputControl.svelte +115 -50
- package/dist/components/preview/InputControl.svelte.d.ts +5 -1
- package/dist/components/preview/OutputDisplay.svelte +26 -17
- package/dist/components/preview/TabBar.svelte +8 -6
- package/dist/components/preview/TabBar.svelte.d.ts +2 -0
- package/dist/components/preview/TabContent.svelte +65 -33
- package/dist/components/preview/TabContent.svelte.d.ts +5 -1
- package/dist/components/preview/TabLayout.svelte +29 -8
- package/dist/components/preview/TabLayout.svelte.d.ts +5 -0
- package/dist/components/preview/inputs/CheckboxInput.svelte +1 -1
- package/dist/components/preview/inputs/ChecklistInput.svelte +11 -6
- package/dist/components/preview/inputs/ChecklistInput.svelte.d.ts +2 -1
- package/dist/components/preview/inputs/ColorInput.svelte +356 -14
- package/dist/components/preview/inputs/ColorInput.svelte.d.ts +1 -0
- package/dist/components/preview/inputs/DropdownInput.svelte +18 -6
- package/dist/components/preview/inputs/DropdownInput.svelte.d.ts +2 -1
- package/dist/components/preview/inputs/FileInput.svelte +71 -75
- package/dist/components/preview/inputs/GeometryInput.svelte +128 -0
- package/dist/components/preview/inputs/GeometryInput.svelte.d.ts +21 -0
- package/dist/components/preview/inputs/NumberInput.svelte +8 -8
- package/dist/components/preview/inputs/OptionGlyph.svelte +29 -0
- package/dist/components/preview/inputs/OptionGlyph.svelte.d.ts +8 -0
- package/dist/components/preview/inputs/SegmentedInput.svelte +45 -0
- package/dist/components/preview/inputs/SegmentedInput.svelte.d.ts +12 -0
- package/dist/components/preview/inputs/TextInput.svelte +5 -5
- package/dist/components/preview/inputs/TileInput.svelte +83 -0
- package/dist/components/preview/inputs/TileInput.svelte.d.ts +12 -0
- package/dist/components/preview/inputs/index.d.ts +3 -0
- package/dist/components/preview/inputs/index.js +3 -0
- package/dist/components/primitives/CalculateButton.svelte +10 -4
- package/dist/components/primitives/Callout.svelte +8 -6
- package/dist/components/primitives/Callout.svelte.d.ts +4 -4
- package/dist/components/primitives/DataTable.svelte +1 -1
- package/dist/components/primitives/FilterableDropdown.svelte +13 -1
- package/dist/components/primitives/FilterableDropdown.svelte.d.ts +5 -0
- package/dist/components/primitives/ImageUploadField.svelte +1 -1
- package/dist/components/primitives/SolvingIndicator.svelte +245 -19
- package/dist/components/primitives/SolvingIndicator.svelte.d.ts +15 -0
- package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
- package/dist/components/primitives/badge/badge.svelte +2 -2
- package/dist/components/primitives/badge/badge.svelte.d.ts +6 -6
- package/dist/components/primitives/button/button.svelte +9 -7
- package/dist/components/primitives/button/button.svelte.d.ts +19 -16
- package/dist/components/primitives/card/card.svelte +1 -1
- package/dist/components/primitives/card/card.svelte.d.ts +1 -1
- package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
- package/dist/components/primitives/command/command-input.svelte +1 -1
- package/dist/components/primitives/command/command-item.svelte +1 -1
- package/dist/components/primitives/command/command-link-item.svelte +1 -1
- package/dist/components/primitives/command/command.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-item.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
- package/dist/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
- package/dist/components/primitives/dialog/dialog-content.svelte +3 -3
- package/dist/components/primitives/drawer/Drawer.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
- package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +2 -0
- package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +2 -0
- package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
- package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +2 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
- package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
- package/dist/components/primitives/index.d.ts +1 -1
- package/dist/components/primitives/index.js +1 -1
- package/dist/components/primitives/input/input.svelte +2 -2
- package/dist/components/primitives/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/primitives/input-group/input-group.svelte +1 -1
- package/dist/components/primitives/mode-toggle/mode-toggle.svelte +2 -2
- package/dist/components/primitives/popover/popover-content.svelte +1 -1
- package/dist/components/primitives/resizable/resizable-handle.svelte +2 -2
- package/dist/components/primitives/search/search.svelte +1 -1
- package/dist/components/primitives/select/select-content.svelte +1 -1
- package/dist/components/primitives/select/select-item.svelte +1 -1
- package/dist/components/primitives/select/select-trigger.svelte +1 -1
- package/dist/components/primitives/slider/slider.svelte +1 -1
- package/dist/components/primitives/switch/switch.svelte +1 -1
- package/dist/components/primitives/tabs/tabs-list.svelte +2 -2
- package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +6 -6
- package/dist/components/primitives/tabs/tabs-trigger.svelte +2 -2
- package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +6 -6
- package/dist/components/primitives/textarea/textarea.svelte +1 -1
- package/dist/components/primitives/toggle/toggle.svelte +11 -3
- package/dist/components/primitives/toggle/toggle.svelte.d.ts +13 -4
- package/dist/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
- package/dist/components/primitives/toggle-group/toggle-group.svelte +1 -1
- package/dist/components/primitives/tooltip/index.d.ts +6 -0
- package/dist/components/primitives/tooltip/index.js +8 -0
- package/dist/components/primitives/tooltip/tooltip-content.svelte +34 -0
- package/dist/components/primitives/tooltip/tooltip-content.svelte.d.ts +10 -0
- package/dist/components/primitives/tooltip/tooltip-portal.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip-portal.svelte.d.ts +4 -0
- package/dist/components/primitives/tooltip/tooltip-provider.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip-provider.svelte.d.ts +4 -0
- package/dist/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip-trigger.svelte.d.ts +4 -0
- package/dist/components/primitives/tooltip/tooltip.svelte +7 -0
- package/dist/components/primitives/tooltip/tooltip.svelte.d.ts +4 -0
- package/dist/components/viewer/MeshMetadataPanel.svelte +2 -2
- package/dist/components/viewer/SceneManager.svelte +32 -21
- package/dist/components/viewer/SceneManager.svelte.d.ts +3 -1
- package/dist/components/viewer/Viewer.svelte +135 -185
- package/dist/components/viewer/Viewer.svelte.d.ts +7 -2
- package/dist/components/viewer/ViewerDock.svelte +404 -0
- package/dist/components/viewer/ViewerDock.svelte.d.ts +42 -0
- package/dist/composables/useFooterItem.svelte.d.ts +1 -1
- package/dist/composables/useFooterItem.svelte.js +1 -1
- package/dist/compute/liveProgress.svelte.d.ts +16 -0
- package/dist/compute/liveProgress.svelte.js +90 -0
- package/dist/compute/solving.svelte.d.ts +11 -7
- package/dist/compute/solving.svelte.js +64 -45
- package/dist/constants.d.ts +6 -8
- package/dist/constants.js +8 -11
- package/dist/contexts/hostContext.svelte.d.ts +39 -0
- package/dist/contexts/hostContext.svelte.js +9 -0
- package/dist/features/chart/plotly-compat.d.ts +15 -0
- package/dist/features/chart/plotly-compat.js +124 -0
- package/dist/i18n/messages.d.ts +50 -0
- package/dist/i18n/messages.js +84 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/public.d.ts +1 -0
- package/dist/public.js +1 -0
- package/dist/schema/host-support.d.ts +29 -0
- package/dist/schema/host-support.js +97 -0
- package/dist/schema/option-display.d.ts +12 -0
- package/dist/schema/option-display.js +29 -0
- package/dist/schema/param-exporter.d.ts +4 -1
- package/dist/schema/param-exporter.js +10 -6
- package/dist/schema/visibility-rules.d.ts +7 -1
- package/dist/schema/visibility-rules.js +17 -0
- package/dist/styles/base.css +94 -84
- package/dist/utils/color-space.d.ts +21 -0
- package/dist/utils/color-space.js +90 -0
- package/dist/utils/file-download.js +6 -10
- package/dist/utils/host-files.d.ts +12 -0
- package/dist/utils/host-files.js +46 -0
- package/dist/utils/webp-to-png.d.ts +10 -0
- package/dist/utils/webp-to-png.js +41 -0
- package/package.json +31 -23
- package/src/lib/components/ErrorScreen.svelte +1 -1
- package/src/lib/components/compute/AppLayout.svelte +96 -38
- package/src/lib/components/compute/CollapsedPanelStrip.svelte +4 -2
- package/src/lib/components/compute/ComputeApp.svelte +6 -2
- package/src/lib/components/compute/ComputeMessagesDialog.svelte +4 -4
- package/src/lib/components/compute/ParameterPresetManager.svelte +9 -21
- package/src/lib/components/compute/SolveMessages.svelte +103 -89
- package/src/lib/components/layout/AppShell.svelte +2 -2
- package/src/lib/components/layout/FooterItemRenderer.svelte +2 -2
- package/src/lib/components/layout/PageContent.svelte +3 -2
- package/src/lib/components/layout/PageFooter.svelte +8 -6
- package/src/lib/components/layout/PageHeader.svelte +3 -3
- package/src/lib/components/layout/SideNav.svelte +56 -15
- package/src/lib/components/layout/SubNav.svelte +2 -2
- package/src/lib/components/preview/ChartOutput.svelte +158 -62
- package/src/lib/components/preview/Group.svelte +14 -9
- package/src/lib/components/preview/HelpButton.svelte +1 -1
- package/src/lib/components/preview/ImageOutput.svelte +16 -11
- package/src/lib/components/preview/InputControl.svelte +115 -50
- package/src/lib/components/preview/OutputDisplay.svelte +26 -17
- package/src/lib/components/preview/TabBar.svelte +8 -6
- package/src/lib/components/preview/TabContent.svelte +65 -33
- package/src/lib/components/preview/TabLayout.svelte +29 -8
- package/src/lib/components/preview/inputs/CheckboxInput.svelte +1 -1
- package/src/lib/components/preview/inputs/ChecklistInput.svelte +11 -6
- package/src/lib/components/preview/inputs/ColorInput.svelte +356 -14
- package/src/lib/components/preview/inputs/DropdownInput.svelte +18 -6
- package/src/lib/components/preview/inputs/FileInput.svelte +71 -75
- package/src/lib/components/preview/inputs/GeometryInput.svelte +128 -0
- package/src/lib/components/preview/inputs/NumberInput.svelte +8 -8
- package/src/lib/components/preview/inputs/OptionGlyph.svelte +29 -0
- package/src/lib/components/preview/inputs/SegmentedInput.svelte +45 -0
- package/src/lib/components/preview/inputs/TextInput.svelte +5 -5
- package/src/lib/components/preview/inputs/TileInput.svelte +83 -0
- package/src/lib/components/preview/inputs/index.ts +3 -0
- package/src/lib/components/primitives/CalculateButton.svelte +10 -4
- package/src/lib/components/primitives/Callout.svelte +9 -7
- package/src/lib/components/primitives/DataTable.svelte +1 -1
- package/src/lib/components/primitives/FilterableDropdown.svelte +14 -2
- package/src/lib/components/primitives/ImageUploadField.svelte +1 -1
- package/src/lib/components/primitives/IssueList.svelte +1 -1
- package/src/lib/components/primitives/SeverityIcon.svelte +1 -1
- package/src/lib/components/primitives/SolvingIndicator.svelte +245 -19
- package/src/lib/components/primitives/StateDisplay.svelte +1 -1
- package/src/lib/components/primitives/alert-dialog/alert-dialog-action.svelte +2 -2
- package/src/lib/components/primitives/alert-dialog/alert-dialog-cancel.svelte +2 -2
- package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +2 -2
- package/src/lib/components/primitives/alert-dialog/alert-dialog-description.svelte +1 -1
- package/src/lib/components/primitives/alert-dialog/alert-dialog-footer.svelte +1 -1
- package/src/lib/components/primitives/alert-dialog/alert-dialog-header.svelte +1 -1
- package/src/lib/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
- package/src/lib/components/primitives/alert-dialog/alert-dialog-title.svelte +1 -1
- package/src/lib/components/primitives/badge/badge.svelte +3 -3
- package/src/lib/components/primitives/button/button.svelte +9 -7
- package/src/lib/components/primitives/button-group/button-group-separator.svelte +2 -2
- package/src/lib/components/primitives/button-group/button-group-text.svelte +1 -1
- package/src/lib/components/primitives/button-group/button-group.svelte +1 -1
- package/src/lib/components/primitives/card/card-content.svelte +1 -1
- package/src/lib/components/primitives/card/card-description.svelte +1 -1
- package/src/lib/components/primitives/card/card-footer.svelte +1 -1
- package/src/lib/components/primitives/card/card-header.svelte +1 -1
- package/src/lib/components/primitives/card/card-title.svelte +1 -1
- package/src/lib/components/primitives/card/card.svelte +2 -2
- package/src/lib/components/primitives/checkbox/checkbox.svelte +2 -2
- package/src/lib/components/primitives/command/command-dialog.svelte +2 -2
- package/src/lib/components/primitives/command/command-empty.svelte +1 -1
- package/src/lib/components/primitives/command/command-group.svelte +1 -1
- package/src/lib/components/primitives/command/command-input.svelte +3 -3
- package/src/lib/components/primitives/command/command-item.svelte +2 -2
- package/src/lib/components/primitives/command/command-link-item.svelte +2 -2
- package/src/lib/components/primitives/command/command-list.svelte +1 -1
- package/src/lib/components/primitives/command/command-separator.svelte +1 -1
- package/src/lib/components/primitives/command/command-shortcut.svelte +1 -1
- package/src/lib/components/primitives/command/command.svelte +2 -2
- package/src/lib/components/primitives/context-menu/context-menu-checkbox-item.svelte +2 -2
- package/src/lib/components/primitives/context-menu/context-menu-content.svelte +2 -2
- package/src/lib/components/primitives/context-menu/context-menu-group-heading.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-item.svelte +2 -2
- package/src/lib/components/primitives/context-menu/context-menu-label.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-radio-item.svelte +2 -2
- package/src/lib/components/primitives/context-menu/context-menu-separator.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-shortcut.svelte +1 -1
- package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +2 -2
- package/src/lib/components/primitives/context-menu/context-menu-sub-trigger.svelte +2 -2
- package/src/lib/components/primitives/dialog/dialog-content.svelte +4 -4
- package/src/lib/components/primitives/dialog/dialog-description.svelte +1 -1
- package/src/lib/components/primitives/dialog/dialog-footer.svelte +1 -1
- package/src/lib/components/primitives/dialog/dialog-header.svelte +1 -1
- package/src/lib/components/primitives/dialog/dialog-overlay.svelte +1 -1
- package/src/lib/components/primitives/dialog/dialog-title.svelte +1 -1
- package/src/lib/components/primitives/drawer/Drawer.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +32 -12
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +6 -2
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +5 -3
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-label.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +2 -2
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +1 -1
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +2 -2
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +2 -2
- package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +2 -2
- package/src/lib/components/primitives/empty/empty-content.svelte +1 -1
- package/src/lib/components/primitives/empty/empty-description.svelte +1 -1
- package/src/lib/components/primitives/empty/empty-header.svelte +1 -1
- package/src/lib/components/primitives/empty/empty-media.svelte +1 -1
- package/src/lib/components/primitives/empty/empty-title.svelte +1 -1
- package/src/lib/components/primitives/empty/empty.svelte +1 -1
- package/src/lib/components/primitives/field/field-content.svelte +1 -1
- package/src/lib/components/primitives/field/field-description.svelte +1 -1
- package/src/lib/components/primitives/field/field-error.svelte +1 -1
- package/src/lib/components/primitives/field/field-group.svelte +1 -1
- package/src/lib/components/primitives/field/field-label.svelte +2 -2
- package/src/lib/components/primitives/field/field-legend.svelte +1 -1
- package/src/lib/components/primitives/field/field-separator.svelte +2 -2
- package/src/lib/components/primitives/field/field-set.svelte +1 -1
- package/src/lib/components/primitives/field/field-title.svelte +1 -1
- package/src/lib/components/primitives/field/field.svelte +1 -1
- package/src/lib/components/primitives/index.ts +1 -1
- package/src/lib/components/primitives/input/input.svelte +3 -3
- package/src/lib/components/primitives/input-group/input-group-addon.svelte +1 -1
- package/src/lib/components/primitives/input-group/input-group-button.svelte +2 -2
- package/src/lib/components/primitives/input-group/input-group-input.svelte +2 -2
- package/src/lib/components/primitives/input-group/input-group-text.svelte +1 -1
- package/src/lib/components/primitives/input-group/input-group-textarea.svelte +2 -2
- package/src/lib/components/primitives/input-group/input-group.svelte +2 -2
- package/src/lib/components/primitives/label/label.svelte +1 -1
- package/src/lib/components/primitives/mode-toggle/mode-toggle.svelte +2 -2
- package/src/lib/components/primitives/popover/popover-content.svelte +2 -2
- package/src/lib/components/primitives/popover/popover-description.svelte +1 -1
- package/src/lib/components/primitives/popover/popover-header.svelte +1 -1
- package/src/lib/components/primitives/popover/popover-title.svelte +1 -1
- package/src/lib/components/primitives/popover/popover-trigger.svelte +1 -1
- package/src/lib/components/primitives/resizable/resizable-handle.svelte +3 -3
- package/src/lib/components/primitives/resizable/resizable-pane-group.svelte +1 -1
- package/src/lib/components/primitives/scroll-area/scroll-area-scrollbar.svelte +1 -1
- package/src/lib/components/primitives/scroll-area/scroll-area.svelte +1 -1
- package/src/lib/components/primitives/search/search.svelte +2 -2
- package/src/lib/components/primitives/select/select-content.svelte +2 -2
- package/src/lib/components/primitives/select/select-group-heading.svelte +1 -1
- package/src/lib/components/primitives/select/select-item.svelte +2 -2
- package/src/lib/components/primitives/select/select-label.svelte +1 -1
- package/src/lib/components/primitives/select/select-scroll-down-button.svelte +1 -1
- package/src/lib/components/primitives/select/select-scroll-up-button.svelte +1 -1
- package/src/lib/components/primitives/select/select-separator.svelte +2 -2
- package/src/lib/components/primitives/select/select-trigger.svelte +2 -2
- package/src/lib/components/primitives/separator/separator.svelte +1 -1
- package/src/lib/components/primitives/slider/slider.svelte +2 -2
- package/src/lib/components/primitives/spinner/spinner.svelte +1 -1
- package/src/lib/components/primitives/switch/switch.svelte +2 -2
- package/src/lib/components/primitives/tabs/tabs-content.svelte +1 -1
- package/src/lib/components/primitives/tabs/tabs-list.svelte +3 -3
- package/src/lib/components/primitives/tabs/tabs-trigger.svelte +3 -3
- package/src/lib/components/primitives/tabs/tabs.svelte +1 -1
- package/src/lib/components/primitives/textarea/textarea.svelte +2 -2
- package/src/lib/components/primitives/toggle/toggle.svelte +12 -4
- package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +3 -3
- package/src/lib/components/primitives/toggle-group/toggle-group.svelte +3 -3
- package/src/lib/components/primitives/tooltip/index.ts +19 -0
- package/src/lib/components/primitives/tooltip/tooltip-content.svelte +34 -0
- package/src/lib/components/primitives/tooltip/tooltip-portal.svelte +7 -0
- package/src/lib/components/primitives/tooltip/tooltip-provider.svelte +7 -0
- package/src/lib/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
- package/src/lib/components/primitives/tooltip/tooltip.svelte +7 -0
- package/src/lib/components/viewer/MeshMetadataPanel.svelte +2 -2
- package/src/lib/components/viewer/SceneManager.svelte +32 -21
- package/src/lib/components/viewer/Viewer.svelte +136 -186
- package/src/lib/components/viewer/ViewerDock.svelte +404 -0
- package/src/lib/composables/useFooterItem.svelte.ts +1 -1
- package/src/lib/compute/liveProgress.svelte.ts +103 -0
- package/src/lib/compute/solving.svelte.ts +66 -50
- package/src/lib/constants.ts +8 -11
- package/src/lib/contexts/hostContext.svelte.ts +51 -0
- package/src/lib/features/chart/plotly-compat.ts +141 -0
- package/src/lib/i18n/messages.ts +139 -0
- package/src/lib/index.ts +22 -0
- package/src/lib/public.ts +1 -0
- package/src/lib/schema/host-support.ts +129 -0
- package/src/lib/schema/option-display.ts +44 -0
- package/src/lib/schema/param-exporter.ts +10 -7
- package/src/lib/schema/visibility-rules.ts +27 -1
- package/src/lib/styles/base.css +94 -84
- package/src/lib/utils/color-space.ts +107 -0
- package/src/lib/utils/file-download.ts +7 -11
- package/src/lib/utils/host-files.ts +61 -0
- package/src/lib/utils/webp-to-png.ts +48 -0
- package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
- package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte.d.ts +0 -3
- package/dist/components/primitives/theme-switcher/index.d.ts +0 -1
- package/dist/components/primitives/theme-switcher/index.js +0 -1
- package/dist/styles/themes/cyberpunk.css +0 -98
- package/dist/styles/themes/neutral.css +0 -72
- package/dist/styles/themes/ocean.css +0 -75
- package/dist/styles/themes/selva.css +0 -105
- package/dist/theme/themeStore.svelte.d.ts +0 -12
- package/dist/theme/themeStore.svelte.js +0 -40
- package/dist/theme/themes.d.ts +0 -5
- package/dist/theme/themes.js +0 -13
- package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
- package/src/lib/components/primitives/theme-switcher/index.ts +0 -1
- package/src/lib/styles/themes/cyberpunk.css +0 -98
- package/src/lib/styles/themes/neutral.css +0 -72
- package/src/lib/styles/themes/ocean.css +0 -75
- package/src/lib/styles/themes/selva.css +0 -105
- package/src/lib/theme/themeStore.svelte.ts +0 -50
- package/src/lib/theme/themes.ts +0 -18
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { randomId } from '../utils/randomId';
|
|
2
|
+
import { saveBlob } from '../utils/host-files';
|
|
2
3
|
import { getGroups } from '@selvajs/schemas';
|
|
4
|
+
import { isValuelessInput, valuelessInputIds } from './host-support';
|
|
3
5
|
import type {
|
|
4
6
|
UISchema,
|
|
5
7
|
ParameterPreset,
|
|
@@ -24,6 +26,7 @@ export function createSavedState(
|
|
|
24
26
|
if (item.type !== 'input') continue;
|
|
25
27
|
const value = currentValues[item.paramId];
|
|
26
28
|
const inputDef = schema.inputs.find((i) => i.id === item.paramId);
|
|
29
|
+
if (isValuelessInput(item, inputDef)) continue;
|
|
27
30
|
if (value !== undefined && inputDef) {
|
|
28
31
|
parameters.push({
|
|
29
32
|
paramId: item.paramId,
|
|
@@ -110,15 +113,18 @@ export function validateSavedState(
|
|
|
110
113
|
* Params the current schema no longer has are dropped. That's the same condition
|
|
111
114
|
* `validateSavedState` flags as an error, but the rule is re-derived from the schema here
|
|
112
115
|
* rather than read back off the issue list — nothing couples to how sentinel ids are encoded.
|
|
116
|
+
* Valueless inputs are dropped too: a preset saved before an input became a button or a
|
|
117
|
+
* geometry input still holds a value for it.
|
|
113
118
|
*/
|
|
114
119
|
export function extractLoadableValues(
|
|
115
120
|
savedState: ParameterPreset,
|
|
116
121
|
currentSchema: UISchema
|
|
117
122
|
): Record<string, unknown> {
|
|
118
123
|
const values: Record<string, unknown> = {};
|
|
124
|
+
const valueless = valuelessInputIds(currentSchema);
|
|
119
125
|
for (const paramState of savedState.parameters) {
|
|
120
126
|
const exists = currentSchema.inputs.some((i) => i.id === paramState.paramId);
|
|
121
|
-
if (exists) {
|
|
127
|
+
if (exists && !valueless.has(paramState.paramId)) {
|
|
122
128
|
values[paramState.paramId] = paramState.value;
|
|
123
129
|
}
|
|
124
130
|
}
|
|
@@ -147,18 +153,15 @@ export function loadPreset(savedState: ParameterPreset, currentSchema: UISchema)
|
|
|
147
153
|
};
|
|
148
154
|
}
|
|
149
155
|
|
|
150
|
-
|
|
156
|
+
/** Resolves false when the user cancelled the save. */
|
|
157
|
+
export function exportStateAsJson(savedState: ParameterPreset): Promise<boolean> {
|
|
151
158
|
const blob = new Blob([JSON.stringify(savedState, null, 2)], { type: 'application/json' });
|
|
152
159
|
const safeName = savedState.name.replace(/[^a-z0-9]/gi, '_');
|
|
153
160
|
const date = savedState.timestamp.split('T')[0].replace(/-/g, '_');
|
|
154
161
|
|
|
155
|
-
const link = document.createElement('a');
|
|
156
|
-
link.href = URL.createObjectURL(blob);
|
|
157
162
|
// .slvp = Selva parameter preset. Import still accepts the pre-rename .sps — same JSON,
|
|
158
163
|
// only the writer's extension changed.
|
|
159
|
-
|
|
160
|
-
link.click();
|
|
161
|
-
URL.revokeObjectURL(link.href);
|
|
164
|
+
return saveBlob(blob, `${safeName}_${date}.slvp`);
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
export async function importStateFromJson(file: File): Promise<ParameterPreset> {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
InputLayoutItem,
|
|
2
3
|
LayoutItem,
|
|
3
4
|
OutputLayoutItem,
|
|
4
5
|
VisibilityRule,
|
|
5
6
|
VisibilityCondition,
|
|
6
7
|
GroupVisibilityCondition,
|
|
7
|
-
TabConfig
|
|
8
|
+
TabConfig,
|
|
9
|
+
UISchema
|
|
8
10
|
} from '@selvajs/schemas';
|
|
9
11
|
import { isFileData } from '../utils/file-download';
|
|
10
12
|
|
|
@@ -148,6 +150,30 @@ export function isTabVisible(tab: TabConfig, values: Record<string, unknown>): b
|
|
|
148
150
|
);
|
|
149
151
|
}
|
|
150
152
|
|
|
153
|
+
/**
|
|
154
|
+
* The input items the form shows for these values: each passes its own condition, inside a
|
|
155
|
+
* shown group and, when tabbed, a shown tab. For a host that repeats the form back, such as a
|
|
156
|
+
* summary before a commit, without re-deriving the renderer's rules.
|
|
157
|
+
*/
|
|
158
|
+
export function visibleInputItems(
|
|
159
|
+
schema: UISchema,
|
|
160
|
+
values: Record<string, unknown>
|
|
161
|
+
): InputLayoutItem[] {
|
|
162
|
+
const layout = schema?.layout;
|
|
163
|
+
const groups =
|
|
164
|
+
layout?.type === 'tabbed'
|
|
165
|
+
? (layout.tabs ?? []).filter((t) => isTabVisible(t, values)).flatMap((t) => t.groups ?? [])
|
|
166
|
+
: layout?.type === 'flat'
|
|
167
|
+
? (layout.groups ?? [])
|
|
168
|
+
: [];
|
|
169
|
+
return groups
|
|
170
|
+
.filter((g) => evaluateGroupVisibility(g, values))
|
|
171
|
+
.flatMap((g) => g.items ?? [])
|
|
172
|
+
.filter(
|
|
173
|
+
(i): i is InputLayoutItem => i.type === 'input' && evaluateVisibility(i, values).visible
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
151
177
|
/**
|
|
152
178
|
* A linebreak is always "visible" but renders as a divider only, so a group left with
|
|
153
179
|
* nothing but separators counts as empty.
|
package/src/lib/styles/base.css
CHANGED
|
@@ -1,77 +1,95 @@
|
|
|
1
|
+
@import '@fontsource-variable/hanken-grotesk';
|
|
2
|
+
|
|
1
3
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
2
4
|
|
|
5
|
+
/* Surfaces, edges and states share one set of names in both modes; each mode only changes the
|
|
6
|
+
values. Ratios are WCAG 2.x. The roles and when to use each: docs/contributing/design-system.md. */
|
|
3
7
|
:root {
|
|
4
|
-
--radius: 0.625rem;
|
|
5
8
|
--header-h: 3.5rem;
|
|
9
|
+
--below-header-h: calc(100svh - var(--header-h));
|
|
6
10
|
--page-px: 1rem;
|
|
7
11
|
--page-py: 1.75rem;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
--
|
|
12
|
-
--
|
|
13
|
-
--
|
|
14
|
-
|
|
15
|
-
--
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
--
|
|
12
|
+
/* Canvas, then surface (cards, panels), then raised (rows, menus, popovers). Light can't go
|
|
13
|
+
lighter than white, so raised is white too and lifts with a border and a shadow instead.
|
|
14
|
+
Values follow stock shadcn neutral. */
|
|
15
|
+
--background: oklch(0.985 0 0);
|
|
16
|
+
--card: oklch(1 0 0);
|
|
17
|
+
--raised: oklch(1 0 0);
|
|
18
|
+
/* Recessed fill for trays, table heads, code and skeletons: darker than the surface around it. */
|
|
19
|
+
--muted: oklch(0.97 0 0);
|
|
20
|
+
/* Fill of anything you type into, on a card: recessed, the same step as muted, in both modes.
|
|
21
|
+
The edge (`--input`) carries the boundary. */
|
|
22
|
+
--field: var(--muted);
|
|
23
|
+
--foreground: oklch(0.145 0 0);
|
|
24
|
+
--card-foreground: oklch(0.145 0 0);
|
|
25
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
26
|
+
/* 4.5:1 on every surface. */
|
|
19
27
|
--muted-foreground: oklch(0.5 0 0);
|
|
20
|
-
|
|
21
|
-
--
|
|
22
|
-
/*
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
--
|
|
30
|
-
|
|
28
|
+
/* Icons, placeholders, disabled text: 3:1 on every surface. */
|
|
29
|
+
--faint-foreground: oklch(0.6 0 0);
|
|
30
|
+
/* State overlays, laid over whatever surface is underneath. */
|
|
31
|
+
--hover: oklch(0 0 0 / 4%);
|
|
32
|
+
--selected: oklch(0 0 0 / 8%);
|
|
33
|
+
/* Rest fill of a neutral filled control; its hover is `selected`. */
|
|
34
|
+
--secondary: oklch(0.97 0 0);
|
|
35
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
36
|
+
/* Outlines and separators. */
|
|
37
|
+
--border: oklch(0 0 0 / 8%);
|
|
38
|
+
/* Popovers, outline buttons, dashed drop zones. */
|
|
39
|
+
--border-strong: oklch(0 0 0 / 14%);
|
|
40
|
+
/* Edge of anything you type into or toggle, slider tracks included. Softer than the 3:1 WCAG
|
|
41
|
+
boundary minimum in both modes: the recessed field fill carries the rest of the boundary. */
|
|
42
|
+
--input: oklch(0 0 0 / 10%);
|
|
43
|
+
--ring: oklch(0.708 0 0);
|
|
44
|
+
--elevation-raised: 0 1px 2px oklch(0 0 0 / 5%);
|
|
45
|
+
--elevation-popover: 0 8px 24px oklch(0 0 0 / 10%);
|
|
46
|
+
/* 4.5:1 as text on a card and under its foreground; 3:1 as a fill on every surface. */
|
|
47
|
+
--primary: oklch(0.205 0 0);
|
|
48
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
49
|
+
--destructive: #c9302d;
|
|
50
|
+
--destructive-foreground: #fbfbfb;
|
|
51
|
+
/* 3:1 on every surface, as icons and dots. */
|
|
52
|
+
--success: #51a556;
|
|
31
53
|
--success-foreground: oklch(0.99 0 0);
|
|
32
|
-
--warning:
|
|
54
|
+
--warning: #bd8630;
|
|
33
55
|
--warning-foreground: oklch(0.25 0.04 75);
|
|
34
|
-
--info:
|
|
56
|
+
--info: #5399d1;
|
|
35
57
|
--info-foreground: oklch(0.99 0 0);
|
|
36
58
|
--chart-1: oklch(0.65 0.14 135);
|
|
37
59
|
--chart-2: oklch(0.7 0.11 200);
|
|
38
60
|
--chart-3: oklch(0.6 0.13 260);
|
|
39
61
|
--chart-4: oklch(0.78 0.13 80);
|
|
40
62
|
--chart-5: oklch(0.68 0.15 30);
|
|
41
|
-
--sidebar: oklch(0.96 0 0);
|
|
42
|
-
--sidebar-foreground: oklch(0.25 0 0);
|
|
43
|
-
--sidebar-primary: oklch(0.57 0.07 135);
|
|
44
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
45
|
-
--sidebar-accent: oklch(0.93 0 0);
|
|
46
|
-
--sidebar-accent-foreground: oklch(0.3 0 0);
|
|
47
|
-
--sidebar-border: oklch(0.88 0 0);
|
|
48
|
-
--sidebar-ring: oklch(0.55 0 0);
|
|
49
|
-
--inputparam: oklch(1 0 0);
|
|
50
|
-
--outputparam: oklch(1 0 0);
|
|
51
63
|
/* Scrim behind modal dialogs and drawers. */
|
|
52
64
|
--overlay: oklch(0 0 0 / 50%);
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
.dark {
|
|
56
|
-
--background: oklch(0.
|
|
57
|
-
--
|
|
58
|
-
--
|
|
59
|
-
--
|
|
60
|
-
--
|
|
61
|
-
--
|
|
62
|
-
--
|
|
63
|
-
--
|
|
64
|
-
--
|
|
65
|
-
--
|
|
66
|
-
--
|
|
67
|
-
--
|
|
68
|
-
--
|
|
69
|
-
--
|
|
68
|
+
--background: oklch(0.145 0 0);
|
|
69
|
+
--card: oklch(0.205 0 0);
|
|
70
|
+
--raised: oklch(0.24 0 0);
|
|
71
|
+
--muted: oklch(0.269 0 0);
|
|
72
|
+
--foreground: oklch(0.985 0 0);
|
|
73
|
+
--card-foreground: oklch(0.985 0 0);
|
|
74
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
75
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
76
|
+
--faint-foreground: oklch(0.556 0 0);
|
|
77
|
+
--hover: oklch(1 0 0 / 6%);
|
|
78
|
+
--selected: oklch(1 0 0 / 10%);
|
|
79
|
+
--secondary: oklch(0.269 0 0);
|
|
80
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
81
|
+
--border: oklch(1 0 0 / 10%);
|
|
82
|
+
--border-strong: oklch(1 0 0 / 18%);
|
|
83
|
+
--input: oklch(1 0 0 / 15%);
|
|
84
|
+
--ring: oklch(0.556 0 0);
|
|
85
|
+
/* Shadows barely show on a dark canvas, so raised things lean on the border instead. */
|
|
86
|
+
--elevation-raised: none;
|
|
87
|
+
--elevation-popover: 0 8px 24px oklch(0 0 0 / 50%);
|
|
88
|
+
--primary: oklch(0.922 0 0);
|
|
89
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
90
|
+
/* White text can't reach 4.5:1 on a red light enough to also be text on the dark card. */
|
|
70
91
|
--destructive: oklch(0.7 0.17 22);
|
|
71
|
-
--destructive-foreground: oklch(0.
|
|
72
|
-
--border: oklch(1 0 0 / 14%);
|
|
73
|
-
--input: oklch(1 0 0 / 36%);
|
|
74
|
-
--ring: oklch(0.74 0 0);
|
|
92
|
+
--destructive-foreground: oklch(0.18 0 0);
|
|
75
93
|
--success: oklch(0.74 0.15 145);
|
|
76
94
|
--success-foreground: oklch(0.18 0.03 145);
|
|
77
95
|
--warning: oklch(0.8 0.14 75);
|
|
@@ -83,41 +101,39 @@
|
|
|
83
101
|
--chart-3: oklch(0.68 0.14 260);
|
|
84
102
|
--chart-4: oklch(0.78 0.14 80);
|
|
85
103
|
--chart-5: oklch(0.7 0.16 30);
|
|
86
|
-
--sidebar: oklch(0.21 0 0);
|
|
87
|
-
--sidebar-foreground: oklch(0.96 0 0);
|
|
88
|
-
--sidebar-primary: oklch(0.74 0.07 135);
|
|
89
|
-
--sidebar-primary-foreground: oklch(0.18 0 0);
|
|
90
|
-
--sidebar-accent: oklch(0.32 0 0);
|
|
91
|
-
--sidebar-accent-foreground: oklch(0.92 0 0);
|
|
92
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
93
|
-
--sidebar-ring: oklch(0.74 0 0);
|
|
94
|
-
--inputparam: oklch(0.15 0 0);
|
|
95
|
-
--outputparam: oklch(0.25 0 0);
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
@theme inline {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
--radius-
|
|
107
|
+
/* Concentric: an item inside a padded container takes the container's radius minus the
|
|
108
|
+
padding. A 12px menu with 4px padding holds 8px items; a 10px control with 2px padding
|
|
109
|
+
holds 8px segments. */
|
|
110
|
+
--radius-sm: 6px;
|
|
111
|
+
--radius-inner: 8px;
|
|
112
|
+
--radius-md: 10px;
|
|
113
|
+
--radius-lg: 12px;
|
|
114
|
+
--radius-xl: 16px;
|
|
103
115
|
--color-background: var(--background);
|
|
104
116
|
--color-foreground: var(--foreground);
|
|
105
117
|
--color-card: var(--card);
|
|
106
118
|
--color-card-foreground: var(--card-foreground);
|
|
107
|
-
--color-
|
|
119
|
+
--color-raised: var(--raised);
|
|
120
|
+
--color-popover: var(--raised);
|
|
108
121
|
--color-popover-foreground: var(--popover-foreground);
|
|
109
|
-
--color-
|
|
110
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
111
|
-
--color-secondary: var(--secondary);
|
|
112
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
122
|
+
--color-field: var(--field);
|
|
113
123
|
--color-muted: var(--muted);
|
|
114
124
|
--color-muted-foreground: var(--muted-foreground);
|
|
115
|
-
--color-
|
|
116
|
-
--color-
|
|
125
|
+
--color-faint-foreground: var(--faint-foreground);
|
|
126
|
+
--color-hover: var(--hover);
|
|
127
|
+
--color-selected: var(--selected);
|
|
128
|
+
--color-secondary: var(--secondary);
|
|
129
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
130
|
+
--color-primary: var(--primary);
|
|
131
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
117
132
|
--color-destructive: var(--destructive);
|
|
118
133
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
119
134
|
--color-overlay: var(--overlay);
|
|
120
135
|
--color-border: var(--border);
|
|
136
|
+
--color-border-strong: var(--border-strong);
|
|
121
137
|
--color-input: var(--input);
|
|
122
138
|
--color-ring: var(--ring);
|
|
123
139
|
--color-success: var(--success);
|
|
@@ -131,21 +147,15 @@
|
|
|
131
147
|
--color-chart-3: var(--chart-3);
|
|
132
148
|
--color-chart-4: var(--chart-4);
|
|
133
149
|
--color-chart-5: var(--chart-5);
|
|
134
|
-
--
|
|
135
|
-
--
|
|
136
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
137
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
138
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
139
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
140
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
141
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
142
|
-
--color-inputparam: var(--inputparam);
|
|
143
|
-
--color-outputparam: var(--outputparam);
|
|
150
|
+
--shadow-raised: var(--elevation-raised);
|
|
151
|
+
--shadow-popover: var(--elevation-popover);
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
/* Design tokens beyond Tailwind's defaults. Reach for these before an arbitrary value: the
|
|
147
155
|
design-system lint rejects `text-[10px]`, `max-h-[85dvh]` and the like. */
|
|
148
156
|
@theme {
|
|
157
|
+
/* Hanken Grotesk (OFL), self-hosted via Fontsource: the plugin UI runs offline from the .gha. */
|
|
158
|
+
--font-sans: 'Hanken Grotesk Variable', ui-sans-serif, system-ui, sans-serif;
|
|
149
159
|
/* Type tags, counters and other dense labels. */
|
|
150
160
|
--text-2xs: 0.625rem;
|
|
151
161
|
--text-2xs--line-height: calc(0.875 / 0.625);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export interface Rgb {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** h in [0, 360), s and v in [0, 1]. */
|
|
8
|
+
export interface Hsv {
|
|
9
|
+
h: number;
|
|
10
|
+
s: number;
|
|
11
|
+
v: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n));
|
|
15
|
+
|
|
16
|
+
/** Accepts `#rgb`, `#rrggbb` and the same without `#`. Returns lowercase `#rrggbb`, or null. */
|
|
17
|
+
export function normalizeHex(input: string): string | null {
|
|
18
|
+
const raw = input.trim().replace(/^#/, '');
|
|
19
|
+
if (/^[0-9a-f]{3}$/i.test(raw)) {
|
|
20
|
+
return `#${raw
|
|
21
|
+
.split('')
|
|
22
|
+
.map((c) => c + c)
|
|
23
|
+
.join('')
|
|
24
|
+
.toLowerCase()}`;
|
|
25
|
+
}
|
|
26
|
+
if (/^[0-9a-f]{6}$/i.test(raw)) return `#${raw.toLowerCase()}`;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function hexToRgb(hex: string): Rgb | null {
|
|
31
|
+
const normalized = normalizeHex(hex);
|
|
32
|
+
if (!normalized) return null;
|
|
33
|
+
return {
|
|
34
|
+
r: parseInt(normalized.slice(1, 3), 16),
|
|
35
|
+
g: parseInt(normalized.slice(3, 5), 16),
|
|
36
|
+
b: parseInt(normalized.slice(5, 7), 16)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function rgbToHex({ r, g, b }: Rgb): string {
|
|
41
|
+
const channel = (n: number) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0');
|
|
42
|
+
return `#${channel(r)}${channel(g)}${channel(b)}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function rgbToHsv({ r, g, b }: Rgb): Hsv {
|
|
46
|
+
const rn = r / 255;
|
|
47
|
+
const gn = g / 255;
|
|
48
|
+
const bn = b / 255;
|
|
49
|
+
const max = Math.max(rn, gn, bn);
|
|
50
|
+
const delta = max - Math.min(rn, gn, bn);
|
|
51
|
+
|
|
52
|
+
let h = 0;
|
|
53
|
+
if (delta !== 0) {
|
|
54
|
+
if (max === rn) h = ((gn - bn) / delta) % 6;
|
|
55
|
+
else if (max === gn) h = (bn - rn) / delta + 2;
|
|
56
|
+
else h = (rn - gn) / delta + 4;
|
|
57
|
+
h *= 60;
|
|
58
|
+
if (h < 0) h += 360;
|
|
59
|
+
}
|
|
60
|
+
return { h, s: max === 0 ? 0 : delta / max, v: max };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function hsvToRgb({ h, s, v }: Hsv): Rgb {
|
|
64
|
+
const c = v * s;
|
|
65
|
+
const hp = (((h % 360) + 360) % 360) / 60;
|
|
66
|
+
const x = c * (1 - Math.abs((hp % 2) - 1));
|
|
67
|
+
const [r1, g1, b1] =
|
|
68
|
+
hp < 1
|
|
69
|
+
? [c, x, 0]
|
|
70
|
+
: hp < 2
|
|
71
|
+
? [x, c, 0]
|
|
72
|
+
: hp < 3
|
|
73
|
+
? [0, c, x]
|
|
74
|
+
: hp < 4
|
|
75
|
+
? [0, x, c]
|
|
76
|
+
: hp < 5
|
|
77
|
+
? [x, 0, c]
|
|
78
|
+
: [c, 0, x];
|
|
79
|
+
const m = v - c;
|
|
80
|
+
return { r: (r1 + m) * 255, g: (g1 + m) * 255, b: (b1 + m) * 255 };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const hexToHsv = (hex: string): Hsv | null => {
|
|
84
|
+
const rgb = hexToRgb(hex);
|
|
85
|
+
return rgb ? rgbToHsv(rgb) : null;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const hsvToHex = (hsv: Hsv): string => rgbToHex(hsvToRgb(hsv));
|
|
89
|
+
|
|
90
|
+
/** WCAG relative luminance of an sRGB color. */
|
|
91
|
+
function luminance({ r, g, b }: Rgb): number {
|
|
92
|
+
const lin = (c: number) => {
|
|
93
|
+
const n = c / 255;
|
|
94
|
+
return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
|
|
95
|
+
};
|
|
96
|
+
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** WCAG contrast ratio between two hex colors, 1 to 21. Null if either isn't a hex color. */
|
|
100
|
+
export function contrastRatio(a: string, b: string): number | null {
|
|
101
|
+
const ra = hexToRgb(a);
|
|
102
|
+
const rb = hexToRgb(b);
|
|
103
|
+
if (!ra || !rb) return null;
|
|
104
|
+
const la = luminance(ra);
|
|
105
|
+
const lb = luminance(rb);
|
|
106
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
107
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { decodeBase64ToBinary, downloadFileDataByRoot, type FileData } from '@selvajs/compute/core';
|
|
2
2
|
import { SvelteMap } from 'svelte/reactivity';
|
|
3
3
|
import { APP_DEFAULTS } from '../constants';
|
|
4
|
+
import { saveBlob } from './host-files';
|
|
4
5
|
|
|
5
6
|
export const MIME_BY_EXT: Record<string, string> = {
|
|
6
7
|
'.png': 'image/png',
|
|
@@ -19,7 +20,7 @@ export const MIME_BY_EXT: Record<string, string> = {
|
|
|
19
20
|
'.stl': 'model/stl'
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
function saveSingleFile(file: FileData):
|
|
23
|
+
function saveSingleFile(file: FileData): Promise<boolean> {
|
|
23
24
|
const ext = (file.fileType ?? '').toLowerCase();
|
|
24
25
|
const mime = MIME_BY_EXT[ext] ?? 'application/octet-stream';
|
|
25
26
|
|
|
@@ -27,12 +28,7 @@ function saveSingleFile(file: FileData): void {
|
|
|
27
28
|
// decodeBase64ToBinary returns an ArrayBuffer-backed copy, never a SharedArrayBuffer view.
|
|
28
29
|
const blob = new Blob([content as BlobPart], { type: mime });
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
const a = document.createElement('a');
|
|
32
|
-
a.href = URL.createObjectURL(blob);
|
|
33
|
-
a.download = fullName;
|
|
34
|
-
a.click();
|
|
35
|
-
URL.revokeObjectURL(a.href);
|
|
31
|
+
return saveBlob(blob, `${file.fileName}${file.fileType ?? ''}`);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
export async function downloadFiles(
|
|
@@ -47,14 +43,14 @@ export async function downloadFiles(
|
|
|
47
43
|
return;
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
// A lone file is saved as itself rather than zipped
|
|
51
|
-
//
|
|
46
|
+
// A lone file is saved as itself rather than zipped. Everything else, including the
|
|
47
|
+
// per-root archive split, belongs to compute.
|
|
52
48
|
if (filesArray.length === 1) {
|
|
53
|
-
saveSingleFile(filesArray[0]);
|
|
49
|
+
await saveSingleFile(filesArray[0]);
|
|
54
50
|
return;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
await downloadFileDataByRoot(filesArray, fileName);
|
|
53
|
+
await downloadFileDataByRoot(filesArray, fileName, null, { saveBlob });
|
|
58
54
|
} catch (error) {
|
|
59
55
|
console.error('[FileDownload] Error downloading files:', error);
|
|
60
56
|
throw error;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Where a file the page offers goes, and where a file the page asks for comes from. A browser
|
|
2
|
+
// gets an <a download> click and a hidden <input type=file>. A host whose WebView can't do
|
|
3
|
+
// either safely (the Rhino panel) installs its own once at start-up, before the first save or pick.
|
|
4
|
+
|
|
5
|
+
/** Resolves false when the user cancelled. An anchor download always resolves true: it can't tell. */
|
|
6
|
+
export type BlobSaver = (blob: Blob, fileName: string) => Promise<boolean>;
|
|
7
|
+
|
|
8
|
+
/** `accept` is what `<input accept>` takes: `.slvp,.sps`, `image/*`. Resolves null on cancel. */
|
|
9
|
+
export type FilePicker = (options: { accept: string }) => Promise<File | null>;
|
|
10
|
+
|
|
11
|
+
let saver: BlobSaver = saveWithAnchor;
|
|
12
|
+
let picker: FilePicker = pickWithInput;
|
|
13
|
+
|
|
14
|
+
export function setBlobSaver(next: BlobSaver): void {
|
|
15
|
+
saver = next;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function setFilePicker(next: FilePicker): void {
|
|
19
|
+
picker = next;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function saveBlob(blob: Blob, fileName: string): Promise<boolean> {
|
|
23
|
+
return saver(blob, fileName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function pickFile(options: { accept: string }): Promise<File | null> {
|
|
27
|
+
return picker(options);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function saveWithAnchor(blob: Blob, fileName: string): Promise<boolean> {
|
|
31
|
+
const url = URL.createObjectURL(blob);
|
|
32
|
+
const a = document.createElement('a');
|
|
33
|
+
a.href = url;
|
|
34
|
+
a.download = fileName;
|
|
35
|
+
// Firefox only downloads from an anchor that is in the document.
|
|
36
|
+
document.body.appendChild(a);
|
|
37
|
+
a.click();
|
|
38
|
+
a.remove();
|
|
39
|
+
// Revoking synchronously can abort the download: the browser pins the blob only once it starts.
|
|
40
|
+
setTimeout(() => URL.revokeObjectURL(url), 10_000);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function pickWithInput({ accept }: { accept: string }): Promise<File | null> {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const input = document.createElement('input');
|
|
47
|
+
input.type = 'file';
|
|
48
|
+
input.accept = accept;
|
|
49
|
+
input.style.display = 'none';
|
|
50
|
+
|
|
51
|
+
const settle = (file: File | null) => {
|
|
52
|
+
input.remove();
|
|
53
|
+
resolve(file);
|
|
54
|
+
};
|
|
55
|
+
input.addEventListener('change', () => settle(input.files?.[0] ?? null), { once: true });
|
|
56
|
+
input.addEventListener('cancel', () => settle(null), { once: true });
|
|
57
|
+
|
|
58
|
+
document.body.appendChild(input);
|
|
59
|
+
input.click();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Grasshopper's PDF export can't read WebP: the plugin's PDF library has no WebP decoder, and
|
|
2
|
+
// nothing on Windows or Rhino.Compute decodes it reliably. The browser can, so a WebP picked for
|
|
3
|
+
// an input that also takes PNG is re-encoded here. PNG rather than JPEG keeps transparency.
|
|
4
|
+
|
|
5
|
+
/** True when a WebP should go out as PNG: the input takes PNG, so a PNG is a valid value. */
|
|
6
|
+
export function shouldConvertWebp(fileEnding: string, acceptedFormats: readonly string[]): boolean {
|
|
7
|
+
return fileEnding === '.webp' && acceptedFormats.includes('.png');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Whether the input takes a file with this ending, counting a WebP it would get as PNG. */
|
|
11
|
+
export function acceptsFileEnding(fileEnding: string, acceptedFormats: readonly string[]): boolean {
|
|
12
|
+
const ending = fileEnding.toLowerCase();
|
|
13
|
+
if (acceptedFormats.length === 0 || acceptedFormats.includes(ending)) return true;
|
|
14
|
+
return shouldConvertWebp(ending, acceptedFormats);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ascii = (bytes: Uint8Array, start: number, end: number) =>
|
|
18
|
+
String.fromCharCode(...bytes.subarray(start, end));
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* By content, not name: image CDNs serve WebP from URLs with no extension or one naming another
|
|
22
|
+
* format. A WebP file starts `RIFF`, a 4-byte size, then `WEBP`.
|
|
23
|
+
*/
|
|
24
|
+
export async function isWebp(blob: Blob): Promise<boolean> {
|
|
25
|
+
if (blob.type === 'image/webp') return true;
|
|
26
|
+
const head = new Uint8Array(await blob.slice(0, 12).arrayBuffer());
|
|
27
|
+
return head.length === 12 && ascii(head, 0, 4) === 'RIFF' && ascii(head, 8, 12) === 'WEBP';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function webpToPng(blob: Blob): Promise<Blob> {
|
|
31
|
+
const bitmap = await createImageBitmap(blob);
|
|
32
|
+
try {
|
|
33
|
+
const canvas = document.createElement('canvas');
|
|
34
|
+
canvas.width = bitmap.width;
|
|
35
|
+
canvas.height = bitmap.height;
|
|
36
|
+
const context = canvas.getContext('2d');
|
|
37
|
+
if (!context) throw new Error('Canvas 2D context unavailable');
|
|
38
|
+
context.drawImage(bitmap, 0, 0);
|
|
39
|
+
return await new Promise<Blob>((resolve, reject) =>
|
|
40
|
+
canvas.toBlob(
|
|
41
|
+
(png) => (png ? resolve(png) : reject(new Error('PNG encoding failed'))),
|
|
42
|
+
'image/png'
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
} finally {
|
|
46
|
+
bitmap.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { themeStore, type Theme } from '../../../theme/themeStore.svelte';
|
|
3
|
-
import * as Select from '../select';
|
|
4
|
-
import { Palette } from '@lucide/svelte';
|
|
5
|
-
|
|
6
|
-
const themes: { value: Theme; label: string; description: string }[] = [
|
|
7
|
-
{ value: 'neutral', label: 'Neutral', description: 'Classic grayscale' },
|
|
8
|
-
{ value: 'selva', label: 'Selva', description: 'Forest green' },
|
|
9
|
-
{ value: 'ocean', label: 'Ocean', description: 'Calm blue tones' },
|
|
10
|
-
{ value: 'cyberpunk', label: 'Cyberpunk', description: 'Vibrant neon colors' }
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
let value = $state(themeStore.current);
|
|
14
|
-
|
|
15
|
-
$effect(() => {
|
|
16
|
-
if (value) {
|
|
17
|
-
themeStore.set(value as Theme);
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const triggerContent = $derived(themes.find((t) => t.value === value)?.label ?? 'Select theme');
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<Select.Root type="single" bind:value>
|
|
25
|
-
<Select.Trigger class="gap-2 w-35">
|
|
26
|
-
<Palette size={14} />
|
|
27
|
-
<span>{triggerContent}</span>
|
|
28
|
-
</Select.Trigger>
|
|
29
|
-
<Select.Content>
|
|
30
|
-
{#each themes as theme (theme.value)}
|
|
31
|
-
<Select.Item value={theme.value} label={theme.label}>
|
|
32
|
-
<div class="flex flex-col items-start">
|
|
33
|
-
<span class="font-medium text-sm">{theme.label}</span>
|
|
34
|
-
<span class="text-xs text-muted-foreground">{theme.description}</span>
|
|
35
|
-
</div>
|
|
36
|
-
</Select.Item>
|
|
37
|
-
{/each}
|
|
38
|
-
</Select.Content>
|
|
39
|
-
</Select.Root>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as ThemeSwitcher } from './ThemeSwitcher.svelte';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as ThemeSwitcher } from './ThemeSwitcher.svelte';
|