@selvajs/ui 6.6.0-beta.2 → 6.6.0-beta.20
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 +7 -3
- package/dist/components/compute/AppLayout.svelte +307 -123
- package/dist/components/compute/AppLayout.svelte.d.ts +28 -0
- package/dist/components/compute/CollapsedPanelStrip.svelte +4 -2
- package/dist/components/compute/ComputeApp.svelte +46 -22
- package/dist/components/compute/ComputeApp.svelte.d.ts +20 -3
- package/dist/components/compute/ComputeMessagesDialog.svelte +31 -41
- package/dist/components/compute/ParameterPresetManager.svelte +16 -65
- package/dist/components/compute/ParameterPresetManager.svelte.d.ts +4 -5
- package/dist/components/compute/SolveMessages.svelte +128 -121
- package/dist/components/compute/SolveMessages.svelte.d.ts +12 -12
- package/dist/components/layout/AppShell.svelte +10 -6
- 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 +26 -14
- package/dist/components/layout/PageHeader.svelte +14 -10
- package/dist/components/layout/SectionHeader.svelte +4 -2
- package/dist/components/layout/SideNav.svelte +57 -8
- package/dist/components/layout/SubNav.svelte +2 -2
- package/dist/components/preview/ChartOutput.svelte +131 -65
- package/dist/components/preview/Group.svelte +42 -22
- package/dist/components/preview/HelpButton.svelte +5 -3
- package/dist/components/preview/ImageOutput.svelte +435 -71
- package/dist/components/preview/InputControl.svelte +125 -51
- package/dist/components/preview/InputControl.svelte.d.ts +5 -1
- package/dist/components/preview/OutputDisplay.svelte +42 -28
- package/dist/components/preview/TabBar.svelte +8 -6
- package/dist/components/preview/TabBar.svelte.d.ts +2 -0
- package/dist/components/preview/TabContent.svelte +66 -31
- 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 +357 -13
- package/dist/components/preview/inputs/ColorInput.svelte.d.ts +2 -0
- package/dist/components/preview/inputs/DropdownInput.svelte +20 -5
- package/dist/components/preview/inputs/DropdownInput.svelte.d.ts +2 -1
- package/dist/components/preview/inputs/FileInput.svelte +115 -103
- 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 +98 -16
- package/dist/components/preview/inputs/NumberInput.svelte.d.ts +2 -0
- 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 +11 -6
- 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 +56 -24
- package/dist/components/primitives/CalculateButton.svelte.d.ts +3 -0
- package/dist/components/primitives/Callout.svelte +8 -6
- package/dist/components/primitives/Callout.svelte.d.ts +4 -4
- package/dist/components/primitives/DataTable.svelte +38 -4
- package/dist/components/primitives/DataTable.svelte.d.ts +2 -0
- 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 -15
- 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 +4 -4
- package/dist/components/primitives/badge/badge.svelte.d.ts +10 -10
- package/dist/components/primitives/button/button.svelte +10 -8
- package/dist/components/primitives/button/button.svelte.d.ts +20 -17
- 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 +7 -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 +4 -4
- 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 +5 -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/scroll-area/scroll-area.svelte +1 -1
- package/dist/components/primitives/search/search.svelte +2 -2
- 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 +8 -3
- package/dist/components/primitives/slider/slider.svelte.d.ts +5 -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 +3 -3
- package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +7 -7
- 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 +119 -0
- package/dist/components/viewer/MeshMetadataPanel.svelte.d.ts +9 -0
- package/dist/components/viewer/SceneManager.svelte +32 -21
- package/dist/components/viewer/SceneManager.svelte.d.ts +3 -1
- package/dist/components/viewer/Viewer.svelte +195 -207
- 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/run-panel-action.d.ts +8 -0
- package/dist/compute/run-panel-action.js +12 -0
- package/dist/compute/solving.svelte.d.ts +11 -7
- package/dist/compute/solving.svelte.js +64 -45
- package/dist/compute/useSolveSession.svelte.js +3 -0
- 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/localeContext.svelte.d.ts +10 -3
- package/dist/i18n/localeContext.svelte.js +27 -5
- package/dist/i18n/messages.d.ts +164 -2
- package/dist/i18n/messages.js +281 -11
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -0
- package/dist/public.d.ts +2 -1
- 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 +38 -0
- package/dist/styles/base.css +116 -83
- package/dist/types/actionButton.d.ts +17 -3
- package/dist/types/presetLabels.d.ts +3 -2
- package/dist/types/presetLabels.js +9 -8
- package/dist/utils/color-space.d.ts +21 -0
- package/dist/utils/color-space.js +90 -0
- package/dist/utils/file-download.js +10 -22
- package/dist/utils/host-files.d.ts +12 -0
- package/dist/utils/host-files.js +46 -0
- package/dist/utils/loadScript.d.ts +2 -0
- package/dist/utils/loadScript.js +2 -0
- package/dist/utils/webp-to-png.d.ts +10 -0
- package/dist/utils/webp-to-png.js +41 -0
- package/package.json +30 -22
- package/src/lib/components/ErrorScreen.svelte +7 -3
- package/src/lib/components/compute/AppLayout.svelte +307 -123
- package/src/lib/components/compute/CollapsedPanelStrip.svelte +4 -2
- package/src/lib/components/compute/ComputeApp.svelte +46 -22
- package/src/lib/components/compute/ComputeMessagesDialog.svelte +32 -42
- package/src/lib/components/compute/ParameterPresetManager.svelte +16 -65
- package/src/lib/components/compute/SolveMessages.svelte +130 -123
- package/src/lib/components/layout/AppShell.svelte +10 -6
- 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 +26 -14
- package/src/lib/components/layout/PageHeader.svelte +14 -10
- package/src/lib/components/layout/SectionHeader.svelte +4 -2
- package/src/lib/components/layout/SideNav.svelte +57 -8
- package/src/lib/components/layout/SubNav.svelte +2 -2
- package/src/lib/components/preview/ChartOutput.svelte +131 -65
- package/src/lib/components/preview/Group.svelte +42 -22
- package/src/lib/components/preview/HelpButton.svelte +5 -3
- package/src/lib/components/preview/ImageOutput.svelte +435 -71
- package/src/lib/components/preview/InputControl.svelte +125 -51
- package/src/lib/components/preview/OutputDisplay.svelte +42 -28
- package/src/lib/components/preview/TabBar.svelte +8 -6
- package/src/lib/components/preview/TabContent.svelte +66 -31
- 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 +357 -13
- package/src/lib/components/preview/inputs/DropdownInput.svelte +20 -5
- package/src/lib/components/preview/inputs/FileInput.svelte +115 -103
- package/src/lib/components/preview/inputs/GeometryInput.svelte +128 -0
- package/src/lib/components/preview/inputs/NumberInput.svelte +98 -16
- 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 +11 -6
- 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 +56 -24
- package/src/lib/components/primitives/Callout.svelte +9 -7
- package/src/lib/components/primitives/DataTable.svelte +38 -4
- 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 -15
- 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 +5 -5
- package/src/lib/components/primitives/button/button.svelte +10 -8
- 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 +8 -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 +5 -5
- 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 +5 -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 +2 -2
- package/src/lib/components/primitives/search/search.svelte +3 -3
- 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 +9 -4
- 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 +4 -4
- 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 +119 -0
- package/src/lib/components/viewer/SceneManager.svelte +32 -21
- package/src/lib/components/viewer/Viewer.svelte +196 -208
- 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/run-panel-action.ts +16 -0
- package/src/lib/compute/solving.svelte.ts +66 -50
- package/src/lib/compute/useSolveSession.svelte.ts +3 -0
- 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/localeContext.svelte.ts +40 -5
- package/src/lib/i18n/messages.ts +487 -13
- package/src/lib/index.ts +23 -0
- package/src/lib/public.ts +2 -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 +50 -1
- package/src/lib/styles/base.css +116 -83
- package/src/lib/types/actionButton.ts +16 -3
- package/src/lib/types/presetLabels.ts +13 -11
- package/src/lib/utils/color-space.ts +107 -0
- package/src/lib/utils/file-download.ts +11 -21
- package/src/lib/utils/host-files.ts +61 -0
- package/src/lib/utils/loadScript.ts +3 -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/components/viewer/MeshMetadataDialog.svelte +0 -126
- package/dist/components/viewer/MeshMetadataDialog.svelte.d.ts +0 -10
- 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/components/viewer/MeshMetadataDialog.svelte +0 -126
- 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,10 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { Input } from '
|
|
3
|
-
import { Button } from '
|
|
4
|
-
import { Label } from '
|
|
5
|
-
import { toast } from '
|
|
2
|
+
import { Input } from '#lib/components/primitives/input/index.js';
|
|
3
|
+
import { Button } from '#lib/components/primitives/button/index.js';
|
|
4
|
+
import { Label } from '#lib/components/primitives/label/index.js';
|
|
5
|
+
import { toast } from '#lib/components/primitives/sonner/index.js';
|
|
6
6
|
import { FileUp, Link, CircleAlert, CircleCheck } from '@lucide/svelte';
|
|
7
|
-
import { APP_DEFAULTS } from '
|
|
7
|
+
import { APP_DEFAULTS } from '#lib/constants.js';
|
|
8
|
+
import { getLocaleContext } from '#lib/i18n/localeContext.svelte.js';
|
|
9
|
+
import { formatMessage } from '#lib/i18n/messages.js';
|
|
10
|
+
import { pickFile } from '#lib/utils/host-files.js';
|
|
11
|
+
import {
|
|
12
|
+
acceptsFileEnding,
|
|
13
|
+
isWebp,
|
|
14
|
+
shouldConvertWebp,
|
|
15
|
+
webpToPng
|
|
16
|
+
} from '#lib/utils/webp-to-png.js';
|
|
8
17
|
|
|
9
18
|
interface Props {
|
|
10
19
|
value?: string;
|
|
@@ -22,6 +31,9 @@
|
|
|
22
31
|
onChange
|
|
23
32
|
}: Props = $props();
|
|
24
33
|
|
|
34
|
+
const locale = getLocaleContext();
|
|
35
|
+
const t = $derived(locale.messages);
|
|
36
|
+
|
|
25
37
|
let effectiveModes = $derived(
|
|
26
38
|
allowedInputModes && allowedInputModes.length > 0
|
|
27
39
|
? allowedInputModes
|
|
@@ -35,7 +47,6 @@
|
|
|
35
47
|
activeMode = effectiveModes.includes(preferred) ? preferred : effectiveModes[0];
|
|
36
48
|
});
|
|
37
49
|
|
|
38
|
-
let fileInput: HTMLInputElement | null = $state(null);
|
|
39
50
|
let uploadedFileName = $state('');
|
|
40
51
|
let urlInput = $state('');
|
|
41
52
|
let isDragging = $state(false);
|
|
@@ -52,9 +63,12 @@
|
|
|
52
63
|
if (parsed.type === 'base64') {
|
|
53
64
|
if (parsed._isMetadata && parsed._fileSize) {
|
|
54
65
|
const sizeMB = (parsed._fileSize / 1024 / 1024).toFixed(2);
|
|
55
|
-
uploadedFileName =
|
|
66
|
+
uploadedFileName = formatMessage(t.uploadedFileSize, {
|
|
67
|
+
ext: parsed.fileEnding,
|
|
68
|
+
size: sizeMB
|
|
69
|
+
});
|
|
56
70
|
} else {
|
|
57
|
-
uploadedFileName =
|
|
71
|
+
uploadedFileName = formatMessage(t.uploadedFile, { ext: parsed.fileEnding });
|
|
58
72
|
}
|
|
59
73
|
} else if (parsed.type === 'url') {
|
|
60
74
|
urlInput = parsed.file;
|
|
@@ -65,6 +79,20 @@
|
|
|
65
79
|
}
|
|
66
80
|
});
|
|
67
81
|
|
|
82
|
+
function formatNotAccepted(fileEnding: string): string {
|
|
83
|
+
return formatMessage(t.fileFormatNotAccepted, {
|
|
84
|
+
ext: fileEnding,
|
|
85
|
+
formats: acceptedFormats.join(', ')
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formatTooLarge(bytes: number): string {
|
|
90
|
+
return formatMessage(t.fileTooLarge, {
|
|
91
|
+
size: (bytes / 1024 / 1024).toFixed(2),
|
|
92
|
+
max: APP_DEFAULTS.FILE_UPLOAD.MAX_SIZE_MB
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
68
96
|
function getFileExtension(filename: string): string {
|
|
69
97
|
// Strip query strings before extracting extension
|
|
70
98
|
const cleanPath = filename.split('?')[0];
|
|
@@ -72,9 +100,27 @@
|
|
|
72
100
|
return ext.toLowerCase();
|
|
73
101
|
}
|
|
74
102
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
103
|
+
// Runs before the size cap: the PNG is larger than the WebP it came from. A WebP already over
|
|
104
|
+
// the cap is passed through unconverted, so the cap rejects it without a full decode.
|
|
105
|
+
async function prepare(
|
|
106
|
+
blob: Blob,
|
|
107
|
+
fileEnding: string
|
|
108
|
+
): Promise<{ blob: Blob; fileEnding: string }> {
|
|
109
|
+
if (blob.size > APP_DEFAULTS.FILE_UPLOAD.MAX_SIZE_BYTES) return { blob, fileEnding };
|
|
110
|
+
if (!shouldConvertWebp(fileEnding, acceptedFormats)) return { blob, fileEnding };
|
|
111
|
+
return { blob: await webpToPng(blob), fileEnding: '.png' };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function readBase64(blob: Blob): Promise<string> {
|
|
115
|
+
return new Promise<string>((resolve, reject) => {
|
|
116
|
+
const reader = new FileReader();
|
|
117
|
+
reader.onload = (e) => {
|
|
118
|
+
const result = e.target?.result as string;
|
|
119
|
+
resolve(result.includes(',') ? result.split(',')[1] : result);
|
|
120
|
+
};
|
|
121
|
+
reader.onerror = () => reject(new Error(t.fileReadFailed));
|
|
122
|
+
reader.readAsDataURL(blob);
|
|
123
|
+
});
|
|
78
124
|
}
|
|
79
125
|
|
|
80
126
|
function isCorsError(error: unknown): boolean {
|
|
@@ -96,56 +142,52 @@
|
|
|
96
142
|
try {
|
|
97
143
|
parsedUrl = new URL(urlInput);
|
|
98
144
|
} catch {
|
|
99
|
-
urlError = { message:
|
|
145
|
+
urlError = { message: t.urlInvalid, isCors: false };
|
|
100
146
|
return;
|
|
101
147
|
}
|
|
102
148
|
|
|
103
149
|
if (parsedUrl.protocol !== 'https:' && parsedUrl.protocol !== 'http:') {
|
|
104
|
-
urlError = { message:
|
|
150
|
+
urlError = { message: t.urlProtocolUnsupported, isCors: false };
|
|
105
151
|
return;
|
|
106
152
|
}
|
|
107
153
|
|
|
108
154
|
const response = await fetch(urlInput);
|
|
109
155
|
if (!response.ok) {
|
|
110
156
|
urlError = {
|
|
111
|
-
message:
|
|
157
|
+
message: formatMessage(t.urlServerError, {
|
|
158
|
+
status: `${response.status} ${response.statusText}`.trim()
|
|
159
|
+
}),
|
|
112
160
|
isCors: false
|
|
113
161
|
};
|
|
114
162
|
return;
|
|
115
163
|
}
|
|
116
164
|
|
|
117
|
-
const
|
|
118
|
-
const
|
|
165
|
+
const fetched = await response.blob();
|
|
166
|
+
const requestedEnding = (await isWebp(fetched)) ? '.webp' : getFileExtension(urlInput);
|
|
119
167
|
|
|
120
|
-
if (!
|
|
121
|
-
urlError = {
|
|
122
|
-
message: `File format "${fileEnding}" is not accepted. Allowed: ${acceptedFormats.join(', ')}`,
|
|
123
|
-
isCors: false
|
|
124
|
-
};
|
|
168
|
+
if (!acceptsFileEnding(requestedEnding, acceptedFormats)) {
|
|
169
|
+
urlError = { message: formatNotAccepted(requestedEnding), isCors: false };
|
|
125
170
|
return;
|
|
126
171
|
}
|
|
127
172
|
|
|
173
|
+
const { blob, fileEnding } = await prepare(fetched, requestedEnding);
|
|
174
|
+
|
|
128
175
|
const maxSize = APP_DEFAULTS.FILE_UPLOAD.MAX_SIZE_BYTES;
|
|
129
176
|
if (blob.size > maxSize) {
|
|
130
177
|
urlError = {
|
|
131
|
-
message:
|
|
178
|
+
message: `${formatTooLarge(blob.size)} ${t.fileTooLargeUseUpload}`,
|
|
132
179
|
isCors: false
|
|
133
180
|
};
|
|
134
181
|
return;
|
|
135
182
|
}
|
|
136
183
|
|
|
137
|
-
const base64Data = await
|
|
138
|
-
const reader = new FileReader();
|
|
139
|
-
reader.onload = (e) => {
|
|
140
|
-
const result = e.target?.result as string;
|
|
141
|
-
resolve(result.includes(',') ? result.split(',')[1] : result);
|
|
142
|
-
};
|
|
143
|
-
reader.onerror = () => reject(new Error('Failed to read the downloaded file.'));
|
|
144
|
-
reader.readAsDataURL(blob);
|
|
145
|
-
});
|
|
184
|
+
const base64Data = await readBase64(blob);
|
|
146
185
|
const data = JSON.stringify({ file: base64Data, type: 'base64', fileEnding });
|
|
147
186
|
value = data;
|
|
148
|
-
urlSuccess =
|
|
187
|
+
urlSuccess = formatMessage(t.fileLoaded, {
|
|
188
|
+
ext: fileEnding,
|
|
189
|
+
size: (blob.size / 1024).toFixed(0)
|
|
190
|
+
});
|
|
149
191
|
onChange(data);
|
|
150
192
|
} catch (error) {
|
|
151
193
|
if (isCorsError(error)) {
|
|
@@ -158,26 +200,22 @@
|
|
|
158
200
|
})();
|
|
159
201
|
// The opaque TypeError hides CORS blocks and plain network failures (404, DNS)
|
|
160
202
|
// alike, so the message has to cover both.
|
|
161
|
-
let hint =
|
|
162
|
-
'Check that the URL is correct and the file is publicly accessible. If the server requires login, download the file and use the Upload option instead.';
|
|
203
|
+
let hint = t.urlUnreachableHint;
|
|
163
204
|
if (host.includes('sharepoint.com') || host.includes('onedrive.com')) {
|
|
164
|
-
hint =
|
|
165
|
-
'SharePoint/OneDrive blocks browser access. Open the file in SharePoint, use "Download" to save it locally, then upload it here.';
|
|
205
|
+
hint = t.urlHintSharePoint;
|
|
166
206
|
} else if (host.includes('drive.google.com')) {
|
|
167
|
-
hint =
|
|
168
|
-
'Google Drive blocks browser access. Use "Download" in Google Drive to save the file locally, then upload it here.';
|
|
207
|
+
hint = t.urlHintGoogleDrive;
|
|
169
208
|
} else if (host.includes('dropbox.com')) {
|
|
170
|
-
hint =
|
|
171
|
-
'Dropbox links may block browser access. Try changing "?dl=0" to "?dl=1" in the URL, or download and upload instead.';
|
|
209
|
+
hint = t.urlHintDropbox;
|
|
172
210
|
}
|
|
173
211
|
urlError = {
|
|
174
|
-
message:
|
|
212
|
+
message: formatMessage(t.urlUnreachable, { host }),
|
|
175
213
|
isCors: true,
|
|
176
214
|
hint
|
|
177
215
|
};
|
|
178
216
|
} else {
|
|
179
217
|
urlError = {
|
|
180
|
-
message: error instanceof Error ? error.message :
|
|
218
|
+
message: error instanceof Error ? error.message : t.unknownError,
|
|
181
219
|
isCors: false
|
|
182
220
|
};
|
|
183
221
|
}
|
|
@@ -186,50 +224,39 @@
|
|
|
186
224
|
}
|
|
187
225
|
}
|
|
188
226
|
|
|
189
|
-
function
|
|
190
|
-
const
|
|
191
|
-
const file = target.files?.[0];
|
|
192
|
-
if (!file) return;
|
|
193
|
-
|
|
194
|
-
const fileEnding = getFileExtension(file.name);
|
|
227
|
+
async function handleFile(file: File) {
|
|
228
|
+
const requestedEnding = getFileExtension(file.name);
|
|
195
229
|
|
|
196
|
-
if (!
|
|
197
|
-
toast.error(
|
|
230
|
+
if (!acceptsFileEnding(requestedEnding, acceptedFormats)) {
|
|
231
|
+
toast.error(formatNotAccepted(requestedEnding));
|
|
198
232
|
return;
|
|
199
233
|
}
|
|
200
234
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
uploadedFileName = file.name;
|
|
214
|
-
|
|
215
|
-
const reader = new FileReader();
|
|
216
|
-
reader.onload = (e) => {
|
|
217
|
-
const base64 = e.target?.result as string;
|
|
218
|
-
const base64Data = base64.includes(',') ? base64.split(',')[1] : base64;
|
|
235
|
+
try {
|
|
236
|
+
const { blob, fileEnding } = await prepare(file, requestedEnding);
|
|
237
|
+
|
|
238
|
+
// The file is base64-embedded into the compute request body, so without a
|
|
239
|
+
// client-side cap an oversize file comes back as an opaque 413. This cap is
|
|
240
|
+
// currently LOOSER than the server's COMPUTE_REQUEST_MAX_BYTES (see the TEMP
|
|
241
|
+
// note in constants.ts) — some files pass here and still 413. The URL import
|
|
242
|
+
// path applies the same cap.
|
|
243
|
+
if (blob.size > APP_DEFAULTS.FILE_UPLOAD.MAX_SIZE_BYTES) {
|
|
244
|
+
toast.error(formatTooLarge(blob.size));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
219
247
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
type: 'base64',
|
|
223
|
-
fileEnding
|
|
224
|
-
});
|
|
248
|
+
uploadedFileName = file.name;
|
|
249
|
+
const data = JSON.stringify({ file: await readBase64(blob), type: 'base64', fileEnding });
|
|
225
250
|
value = data;
|
|
226
251
|
onChange(data);
|
|
227
|
-
}
|
|
228
|
-
|
|
252
|
+
} catch (error) {
|
|
253
|
+
toast.error(error instanceof Error ? error.message : t.fileReadFailed);
|
|
254
|
+
}
|
|
229
255
|
}
|
|
230
256
|
|
|
231
|
-
function openFilePicker() {
|
|
232
|
-
|
|
257
|
+
async function openFilePicker() {
|
|
258
|
+
const file = await pickFile({ accept: acceptAttribute });
|
|
259
|
+
if (file) handleFile(file);
|
|
233
260
|
}
|
|
234
261
|
|
|
235
262
|
function handleDragOver(e: DragEvent) {
|
|
@@ -245,27 +272,14 @@
|
|
|
245
272
|
e.preventDefault();
|
|
246
273
|
isDragging = false;
|
|
247
274
|
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
250
|
-
const fakeEvent = new Event('change');
|
|
251
|
-
const fakeTarget = { files: files } as HTMLInputElement;
|
|
252
|
-
Object.defineProperty(fakeEvent, 'target', { value: fakeTarget });
|
|
253
|
-
handleFileUpload(fakeEvent);
|
|
254
|
-
}
|
|
275
|
+
const file = e.dataTransfer?.files?.[0];
|
|
276
|
+
if (file) handleFile(file);
|
|
255
277
|
}
|
|
256
278
|
|
|
257
279
|
const acceptAttribute = $derived(acceptedFormats.length > 0 ? acceptedFormats.join(',') : '*');
|
|
258
280
|
</script>
|
|
259
281
|
|
|
260
282
|
<div class="gap-2.5 flex w-full flex-col">
|
|
261
|
-
<input
|
|
262
|
-
bind:this={fileInput}
|
|
263
|
-
type="file"
|
|
264
|
-
accept={acceptAttribute}
|
|
265
|
-
onchange={handleFileUpload}
|
|
266
|
-
class="hidden"
|
|
267
|
-
/>
|
|
268
|
-
|
|
269
283
|
{#if showToggle}
|
|
270
284
|
<div class="p-0.5 text-xs flex rounded-md bg-muted">
|
|
271
285
|
{#each effectiveModes as mode (mode)}
|
|
@@ -273,13 +287,13 @@
|
|
|
273
287
|
type="button"
|
|
274
288
|
onclick={() => (activeMode = mode)}
|
|
275
289
|
aria-pressed={activeMode === mode}
|
|
276
|
-
class={`px-3 py-1 font-medium flex-1 rounded-sm transition-colors ${
|
|
290
|
+
class={`px-3 py-1 font-medium flex-1 rounded-sm border transition-colors ${
|
|
277
291
|
activeMode === mode
|
|
278
|
-
? 'shadow-xs bg-
|
|
279
|
-
: 'text-muted-foreground hover:text-foreground'
|
|
292
|
+
? 'shadow-xs border-border-strong bg-raised text-foreground'
|
|
293
|
+
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
280
294
|
}`}
|
|
281
295
|
>
|
|
282
|
-
{mode === 'upload' ?
|
|
296
|
+
{mode === 'upload' ? t.fileModeUpload : t.fileModeUrl}
|
|
283
297
|
</button>
|
|
284
298
|
{/each}
|
|
285
299
|
</div>
|
|
@@ -289,7 +303,7 @@
|
|
|
289
303
|
<div class="gap-2 flex flex-col">
|
|
290
304
|
<Label for="url-input">
|
|
291
305
|
<Link size={16} />
|
|
292
|
-
|
|
306
|
+
{t.fileUrlLabel}
|
|
293
307
|
</Label>
|
|
294
308
|
<div class="gap-2 flex">
|
|
295
309
|
<Input
|
|
@@ -310,7 +324,7 @@
|
|
|
310
324
|
disabled={isLoading || !urlInput.trim()}
|
|
311
325
|
onclick={handleUrlChange}
|
|
312
326
|
>
|
|
313
|
-
{isLoading ?
|
|
327
|
+
{isLoading ? t.fetchingFile : t.fetchFile}
|
|
314
328
|
</Button>
|
|
315
329
|
</div>
|
|
316
330
|
|
|
@@ -336,9 +350,7 @@
|
|
|
336
350
|
{/if}
|
|
337
351
|
|
|
338
352
|
{#if !urlError && !urlSuccess}
|
|
339
|
-
<p class="text-xs text-muted-foreground">
|
|
340
|
-
URL must be publicly accessible (no login required).
|
|
341
|
-
</p>
|
|
353
|
+
<p class="text-xs text-muted-foreground">{t.fileUrlPublic}</p>
|
|
342
354
|
{/if}
|
|
343
355
|
</div>
|
|
344
356
|
{:else}
|
|
@@ -353,11 +365,11 @@
|
|
|
353
365
|
ondrop={handleDrop}
|
|
354
366
|
>
|
|
355
367
|
<FileUp size={16} />
|
|
356
|
-
{uploadedFileName ||
|
|
368
|
+
{uploadedFileName || t.chooseFile}
|
|
357
369
|
</Button>
|
|
358
370
|
{#if acceptedFormats.length > 0}
|
|
359
371
|
<p class="text-xs text-muted-foreground">
|
|
360
|
-
|
|
372
|
+
{formatMessage(t.acceptedFormats, { formats: acceptedFormats.join(', ') })}
|
|
361
373
|
</p>
|
|
362
374
|
{/if}
|
|
363
375
|
</div>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '#lib/components/primitives/button/index.js';
|
|
3
|
+
import type { GeometryPickResult, PickKind } from '#lib/contexts/hostContext.svelte.js';
|
|
4
|
+
import { getLocaleContext } from '#lib/i18n/localeContext.svelte.js';
|
|
5
|
+
import type { ViewerMessages } from '#lib/i18n/messages.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
inputId: string;
|
|
9
|
+
/** The kinds of object the input takes; one kind names the button after it. */
|
|
10
|
+
kinds?: readonly PickKind[];
|
|
11
|
+
/** 1 picks a single object. */
|
|
12
|
+
atMost?: number;
|
|
13
|
+
/** What the host reports the param holds, such as "2 curves"; absent until something is picked. */
|
|
14
|
+
summary?: string;
|
|
15
|
+
/** Absent where this page may not pick, such as a browser tab beside the Rhino panel. */
|
|
16
|
+
onPick?: () => Promise<GeometryPickResult>;
|
|
17
|
+
onClear?: () => void;
|
|
18
|
+
/** Subscribes to what the host holds after any page picks or clears; returns an unsubscribe. */
|
|
19
|
+
watchSummary?: (listener: (summary: string | null) => void) => () => void;
|
|
20
|
+
/** Shown when the pick fails; the host's own message. */
|
|
21
|
+
onError?: (message: string) => void;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
inputId,
|
|
27
|
+
kinds,
|
|
28
|
+
atMost,
|
|
29
|
+
summary,
|
|
30
|
+
onPick,
|
|
31
|
+
onClear,
|
|
32
|
+
watchSummary,
|
|
33
|
+
onError,
|
|
34
|
+
disabled = false
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
const locale = getLocaleContext();
|
|
38
|
+
const t = $derived(locale.messages);
|
|
39
|
+
|
|
40
|
+
type PickLabels = readonly [one: keyof ViewerMessages, other: keyof ViewerMessages];
|
|
41
|
+
|
|
42
|
+
const ANY_LABELS: PickLabels = ['geometryPickAnyOne', 'geometryPickAnyOther'];
|
|
43
|
+
const KIND_LABELS: Record<PickKind, PickLabels> = {
|
|
44
|
+
point: ['geometryPickPointOne', 'geometryPickPointOther'],
|
|
45
|
+
pointCloud: ['geometryPickPointCloudOne', 'geometryPickPointCloudOther'],
|
|
46
|
+
curve: ['geometryPickCurveOne', 'geometryPickCurveOther'],
|
|
47
|
+
line: ['geometryPickLineOne', 'geometryPickLineOther'],
|
|
48
|
+
plane: ['geometryPickPlaneOne', 'geometryPickPlaneOther'],
|
|
49
|
+
surface: ['geometryPickSurfaceOne', 'geometryPickSurfaceOther'],
|
|
50
|
+
polysurface: ['geometryPickPolysurfaceOne', 'geometryPickPolysurfaceOther'],
|
|
51
|
+
extrusion: ['geometryPickExtrusionOne', 'geometryPickExtrusionOther'],
|
|
52
|
+
mesh: ['geometryPickMeshOne', 'geometryPickMeshOther'],
|
|
53
|
+
subd: ['geometryPickSubdOne', 'geometryPickSubdOther'],
|
|
54
|
+
block: ['geometryPickBlockOne', 'geometryPickBlockOther']
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const pickLabel = $derived.by(() => {
|
|
58
|
+
const [one, other] = kinds?.length === 1 ? KIND_LABELS[kinds[0]] : ANY_LABELS;
|
|
59
|
+
return t[atMost === 1 ? one : other];
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Rhino's command line prompts during the pick; the page keeps the button from being pressed
|
|
63
|
+
// twice and says where to look.
|
|
64
|
+
let picking = $state(false);
|
|
65
|
+
// The last pick or clear wins over what the host reported when the page loaded.
|
|
66
|
+
let local = $state<string | null | undefined>(undefined);
|
|
67
|
+
const shown = $derived(local !== undefined ? local : (summary ?? null));
|
|
68
|
+
|
|
69
|
+
$effect(() =>
|
|
70
|
+
watchSummary?.((held) => {
|
|
71
|
+
local = held;
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
async function pick(onPick: () => Promise<GeometryPickResult>) {
|
|
76
|
+
picking = true;
|
|
77
|
+
try {
|
|
78
|
+
const result = await onPick();
|
|
79
|
+
if (result.outcome === 'picked') local = result.summary;
|
|
80
|
+
else if (result.outcome === 'failed') onError?.(result.error);
|
|
81
|
+
} finally {
|
|
82
|
+
picking = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// The field label points at inputId: the button where there is one, else an <output>, which
|
|
87
|
+
// unlike a <span> can be labelled.
|
|
88
|
+
const readout = $derived(onPick ? 'span' : 'output');
|
|
89
|
+
|
|
90
|
+
function clear(onClear: () => void) {
|
|
91
|
+
local = null;
|
|
92
|
+
onClear();
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<div class="gap-2 min-w-0 flex flex-wrap items-center">
|
|
97
|
+
{#if onPick}
|
|
98
|
+
<Button
|
|
99
|
+
id={inputId}
|
|
100
|
+
variant="outline"
|
|
101
|
+
size="sm"
|
|
102
|
+
onclick={() => pick(onPick)}
|
|
103
|
+
disabled={disabled || picking}
|
|
104
|
+
>
|
|
105
|
+
{shown ? t.geometryPickAgain : pickLabel}
|
|
106
|
+
</Button>
|
|
107
|
+
{/if}
|
|
108
|
+
{#if picking}
|
|
109
|
+
<span class="text-sm min-w-0 text-muted-foreground" role="status">{t.geometryPicking}</span>
|
|
110
|
+
{:else if shown}
|
|
111
|
+
<svelte:element
|
|
112
|
+
this={readout}
|
|
113
|
+
id={onPick ? undefined : inputId}
|
|
114
|
+
class="text-sm min-w-0 truncate text-foreground"
|
|
115
|
+
>
|
|
116
|
+
{shown}
|
|
117
|
+
</svelte:element>
|
|
118
|
+
{#if onClear}
|
|
119
|
+
<Button variant="ghost" size="sm" onclick={() => clear(onClear)} {disabled}>
|
|
120
|
+
{t.geometryClear}
|
|
121
|
+
</Button>
|
|
122
|
+
{/if}
|
|
123
|
+
{:else if !onPick}
|
|
124
|
+
<output id={inputId} class="text-sm min-w-0 text-muted-foreground"
|
|
125
|
+
>{t.geometryPickInPanel}</output
|
|
126
|
+
>
|
|
127
|
+
{/if}
|
|
128
|
+
</div>
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { NumberWidgetConfig, SupportedTypes } from '@selvajs/schemas';
|
|
3
|
-
import { debounce } from '
|
|
4
|
-
import { Input } from '
|
|
5
|
-
import { Slider } from '
|
|
6
|
-
import * as InputGroup from '
|
|
7
|
-
import { Separator } from '
|
|
8
|
-
import * as Field from '
|
|
3
|
+
import { debounce } from '#lib/utils/debounce.js';
|
|
4
|
+
import { Input } from '#lib/components/primitives/input/index.js';
|
|
5
|
+
import { Slider } from '#lib/components/primitives/slider/index.js';
|
|
6
|
+
import * as InputGroup from '#lib/components/primitives/input-group/index.js';
|
|
7
|
+
import { Separator } from '#lib/components/primitives/separator/index.js';
|
|
8
|
+
import * as Field from '#lib/components/primitives/field/index.js';
|
|
9
|
+
import { getLocaleContext } from '#lib/i18n/localeContext.svelte.js';
|
|
10
|
+
import { formatMessage } from '#lib/i18n/messages.js';
|
|
9
11
|
|
|
10
12
|
interface Props {
|
|
11
13
|
inputId: string;
|
|
14
|
+
/** Id of the field's label, which also names the slider thumb. */
|
|
15
|
+
labelId?: string;
|
|
12
16
|
value?: number;
|
|
13
17
|
config?: NumberWidgetConfig;
|
|
14
18
|
onChange: (value: SupportedTypes) => void;
|
|
15
19
|
disabled?: boolean;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
let {
|
|
22
|
+
let {
|
|
23
|
+
inputId,
|
|
24
|
+
labelId,
|
|
25
|
+
value = $bindable(),
|
|
26
|
+
config,
|
|
27
|
+
onChange,
|
|
28
|
+
disabled = false
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const locale = getLocaleContext();
|
|
32
|
+
const t = $derived(locale.messages);
|
|
19
33
|
|
|
20
34
|
let validationError = $state<string | null>(null);
|
|
21
35
|
|
|
@@ -29,9 +43,9 @@
|
|
|
29
43
|
|
|
30
44
|
function validateNumber(val: number): string | null {
|
|
31
45
|
if (config?.minimum !== undefined && val < config.minimum)
|
|
32
|
-
return
|
|
46
|
+
return formatMessage(t.minValue, { min: config.minimum });
|
|
33
47
|
if (config?.maximum !== undefined && val > config.maximum)
|
|
34
|
-
return
|
|
48
|
+
return formatMessage(t.maxValue, { max: config.maximum });
|
|
35
49
|
return null;
|
|
36
50
|
}
|
|
37
51
|
|
|
@@ -44,11 +58,69 @@
|
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
// bits-ui builds an array of every step and rebuilds a tick object per step on each value
|
|
62
|
+
// change, so a fine-grained range freezes the drag. Past this many steps the track drives a
|
|
63
|
+
// coarse proxy scale instead; the number box next to it still sets any exact value.
|
|
64
|
+
const MAX_TRACK_STEPS = 1000;
|
|
65
|
+
const FINE_DRAG_FACTOR = 10;
|
|
66
|
+
const stepCount = $derived((max - min) / step);
|
|
67
|
+
const useProxyTrack = $derived(
|
|
68
|
+
step > 0 && Number.isFinite(stepCount) && stepCount > MAX_TRACK_STEPS
|
|
69
|
+
);
|
|
70
|
+
const trackMin = $derived(useProxyTrack ? 0 : min);
|
|
71
|
+
const trackMax = $derived(useProxyTrack ? MAX_TRACK_STEPS : max);
|
|
72
|
+
const stepPrecision = $derived((String(step).split('.')[1] ?? '').length);
|
|
73
|
+
|
|
74
|
+
let dragging = false;
|
|
75
|
+
let shiftHeld = false;
|
|
76
|
+
let lastTrack: number | null = null;
|
|
77
|
+
let fineAnchor: { track: number; value: number } | null = null;
|
|
78
|
+
|
|
79
|
+
function snapToStep(v: number): number {
|
|
80
|
+
const clamped = Math.min(max, Math.max(min, v));
|
|
81
|
+
if (!(step > 0)) return clamped;
|
|
82
|
+
const snapped = min + Math.round((clamped - min) / step) * step;
|
|
83
|
+
return Math.min(max, Number(snapped.toFixed(stepPrecision)));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function toTrack(v: number): number {
|
|
87
|
+
return useProxyTrack ? Math.round(((v - min) / (max - min)) * MAX_TRACK_STEPS) : v;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function trackSpanToValue(trackDelta: number): number {
|
|
91
|
+
return (trackDelta / (trackMax - trackMin)) * (max - min);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// bits-ui maps the pointer to an absolute track position, so fine drag works in deltas: the
|
|
95
|
+
// value moves FINE_DRAG_FACTOR times slower than the pointer from where Shift took effect.
|
|
96
|
+
function setFromTrack(track: number) {
|
|
97
|
+
if (max <= min) return;
|
|
98
|
+
let newValue: number;
|
|
99
|
+
if (dragging && shiftHeld) {
|
|
100
|
+
fineAnchor ??= { track: lastTrack ?? track, value: numValue };
|
|
101
|
+
newValue = snapToStep(
|
|
102
|
+
fineAnchor.value + trackSpanToValue(track - fineAnchor.track) / FINE_DRAG_FACTOR
|
|
103
|
+
);
|
|
104
|
+
} else {
|
|
105
|
+
fineAnchor = null;
|
|
106
|
+
newValue = snapToStep(min + trackSpanToValue(track - trackMin));
|
|
107
|
+
}
|
|
108
|
+
lastTrack = track;
|
|
48
109
|
value = newValue;
|
|
49
110
|
commitSliderDebounced(newValue);
|
|
50
111
|
}
|
|
51
112
|
|
|
113
|
+
function startDrag(e: PointerEvent) {
|
|
114
|
+
dragging = true;
|
|
115
|
+
shiftHeld = e.shiftKey;
|
|
116
|
+
lastTrack = null;
|
|
117
|
+
fineAnchor = null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function setShift(e: KeyboardEvent) {
|
|
121
|
+
if (e.key === 'Shift') shiftHeld = e.type === 'keydown';
|
|
122
|
+
}
|
|
123
|
+
|
|
52
124
|
const decimals = $derived(Math.max(0, -Math.floor(Math.log10(step))));
|
|
53
125
|
|
|
54
126
|
function commitTypedValue(input: HTMLInputElement) {
|
|
@@ -65,6 +137,13 @@
|
|
|
65
137
|
}
|
|
66
138
|
</script>
|
|
67
139
|
|
|
140
|
+
<svelte:window
|
|
141
|
+
onkeydown={setShift}
|
|
142
|
+
onkeyup={setShift}
|
|
143
|
+
onpointerup={() => (dragging = false)}
|
|
144
|
+
onblur={() => (shiftHeld = dragging = false)}
|
|
145
|
+
/>
|
|
146
|
+
|
|
68
147
|
{#if config?.renderAsSlider}
|
|
69
148
|
<!-- Value and track share one box, the height of a number input, so mixed rows line up and
|
|
70
149
|
a value can't be read as belonging to the neighbouring slider. -->
|
|
@@ -84,14 +163,17 @@
|
|
|
84
163
|
}}
|
|
85
164
|
/>
|
|
86
165
|
<Separator orientation="vertical" />
|
|
87
|
-
<div class="px-3.5 min-w-0 flex flex-1 items-center">
|
|
166
|
+
<div class="px-3.5 min-w-0 flex flex-1 items-center" onpointerdowncapture={startDrag}>
|
|
167
|
+
<!-- A function binding, not value + onValueChange: when a fine drag leaves the value
|
|
168
|
+
unchanged, the thumb must stay put instead of following the pointer. -->
|
|
88
169
|
<Slider
|
|
89
170
|
type="single"
|
|
90
|
-
value={numValue}
|
|
91
|
-
{
|
|
92
|
-
{
|
|
93
|
-
{step}
|
|
94
|
-
|
|
171
|
+
bind:value={() => toTrack(numValue), setFromTrack}
|
|
172
|
+
min={trackMin}
|
|
173
|
+
max={trackMax}
|
|
174
|
+
step={useProxyTrack ? 1 : step}
|
|
175
|
+
title={t.fineDragHint}
|
|
176
|
+
thumbLabelledby={labelId}
|
|
95
177
|
{disabled}
|
|
96
178
|
/>
|
|
97
179
|
</div>
|