@selvajs/ui 6.5.1-beta.1 → 6.6.0-beta.12

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