@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.
Files changed (401) hide show
  1. package/README.md +0 -3
  2. package/dist/components/ErrorScreen.svelte +7 -3
  3. package/dist/components/compute/AppLayout.svelte +307 -123
  4. package/dist/components/compute/AppLayout.svelte.d.ts +28 -0
  5. package/dist/components/compute/CollapsedPanelStrip.svelte +4 -2
  6. package/dist/components/compute/ComputeApp.svelte +46 -22
  7. package/dist/components/compute/ComputeApp.svelte.d.ts +20 -3
  8. package/dist/components/compute/ComputeMessagesDialog.svelte +31 -41
  9. package/dist/components/compute/ParameterPresetManager.svelte +16 -65
  10. package/dist/components/compute/ParameterPresetManager.svelte.d.ts +4 -5
  11. package/dist/components/compute/SolveMessages.svelte +128 -121
  12. package/dist/components/compute/SolveMessages.svelte.d.ts +12 -12
  13. package/dist/components/layout/AppShell.svelte +10 -6
  14. package/dist/components/layout/FooterItemRenderer.svelte +2 -2
  15. package/dist/components/layout/FooterItemRenderer.svelte.d.ts +1 -1
  16. package/dist/components/layout/PageContent.svelte +3 -2
  17. package/dist/components/layout/PageFooter.svelte +26 -14
  18. package/dist/components/layout/PageHeader.svelte +14 -10
  19. package/dist/components/layout/SectionHeader.svelte +4 -2
  20. package/dist/components/layout/SideNav.svelte +57 -8
  21. package/dist/components/layout/SubNav.svelte +2 -2
  22. package/dist/components/preview/ChartOutput.svelte +131 -65
  23. package/dist/components/preview/Group.svelte +42 -22
  24. package/dist/components/preview/HelpButton.svelte +5 -3
  25. package/dist/components/preview/ImageOutput.svelte +435 -71
  26. package/dist/components/preview/InputControl.svelte +125 -51
  27. package/dist/components/preview/InputControl.svelte.d.ts +5 -1
  28. package/dist/components/preview/OutputDisplay.svelte +42 -28
  29. package/dist/components/preview/TabBar.svelte +8 -6
  30. package/dist/components/preview/TabBar.svelte.d.ts +2 -0
  31. package/dist/components/preview/TabContent.svelte +66 -31
  32. package/dist/components/preview/TabContent.svelte.d.ts +5 -1
  33. package/dist/components/preview/TabLayout.svelte +29 -8
  34. package/dist/components/preview/TabLayout.svelte.d.ts +5 -0
  35. package/dist/components/preview/inputs/CheckboxInput.svelte +1 -1
  36. package/dist/components/preview/inputs/ChecklistInput.svelte +11 -6
  37. package/dist/components/preview/inputs/ChecklistInput.svelte.d.ts +2 -1
  38. package/dist/components/preview/inputs/ColorInput.svelte +357 -13
  39. package/dist/components/preview/inputs/ColorInput.svelte.d.ts +2 -0
  40. package/dist/components/preview/inputs/DropdownInput.svelte +20 -5
  41. package/dist/components/preview/inputs/DropdownInput.svelte.d.ts +2 -1
  42. package/dist/components/preview/inputs/FileInput.svelte +115 -103
  43. package/dist/components/preview/inputs/GeometryInput.svelte +128 -0
  44. package/dist/components/preview/inputs/GeometryInput.svelte.d.ts +21 -0
  45. package/dist/components/preview/inputs/NumberInput.svelte +98 -16
  46. package/dist/components/preview/inputs/NumberInput.svelte.d.ts +2 -0
  47. package/dist/components/preview/inputs/OptionGlyph.svelte +29 -0
  48. package/dist/components/preview/inputs/OptionGlyph.svelte.d.ts +8 -0
  49. package/dist/components/preview/inputs/SegmentedInput.svelte +45 -0
  50. package/dist/components/preview/inputs/SegmentedInput.svelte.d.ts +12 -0
  51. package/dist/components/preview/inputs/TextInput.svelte +11 -6
  52. package/dist/components/preview/inputs/TileInput.svelte +83 -0
  53. package/dist/components/preview/inputs/TileInput.svelte.d.ts +12 -0
  54. package/dist/components/preview/inputs/index.d.ts +3 -0
  55. package/dist/components/preview/inputs/index.js +3 -0
  56. package/dist/components/primitives/CalculateButton.svelte +56 -24
  57. package/dist/components/primitives/CalculateButton.svelte.d.ts +3 -0
  58. package/dist/components/primitives/Callout.svelte +8 -6
  59. package/dist/components/primitives/Callout.svelte.d.ts +4 -4
  60. package/dist/components/primitives/DataTable.svelte +38 -4
  61. package/dist/components/primitives/DataTable.svelte.d.ts +2 -0
  62. package/dist/components/primitives/FilterableDropdown.svelte +13 -1
  63. package/dist/components/primitives/FilterableDropdown.svelte.d.ts +5 -0
  64. package/dist/components/primitives/ImageUploadField.svelte +1 -1
  65. package/dist/components/primitives/SolvingIndicator.svelte +245 -15
  66. package/dist/components/primitives/SolvingIndicator.svelte.d.ts +15 -0
  67. package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  68. package/dist/components/primitives/badge/badge.svelte +4 -4
  69. package/dist/components/primitives/badge/badge.svelte.d.ts +10 -10
  70. package/dist/components/primitives/button/button.svelte +10 -8
  71. package/dist/components/primitives/button/button.svelte.d.ts +20 -17
  72. package/dist/components/primitives/card/card.svelte +1 -1
  73. package/dist/components/primitives/card/card.svelte.d.ts +1 -1
  74. package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
  75. package/dist/components/primitives/command/command-input.svelte +1 -1
  76. package/dist/components/primitives/command/command-item.svelte +1 -1
  77. package/dist/components/primitives/command/command-link-item.svelte +1 -1
  78. package/dist/components/primitives/command/command.svelte +1 -1
  79. package/dist/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
  80. package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
  81. package/dist/components/primitives/context-menu/context-menu-item.svelte +1 -1
  82. package/dist/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
  83. package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  84. package/dist/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
  85. package/dist/components/primitives/dialog/dialog-content.svelte +7 -3
  86. package/dist/components/primitives/drawer/Drawer.svelte +1 -1
  87. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
  88. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +2 -0
  89. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
  90. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +2 -0
  91. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
  92. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +2 -1
  93. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
  94. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
  95. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
  96. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
  97. package/dist/components/primitives/index.d.ts +1 -1
  98. package/dist/components/primitives/index.js +1 -1
  99. package/dist/components/primitives/input/input.svelte +4 -4
  100. package/dist/components/primitives/input-group/input-group-input.svelte.d.ts +1 -1
  101. package/dist/components/primitives/input-group/input-group.svelte +1 -1
  102. package/dist/components/primitives/mode-toggle/mode-toggle.svelte +5 -2
  103. package/dist/components/primitives/popover/popover-content.svelte +1 -1
  104. package/dist/components/primitives/resizable/resizable-handle.svelte +2 -2
  105. package/dist/components/primitives/scroll-area/scroll-area.svelte +1 -1
  106. package/dist/components/primitives/search/search.svelte +2 -2
  107. package/dist/components/primitives/select/select-content.svelte +1 -1
  108. package/dist/components/primitives/select/select-item.svelte +1 -1
  109. package/dist/components/primitives/select/select-trigger.svelte +1 -1
  110. package/dist/components/primitives/slider/slider.svelte +8 -3
  111. package/dist/components/primitives/slider/slider.svelte.d.ts +5 -1
  112. package/dist/components/primitives/switch/switch.svelte +1 -1
  113. package/dist/components/primitives/tabs/tabs-list.svelte +2 -2
  114. package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +6 -6
  115. package/dist/components/primitives/tabs/tabs-trigger.svelte +3 -3
  116. package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +7 -7
  117. package/dist/components/primitives/textarea/textarea.svelte +1 -1
  118. package/dist/components/primitives/toggle/toggle.svelte +11 -3
  119. package/dist/components/primitives/toggle/toggle.svelte.d.ts +13 -4
  120. package/dist/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
  121. package/dist/components/primitives/toggle-group/toggle-group.svelte +1 -1
  122. package/dist/components/primitives/tooltip/index.d.ts +6 -0
  123. package/dist/components/primitives/tooltip/index.js +8 -0
  124. package/dist/components/primitives/tooltip/tooltip-content.svelte +34 -0
  125. package/dist/components/primitives/tooltip/tooltip-content.svelte.d.ts +10 -0
  126. package/dist/components/primitives/tooltip/tooltip-portal.svelte +7 -0
  127. package/dist/components/primitives/tooltip/tooltip-portal.svelte.d.ts +4 -0
  128. package/dist/components/primitives/tooltip/tooltip-provider.svelte +7 -0
  129. package/dist/components/primitives/tooltip/tooltip-provider.svelte.d.ts +4 -0
  130. package/dist/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
  131. package/dist/components/primitives/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  132. package/dist/components/primitives/tooltip/tooltip.svelte +7 -0
  133. package/dist/components/primitives/tooltip/tooltip.svelte.d.ts +4 -0
  134. package/dist/components/viewer/MeshMetadataPanel.svelte +119 -0
  135. package/dist/components/viewer/MeshMetadataPanel.svelte.d.ts +9 -0
  136. package/dist/components/viewer/SceneManager.svelte +32 -21
  137. package/dist/components/viewer/SceneManager.svelte.d.ts +3 -1
  138. package/dist/components/viewer/Viewer.svelte +195 -207
  139. package/dist/components/viewer/Viewer.svelte.d.ts +7 -2
  140. package/dist/components/viewer/ViewerDock.svelte +404 -0
  141. package/dist/components/viewer/ViewerDock.svelte.d.ts +42 -0
  142. package/dist/composables/useFooterItem.svelte.d.ts +1 -1
  143. package/dist/composables/useFooterItem.svelte.js +1 -1
  144. package/dist/compute/liveProgress.svelte.d.ts +16 -0
  145. package/dist/compute/liveProgress.svelte.js +90 -0
  146. package/dist/compute/run-panel-action.d.ts +8 -0
  147. package/dist/compute/run-panel-action.js +12 -0
  148. package/dist/compute/solving.svelte.d.ts +11 -7
  149. package/dist/compute/solving.svelte.js +64 -45
  150. package/dist/compute/useSolveSession.svelte.js +3 -0
  151. package/dist/constants.d.ts +6 -8
  152. package/dist/constants.js +8 -11
  153. package/dist/contexts/hostContext.svelte.d.ts +39 -0
  154. package/dist/contexts/hostContext.svelte.js +9 -0
  155. package/dist/features/chart/plotly-compat.d.ts +15 -0
  156. package/dist/features/chart/plotly-compat.js +124 -0
  157. package/dist/i18n/localeContext.svelte.d.ts +10 -3
  158. package/dist/i18n/localeContext.svelte.js +27 -5
  159. package/dist/i18n/messages.d.ts +164 -2
  160. package/dist/i18n/messages.js +281 -11
  161. package/dist/index.d.ts +6 -1
  162. package/dist/index.js +5 -0
  163. package/dist/public.d.ts +2 -1
  164. package/dist/public.js +1 -0
  165. package/dist/schema/host-support.d.ts +29 -0
  166. package/dist/schema/host-support.js +97 -0
  167. package/dist/schema/option-display.d.ts +12 -0
  168. package/dist/schema/option-display.js +29 -0
  169. package/dist/schema/param-exporter.d.ts +4 -1
  170. package/dist/schema/param-exporter.js +10 -6
  171. package/dist/schema/visibility-rules.d.ts +7 -1
  172. package/dist/schema/visibility-rules.js +38 -0
  173. package/dist/styles/base.css +116 -83
  174. package/dist/types/actionButton.d.ts +17 -3
  175. package/dist/types/presetLabels.d.ts +3 -2
  176. package/dist/types/presetLabels.js +9 -8
  177. package/dist/utils/color-space.d.ts +21 -0
  178. package/dist/utils/color-space.js +90 -0
  179. package/dist/utils/file-download.js +10 -22
  180. package/dist/utils/host-files.d.ts +12 -0
  181. package/dist/utils/host-files.js +46 -0
  182. package/dist/utils/loadScript.d.ts +2 -0
  183. package/dist/utils/loadScript.js +2 -0
  184. package/dist/utils/webp-to-png.d.ts +10 -0
  185. package/dist/utils/webp-to-png.js +41 -0
  186. package/package.json +30 -22
  187. package/src/lib/components/ErrorScreen.svelte +7 -3
  188. package/src/lib/components/compute/AppLayout.svelte +307 -123
  189. package/src/lib/components/compute/CollapsedPanelStrip.svelte +4 -2
  190. package/src/lib/components/compute/ComputeApp.svelte +46 -22
  191. package/src/lib/components/compute/ComputeMessagesDialog.svelte +32 -42
  192. package/src/lib/components/compute/ParameterPresetManager.svelte +16 -65
  193. package/src/lib/components/compute/SolveMessages.svelte +130 -123
  194. package/src/lib/components/layout/AppShell.svelte +10 -6
  195. package/src/lib/components/layout/FooterItemRenderer.svelte +2 -2
  196. package/src/lib/components/layout/PageContent.svelte +3 -2
  197. package/src/lib/components/layout/PageFooter.svelte +26 -14
  198. package/src/lib/components/layout/PageHeader.svelte +14 -10
  199. package/src/lib/components/layout/SectionHeader.svelte +4 -2
  200. package/src/lib/components/layout/SideNav.svelte +57 -8
  201. package/src/lib/components/layout/SubNav.svelte +2 -2
  202. package/src/lib/components/preview/ChartOutput.svelte +131 -65
  203. package/src/lib/components/preview/Group.svelte +42 -22
  204. package/src/lib/components/preview/HelpButton.svelte +5 -3
  205. package/src/lib/components/preview/ImageOutput.svelte +435 -71
  206. package/src/lib/components/preview/InputControl.svelte +125 -51
  207. package/src/lib/components/preview/OutputDisplay.svelte +42 -28
  208. package/src/lib/components/preview/TabBar.svelte +8 -6
  209. package/src/lib/components/preview/TabContent.svelte +66 -31
  210. package/src/lib/components/preview/TabLayout.svelte +29 -8
  211. package/src/lib/components/preview/inputs/CheckboxInput.svelte +1 -1
  212. package/src/lib/components/preview/inputs/ChecklistInput.svelte +11 -6
  213. package/src/lib/components/preview/inputs/ColorInput.svelte +357 -13
  214. package/src/lib/components/preview/inputs/DropdownInput.svelte +20 -5
  215. package/src/lib/components/preview/inputs/FileInput.svelte +115 -103
  216. package/src/lib/components/preview/inputs/GeometryInput.svelte +128 -0
  217. package/src/lib/components/preview/inputs/NumberInput.svelte +98 -16
  218. package/src/lib/components/preview/inputs/OptionGlyph.svelte +29 -0
  219. package/src/lib/components/preview/inputs/SegmentedInput.svelte +45 -0
  220. package/src/lib/components/preview/inputs/TextInput.svelte +11 -6
  221. package/src/lib/components/preview/inputs/TileInput.svelte +83 -0
  222. package/src/lib/components/preview/inputs/index.ts +3 -0
  223. package/src/lib/components/primitives/CalculateButton.svelte +56 -24
  224. package/src/lib/components/primitives/Callout.svelte +9 -7
  225. package/src/lib/components/primitives/DataTable.svelte +38 -4
  226. package/src/lib/components/primitives/FilterableDropdown.svelte +14 -2
  227. package/src/lib/components/primitives/ImageUploadField.svelte +1 -1
  228. package/src/lib/components/primitives/IssueList.svelte +1 -1
  229. package/src/lib/components/primitives/SeverityIcon.svelte +1 -1
  230. package/src/lib/components/primitives/SolvingIndicator.svelte +245 -15
  231. package/src/lib/components/primitives/StateDisplay.svelte +1 -1
  232. package/src/lib/components/primitives/alert-dialog/alert-dialog-action.svelte +2 -2
  233. package/src/lib/components/primitives/alert-dialog/alert-dialog-cancel.svelte +2 -2
  234. package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +2 -2
  235. package/src/lib/components/primitives/alert-dialog/alert-dialog-description.svelte +1 -1
  236. package/src/lib/components/primitives/alert-dialog/alert-dialog-footer.svelte +1 -1
  237. package/src/lib/components/primitives/alert-dialog/alert-dialog-header.svelte +1 -1
  238. package/src/lib/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  239. package/src/lib/components/primitives/alert-dialog/alert-dialog-title.svelte +1 -1
  240. package/src/lib/components/primitives/badge/badge.svelte +5 -5
  241. package/src/lib/components/primitives/button/button.svelte +10 -8
  242. package/src/lib/components/primitives/button-group/button-group-separator.svelte +2 -2
  243. package/src/lib/components/primitives/button-group/button-group-text.svelte +1 -1
  244. package/src/lib/components/primitives/button-group/button-group.svelte +1 -1
  245. package/src/lib/components/primitives/card/card-content.svelte +1 -1
  246. package/src/lib/components/primitives/card/card-description.svelte +1 -1
  247. package/src/lib/components/primitives/card/card-footer.svelte +1 -1
  248. package/src/lib/components/primitives/card/card-header.svelte +1 -1
  249. package/src/lib/components/primitives/card/card-title.svelte +1 -1
  250. package/src/lib/components/primitives/card/card.svelte +2 -2
  251. package/src/lib/components/primitives/checkbox/checkbox.svelte +2 -2
  252. package/src/lib/components/primitives/command/command-dialog.svelte +2 -2
  253. package/src/lib/components/primitives/command/command-empty.svelte +1 -1
  254. package/src/lib/components/primitives/command/command-group.svelte +1 -1
  255. package/src/lib/components/primitives/command/command-input.svelte +3 -3
  256. package/src/lib/components/primitives/command/command-item.svelte +2 -2
  257. package/src/lib/components/primitives/command/command-link-item.svelte +2 -2
  258. package/src/lib/components/primitives/command/command-list.svelte +1 -1
  259. package/src/lib/components/primitives/command/command-separator.svelte +1 -1
  260. package/src/lib/components/primitives/command/command-shortcut.svelte +1 -1
  261. package/src/lib/components/primitives/command/command.svelte +2 -2
  262. package/src/lib/components/primitives/context-menu/context-menu-checkbox-item.svelte +2 -2
  263. package/src/lib/components/primitives/context-menu/context-menu-content.svelte +2 -2
  264. package/src/lib/components/primitives/context-menu/context-menu-group-heading.svelte +1 -1
  265. package/src/lib/components/primitives/context-menu/context-menu-item.svelte +2 -2
  266. package/src/lib/components/primitives/context-menu/context-menu-label.svelte +1 -1
  267. package/src/lib/components/primitives/context-menu/context-menu-radio-item.svelte +2 -2
  268. package/src/lib/components/primitives/context-menu/context-menu-separator.svelte +1 -1
  269. package/src/lib/components/primitives/context-menu/context-menu-shortcut.svelte +1 -1
  270. package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +2 -2
  271. package/src/lib/components/primitives/context-menu/context-menu-sub-trigger.svelte +2 -2
  272. package/src/lib/components/primitives/dialog/dialog-content.svelte +8 -4
  273. package/src/lib/components/primitives/dialog/dialog-description.svelte +1 -1
  274. package/src/lib/components/primitives/dialog/dialog-footer.svelte +1 -1
  275. package/src/lib/components/primitives/dialog/dialog-header.svelte +1 -1
  276. package/src/lib/components/primitives/dialog/dialog-overlay.svelte +1 -1
  277. package/src/lib/components/primitives/dialog/dialog-title.svelte +1 -1
  278. package/src/lib/components/primitives/drawer/Drawer.svelte +1 -1
  279. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +32 -12
  280. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +6 -2
  281. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
  282. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +5 -3
  283. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-label.svelte +1 -1
  284. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +2 -2
  285. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +1 -1
  286. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +2 -2
  287. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +2 -2
  288. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +2 -2
  289. package/src/lib/components/primitives/empty/empty-content.svelte +1 -1
  290. package/src/lib/components/primitives/empty/empty-description.svelte +1 -1
  291. package/src/lib/components/primitives/empty/empty-header.svelte +1 -1
  292. package/src/lib/components/primitives/empty/empty-media.svelte +1 -1
  293. package/src/lib/components/primitives/empty/empty-title.svelte +1 -1
  294. package/src/lib/components/primitives/empty/empty.svelte +1 -1
  295. package/src/lib/components/primitives/field/field-content.svelte +1 -1
  296. package/src/lib/components/primitives/field/field-description.svelte +1 -1
  297. package/src/lib/components/primitives/field/field-error.svelte +1 -1
  298. package/src/lib/components/primitives/field/field-group.svelte +1 -1
  299. package/src/lib/components/primitives/field/field-label.svelte +2 -2
  300. package/src/lib/components/primitives/field/field-legend.svelte +1 -1
  301. package/src/lib/components/primitives/field/field-separator.svelte +2 -2
  302. package/src/lib/components/primitives/field/field-set.svelte +1 -1
  303. package/src/lib/components/primitives/field/field-title.svelte +1 -1
  304. package/src/lib/components/primitives/field/field.svelte +1 -1
  305. package/src/lib/components/primitives/index.ts +1 -1
  306. package/src/lib/components/primitives/input/input.svelte +5 -5
  307. package/src/lib/components/primitives/input-group/input-group-addon.svelte +1 -1
  308. package/src/lib/components/primitives/input-group/input-group-button.svelte +2 -2
  309. package/src/lib/components/primitives/input-group/input-group-input.svelte +2 -2
  310. package/src/lib/components/primitives/input-group/input-group-text.svelte +1 -1
  311. package/src/lib/components/primitives/input-group/input-group-textarea.svelte +2 -2
  312. package/src/lib/components/primitives/input-group/input-group.svelte +2 -2
  313. package/src/lib/components/primitives/label/label.svelte +1 -1
  314. package/src/lib/components/primitives/mode-toggle/mode-toggle.svelte +5 -2
  315. package/src/lib/components/primitives/popover/popover-content.svelte +2 -2
  316. package/src/lib/components/primitives/popover/popover-description.svelte +1 -1
  317. package/src/lib/components/primitives/popover/popover-header.svelte +1 -1
  318. package/src/lib/components/primitives/popover/popover-title.svelte +1 -1
  319. package/src/lib/components/primitives/popover/popover-trigger.svelte +1 -1
  320. package/src/lib/components/primitives/resizable/resizable-handle.svelte +3 -3
  321. package/src/lib/components/primitives/resizable/resizable-pane-group.svelte +1 -1
  322. package/src/lib/components/primitives/scroll-area/scroll-area-scrollbar.svelte +1 -1
  323. package/src/lib/components/primitives/scroll-area/scroll-area.svelte +2 -2
  324. package/src/lib/components/primitives/search/search.svelte +3 -3
  325. package/src/lib/components/primitives/select/select-content.svelte +2 -2
  326. package/src/lib/components/primitives/select/select-group-heading.svelte +1 -1
  327. package/src/lib/components/primitives/select/select-item.svelte +2 -2
  328. package/src/lib/components/primitives/select/select-label.svelte +1 -1
  329. package/src/lib/components/primitives/select/select-scroll-down-button.svelte +1 -1
  330. package/src/lib/components/primitives/select/select-scroll-up-button.svelte +1 -1
  331. package/src/lib/components/primitives/select/select-separator.svelte +2 -2
  332. package/src/lib/components/primitives/select/select-trigger.svelte +2 -2
  333. package/src/lib/components/primitives/separator/separator.svelte +1 -1
  334. package/src/lib/components/primitives/slider/slider.svelte +9 -4
  335. package/src/lib/components/primitives/spinner/spinner.svelte +1 -1
  336. package/src/lib/components/primitives/switch/switch.svelte +2 -2
  337. package/src/lib/components/primitives/tabs/tabs-content.svelte +1 -1
  338. package/src/lib/components/primitives/tabs/tabs-list.svelte +3 -3
  339. package/src/lib/components/primitives/tabs/tabs-trigger.svelte +4 -4
  340. package/src/lib/components/primitives/tabs/tabs.svelte +1 -1
  341. package/src/lib/components/primitives/textarea/textarea.svelte +2 -2
  342. package/src/lib/components/primitives/toggle/toggle.svelte +12 -4
  343. package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +3 -3
  344. package/src/lib/components/primitives/toggle-group/toggle-group.svelte +3 -3
  345. package/src/lib/components/primitives/tooltip/index.ts +19 -0
  346. package/src/lib/components/primitives/tooltip/tooltip-content.svelte +34 -0
  347. package/src/lib/components/primitives/tooltip/tooltip-portal.svelte +7 -0
  348. package/src/lib/components/primitives/tooltip/tooltip-provider.svelte +7 -0
  349. package/src/lib/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
  350. package/src/lib/components/primitives/tooltip/tooltip.svelte +7 -0
  351. package/src/lib/components/viewer/MeshMetadataPanel.svelte +119 -0
  352. package/src/lib/components/viewer/SceneManager.svelte +32 -21
  353. package/src/lib/components/viewer/Viewer.svelte +196 -208
  354. package/src/lib/components/viewer/ViewerDock.svelte +404 -0
  355. package/src/lib/composables/useFooterItem.svelte.ts +1 -1
  356. package/src/lib/compute/liveProgress.svelte.ts +103 -0
  357. package/src/lib/compute/run-panel-action.ts +16 -0
  358. package/src/lib/compute/solving.svelte.ts +66 -50
  359. package/src/lib/compute/useSolveSession.svelte.ts +3 -0
  360. package/src/lib/constants.ts +8 -11
  361. package/src/lib/contexts/hostContext.svelte.ts +51 -0
  362. package/src/lib/features/chart/plotly-compat.ts +141 -0
  363. package/src/lib/i18n/localeContext.svelte.ts +40 -5
  364. package/src/lib/i18n/messages.ts +487 -13
  365. package/src/lib/index.ts +23 -0
  366. package/src/lib/public.ts +2 -0
  367. package/src/lib/schema/host-support.ts +129 -0
  368. package/src/lib/schema/option-display.ts +44 -0
  369. package/src/lib/schema/param-exporter.ts +10 -7
  370. package/src/lib/schema/visibility-rules.ts +50 -1
  371. package/src/lib/styles/base.css +116 -83
  372. package/src/lib/types/actionButton.ts +16 -3
  373. package/src/lib/types/presetLabels.ts +13 -11
  374. package/src/lib/utils/color-space.ts +107 -0
  375. package/src/lib/utils/file-download.ts +11 -21
  376. package/src/lib/utils/host-files.ts +61 -0
  377. package/src/lib/utils/loadScript.ts +3 -0
  378. package/src/lib/utils/webp-to-png.ts +48 -0
  379. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
  380. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte.d.ts +0 -3
  381. package/dist/components/primitives/theme-switcher/index.d.ts +0 -1
  382. package/dist/components/primitives/theme-switcher/index.js +0 -1
  383. package/dist/components/viewer/MeshMetadataDialog.svelte +0 -126
  384. package/dist/components/viewer/MeshMetadataDialog.svelte.d.ts +0 -10
  385. package/dist/styles/themes/cyberpunk.css +0 -98
  386. package/dist/styles/themes/neutral.css +0 -72
  387. package/dist/styles/themes/ocean.css +0 -75
  388. package/dist/styles/themes/selva.css +0 -105
  389. package/dist/theme/themeStore.svelte.d.ts +0 -12
  390. package/dist/theme/themeStore.svelte.js +0 -40
  391. package/dist/theme/themes.d.ts +0 -5
  392. package/dist/theme/themes.js +0 -13
  393. package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
  394. package/src/lib/components/primitives/theme-switcher/index.ts +0 -1
  395. package/src/lib/components/viewer/MeshMetadataDialog.svelte +0 -126
  396. package/src/lib/styles/themes/cyberpunk.css +0 -98
  397. package/src/lib/styles/themes/neutral.css +0 -72
  398. package/src/lib/styles/themes/ocean.css +0 -75
  399. package/src/lib/styles/themes/selva.css +0 -105
  400. package/src/lib/theme/themeStore.svelte.ts +0 -50
  401. package/src/lib/theme/themes.ts +0 -18
