@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
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MediaQuery } from 'svelte/reactivity';
|
|
3
|
+
import {
|
|
4
|
+
Camera,
|
|
5
|
+
Grid3x3,
|
|
6
|
+
Layers,
|
|
7
|
+
LocateFixed,
|
|
8
|
+
Maximize,
|
|
9
|
+
Minimize,
|
|
10
|
+
Moon,
|
|
11
|
+
Ellipsis,
|
|
12
|
+
Palette,
|
|
13
|
+
Ruler,
|
|
14
|
+
Settings2,
|
|
15
|
+
Spline,
|
|
16
|
+
Cuboid
|
|
17
|
+
} from '@lucide/svelte';
|
|
18
|
+
import type { CameraProjection, Look, ViewPreset } from '@selvajs/visualization/render';
|
|
19
|
+
import * as DropdownMenu from '#lib/components/primitives/dropdown-menu/index.js';
|
|
20
|
+
import type { ViewerMessages } from '#lib/i18n/messages.js';
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
t: ViewerMessages;
|
|
24
|
+
/** Fades the dock out while a host drawer covers the viewer. */
|
|
25
|
+
hidden?: boolean;
|
|
26
|
+
showDisplay: boolean;
|
|
27
|
+
showGrid: boolean;
|
|
28
|
+
showSceneManager: boolean;
|
|
29
|
+
showScreenshot: boolean;
|
|
30
|
+
showFullscreen: boolean;
|
|
31
|
+
styleOptions: { look: Look; label: string }[];
|
|
32
|
+
viewPresets: { preset: ViewPreset; label: string }[];
|
|
33
|
+
renderStyle: Look;
|
|
34
|
+
nightOn: boolean;
|
|
35
|
+
edgesVisible: boolean;
|
|
36
|
+
measureActive: boolean;
|
|
37
|
+
gridVisible: boolean;
|
|
38
|
+
sceneManagerOpen: boolean;
|
|
39
|
+
projection: CameraProjection;
|
|
40
|
+
isFullscreen: boolean;
|
|
41
|
+
onSetLook: (look: Look) => void;
|
|
42
|
+
onToggleNight: () => void;
|
|
43
|
+
onToggleEdges: () => void;
|
|
44
|
+
onToggleMeasure: () => void;
|
|
45
|
+
onToggleGrid: () => void;
|
|
46
|
+
onToggleSceneManager: () => void;
|
|
47
|
+
onToggleProjection: () => void;
|
|
48
|
+
onSetView: (preset: ViewPreset) => void;
|
|
49
|
+
onFit: () => void;
|
|
50
|
+
onScreenshot: () => void;
|
|
51
|
+
onToggleFullscreen: () => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let {
|
|
55
|
+
t,
|
|
56
|
+
hidden = false,
|
|
57
|
+
showDisplay,
|
|
58
|
+
showGrid,
|
|
59
|
+
showSceneManager,
|
|
60
|
+
showScreenshot,
|
|
61
|
+
showFullscreen,
|
|
62
|
+
styleOptions,
|
|
63
|
+
viewPresets,
|
|
64
|
+
renderStyle,
|
|
65
|
+
nightOn,
|
|
66
|
+
edgesVisible,
|
|
67
|
+
measureActive,
|
|
68
|
+
gridVisible,
|
|
69
|
+
sceneManagerOpen,
|
|
70
|
+
projection,
|
|
71
|
+
isFullscreen,
|
|
72
|
+
onSetLook,
|
|
73
|
+
onToggleNight,
|
|
74
|
+
onToggleEdges,
|
|
75
|
+
onToggleMeasure,
|
|
76
|
+
onToggleGrid,
|
|
77
|
+
onToggleSceneManager,
|
|
78
|
+
onToggleProjection,
|
|
79
|
+
onSetView,
|
|
80
|
+
onFit,
|
|
81
|
+
onScreenshot,
|
|
82
|
+
onToggleFullscreen
|
|
83
|
+
}: Props = $props();
|
|
84
|
+
|
|
85
|
+
// A hairline pill at the bottom edge is the iOS home indicator on a phone, and swiping it leaves
|
|
86
|
+
// the app. Touch gets a visible round button instead.
|
|
87
|
+
const touch = new MediaQuery('(pointer: coarse)');
|
|
88
|
+
const reducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)');
|
|
89
|
+
|
|
90
|
+
const COLLAPSED = $derived(touch.current ? { w: 44, h: 44 } : { w: 56, h: 5 });
|
|
91
|
+
const EXPANDED_H = $derived(touch.current ? 54 : 42);
|
|
92
|
+
const PAD = 4;
|
|
93
|
+
|
|
94
|
+
let dockEl: HTMLDivElement | undefined = $state();
|
|
95
|
+
let rowWidth = $state(0);
|
|
96
|
+
let engaged = $state(false);
|
|
97
|
+
// Set by an outside press: the dock was dismissed, so it shrinks faster.
|
|
98
|
+
let dismissed = $state(false);
|
|
99
|
+
let menusOpen = $state(0);
|
|
100
|
+
let focusWithin = $state(false);
|
|
101
|
+
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
102
|
+
|
|
103
|
+
const wantOpen = $derived(engaged || menusOpen > 0 || focusWithin);
|
|
104
|
+
|
|
105
|
+
// Opening leads: the pill grows first and the buttons pop in behind it. Closing mirrors it: the
|
|
106
|
+
// buttons fade first, then the pill shrinks. Without the delay on close the row is clipped mid-fade.
|
|
107
|
+
const EASE = 'cubic-bezier(0.32, 0.72, 0, 1)';
|
|
108
|
+
const containerStyle = $derived.by(() => {
|
|
109
|
+
const bottom = touch.current ? 'max(1rem,env(safe-area-inset-bottom))' : '0.75rem';
|
|
110
|
+
const geometry = wantOpen
|
|
111
|
+
? `width:${rowWidth + PAD * 2 + 2}px;height:${EXPANDED_H}px;border-radius:12px;padding:${PAD}px`
|
|
112
|
+
: `width:${COLLAPSED.w}px;height:${COLLAPSED.h}px;border-radius:${touch.current ? 22 : 99}px;padding:0px`;
|
|
113
|
+
const size = `bottom:${bottom};${geometry}`;
|
|
114
|
+
if (reducedMotion.current) return size;
|
|
115
|
+
const ms = wantOpen ? 420 : dismissed ? 130 : 170;
|
|
116
|
+
const delay = wantOpen || dismissed ? 0 : 20;
|
|
117
|
+
const props = ['width', 'height', 'border-radius', 'padding'];
|
|
118
|
+
return `${size};transition:${props.map((p) => `${p} ${ms}ms ${EASE} ${delay}ms`).join(',')},opacity 200ms ease`;
|
|
119
|
+
});
|
|
120
|
+
const itemStyle = (index: number) => {
|
|
121
|
+
if (reducedMotion.current) return `opacity:${wantOpen ? 1 : 0}`;
|
|
122
|
+
const delay = 110 + index * 35;
|
|
123
|
+
return wantOpen
|
|
124
|
+
? `opacity:1;transform:none;transition:opacity 240ms ease ${delay}ms,transform 420ms cubic-bezier(0.34,1.45,0.5,1) ${delay}ms`
|
|
125
|
+
: 'opacity:0;transform:translateY(6px) scale(0.85);transition:opacity 100ms ease,transform 100ms ease';
|
|
126
|
+
};
|
|
127
|
+
// The touch button's settings glyph hands over to the row.
|
|
128
|
+
const glyphStyle = $derived(
|
|
129
|
+
`opacity:${touch.current && !wantOpen ? 1 : 0};${reducedMotion.current ? '' : `transition:opacity ${wantOpen ? 100 : 100}ms ease ${wantOpen || dismissed ? 0 : 60}ms`}`
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
function scheduleCollapse(delay = touch.current ? 3500 : 150) {
|
|
133
|
+
clearTimeout(idleTimer);
|
|
134
|
+
idleTimer = setTimeout(() => (engaged = false), delay);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function engage() {
|
|
138
|
+
engaged = true;
|
|
139
|
+
dismissed = false;
|
|
140
|
+
clearTimeout(idleTimer);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function handlePointerMove(event: PointerEvent) {
|
|
144
|
+
if (!dockEl || touch.current || event.pointerType === 'touch') return;
|
|
145
|
+
// Orbiting or box-dragging with the button down should never pop the dock open.
|
|
146
|
+
if (event.buttons) return;
|
|
147
|
+
const host = dockEl.offsetParent;
|
|
148
|
+
if (!host || !(event.target instanceof Node) || !host.contains(event.target)) {
|
|
149
|
+
if (engaged) scheduleCollapse();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const r = dockEl.getBoundingClientRect();
|
|
153
|
+
const near =
|
|
154
|
+
event.clientY > r.top - 36 && Math.abs(event.clientX - (r.left + r.width / 2)) < 110;
|
|
155
|
+
if (near) engage();
|
|
156
|
+
else if (engaged) scheduleCollapse();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function handleWindowPointerDown(event: PointerEvent) {
|
|
160
|
+
if (!engaged || !dockEl || !(event.target instanceof Node)) return;
|
|
161
|
+
if (!dockEl.contains(event.target)) {
|
|
162
|
+
clearTimeout(idleTimer);
|
|
163
|
+
dismissed = true;
|
|
164
|
+
engaged = false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function openFromCollapsed() {
|
|
169
|
+
if (wantOpen) return;
|
|
170
|
+
engage();
|
|
171
|
+
if (touch.current) scheduleCollapse();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Any use of the open dock on touch pushes the idle collapse back.
|
|
175
|
+
function keepAlive() {
|
|
176
|
+
if (touch.current && engaged) scheduleCollapse();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function trackMenu(open: boolean) {
|
|
180
|
+
menusOpen += open ? 1 : -1;
|
|
181
|
+
if (!open && !touch.current) scheduleCollapse(250);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Buttons in the row, in order, so the stagger index stays right when some are hidden.
|
|
185
|
+
const buttons = $derived(
|
|
186
|
+
[
|
|
187
|
+
showDisplay && 'look',
|
|
188
|
+
showSceneManager && 'scene',
|
|
189
|
+
'measure',
|
|
190
|
+
'fit',
|
|
191
|
+
showFullscreen && 'full',
|
|
192
|
+
'more'
|
|
193
|
+
].filter(Boolean) as string[]
|
|
194
|
+
);
|
|
195
|
+
const idx = (id: string) => buttons.indexOf(id);
|
|
196
|
+
|
|
197
|
+
const BTN =
|
|
198
|
+
'relative grid size-8.5 shrink-0 place-items-center rounded-lg text-foreground outline-none transition-colors hover:bg-hover hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring aria-pressed:text-foreground data-[state=open]:bg-selected data-[state=open]:text-foreground pointer-coarse:size-11';
|
|
199
|
+
const DOT =
|
|
200
|
+
'aria-pressed:after:absolute aria-pressed:after:bottom-0.5 aria-pressed:after:size-1 aria-pressed:after:rounded-full aria-pressed:after:bg-primary';
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<!-- eslint-disable shadcn/no-inline-styles -- the pill animates between measured pixel sizes and staggered per-item delays; no class can express those -->
|
|
204
|
+
|
|
205
|
+
<svelte:window onpointermove={handlePointerMove} onpointerdown={handleWindowPointerDown} />
|
|
206
|
+
|
|
207
|
+
<!-- Resting state is a pill; it grows into the row. Only the container is clickable while collapsed. -->
|
|
208
|
+
<div
|
|
209
|
+
bind:this={dockEl}
|
|
210
|
+
role="toolbar"
|
|
211
|
+
aria-label={t.toolsMenu}
|
|
212
|
+
tabindex="-1"
|
|
213
|
+
class="backdrop-blur-md absolute left-1/2 z-50 -translate-x-1/2 overflow-hidden transition-colors {hidden
|
|
214
|
+
? 'pointer-events-none opacity-0'
|
|
215
|
+
: ''} {wantOpen
|
|
216
|
+
? 'shadow-md border border-border-strong bg-card/75'
|
|
217
|
+
: touch.current
|
|
218
|
+
? 'shadow-lg cursor-pointer border border-border bg-background/90'
|
|
219
|
+
: nightOn
|
|
220
|
+
? 'border-white/25 bg-white/35 hover:bg-white/70 cursor-pointer border'
|
|
221
|
+
: 'shadow-sm cursor-pointer border border-border-strong bg-card/90 hover:bg-card'}"
|
|
222
|
+
style={containerStyle}
|
|
223
|
+
onclick={openFromCollapsed}
|
|
224
|
+
onkeydown={(e) => (e.key === 'Escape' ? (engaged = false) : undefined)}
|
|
225
|
+
onfocusin={() => (focusWithin = true)}
|
|
226
|
+
onfocusout={(e) => {
|
|
227
|
+
if (!dockEl?.contains(e.relatedTarget as Node | null)) focusWithin = false;
|
|
228
|
+
}}
|
|
229
|
+
onpointerdown={keepAlive}
|
|
230
|
+
>
|
|
231
|
+
{#if touch.current}
|
|
232
|
+
<span
|
|
233
|
+
class="inset-0 pointer-events-none absolute grid place-items-center text-foreground"
|
|
234
|
+
style={glyphStyle}
|
|
235
|
+
aria-hidden="true"
|
|
236
|
+
>
|
|
237
|
+
<Settings2 class="size-5" />
|
|
238
|
+
</span>
|
|
239
|
+
{/if}
|
|
240
|
+
|
|
241
|
+
<div
|
|
242
|
+
bind:offsetWidth={rowWidth}
|
|
243
|
+
class="gap-0.5 mx-auto flex w-max items-center"
|
|
244
|
+
inert={!wantOpen}
|
|
245
|
+
>
|
|
246
|
+
{#if showDisplay}
|
|
247
|
+
<div style={itemStyle(idx('look'))}>
|
|
248
|
+
<DropdownMenu.Root onOpenChange={trackMenu}>
|
|
249
|
+
<DropdownMenu.Trigger>
|
|
250
|
+
{#snippet child({ props })}
|
|
251
|
+
<button {...props} class={BTN} title={t.display} aria-label={t.display}>
|
|
252
|
+
<Palette class="size-4.5" />
|
|
253
|
+
</button>
|
|
254
|
+
{/snippet}
|
|
255
|
+
</DropdownMenu.Trigger>
|
|
256
|
+
<DropdownMenu.Content
|
|
257
|
+
side="top"
|
|
258
|
+
align="center"
|
|
259
|
+
sideOffset={12}
|
|
260
|
+
variant="glass"
|
|
261
|
+
class="min-w-52 z-10001 w-auto whitespace-nowrap"
|
|
262
|
+
>
|
|
263
|
+
<DropdownMenu.RadioGroup
|
|
264
|
+
value={renderStyle}
|
|
265
|
+
onValueChange={(look) => onSetLook(look as Look)}
|
|
266
|
+
>
|
|
267
|
+
{#each styleOptions as { look, label } (look)}
|
|
268
|
+
<DropdownMenu.RadioItem value={look} closeOnSelect={false}>
|
|
269
|
+
{label}
|
|
270
|
+
</DropdownMenu.RadioItem>
|
|
271
|
+
{/each}
|
|
272
|
+
</DropdownMenu.RadioGroup>
|
|
273
|
+
<DropdownMenu.Separator />
|
|
274
|
+
<DropdownMenu.CheckboxItem
|
|
275
|
+
switch
|
|
276
|
+
checked={edgesVisible}
|
|
277
|
+
onCheckedChange={onToggleEdges}
|
|
278
|
+
>
|
|
279
|
+
<Spline />
|
|
280
|
+
{t.edges}
|
|
281
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">E</DropdownMenu.Shortcut>
|
|
282
|
+
</DropdownMenu.CheckboxItem>
|
|
283
|
+
</DropdownMenu.Content>
|
|
284
|
+
</DropdownMenu.Root>
|
|
285
|
+
</div>
|
|
286
|
+
{/if}
|
|
287
|
+
|
|
288
|
+
{#if showSceneManager}
|
|
289
|
+
<div style={itemStyle(idx('scene'))}>
|
|
290
|
+
<button
|
|
291
|
+
class="{BTN} {DOT}"
|
|
292
|
+
title="{t.sceneManager} (S)"
|
|
293
|
+
aria-label={t.sceneManager}
|
|
294
|
+
aria-pressed={sceneManagerOpen}
|
|
295
|
+
onclick={onToggleSceneManager}
|
|
296
|
+
>
|
|
297
|
+
<Layers class="size-4.5" />
|
|
298
|
+
</button>
|
|
299
|
+
</div>
|
|
300
|
+
{/if}
|
|
301
|
+
|
|
302
|
+
<div style={itemStyle(idx('measure'))}>
|
|
303
|
+
<button
|
|
304
|
+
class="{BTN} {DOT}"
|
|
305
|
+
title="{t.measure} (M)"
|
|
306
|
+
aria-label={t.measure}
|
|
307
|
+
aria-pressed={measureActive}
|
|
308
|
+
onclick={onToggleMeasure}
|
|
309
|
+
>
|
|
310
|
+
<Ruler class="size-4.5" />
|
|
311
|
+
</button>
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<div style={itemStyle(idx('fit'))}>
|
|
315
|
+
<button class={BTN} title="{t.fitToView} (F)" aria-label="{t.fitToView} (F)" onclick={onFit}>
|
|
316
|
+
<LocateFixed class="size-4.5" />
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{#if showFullscreen}
|
|
321
|
+
<div style={itemStyle(idx('full'))}>
|
|
322
|
+
<button
|
|
323
|
+
class={BTN}
|
|
324
|
+
title={isFullscreen ? t.exitFullscreen : t.fullscreen}
|
|
325
|
+
aria-label={isFullscreen ? t.exitFullscreen : t.fullscreen}
|
|
326
|
+
onclick={onToggleFullscreen}
|
|
327
|
+
>
|
|
328
|
+
{#if isFullscreen}<Minimize class="size-4.5" />{:else}<Maximize class="size-4.5" />{/if}
|
|
329
|
+
</button>
|
|
330
|
+
</div>
|
|
331
|
+
{/if}
|
|
332
|
+
|
|
333
|
+
<div style={itemStyle(idx('more'))}>
|
|
334
|
+
<DropdownMenu.Root onOpenChange={trackMenu}>
|
|
335
|
+
<DropdownMenu.Trigger>
|
|
336
|
+
{#snippet child({ props })}
|
|
337
|
+
<button {...props} class={BTN} title={t.toolsMenu} aria-label={t.toolsMenu}>
|
|
338
|
+
<Ellipsis class="size-4.5" />
|
|
339
|
+
</button>
|
|
340
|
+
{/snippet}
|
|
341
|
+
</DropdownMenu.Trigger>
|
|
342
|
+
<DropdownMenu.Content
|
|
343
|
+
side="top"
|
|
344
|
+
align="end"
|
|
345
|
+
sideOffset={12}
|
|
346
|
+
variant="glass"
|
|
347
|
+
class="min-w-60 z-10001 w-auto whitespace-nowrap"
|
|
348
|
+
>
|
|
349
|
+
<DropdownMenu.Label>{t.views}</DropdownMenu.Label>
|
|
350
|
+
<div class="gap-1 px-1 pb-1 grid grid-cols-4">
|
|
351
|
+
{#each viewPresets as { preset, label } (preset)}
|
|
352
|
+
<DropdownMenu.Item
|
|
353
|
+
closeOnSelect={false}
|
|
354
|
+
onSelect={() => onSetView(preset)}
|
|
355
|
+
variant="chip"
|
|
356
|
+
>
|
|
357
|
+
{label}
|
|
358
|
+
</DropdownMenu.Item>
|
|
359
|
+
{/each}
|
|
360
|
+
</div>
|
|
361
|
+
|
|
362
|
+
<DropdownMenu.Separator />
|
|
363
|
+
|
|
364
|
+
<DropdownMenu.CheckboxItem
|
|
365
|
+
switch
|
|
366
|
+
checked={projection === 'orthographic'}
|
|
367
|
+
onCheckedChange={onToggleProjection}
|
|
368
|
+
>
|
|
369
|
+
<Cuboid />
|
|
370
|
+
{t.switchToOrthographic}
|
|
371
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">O</DropdownMenu.Shortcut>
|
|
372
|
+
</DropdownMenu.CheckboxItem>
|
|
373
|
+
|
|
374
|
+
{#if showGrid}
|
|
375
|
+
<DropdownMenu.CheckboxItem switch checked={gridVisible} onCheckedChange={onToggleGrid}>
|
|
376
|
+
<Grid3x3 />
|
|
377
|
+
{t.grid}
|
|
378
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">G</DropdownMenu.Shortcut>
|
|
379
|
+
</DropdownMenu.CheckboxItem>
|
|
380
|
+
{/if}
|
|
381
|
+
|
|
382
|
+
{#if showDisplay}
|
|
383
|
+
<DropdownMenu.CheckboxItem switch checked={nightOn} onCheckedChange={onToggleNight}>
|
|
384
|
+
<Moon />
|
|
385
|
+
{t.nightMode}
|
|
386
|
+
<DropdownMenu.Shortcut class="pointer-coarse:hidden">N</DropdownMenu.Shortcut>
|
|
387
|
+
</DropdownMenu.CheckboxItem>
|
|
388
|
+
{/if}
|
|
389
|
+
|
|
390
|
+
{#if showScreenshot}
|
|
391
|
+
<DropdownMenu.Separator />
|
|
392
|
+
{/if}
|
|
393
|
+
|
|
394
|
+
{#if showScreenshot}
|
|
395
|
+
<DropdownMenu.Item onSelect={onScreenshot}>
|
|
396
|
+
<Camera />
|
|
397
|
+
{t.screenshot}
|
|
398
|
+
</DropdownMenu.Item>
|
|
399
|
+
{/if}
|
|
400
|
+
</DropdownMenu.Content>
|
|
401
|
+
</DropdownMenu.Root>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
type FooterStore,
|
|
4
4
|
type FooterItem,
|
|
5
5
|
FOOTER_CONTEXT_KEY
|
|
6
|
-
} from '
|
|
6
|
+
} from '#lib/contexts/footerContext.svelte.js';
|
|
7
7
|
|
|
8
8
|
export type FooterItemConfig<P extends Record<string, unknown> = Record<string, unknown>> = Omit<
|
|
9
9
|
FooterItem<P>,
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
import type { ProgressPayload } from '@selvajs/schemas';
|
|
3
|
+
import type { LiveSolveState } from '@selvajs/solve/client';
|
|
4
|
+
|
|
5
|
+
/** A progress bar's width (0–1) and how long its transition to that width runs. */
|
|
6
|
+
export interface ProgressBar {
|
|
7
|
+
width: number;
|
|
8
|
+
ms: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The live channel's progress reports, smoothed for display. `headline` is the first source's
|
|
13
|
+
* report, held long enough to read; `bars` is the width each source's bar should animate to.
|
|
14
|
+
*/
|
|
15
|
+
export function createLiveProgress(getLive: () => LiveSolveState): {
|
|
16
|
+
readonly progress: ProgressPayload[];
|
|
17
|
+
readonly headline: ProgressPayload | undefined;
|
|
18
|
+
readonly bars: Readonly<Record<string, ProgressBar>>;
|
|
19
|
+
} {
|
|
20
|
+
const progress = $derived([...getLive().progress.values()]);
|
|
21
|
+
|
|
22
|
+
// A report only arrives when a step starts, so a bar that just shows `fraction` stands still
|
|
23
|
+
// through the step and then jumps. When the plugin knows how long the step took last time, the
|
|
24
|
+
// bar runs linearly to where the step will end, over that time. Steps are weighted by time, so
|
|
25
|
+
// linear gives one steady speed across the solve; any easing curve visibly speeds up and
|
|
26
|
+
// crawls within each step. A slower run waits at the step's end; a faster one snaps ahead.
|
|
27
|
+
let bars = $state.raw<Record<string, ProgressBar>>({});
|
|
28
|
+
// Any live event re-runs the effect below. Only a source with a new report may re-snap;
|
|
29
|
+
// the rest keep easing where they are.
|
|
30
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- bookkeeping for the effect below, never rendered
|
|
31
|
+
let seen = new Map<string, unknown>();
|
|
32
|
+
let targets: Record<string, ProgressBar> = {};
|
|
33
|
+
$effect(() => {
|
|
34
|
+
const snapped: Record<string, ProgressBar> = {};
|
|
35
|
+
const eased: Record<string, ProgressBar> = {};
|
|
36
|
+
const current = untrack(() => bars);
|
|
37
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- bookkeeping for this effect; reactive reads here would re-trigger it
|
|
38
|
+
const nextSeen = new Map<string, unknown>();
|
|
39
|
+
for (const p of progress) {
|
|
40
|
+
if (p.fraction === undefined) continue;
|
|
41
|
+
const key = p.source ?? '';
|
|
42
|
+
nextSeen.set(key, p);
|
|
43
|
+
if (seen.get(key) === p && current[key]) {
|
|
44
|
+
snapped[key] = current[key];
|
|
45
|
+
// This run's cleanup cancelled the frame that would have started the ease.
|
|
46
|
+
if (targets[key] && current[key] !== targets[key]) eased[key] = targets[key];
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
snapped[key] = { width: p.fraction, ms: 150 };
|
|
50
|
+
if (p.nextFraction !== undefined && p.stepMs !== undefined) {
|
|
51
|
+
eased[key] = { width: p.nextFraction, ms: p.stepMs };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
seen = nextSeen;
|
|
55
|
+
targets = eased;
|
|
56
|
+
bars = snapped;
|
|
57
|
+
if (Object.keys(eased).length === 0) return;
|
|
58
|
+
// Two frames: the snapped width must be painted before the eased one is set, or the
|
|
59
|
+
// browser folds both into one style change and there is nothing to transition from.
|
|
60
|
+
let frame = requestAnimationFrame(() => {
|
|
61
|
+
frame = requestAnimationFrame(() => (bars = { ...snapped, ...eased }));
|
|
62
|
+
});
|
|
63
|
+
return () => cancelAnimationFrame(frame);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Steps a few milliseconds long would swap the headline faster than it can be read. Each one
|
|
67
|
+
// stays up for at least this long; steps that end sooner are skipped in the label but still
|
|
68
|
+
// count in the bar. The label and its step count move together so they never disagree.
|
|
69
|
+
const HEADLINE_MIN_MS = 200;
|
|
70
|
+
let headline = $state.raw<ProgressPayload | undefined>(undefined);
|
|
71
|
+
let headlineAt = 0;
|
|
72
|
+
$effect(() => {
|
|
73
|
+
const next = progress[0];
|
|
74
|
+
const shown = untrack(() => headline);
|
|
75
|
+
if (next?.label === shown?.label && next?.done === shown?.done) {
|
|
76
|
+
headline = next;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const show = () => {
|
|
80
|
+
headline = next;
|
|
81
|
+
headlineAt = performance.now();
|
|
82
|
+
};
|
|
83
|
+
const wait = HEADLINE_MIN_MS - (performance.now() - headlineAt);
|
|
84
|
+
if (!shown || wait <= 0) {
|
|
85
|
+
show();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const timer = setTimeout(show, wait);
|
|
89
|
+
return () => clearTimeout(timer);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
get progress() {
|
|
94
|
+
return progress;
|
|
95
|
+
},
|
|
96
|
+
get headline() {
|
|
97
|
+
return headline;
|
|
98
|
+
},
|
|
99
|
+
get bars() {
|
|
100
|
+
return bars;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SolveSettlement } from '@selvajs/solve/client';
|
|
2
|
+
import type { ActionButton } from '../types/actionButton';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Runs a footer-menu action. A `solvesFirst` action waits for the result for the current
|
|
6
|
+
* inputs and runs only on an applied one; a refused or failed solve has already shown the user
|
|
7
|
+
* why. A host that can't solve (`ensureSolved` undefined) runs it straight away.
|
|
8
|
+
*/
|
|
9
|
+
export async function runPanelAction(
|
|
10
|
+
action: ActionButton,
|
|
11
|
+
ensureSolved: (() => Promise<SolveSettlement>) | undefined
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
if (!action.solvesFirst || !ensureSolved) return action.onclick();
|
|
14
|
+
const settled = await ensureSolved();
|
|
15
|
+
if (settled.status === 'applied') await action.onclick(settled);
|
|
16
|
+
}
|
|
@@ -2,76 +2,92 @@ import { untrack } from 'svelte';
|
|
|
2
2
|
import { APP_DEFAULTS } from '../constants';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* When to show that a solve is running. A solve that ends within `SHOW_AFTER_MS` shows nothing:
|
|
6
|
+
* the result updating is feedback enough, and a spinner that flashes for a frame reads as jank.
|
|
7
|
+
* Once shown, the indicator stays `MIN_VISIBLE_MS` for the same reason. `slow` turns on past
|
|
8
|
+
* `SLOW_AFTER_MS`, the case of a Rhino.Compute round trip or a heavy definition.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* - First solve: shown immediately, there is no history yet
|
|
10
|
+
* `freezesDuringSolve` is for a host that can't paint while it solves (an embedded WebView on
|
|
11
|
+
* the solver's thread). A delay can't run there, so the indicator shows on every solve, before
|
|
12
|
+
* the solve takes the thread: it is the only place that host can say Esc aborts.
|
|
12
13
|
*/
|
|
13
|
-
export function createSolvingIndicator(
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export function createSolvingIndicator(
|
|
15
|
+
isSolving: () => boolean,
|
|
16
|
+
options: { freezesDuringSolve?: boolean } = {}
|
|
17
|
+
): { readonly show: boolean; readonly slow: boolean } {
|
|
18
|
+
const { SHOW_AFTER_MS, MIN_VISIBLE_MS, SLOW_AFTER_MS } = APP_DEFAULTS.SOLVING_INDICATOR;
|
|
19
|
+
|
|
20
|
+
if (options.freezesDuringSolve) {
|
|
21
|
+
return {
|
|
22
|
+
// Read straight from `isSolving`, not through an effect: an effect would run a tick
|
|
23
|
+
// later, after the solve has already frozen the page.
|
|
24
|
+
get show() {
|
|
25
|
+
return isSolving();
|
|
26
|
+
},
|
|
27
|
+
slow: false
|
|
28
|
+
};
|
|
29
|
+
}
|
|
16
30
|
|
|
17
31
|
let show = $state(false);
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
|
|
32
|
+
let slow = $state(false);
|
|
33
|
+
let shownAt = 0;
|
|
34
|
+
let showTimer: ReturnType<typeof setTimeout> | null = null;
|
|
35
|
+
let slowTimer: ReturnType<typeof setTimeout> | null = null;
|
|
36
|
+
let hideTimer: ReturnType<typeof setTimeout> | null = null;
|
|
37
|
+
|
|
38
|
+
function clear(timer: ReturnType<typeof setTimeout> | null) {
|
|
39
|
+
if (timer) clearTimeout(timer);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function hide() {
|
|
44
|
+
hideTimer = null;
|
|
45
|
+
show = false;
|
|
46
|
+
slow = false;
|
|
47
|
+
}
|
|
21
48
|
|
|
22
49
|
$effect(() => {
|
|
23
50
|
const solving = isSolving();
|
|
24
|
-
|
|
25
51
|
untrack(() => {
|
|
26
52
|
if (solving) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
: solveHistory.reduce((a, b) => a + b, 0) / solveHistory.length;
|
|
33
|
-
|
|
34
|
-
if (solveHistory.length > 0 && avg < FAST_THRESHOLD_MS) return;
|
|
35
|
-
|
|
36
|
-
const delay =
|
|
37
|
-
avg === Infinity || avg >= SLOW_THRESHOLD_MS
|
|
38
|
-
? 0
|
|
39
|
-
: ((avg - FAST_THRESHOLD_MS) / (SLOW_THRESHOLD_MS - FAST_THRESHOLD_MS)) *
|
|
40
|
-
ANIMATION_DELAY;
|
|
41
|
-
|
|
42
|
-
if (!timeout) {
|
|
43
|
-
if (delay === 0) {
|
|
53
|
+
// A solve that starts while the last one's indicator is held keeps it up.
|
|
54
|
+
hideTimer = clear(hideTimer);
|
|
55
|
+
if (!show) {
|
|
56
|
+
showTimer ??= setTimeout(() => {
|
|
57
|
+
showTimer = null;
|
|
44
58
|
show = true;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
show = true;
|
|
48
|
-
timeout = null;
|
|
49
|
-
}, delay);
|
|
50
|
-
}
|
|
59
|
+
shownAt = performance.now();
|
|
60
|
+
}, SHOW_AFTER_MS);
|
|
51
61
|
}
|
|
62
|
+
slowTimer ??= setTimeout(() => {
|
|
63
|
+
slowTimer = null;
|
|
64
|
+
slow = true;
|
|
65
|
+
}, SLOW_AFTER_MS);
|
|
52
66
|
} else {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (timeout) {
|
|
60
|
-
clearTimeout(timeout);
|
|
61
|
-
timeout = null;
|
|
62
|
-
}
|
|
63
|
-
show = false;
|
|
67
|
+
showTimer = clear(showTimer);
|
|
68
|
+
slowTimer = clear(slowTimer);
|
|
69
|
+
if (!show) return;
|
|
70
|
+
const remaining = MIN_VISIBLE_MS - (performance.now() - shownAt);
|
|
71
|
+
if (remaining > 0) hideTimer ??= setTimeout(hide, remaining);
|
|
72
|
+
else hide();
|
|
64
73
|
}
|
|
65
74
|
});
|
|
75
|
+
});
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
// Separate from the effect above: a cleanup there would run on every `isSolving` flip and
|
|
78
|
+
// cancel the timers it just set.
|
|
79
|
+
$effect(() => () => {
|
|
80
|
+
clear(showTimer);
|
|
81
|
+
clear(slowTimer);
|
|
82
|
+
clear(hideTimer);
|
|
70
83
|
});
|
|
71
84
|
|
|
72
85
|
return {
|
|
73
86
|
get show() {
|
|
74
87
|
return show;
|
|
88
|
+
},
|
|
89
|
+
get slow() {
|
|
90
|
+
return slow;
|
|
75
91
|
}
|
|
76
92
|
};
|
|
77
93
|
}
|
|
@@ -86,10 +86,13 @@ export function useSolveSession(args: SolveSessionArgs): SolveSession {
|
|
|
86
86
|
discard: () => session.discard(),
|
|
87
87
|
setValue: (id, value, forceSolve) => session.setValue(id, value, forceSolve),
|
|
88
88
|
solve: () => session.solve(),
|
|
89
|
+
ensureSolved: () => session.ensureSolved(),
|
|
90
|
+
onSettled: (listener) => session.onSettled(listener),
|
|
89
91
|
loadValues: (incoming) => session.loadValues(incoming),
|
|
90
92
|
rebuild: (schema, scopeKey) => session.rebuild(schema, scopeKey),
|
|
91
93
|
report: (result) => session.report(result),
|
|
92
94
|
reportError: (message) => session.reportError(message),
|
|
95
|
+
reportAborted: () => session.reportAborted(),
|
|
93
96
|
subscribe: (listener) => session.subscribe(listener),
|
|
94
97
|
notify: () => session.notify(),
|
|
95
98
|
dispose: () => session.dispose()
|