@@ -1,19 +1,24 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
2
3
  import type { UISchema, SupportedTypes, ParameterPreset } from '@selvajs/schemas';
4
+ import type { LiveSolveState, SolveSettlement } from '@selvajs/solve/client';
3
5
  import type { ActionButton } from '../../types/actionButton';
4
6
  import type { PresetLabels } from '../../types/presetLabels';
5
- import { ChevronUp } from '@lucide/svelte';
7
+ import { ChevronUp, Download, Ellipsis, Upload } from '@lucide/svelte';
6
8
  import type { ThreeViewer } from '@selvajs/visualization/render';
7
9
  import Viewer, { type ViewerConfig } from '../viewer/Viewer.svelte';
8
10
  import CalculateButton from '../primitives/CalculateButton.svelte';
9
11
  import SolvingIndicator from '../primitives/SolvingIndicator.svelte';
10
12
  import TabLayout from '../preview/TabLayout.svelte';
11
13
  import CollapsedPanelStrip from './CollapsedPanelStrip.svelte';
12
- import * as Resizable from '../primitives/resizable';
13
- import { Button } from '../..';
14
+ import * as Resizable from '../primitives/resizable/index.js';
15
+ import * as DropdownMenu from '../primitives/dropdown-menu/index.js';
16
+ import { Button } from '../../index.js';
14
17
  import ParameterPresetManager from './ParameterPresetManager.svelte';
18
+ import { runPanelAction } from '../../compute/run-panel-action';
15
19
  import { getLocaleContext } from '../../i18n/localeContext.svelte';
16
20
  import { buildVisibilityMap, isTabVisible, itemKey } from '../../schema/visibility-rules';
21
+ import { DEFAULT_PRESET_LABELS } from '../../types/presetLabels';
17
22
 
18
23
  const locale = getLocaleContext();
19
24
  const t = $derived(locale.messages);
@@ -22,13 +27,39 @@
22
27
 
23
28
  interface Props {
24
29
  schema: UISchema;
30
+ /**
31
+ * Whether to draw the 3D viewer. Each host reads its own switch: the plugin's preview
32
+ * `viewerOptions.enableLocal`, a Rhino.Compute host `viewerOptions.enableRemote`.
33
+ */
34
+ showViewer: boolean;
25
35
  meshes?: any[];
26
36
  isSolving: boolean;
37
+ /**
38
+ * `createSolvingIndicator(...).show`, not `isSolving`: grows the action row's solve status,
39
+ * which would otherwise flash on every sub-second solve.
40
+ */
27
41
  showSolvingIndicator?: boolean;
42
+ /** `createSolvingIndicator(...).slow`: the solve status grows into a card with progress. */
43
+ solveIsSlow?: boolean;
44
+ /** The session's live channel: step, count and bar for that card. */
45
+ live?: LiveSolveState;
46
+ /** Abort in that card. The host's `SolveMessages` should then take `progressShownByHost`. */
47
+ onabort?: () => void;
48
+ /** As passed to `createSolvingIndicator`. */
49
+ freezesDuringSolve?: boolean;
50
+ /** @deprecated Ignored: the solving status always sits in the action row. */
51
+ solvingIndicatorPlacement?: 'floating' | 'inline';
52
+ /** Rendered above the action row, for a host that keeps the solve's messages in the flow. */
53
+ solveMessages?: Snippet;
28
54
  hasPendingChanges?: boolean;
29
55
  hasNeverSolved?: boolean;
30
56
  isViewerFullscreen?: boolean;
31
57
  oncalculate?: () => void;
58
+ /**
59
+ * The result for the current inputs, solving if it must (`session.ensureSolved`). Needed
60
+ * by `solvesFirst` actions; without it they run straight away.
61
+ */
62
+ ensureSolved?: () => Promise<SolveSettlement>;
32
63
  values: Record<string, unknown>;
33
64
  onValueChange: (id: string, val: SupportedTypes, forceSolve?: boolean) => void | Promise<void>;
34
65
  onLoadValues?: (values: Record<string, unknown>) => void | Promise<void>;
@@ -45,13 +76,20 @@
45
76
 
46
77
  let {
47
78
  schema,
79
+ showViewer: hasViewer,
48
80
  meshes = [],
49
81
  isSolving,
50
82
  showSolvingIndicator = false,
83
+ solveIsSlow = false,
84
+ live,
85
+ onabort,
86
+ freezesDuringSolve = false,
87
+ solveMessages,
51
88
  hasPendingChanges = false,
52
89
  hasNeverSolved = false,
53
90
  isViewerFullscreen = $bindable(false),
54
91
  oncalculate = () => {},
92
+ ensureSolved,
55
93
  values,
56
94
  onValueChange,
57
95
  onLoadValues,
@@ -66,9 +104,11 @@
66
104
  onViewerReady
67
105
  }: Props = $props();
68
106
 
69
- const hasViewer = $derived(
70
- !!(schema?.viewerOptions?.enableLocal || schema?.viewerOptions?.enableRemote)
71
- );
107
+ const presetText = $derived({ ...DEFAULT_PRESET_LABELS, ...presetLabels });
108
+ let presets = $state<ParameterPresetManager | null>(null);
109
+ const hasMenu = $derived(showSaveButton || showLoadButton || panelActions.length > 0);
110
+ const resultIsStale = $derived(hasPendingChanges || hasNeverSolved);
111
+
72
112
  const shownTabs = $derived(
73
113
  schema.layout.type === 'tabbed'
74
114
  ? schema.layout.tabs.filter((tab) => isTabVisible(tab, values))
@@ -106,9 +146,14 @@
106
146
  const isTwoPanelMode = $derived(!hasViewer && hasLeftPanel && hasRightPanel);
107
147
 
108
148
  let isMobile = $state(false);
109
- let drawerOpen = $state(false);
149
+ // Half keeps the model visible above the fields so a change can be watched as it solves;
150
+ // full is for long forms.
151
+ type DrawerState = 'closed' | 'half' | 'full';
152
+ let drawer = $state<DrawerState>('closed');
153
+ const DRAWER_STATES: DrawerState[] = ['closed', 'half', 'full'];
110
154
 
111
- const activeLeftTabLabel = $derived(leftTabs[0]?.label ?? t.parametersTab);
155
+ // With several tabs the first tab's name would mislabel whichever one is showing.
156
+ const drawerLabel = $derived(leftTabs.length === 1 ? leftTabs[0].label : t.parametersTab);
112
157
 
113
158
  $effect(() => {
114
159
  const mqMobile = window.matchMedia('(max-width: 639px)');
@@ -120,6 +165,14 @@
120
165
  return () => mqMobile.removeEventListener('change', update);
121
166
  });
122
167
 
168
+ // Without a viewer there's nothing to sit beside, so a narrow host (a docked Rhino panel) gets
169
+ // the phone's single edge-to-edge column instead of a card floating in its own margins.
170
+ const STACK_BELOW_WIDTH = 900;
171
+ let rootWidth = $state(0);
172
+ const stacked = $derived(
173
+ isMobile || (!hasViewer && rootWidth > 0 && rootWidth < STACK_BELOW_WIDTH)
174
+ );
175
+
123
176
  let leftCollapsed = $state(false);
124
177
  let rightCollapsed = $state(false);
125
178
  let leftPaneRef = $state<{ expand: () => void } | null>(null);
@@ -136,185 +189,252 @@
136
189
  await onLoadValues?.(loadedValues);
137
190
  }
138
191
 
139
- let _touchStartY = 0;
192
+ let touchStartY = 0;
193
+
194
+ function stepDrawer(by: number) {
195
+ const index = DRAWER_STATES.indexOf(drawer) + by;
196
+ drawer = DRAWER_STATES[Math.max(0, Math.min(DRAWER_STATES.length - 1, index))];
197
+ }
140
198
 
141
199
  function onDrawerTouchStart(e: TouchEvent) {
142
- _touchStartY = e.touches[0].clientY;
200
+ touchStartY = e.touches[0].clientY;
143
201
  }
144
202
 
145
203
  function onDrawerTouchEnd(e: TouchEvent) {
146
- const deltaY = e.changedTouches[0].clientY - _touchStartY;
204
+ const deltaY = e.changedTouches[0].clientY - touchStartY;
147
205
  const SWIPE_THRESHOLD = 40;
148
206
  if (Math.abs(deltaY) > SWIPE_THRESHOLD) {
149
- drawerOpen = deltaY < 0; // swipe up → open, swipe down → close
207
+ // Suppresses the click a short swipe can still end in, which would step the drawer twice.
208
+ e.preventDefault();
209
+ stepDrawer(deltaY < 0 ? 1 : -1);
150
210
  }
151
211
  }
212
+
213
+ // A tap walks closed → half → full → closed.
214
+ function onDrawerHandleClick() {
215
+ drawer = drawer === 'full' ? 'closed' : DRAWER_STATES[DRAWER_STATES.indexOf(drawer) + 1];
216
+ }
152
217
  </script>
153
218
 
219
+ <!-- Everything but Calculate: saving and loading inputs, then the host's own actions. -->
220
+ {#snippet actionsMenu()}
221
+ <DropdownMenu.Root>
222
+ <DropdownMenu.Trigger>
223
+ {#snippet child({ props })}
224
+ <Button
225
+ {...props}
226
+ variant="outline"
227
+ size="icon-lg"
228
+ class="shrink-0"
229
+ title={t.moreActions}
230
+ aria-label={t.moreActions}
231
+ >
232
+ <Ellipsis />
233
+ </Button>
234
+ {/snippet}
235
+ </DropdownMenu.Trigger>
236
+ <!-- Above the fullscreen viewer, which sits at z-index 10000. -->
237
+ <DropdownMenu.Content side="top" align="end" sideOffset={6} class="min-w-56 z-10001 w-auto">
238
+ {#if showSaveButton}
239
+ <DropdownMenu.Item onSelect={() => presets?.openSave()}>
240
+ <Download />
241
+ {presetText.saveMenuItem}
242
+ </DropdownMenu.Item>
243
+ {/if}
244
+ {#if showLoadButton}
245
+ <DropdownMenu.Item onSelect={() => presets?.openLoad()}>
246
+ <Upload />
247
+ {presetText.loadMenuItem}
248
+ </DropdownMenu.Item>
249
+ {/if}
250
+ {#if (showSaveButton || showLoadButton) && panelActions.length > 0}
251
+ <DropdownMenu.Separator />
252
+ {/if}
253
+ {#each panelActions as action (action.id)}
254
+ {@const hint =
255
+ action.hint ?? (action.solvesFirst && resultIsStale ? t.calculatesFirst : undefined)}
256
+ <DropdownMenu.Item
257
+ disabled={action.disabled}
258
+ variant={action.variant ?? 'default'}
259
+ onSelect={() => runPanelAction(action, ensureSolved)}
260
+ class="items-start"
261
+ >
262
+ {#if action.icon}
263
+ {@const IconComponent = action.icon}
264
+ <IconComponent class="mt-0.5" />
265
+ {/if}
266
+ <span class="flex flex-col">
267
+ {action.label}
268
+ {#if hint}
269
+ <span class="text-xs leading-snug text-muted-foreground">{hint}</span>
270
+ {/if}
271
+ </span>
272
+ </DropdownMenu.Item>
273
+ {/each}
274
+ </DropdownMenu.Content>
275
+ </DropdownMenu.Root>
276
+ {/snippet}
277
+
278
+ <!-- One primary action and one menu. A definition that solves on every change has no Calculate,
279
+ so the menu sits alone at the end of the row. -->
280
+ {#snippet footer()}
281
+ <!-- No wrapper: it would always render (the messages' live region is never empty) and pick up
282
+ the drawer footer's row padding, leaving a gap above Calculate with nothing in it. -->
283
+ {@render solveMessages?.()}
284
+ {#if schema.instanceSolve === false}
285
+ <CalculateButton
286
+ {hasPendingChanges}
287
+ {hasNeverSolved}
288
+ {isSolving}
289
+ {oncalculate}
290
+ trailing={hasMenu ? actionsMenu : undefined}
291
+ />
292
+ {:else}
293
+ <div class="mt-3 pb-3 pt-2 px-2 gap-2 flex items-center border-t border-border">
294
+ <SolvingIndicator
295
+ {isSolving}
296
+ show={showSolvingIndicator}
297
+ slow={solveIsSlow}
298
+ {live}
299
+ {onabort}
300
+ {freezesDuringSolve}
301
+ />
302
+ {#if hasMenu}
303
+ {@render actionsMenu()}
304
+ {/if}
305
+ </div>
306
+ {/if}
307
+ {/snippet}
308
+
309
+ <!-- `hostsFooter` marks the one panel that carries the footer. On a phone the footer moves to the
310
+ drawer's pinned bottom instead. -->
154
311
  {#snippet panelContent(
155
312
  panelFilter: 'left' | 'right' | undefined,
156
313
  requestedTabId: string | null,
157
- showParameterStateManager = true,
158
- showCalculateButton = true
314
+ hostsFooter = true
159
315
  )}
160
316
  <div class="panel-content-wrapper">
161
317
  {#if schema.layout.type === 'tabbed'}
162
- <TabLayout {schema} {values} {onValueChange} {panelFilter} {requestedTabId} />
318
+ <TabLayout {schema} {values} {onValueChange} {panelFilter} {requestedTabId} bare={stacked} />
163
319
  {/if}
164
- {#if showParameterStateManager || (!isMobile && showCalculateButton && schema.instanceSolve === false)}
320
+ {#if hostsFooter && !stacked}
165
321
  <div class="panel-footer px-1">
166
- <div class="gap-2 flex flex-wrap items-center [&>button]:flex-1">
167
- {#if showParameterStateManager}
168
- <ParameterPresetManager
169
- {schema}
170
- currentValues={values}
171
- onLoadValues={handleLoadValues}
172
- {showSaveButton}
173
- {showLoadButton}
174
- {onSaveState}
175
- {onListStates}
176
- labels={presetLabels}
177
- />
178
- {/if}
179
- {#each panelActions as action (action.id)}
180
- <Button
181
- variant={action.variant ?? 'outline'}
182
- size={action.size ?? 'sm'}
183
- onclick={action.onclick}
184
- >
185
- {#if action.icon}
186
- {@const IconComponent = action.icon}
187
- <IconComponent class="mr-2 h-4 w-4" />
188
- {/if}
189
- {action.label}
190
- </Button>
191
- {/each}
192
- </div>
193
- {#if !isMobile && showCalculateButton && schema.instanceSolve === false}
194
- <CalculateButton {hasPendingChanges} {hasNeverSolved} {isSolving} {oncalculate} />
195
- {/if}
322
+ {@render footer()}
196
323
  </div>
197
324
  {/if}
198
325
  </div>
199
326
  {/snippet}
200
327
 
201
- <div
328
+ <!-- Pinned under the scrolling fields, so the primary action never sits at the end of a long list. -->
329
+ {#snippet drawerFooter(isInert: boolean)}
330
+ <div class="drawer-footer" inert={isInert}>
331
+ {@render footer()}
332
+ </div>
333
+ {/snippet}
334
+
335
+ <main
202
336
  data-layout-root
203
- class="min-h-0 sm:flex-row sm:py-(--page-py) flex flex-1 flex-col overflow-hidden {leftCollapsed
204
- ? 'sm:pl-0'
205
- : 'sm:pl-(--page-px)'} {rightCollapsed ? 'sm:pr-0' : 'sm:pr-(--page-px)'}"
337
+ class="min-h-0 flex flex-1 flex-col overflow-hidden {stacked
338
+ ? ''
339
+ : `sm:flex-row sm:py-2 ${leftCollapsed ? 'sm:pl-0' : 'sm:pl-2'} ${rightCollapsed ? 'sm:pr-0' : 'sm:pr-2'}`}"
206
340
  class:fullscreen-layout={isViewerFullscreen}
207
341
  class:relative={isMobile}
342
+ class:mobile-root={stacked}
343
+ bind:clientWidth={rootWidth}
208
344
  >
209
- {#if isMobile}
210
- {#if hasViewer}
211
- <div class="min-h-0 flex flex-1 flex-col">
345
+ {#if stacked}
346
+ {#if isMobile && hasViewer}
347
+ <!-- Ends above the drawer rather than behind it, so the model stays centred in what is
348
+ visible. It jumps straight to its new size: animating it would resize the canvas,
349
+ and its render targets, on every frame of the drawer's slide. -->
350
+ <div class="viewer-region" class:viewer-region-raised={drawer !== 'closed'}>
212
351
  <Viewer
213
352
  {meshes}
214
353
  bind:isFullscreen={isViewerFullscreen}
215
- {isSolving}
216
- isBlurred={drawerOpen}
217
- {drawerOpen}
354
+ drawerOpen={drawer === 'full'}
218
355
  {logoUrl}
219
356
  {onViewerReady}
220
357
  viewerConfig={{
221
358
  ...viewerConfig,
222
- backgroundColor: schema?.viewerOptions?.backgroundColor,
359
+ backgroundColor: schema?.viewerOptions?.backgroundColor ?? viewerConfig.backgroundColor,
360
+ look: schema?.viewerOptions?.look ?? viewerConfig.look,
223
361
  showSceneManager: false
224
362
  }}
225
363
  />
226
364
  </div>
227
365
 
228
366
  <div
229
- class="drawer-container rounded-t-2xl z-40 border-t bg-background shadow-sheet"
230
- class:drawer-open={drawerOpen}
231
- class:drawer-closed={!drawerOpen}
367
+ class="drawer-container z-40 rounded-t-xl border-t bg-background shadow-sheet drawer-{drawer}"
232
368
  >
369
+ <!-- Named by its visible label; aria-expanded carries the open/closed state. -->
233
370
  <button
234
- class="drawer-handle-bar"
235
- onclick={() => (drawerOpen = !drawerOpen)}
371
+ class="drawer-handle-bar touch-none"
372
+ onclick={onDrawerHandleClick}
236
373
  ontouchstart={onDrawerTouchStart}
237
374
  ontouchend={onDrawerTouchEnd}
238
- aria-label={drawerOpen ? t.collapsePanel : t.expandPanel}
239
- aria-expanded={drawerOpen}
375
+ aria-expanded={drawer !== 'closed'}
240
376
  >
241
377
  <div class="drawer-pill"></div>
242
- <span class="text-sm font-medium ml-2 text-foreground">{activeLeftTabLabel}</span>
378
+ <span class="text-sm font-medium ml-2 text-foreground">{drawerLabel}</span>
243
379
  <ChevronUp
244
380
  size={16}
245
- class="ml-auto text-muted-foreground transition-transform duration-300 {drawerOpen
246
- ? ''
247
- : 'rotate-180'}"
381
+ class="ml-auto text-muted-foreground transition-transform duration-300 {drawer ===
382
+ 'full'
383
+ ? 'rotate-180'
384
+ : ''}"
248
385
  />
249
386
  </button>
250
387
 
251
- <div class="drawer-content">
252
- <div class="px-3 pb-6">
388
+ <!-- Inert while closed: the fields are clipped out of view, and tabbing into them
389
+ would scroll the drawer's insides around behind the handle. -->
390
+ <div class="drawer-content" inert={drawer === 'closed'}>
391
+ <div class="px-2 pb-4">
253
392
  {#if hasLeftPanel}
254
- {@render panelContent(
255
- hasRightPanel ? 'left' : undefined,
256
- requestedLeftTabId,
257
- !hasRightPanel,
258
- false
259
- )}
393
+ {@render panelContent(hasRightPanel ? 'left' : undefined, requestedLeftTabId, false)}
260
394
  {/if}
261
395
  {#if hasRightPanel}
262
- <div class="my-4 gap-2 flex items-center">
396
+ <!-- Decorative: the section below names itself with a heading. -->
397
+ <div class="my-4 gap-2 flex items-center" aria-hidden="true">
263
398
  <div class="h-px flex-1 bg-border"></div>
264
399
  <span class="text-xs font-medium px-1 text-muted-foreground">
265
400
  {rightTabs[0]?.label ?? t.moreTab}
266
401
  </span>
267
402
  <div class="h-px flex-1 bg-border"></div>
268
403
  </div>
269
- {@render panelContent('right', requestedRightTabId, true, false)}
404
+ {@render panelContent('right', requestedRightTabId, false)}
270
405
  {/if}
271
406
  </div>
272
407
  </div>
273
408
 
274
- {#if schema.instanceSolve === false}
275
- <div class="drawer-footer">
276
- <CalculateButton {hasPendingChanges} {hasNeverSolved} {isSolving} {oncalculate} />
277
- </div>
278
- {/if}
409
+ {@render drawerFooter(drawer === 'closed')}
279
410
  </div>
280
411
  {:else}
281
412
  <div class="flex flex-1 flex-col overflow-hidden">
282
- <div class="px-3 pt-3 flex-1 overflow-y-auto">
413
+ <div class="px-3 pt-3 sm:px-5 flex-1 overflow-y-auto">
283
414
  {#if hasLeftPanel}
284
- {@render panelContent(
285
- hasRightPanel ? 'left' : undefined,
286
- requestedLeftTabId,
287
- !hasRightPanel,
288
- false
289
- )}
415
+ {@render panelContent(hasRightPanel ? 'left' : undefined, requestedLeftTabId, false)}
290
416
  {/if}
291
417
  {#if hasRightPanel}
292
- <div class="my-4 gap-2 flex items-center">
418
+ <div class="my-4 gap-2 flex items-center" aria-hidden="true">
293
419
  <div class="h-px flex-1 bg-border"></div>
294
420
  <span class="text-xs font-medium px-1 text-muted-foreground">
295
421
  {rightTabs[0]?.label ?? t.moreTab}
296
422
  </span>
297
423
  <div class="h-px flex-1 bg-border"></div>
298
424
  </div>
299
- {@render panelContent('right', requestedRightTabId, true, false)}
425
+ {@render panelContent('right', requestedRightTabId, false)}
300
426
  {/if}
301
427
  </div>
302
- {#if schema.instanceSolve === false}
303
- <div class="drawer-footer">
304
- <CalculateButton {hasPendingChanges} {hasNeverSolved} {isSolving} {oncalculate} />
305
- </div>
306
- {/if}
428
+ {@render drawerFooter(false)}
307
429
  </div>
308
430
  {/if}
309
431
  {:else if !hasViewer && !isTwoPanelMode && hasLeftPanel !== hasRightPanel}
310
432
  <!-- Single centered panel (left-only or right-only), no viewer -->
311
- <div class="min-h-0 flex h-full flex-1 justify-center">
433
+ <div class="min-h-0 p-1 flex h-full flex-1 justify-center">
312
434
  <div class="min-h-0 flex h-full w-4/5 flex-col">
313
435
  {@render panelContent(
314
436
  hasRightPanel ? 'right' : undefined,
315
- hasRightPanel ? requestedRightTabId : requestedLeftTabId,
316
- !hasRightPanel,
317
- !hasRightPanel
437
+ hasRightPanel ? requestedRightTabId : requestedLeftTabId
318
438
  )}
319
439
  </div>
320
440
  </div>
@@ -337,7 +457,7 @@
337
457
  <Resizable.PaneGroup
338
458
  direction="horizontal"
339
459
  autoSaveId="selva-layout-{layoutKey}"
340
- class="min-h-0 flex-1"
460
+ class="min-h-0 m-1 flex-1"
341
461
  >
342
462
  {#if hasLeftPanel}
343
463
  <Resizable.Pane
@@ -350,7 +470,6 @@
350
470
  collapsedSize={0}
351
471
  onCollapse={() => (leftCollapsed = true)}
352
472
  onExpand={() => (leftCollapsed = false)}
353
- class="ml-1"
354
473
  >
355
474
  <div
356
475
  class="min-h-0 flex h-full flex-col {isViewerFullscreen || leftCollapsed
@@ -363,6 +482,7 @@
363
482
  <Resizable.Handle
364
483
  withHandle
365
484
  line={false}
485
+ class="translate-x-1.5 z-20"
366
486
  data-collapsed={leftCollapsed}
367
487
  onclick={() => {
368
488
  if (leftCollapsed) leftPaneRef?.expand();
@@ -371,30 +491,34 @@
371
491
  {/if}
372
492
 
373
493
  {#if hasViewer}
374
- <Resizable.Pane order={2} minSize={20} class="min-h-0 mx-1 flex flex-col">
494
+ <Resizable.Pane order={2} minSize={20} class="min-h-0 flex flex-col">
375
495
  <Viewer
376
496
  {meshes}
377
497
  bind:isFullscreen={isViewerFullscreen}
378
- {isSolving}
379
498
  {logoUrl}
380
499
  {onViewerReady}
381
500
  viewerConfig={{
382
501
  ...viewerConfig,
383
- backgroundColor: schema?.viewerOptions?.backgroundColor
502
+ backgroundColor:
503
+ schema?.viewerOptions?.backgroundColor ?? viewerConfig.backgroundColor,
504
+ look: schema?.viewerOptions?.look ?? viewerConfig.look
384
505
  }}
385
506
  />
386
507
  </Resizable.Pane>
387
508
  {/if}
388
509
 
389
510
  {#if hasRightPanel}
390
- <Resizable.Handle
391
- withHandle
392
- line={false}
393
- data-collapsed={rightCollapsed}
394
- onclick={() => {
395
- if (rightCollapsed) rightPaneRef?.expand();
396
- }}
397
- />
511
+ {#if hasViewer || !hasLeftPanel}
512
+ <Resizable.Handle
513
+ withHandle
514
+ line={false}
515
+ class="-translate-x-1.5 z-20"
516
+ data-collapsed={rightCollapsed}
517
+ onclick={() => {
518
+ if (rightCollapsed) rightPaneRef?.expand();
519
+ }}
520
+ />
521
+ {/if}
398
522
  <Resizable.Pane
399
523
  bind:this={rightPaneRef}
400
524
  order={3}
@@ -405,14 +529,13 @@
405
529
  collapsedSize={0}
406
530
  onCollapse={() => (rightCollapsed = true)}
407
531
  onExpand={() => (rightCollapsed = false)}
408
- class="mr-1"
409
532
  >
410
533
  <div
411
534
  class="min-h-0 flex h-full flex-col {isViewerFullscreen || rightCollapsed
412
535
  ? 'hidden'
413
536
  : ''}"
414
537
  >
415
- {@render panelContent('right', requestedRightTabId, !hasLeftPanel, !hasLeftPanel)}
538
+ {@render panelContent('right', requestedRightTabId, !hasLeftPanel)}
416
539
  </div>
417
540
  </Resizable.Pane>
418
541
  {/if}
@@ -432,9 +555,18 @@
432
555
  {/if}
433
556
  </div>
434
557
  {/if}
435
- </div>
436
-
437
- <SolvingIndicator show={showSolvingIndicator} />
558
+ </main>
559
+
560
+ <!-- The save, load and validation dialogs; the footer's menu opens them. -->
561
+ <ParameterPresetManager
562
+ bind:this={presets}
563
+ {schema}
564
+ currentValues={values}
565
+ onLoadValues={handleLoadValues}
566
+ {onSaveState}
567
+ {onListStates}
568
+ labels={presetLabels}
569
+ />
438
570
 
439
571
  <style>
440
572
  .fullscreen-layout {
@@ -466,6 +598,28 @@
466
598
  gap: 0.5rem;
467
599
  }
468
600
 
601
+ .mobile-root {
602
+ --drawer-closed-h: 60px;
603
+ --drawer-half-h: 60%;
604
+ }
605
+
606
+ /* On a phone the panels stack in one scroller. Held at the scroller's height, a long left
607
+ panel would spill over the right panel below it. */
608
+ .mobile-root .panel-content-wrapper {
609
+ height: auto;
610
+ }
611
+
612
+ .viewer-region {
613
+ position: absolute;
614
+ inset: 0 0 var(--drawer-closed-h) 0;
615
+ display: flex;
616
+ flex-direction: column;
617
+ }
618
+
619
+ .viewer-region-raised {
620
+ bottom: var(--drawer-half-h);
621
+ }
622
+
469
623
  .drawer-container {
470
624
  position: absolute;
471
625
  bottom: 0;
@@ -478,10 +632,14 @@
478
632
  }
479
633
 
480
634
  .drawer-closed {
481
- height: 60px;
635
+ height: var(--drawer-closed-h);
636
+ }
637
+ .drawer-half {
638
+ height: var(--drawer-half-h);
482
639
  }
483
- .drawer-open {
484
- height: 60svh;
640
+ /* Leaves a strip of the model showing, so the sheet still reads as sitting over the viewer. */
641
+ .drawer-full {
642
+ height: calc(100% - 3rem);
485
643
  }
486
644
 
487
645
  .drawer-handle-bar {
@@ -524,6 +682,32 @@
524
682
  .drawer-footer {
525
683
  flex-shrink: 0;
526
684
  padding: 0rem 1rem;
527
- border-top: 1px solid var(--border);
685
+ }
686
+
687
+ /* The sheet's top edge already separates it from the model; the desktop footer's extra
688
+ spacing only takes rows from the fields. */
689
+ .drawer-footer :global(> div) {
690
+ margin-top: 0;
691
+ padding-block: 0.5rem;
692
+ }
693
+
694
+ /* Touch gets 44px targets on the panel's controls; a fine pointer keeps the denser sizes. */
695
+ @media (pointer: coarse) {
696
+ .panel-content-wrapper
697
+ :global(
698
+ :is(
699
+ [data-slot='button'],
700
+ [data-slot='input'],
701
+ [data-slot='select-trigger'],
702
+ [data-slot='input-group']
703
+ )
704
+ ),
705
+ .drawer-footer :global([data-slot='button']) {
706
+ min-height: 2.75rem;
707
+ }
708
+ .panel-content-wrapper :global([data-slot='input-group'] > [data-slot='input-group-control']) {
709
+ align-self: stretch;
710
+ height: auto;
711
+ }
528
712
  }
529
713
  </style>