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

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 (444) 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 +286 -116
  4. package/dist/components/compute/AppLayout.svelte.d.ts +22 -0
  5. package/dist/components/compute/ComputeApp.svelte +45 -24
  6. package/dist/components/compute/ComputeApp.svelte.d.ts +19 -2
  7. package/dist/components/compute/ComputeMessagesDialog.svelte +47 -83
  8. package/dist/components/compute/ParameterPresetManager.svelte +37 -130
  9. package/dist/components/compute/ParameterPresetManager.svelte.d.ts +4 -5
  10. package/dist/components/compute/SolveMessages.svelte +131 -77
  11. package/dist/components/compute/SolveMessages.svelte.d.ts +7 -12
  12. package/dist/components/layout/AppShell.svelte +10 -6
  13. package/dist/components/layout/PageFooter.svelte +23 -11
  14. package/dist/components/layout/PageHeader.svelte +11 -7
  15. package/dist/components/layout/Pagination.svelte +2 -4
  16. package/dist/components/layout/SectionHeader.svelte +4 -2
  17. package/dist/components/layout/SideNav.svelte +56 -7
  18. package/dist/components/preview/ChartOutput.svelte +35 -25
  19. package/dist/components/preview/Group.svelte +92 -103
  20. package/dist/components/preview/Group.svelte.d.ts +1 -1
  21. package/dist/components/preview/HelpButton.svelte +36 -0
  22. package/dist/components/preview/HelpButton.svelte.d.ts +8 -0
  23. package/dist/components/preview/ImageOutput.svelte +430 -71
  24. package/dist/components/preview/InputControl.svelte +163 -90
  25. package/dist/components/preview/InputControl.svelte.d.ts +5 -1
  26. package/dist/components/preview/OutputDisplay.svelte +28 -33
  27. package/dist/components/preview/TabBar.svelte +106 -22
  28. package/dist/components/preview/TabBar.svelte.d.ts +1 -0
  29. package/dist/components/preview/TabContent.svelte +63 -57
  30. package/dist/components/preview/TabContent.svelte.d.ts +5 -1
  31. package/dist/components/preview/TabLayout.svelte +27 -5
  32. package/dist/components/preview/TabLayout.svelte.d.ts +5 -0
  33. package/dist/components/preview/inputs/CheckboxInput.svelte +7 -7
  34. package/dist/components/preview/inputs/CheckboxInput.svelte.d.ts +3 -0
  35. package/dist/components/preview/inputs/ChecklistInput.svelte +7 -2
  36. package/dist/components/preview/inputs/ChecklistInput.svelte.d.ts +2 -1
  37. package/dist/components/preview/inputs/ColorInput.svelte +4 -2
  38. package/dist/components/preview/inputs/ColorInput.svelte.d.ts +1 -0
  39. package/dist/components/preview/inputs/DropdownInput.svelte +19 -4
  40. package/dist/components/preview/inputs/DropdownInput.svelte.d.ts +2 -1
  41. package/dist/components/preview/inputs/FileInput.svelte +112 -108
  42. package/dist/components/preview/inputs/GeometryInput.svelte +128 -0
  43. package/dist/components/preview/inputs/GeometryInput.svelte.d.ts +21 -0
  44. package/dist/components/preview/inputs/NumberInput.svelte +123 -61
  45. package/dist/components/preview/inputs/NumberInput.svelte.d.ts +2 -0
  46. package/dist/components/preview/inputs/OptionGlyph.svelte +29 -0
  47. package/dist/components/preview/inputs/OptionGlyph.svelte.d.ts +8 -0
  48. package/dist/components/preview/inputs/SegmentedInput.svelte +44 -0
  49. package/dist/components/preview/inputs/SegmentedInput.svelte.d.ts +12 -0
  50. package/dist/components/preview/inputs/TextInput.svelte +8 -4
  51. package/dist/components/preview/inputs/TileInput.svelte +80 -0
  52. package/dist/components/preview/inputs/TileInput.svelte.d.ts +12 -0
  53. package/dist/components/preview/inputs/index.d.ts +3 -0
  54. package/dist/components/preview/inputs/index.js +3 -0
  55. package/dist/components/primitives/CalculateButton.svelte +56 -24
  56. package/dist/components/primitives/CalculateButton.svelte.d.ts +3 -0
  57. package/dist/components/primitives/Callout.svelte +11 -18
  58. package/dist/components/primitives/DataTable.svelte +38 -5
  59. package/dist/components/primitives/DataTable.svelte.d.ts +2 -0
  60. package/dist/components/primitives/FilterableDropdown.svelte +42 -129
  61. package/dist/components/primitives/FilterableDropdown.svelte.d.ts +1 -1
  62. package/dist/components/primitives/IssueList.svelte +56 -0
  63. package/dist/components/primitives/IssueList.svelte.d.ts +20 -0
  64. package/dist/components/primitives/SeverityIcon.svelte +15 -0
  65. package/dist/components/primitives/SeverityIcon.svelte.d.ts +8 -0
  66. package/dist/components/primitives/SolvingIndicator.svelte +43 -42
  67. package/dist/components/primitives/SolvingIndicator.svelte.d.ts +6 -0
  68. package/dist/components/primitives/StateDisplay.svelte +19 -79
  69. package/dist/components/primitives/ViewToggle.svelte +18 -33
  70. package/dist/components/primitives/alert-dialog/alert-dialog-action.svelte +9 -3
  71. package/dist/components/primitives/alert-dialog/alert-dialog-action.svelte.d.ts +6 -1
  72. package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  73. package/dist/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  74. package/dist/components/primitives/badge/badge.svelte +2 -2
  75. package/dist/components/primitives/badge/badge.svelte.d.ts +4 -4
  76. package/dist/components/primitives/button/button.svelte +6 -2
  77. package/dist/components/primitives/button/button.svelte.d.ts +10 -4
  78. package/dist/components/primitives/card/card-header.svelte +1 -1
  79. package/dist/components/primitives/card/card.svelte +21 -5
  80. package/dist/components/primitives/card/card.svelte.d.ts +25 -1
  81. package/dist/components/primitives/card/index.d.ts +1 -2
  82. package/dist/components/primitives/card/index.js +2 -3
  83. package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
  84. package/dist/components/primitives/command/command-dialog.svelte +42 -0
  85. package/dist/components/primitives/command/command-dialog.svelte.d.ts +14 -0
  86. package/dist/components/primitives/command/command-empty.svelte +17 -0
  87. package/dist/components/primitives/command/command-empty.svelte.d.ts +4 -0
  88. package/dist/components/primitives/command/command-group.svelte +33 -0
  89. package/dist/components/primitives/command/command-group.svelte.d.ts +7 -0
  90. package/dist/components/primitives/command/command-input.svelte +36 -0
  91. package/dist/components/primitives/command/command-input.svelte.d.ts +4 -0
  92. package/dist/components/primitives/command/command-item.svelte +28 -0
  93. package/dist/components/primitives/command/command-item.svelte.d.ts +4 -0
  94. package/dist/components/primitives/command/command-link-item.svelte +21 -0
  95. package/dist/components/primitives/command/command-link-item.svelte.d.ts +4 -0
  96. package/dist/components/primitives/command/command-list.svelte +20 -0
  97. package/dist/components/primitives/command/command-list.svelte.d.ts +4 -0
  98. package/dist/components/primitives/command/command-loading.svelte +7 -0
  99. package/dist/components/primitives/command/command-loading.svelte.d.ts +4 -0
  100. package/dist/components/primitives/command/command-separator.svelte +17 -0
  101. package/dist/components/primitives/command/command-separator.svelte.d.ts +4 -0
  102. package/dist/components/primitives/command/command-shortcut.svelte +23 -0
  103. package/dist/components/primitives/command/command-shortcut.svelte.d.ts +5 -0
  104. package/dist/components/primitives/command/command.svelte +28 -0
  105. package/dist/components/primitives/command/command.svelte.d.ts +8 -0
  106. package/dist/components/primitives/command/index.d.ts +12 -0
  107. package/dist/components/primitives/command/index.js +14 -0
  108. package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
  109. package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  110. package/dist/components/primitives/dialog/dialog-content.svelte +11 -3
  111. package/dist/components/primitives/dialog/dialog-content.svelte.d.ts +2 -0
  112. package/dist/components/primitives/dialog/dialog-overlay.svelte +1 -1
  113. package/dist/components/primitives/dialog/dialog-title.svelte +6 -2
  114. package/dist/components/primitives/dialog/dialog-title.svelte.d.ts +5 -1
  115. package/dist/components/primitives/drawer/Drawer.svelte +2 -2
  116. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  117. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
  118. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  119. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
  120. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +31 -0
  121. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
  122. package/dist/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  123. package/dist/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
  124. package/dist/components/primitives/dropdown-menu/dropdown-menu-group.svelte +7 -0
  125. package/dist/components/primitives/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
  126. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +27 -0
  127. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
  128. package/dist/components/primitives/dropdown-menu/dropdown-menu-label.svelte +24 -0
  129. package/dist/components/primitives/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
  130. package/dist/components/primitives/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  131. package/dist/components/primitives/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
  132. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  133. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
  134. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  135. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
  136. package/dist/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  137. package/dist/components/primitives/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
  138. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  139. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
  140. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  141. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +10 -0
  142. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  143. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
  144. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  145. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
  146. package/dist/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  147. package/dist/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
  148. package/dist/components/primitives/dropdown-menu/dropdown-menu.svelte +7 -0
  149. package/dist/components/primitives/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
  150. package/dist/components/primitives/dropdown-menu/index.d.ts +18 -0
  151. package/dist/components/primitives/dropdown-menu/index.js +18 -0
  152. package/dist/components/primitives/{alert/alert-description.svelte → empty/empty-content.svelte} +3 -3
  153. package/dist/components/primitives/empty/empty-content.svelte.d.ts +5 -0
  154. package/dist/components/primitives/empty/empty-description.svelte +23 -0
  155. package/dist/components/primitives/{alert/alert-description.svelte.d.ts → empty/empty-description.svelte.d.ts} +4 -4
  156. package/dist/components/primitives/{alert/alert-title.svelte → empty/empty-header.svelte} +3 -3
  157. package/dist/components/primitives/empty/empty-header.svelte.d.ts +5 -0
  158. package/dist/components/primitives/empty/empty-media.svelte +41 -0
  159. package/dist/components/primitives/empty/empty-media.svelte.d.ts +26 -0
  160. package/dist/components/primitives/{card/card-action.svelte → empty/empty-title.svelte} +2 -2
  161. package/dist/components/primitives/{card/card-action.svelte.d.ts → empty/empty-title.svelte.d.ts} +3 -3
  162. package/dist/components/primitives/empty/empty.svelte +23 -0
  163. package/dist/components/primitives/empty/empty.svelte.d.ts +5 -0
  164. package/dist/components/primitives/empty/index.d.ts +7 -0
  165. package/dist/components/primitives/empty/index.js +9 -0
  166. package/dist/components/primitives/index.d.ts +10 -1
  167. package/dist/components/primitives/index.js +10 -1
  168. package/dist/components/primitives/input/input.svelte +4 -4
  169. package/dist/components/primitives/input-group/index.d.ts +7 -0
  170. package/dist/components/primitives/input-group/index.js +9 -0
  171. package/dist/components/primitives/input-group/input-group-addon.svelte +53 -0
  172. package/dist/components/primitives/input-group/input-group-addon.svelte.d.ts +32 -0
  173. package/dist/components/primitives/input-group/input-group-button.svelte +49 -0
  174. package/dist/components/primitives/input-group/input-group-button.svelte.d.ts +32 -0
  175. package/dist/components/primitives/input-group/input-group-input.svelte +25 -0
  176. package/dist/components/primitives/input-group/input-group-input.svelte.d.ts +11 -0
  177. package/dist/components/primitives/input-group/input-group-text.svelte +22 -0
  178. package/dist/components/primitives/input-group/input-group-text.svelte.d.ts +5 -0
  179. package/dist/components/primitives/input-group/input-group-textarea.svelte +23 -0
  180. package/dist/components/primitives/input-group/input-group-textarea.svelte.d.ts +3 -0
  181. package/dist/components/primitives/input-group/input-group.svelte +24 -0
  182. package/dist/components/primitives/{alert/alert-title.svelte.d.ts → input-group/input-group.svelte.d.ts} +4 -4
  183. package/dist/components/primitives/mode-toggle/mode-toggle.svelte +6 -7
  184. package/dist/components/primitives/popover/index.d.ts +9 -0
  185. package/dist/components/primitives/popover/index.js +11 -0
  186. package/dist/components/primitives/popover/popover-close.svelte +7 -0
  187. package/dist/components/primitives/popover/popover-close.svelte.d.ts +4 -0
  188. package/dist/components/primitives/popover/popover-content.svelte +31 -0
  189. package/dist/components/primitives/popover/popover-content.svelte.d.ts +10 -0
  190. package/dist/components/primitives/popover/popover-description.svelte +20 -0
  191. package/dist/components/primitives/popover/popover-description.svelte.d.ts +5 -0
  192. package/dist/components/primitives/popover/popover-header.svelte +20 -0
  193. package/dist/components/primitives/popover/popover-header.svelte.d.ts +5 -0
  194. package/dist/components/primitives/popover/popover-portal.svelte +7 -0
  195. package/dist/components/primitives/popover/popover-portal.svelte.d.ts +3 -0
  196. package/dist/components/primitives/popover/popover-title.svelte +15 -0
  197. package/dist/components/primitives/popover/popover-title.svelte.d.ts +5 -0
  198. package/dist/components/primitives/popover/popover-trigger.svelte +17 -0
  199. package/dist/components/primitives/popover/popover-trigger.svelte.d.ts +4 -0
  200. package/dist/components/primitives/popover/popover.svelte +7 -0
  201. package/dist/components/primitives/popover/popover.svelte.d.ts +3 -0
  202. package/dist/components/primitives/resizable/resizable-handle.svelte +6 -2
  203. package/dist/components/primitives/resizable/resizable-handle.svelte.d.ts +2 -0
  204. package/dist/components/primitives/resizable/resizable-pane-group.svelte +1 -4
  205. package/dist/components/primitives/scroll-area/scroll-area.svelte +1 -1
  206. package/dist/components/primitives/search/search.svelte +3 -3
  207. package/dist/components/primitives/select/select-content.svelte +1 -1
  208. package/dist/components/primitives/select/select-trigger.svelte +1 -1
  209. package/dist/components/primitives/severity.d.ts +7 -0
  210. package/dist/components/primitives/severity.js +19 -0
  211. package/dist/components/primitives/slider/slider.svelte +8 -3
  212. package/dist/components/primitives/slider/slider.svelte.d.ts +5 -1
  213. package/dist/components/primitives/sonner/sonner.svelte +1 -1
  214. package/dist/components/primitives/spinner/index.d.ts +1 -0
  215. package/dist/components/primitives/spinner/index.js +1 -0
  216. package/dist/components/primitives/spinner/spinner.svelte +29 -0
  217. package/dist/components/primitives/spinner/spinner.svelte.d.ts +4 -0
  218. package/dist/components/primitives/switch/switch.svelte +2 -2
  219. package/dist/components/primitives/tabs/tabs-list.svelte +27 -5
  220. package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +22 -1
  221. package/dist/components/primitives/tabs/tabs-trigger.svelte +23 -5
  222. package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +22 -1
  223. package/dist/components/primitives/tabs/tabs.svelte +6 -2
  224. package/dist/components/primitives/tabs/tabs.svelte.d.ts +5 -1
  225. package/dist/components/primitives/textarea/textarea.svelte +1 -1
  226. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +1 -1
  227. package/dist/components/primitives/toggle/index.d.ts +3 -0
  228. package/dist/components/primitives/toggle/index.js +5 -0
  229. package/dist/components/primitives/toggle/toggle.svelte +54 -0
  230. package/dist/components/primitives/toggle/toggle.svelte.d.ts +46 -0
  231. package/dist/components/primitives/toggle-group/index.d.ts +3 -0
  232. package/dist/components/primitives/toggle-group/index.js +5 -0
  233. package/dist/components/primitives/toggle-group/toggle-group-item.svelte +35 -0
  234. package/dist/components/primitives/toggle-group/toggle-group-item.svelte.d.ts +6 -0
  235. package/dist/components/primitives/toggle-group/toggle-group.svelte +75 -0
  236. package/dist/components/primitives/toggle-group/toggle-group.svelte.d.ts +17 -0
  237. package/dist/components/viewer/MeshMetadataPanel.svelte +119 -0
  238. package/dist/components/viewer/MeshMetadataPanel.svelte.d.ts +9 -0
  239. package/dist/components/viewer/SceneManager.svelte +14 -5
  240. package/dist/components/viewer/Viewer.svelte +243 -221
  241. package/dist/components/viewer/Viewer.svelte.d.ts +3 -0
  242. package/dist/compute/run-panel-action.d.ts +8 -0
  243. package/dist/compute/run-panel-action.js +12 -0
  244. package/dist/compute/solving.svelte.d.ts +11 -7
  245. package/dist/compute/solving.svelte.js +64 -45
  246. package/dist/compute/useSolveSession.svelte.js +3 -0
  247. package/dist/constants.d.ts +6 -8
  248. package/dist/constants.js +7 -9
  249. package/dist/contexts/hostContext.svelte.d.ts +39 -0
  250. package/dist/contexts/hostContext.svelte.js +9 -0
  251. package/dist/i18n/localeContext.svelte.d.ts +10 -3
  252. package/dist/i18n/localeContext.svelte.js +27 -5
  253. package/dist/i18n/messages.d.ts +150 -2
  254. package/dist/i18n/messages.js +261 -11
  255. package/dist/index.d.ts +4 -1
  256. package/dist/index.js +3 -0
  257. package/dist/public.d.ts +1 -1
  258. package/dist/schema/host-support.d.ts +29 -0
  259. package/dist/schema/host-support.js +97 -0
  260. package/dist/schema/option-display.d.ts +12 -0
  261. package/dist/schema/option-display.js +29 -0
  262. package/dist/schema/param-exporter.d.ts +4 -1
  263. package/dist/schema/param-exporter.js +10 -6
  264. package/dist/schema/visibility-rules.js +21 -0
  265. package/dist/styles/base.css +80 -10
  266. package/dist/types/actionButton.d.ts +17 -3
  267. package/dist/types/presetLabels.d.ts +3 -2
  268. package/dist/types/presetLabels.js +9 -8
  269. package/dist/utils/file-download.js +10 -22
  270. package/dist/utils/host-files.d.ts +12 -0
  271. package/dist/utils/host-files.js +46 -0
  272. package/dist/utils/loadScript.d.ts +2 -0
  273. package/dist/utils/loadScript.js +2 -0
  274. package/dist/utils/webp-to-png.d.ts +10 -0
  275. package/dist/utils/webp-to-png.js +41 -0
  276. package/package.json +14 -14
  277. package/src/lib/components/ConfirmDialog.svelte +2 -10
  278. package/src/lib/components/ErrorScreen.svelte +21 -27
  279. package/src/lib/components/compute/AppLayout.svelte +286 -116
  280. package/src/lib/components/compute/ComputeApp.svelte +45 -24
  281. package/src/lib/components/compute/ComputeMessagesDialog.svelte +47 -83
  282. package/src/lib/components/compute/ParameterPresetManager.svelte +37 -130
  283. package/src/lib/components/compute/SolveMessages.svelte +131 -77
  284. package/src/lib/components/layout/AppShell.svelte +10 -6
  285. package/src/lib/components/layout/PageFooter.svelte +23 -11
  286. package/src/lib/components/layout/PageHeader.svelte +11 -7
  287. package/src/lib/components/layout/Pagination.svelte +2 -4
  288. package/src/lib/components/layout/SectionHeader.svelte +4 -2
  289. package/src/lib/components/layout/SideNav.svelte +56 -7
  290. package/src/lib/components/preview/ChartOutput.svelte +35 -25
  291. package/src/lib/components/preview/Group.svelte +92 -103
  292. package/src/lib/components/preview/HelpButton.svelte +36 -0
  293. package/src/lib/components/preview/ImageOutput.svelte +430 -71
  294. package/src/lib/components/preview/InputControl.svelte +163 -90
  295. package/src/lib/components/preview/OutputDisplay.svelte +28 -33
  296. package/src/lib/components/preview/TabBar.svelte +106 -22
  297. package/src/lib/components/preview/TabContent.svelte +63 -57
  298. package/src/lib/components/preview/TabLayout.svelte +27 -5
  299. package/src/lib/components/preview/inputs/CheckboxInput.svelte +7 -7
  300. package/src/lib/components/preview/inputs/ChecklistInput.svelte +7 -2
  301. package/src/lib/components/preview/inputs/ColorInput.svelte +4 -2
  302. package/src/lib/components/preview/inputs/DropdownInput.svelte +19 -4
  303. package/src/lib/components/preview/inputs/FileInput.svelte +112 -108
  304. package/src/lib/components/preview/inputs/GeometryInput.svelte +128 -0
  305. package/src/lib/components/preview/inputs/NumberInput.svelte +123 -61
  306. package/src/lib/components/preview/inputs/OptionGlyph.svelte +29 -0
  307. package/src/lib/components/preview/inputs/SegmentedInput.svelte +44 -0
  308. package/src/lib/components/preview/inputs/TextInput.svelte +8 -4
  309. package/src/lib/components/preview/inputs/TileInput.svelte +80 -0
  310. package/src/lib/components/preview/inputs/index.ts +3 -0
  311. package/src/lib/components/primitives/CalculateButton.svelte +56 -24
  312. package/src/lib/components/primitives/Callout.svelte +11 -18
  313. package/src/lib/components/primitives/DataTable.svelte +38 -5
  314. package/src/lib/components/primitives/FilterableDropdown.svelte +42 -129
  315. package/src/lib/components/primitives/IssueList.svelte +56 -0
  316. package/src/lib/components/primitives/SeverityIcon.svelte +15 -0
  317. package/src/lib/components/primitives/SolvingIndicator.svelte +43 -42
  318. package/src/lib/components/primitives/StateDisplay.svelte +19 -79
  319. package/src/lib/components/primitives/ViewToggle.svelte +18 -33
  320. package/src/lib/components/primitives/alert-dialog/alert-dialog-action.svelte +9 -3
  321. package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  322. package/src/lib/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  323. package/src/lib/components/primitives/badge/badge.svelte +2 -2
  324. package/src/lib/components/primitives/button/button.svelte +6 -2
  325. package/src/lib/components/primitives/card/card-header.svelte +1 -1
  326. package/src/lib/components/primitives/card/card.svelte +21 -5
  327. package/src/lib/components/primitives/card/index.ts +1 -4
  328. package/src/lib/components/primitives/checkbox/checkbox.svelte +1 -1
  329. package/src/lib/components/primitives/command/command-dialog.svelte +42 -0
  330. package/src/lib/components/primitives/command/command-empty.svelte +17 -0
  331. package/src/lib/components/primitives/command/command-group.svelte +33 -0
  332. package/src/lib/components/primitives/command/command-input.svelte +36 -0
  333. package/src/lib/components/primitives/command/command-item.svelte +28 -0
  334. package/src/lib/components/primitives/command/command-link-item.svelte +21 -0
  335. package/src/lib/components/primitives/command/command-list.svelte +20 -0
  336. package/src/lib/components/primitives/command/command-loading.svelte +7 -0
  337. package/src/lib/components/primitives/command/command-separator.svelte +17 -0
  338. package/src/lib/components/primitives/command/command-shortcut.svelte +23 -0
  339. package/src/lib/components/primitives/command/command.svelte +28 -0
  340. package/src/lib/components/primitives/command/index.ts +37 -0
  341. package/src/lib/components/primitives/context-menu/context-menu-content.svelte +1 -1
  342. package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  343. package/src/lib/components/primitives/dialog/dialog-content.svelte +11 -3
  344. package/src/lib/components/primitives/dialog/dialog-overlay.svelte +1 -1
  345. package/src/lib/components/primitives/dialog/dialog-title.svelte +6 -2
  346. package/src/lib/components/primitives/drawer/Drawer.svelte +2 -2
  347. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  348. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  349. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +31 -0
  350. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  351. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group.svelte +7 -0
  352. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +27 -0
  353. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-label.svelte +24 -0
  354. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  355. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  356. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  357. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  358. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  359. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  360. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  361. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  362. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  363. package/src/lib/components/primitives/dropdown-menu/dropdown-menu.svelte +7 -0
  364. package/src/lib/components/primitives/dropdown-menu/index.ts +54 -0
  365. package/src/lib/components/primitives/{alert/alert-description.svelte → empty/empty-content.svelte} +3 -3
  366. package/src/lib/components/primitives/empty/empty-description.svelte +23 -0
  367. package/src/lib/components/primitives/{alert/alert-title.svelte → empty/empty-header.svelte} +3 -3
  368. package/src/lib/components/primitives/empty/empty-media.svelte +41 -0
  369. package/src/lib/components/primitives/{card/card-action.svelte → empty/empty-title.svelte} +2 -2
  370. package/src/lib/components/primitives/empty/empty.svelte +23 -0
  371. package/src/lib/components/primitives/empty/index.ts +22 -0
  372. package/src/lib/components/primitives/index.ts +10 -1
  373. package/src/lib/components/primitives/input/input.svelte +4 -4
  374. package/src/lib/components/primitives/input-group/index.ts +22 -0
  375. package/src/lib/components/primitives/input-group/input-group-addon.svelte +53 -0
  376. package/src/lib/components/primitives/input-group/input-group-button.svelte +49 -0
  377. package/src/lib/components/primitives/input-group/input-group-input.svelte +25 -0
  378. package/src/lib/components/primitives/input-group/input-group-text.svelte +22 -0
  379. package/src/lib/components/primitives/input-group/input-group-textarea.svelte +23 -0
  380. package/src/lib/components/primitives/input-group/input-group.svelte +24 -0
  381. package/src/lib/components/primitives/mode-toggle/mode-toggle.svelte +6 -7
  382. package/src/lib/components/primitives/popover/index.ts +28 -0
  383. package/src/lib/components/primitives/popover/popover-close.svelte +7 -0
  384. package/src/lib/components/primitives/popover/popover-content.svelte +31 -0
  385. package/src/lib/components/primitives/popover/popover-description.svelte +20 -0
  386. package/src/lib/components/primitives/popover/popover-header.svelte +20 -0
  387. package/src/lib/components/primitives/popover/popover-portal.svelte +7 -0
  388. package/src/lib/components/primitives/popover/popover-title.svelte +15 -0
  389. package/src/lib/components/primitives/popover/popover-trigger.svelte +17 -0
  390. package/src/lib/components/primitives/popover/popover.svelte +7 -0
  391. package/src/lib/components/primitives/resizable/resizable-handle.svelte +6 -2
  392. package/src/lib/components/primitives/resizable/resizable-pane-group.svelte +1 -4
  393. package/src/lib/components/primitives/scroll-area/scroll-area.svelte +1 -1
  394. package/src/lib/components/primitives/search/search.svelte +3 -3
  395. package/src/lib/components/primitives/select/select-content.svelte +1 -1
  396. package/src/lib/components/primitives/select/select-trigger.svelte +1 -1
  397. package/src/lib/components/primitives/severity.ts +26 -0
  398. package/src/lib/components/primitives/slider/slider.svelte +8 -3
  399. package/src/lib/components/primitives/sonner/sonner.svelte +1 -1
  400. package/src/lib/components/primitives/spinner/index.ts +1 -0
  401. package/src/lib/components/primitives/spinner/spinner.svelte +29 -0
  402. package/src/lib/components/primitives/switch/switch.svelte +2 -2
  403. package/src/lib/components/primitives/tabs/tabs-list.svelte +27 -5
  404. package/src/lib/components/primitives/tabs/tabs-trigger.svelte +23 -5
  405. package/src/lib/components/primitives/tabs/tabs.svelte +6 -2
  406. package/src/lib/components/primitives/textarea/textarea.svelte +1 -1
  407. package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +1 -1
  408. package/src/lib/components/primitives/toggle/index.ts +13 -0
  409. package/src/lib/components/primitives/toggle/toggle.svelte +54 -0
  410. package/src/lib/components/primitives/toggle-group/index.ts +10 -0
  411. package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +35 -0
  412. package/src/lib/components/primitives/toggle-group/toggle-group.svelte +75 -0
  413. package/src/lib/components/viewer/MeshMetadataPanel.svelte +119 -0
  414. package/src/lib/components/viewer/SceneManager.svelte +14 -5
  415. package/src/lib/components/viewer/Viewer.svelte +243 -221
  416. package/src/lib/compute/run-panel-action.ts +16 -0
  417. package/src/lib/compute/solving.svelte.ts +66 -50
  418. package/src/lib/compute/useSolveSession.svelte.ts +3 -0
  419. package/src/lib/constants.ts +7 -9
  420. package/src/lib/contexts/hostContext.svelte.ts +51 -0
  421. package/src/lib/i18n/localeContext.svelte.ts +40 -5
  422. package/src/lib/i18n/messages.ts +453 -13
  423. package/src/lib/index.ts +21 -0
  424. package/src/lib/public.ts +1 -0
  425. package/src/lib/schema/host-support.ts +129 -0
  426. package/src/lib/schema/option-display.ts +44 -0
  427. package/src/lib/schema/param-exporter.ts +10 -7
  428. package/src/lib/schema/visibility-rules.ts +23 -0
  429. package/src/lib/styles/base.css +80 -10
  430. package/src/lib/types/actionButton.ts +16 -3
  431. package/src/lib/types/presetLabels.ts +13 -11
  432. package/src/lib/utils/file-download.ts +11 -21
  433. package/src/lib/utils/host-files.ts +61 -0
  434. package/src/lib/utils/loadScript.ts +3 -0
  435. package/src/lib/utils/webp-to-png.ts +48 -0
  436. package/dist/components/primitives/alert/alert.svelte +0 -44
  437. package/dist/components/primitives/alert/alert.svelte.d.ts +0 -26
  438. package/dist/components/primitives/alert/index.d.ts +0 -5
  439. package/dist/components/primitives/alert/index.js +0 -7
  440. package/dist/components/viewer/MeshMetadataDialog.svelte +0 -126
  441. package/dist/components/viewer/MeshMetadataDialog.svelte.d.ts +0 -10
  442. package/src/lib/components/primitives/alert/alert.svelte +0 -44
  443. package/src/lib/components/primitives/alert/index.ts +0 -14
  444. package/src/lib/components/viewer/MeshMetadataDialog.svelte +0 -126
@@ -26,21 +26,22 @@
26
26
  Frame,
27
27
  Ruler,
28
28
  Grid3x3,
29
- Check,
30
- ChevronRight,
31
29
  Palette,
32
30
  Spline
33
31
  } from '@lucide/svelte';
34
- import { DropdownMenu } from 'bits-ui';
35
32
  import { SvelteSet } from 'svelte/reactivity';
36
33
  import type * as THREE from 'three';
37
34
  import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
38
35
  import { createSceneOutliner, type SceneOutliner } from '@selvajs/visualization/scene';
36
+ import { clearGeometryCache } from '@selvajs/visualization/parse';
39
37
  import SceneManager from './SceneManager.svelte';
40
- import MeshMetadataDialog from './MeshMetadataDialog.svelte';
38
+ import MeshMetadataPanel from './MeshMetadataPanel.svelte';
41
39
  import * as Resizable from '../primitives/resizable/index.js';
40
+ import * as DropdownMenu from '../primitives/dropdown-menu/index.js';
41
+ import { Button } from '../primitives/button/index.js';
42
42
  import type { Locale } from '../../i18n/messages';
43
- import { setLocaleContext, getLocaleContext } from '../../i18n/localeContext.svelte';
43
+ import { layerLocaleContext } from '../../i18n/localeContext.svelte';
44
+ import { saveBlob } from '../../utils/host-files';
44
45
 
45
46
  export interface ViewerConfig {
46
47
  showScreenshotButton?: boolean;
@@ -58,7 +59,10 @@
58
59
  interface Props {
59
60
  meshes: any[];
60
61
  isFullscreen?: boolean;
62
+ /** Shows a progress bar along the top edge. Pass a delayed flag (`createSolvingIndicator`). */
61
63
  isSolving?: boolean;
64
+ /** Dims the stale result while a long solve runs. */
65
+ isSlow?: boolean;
62
66
  isBlurred?: boolean;
63
67
  drawerOpen?: boolean;
64
68
  viewerConfig?: ViewerConfig;
@@ -94,6 +98,7 @@
94
98
  meshes,
95
99
  isFullscreen = $bindable(false),
96
100
  isSolving = false,
101
+ isSlow = false,
97
102
  isBlurred = false,
98
103
  drawerOpen = false,
99
104
  viewerConfig = {},
@@ -104,12 +109,8 @@
104
109
 
105
110
  const config = $derived({ ...defaultViewerConfig, ...viewerConfig });
106
111
 
107
- // Read the host's locale before overriding it for our subtree.
108
- const hostLocale = getLocaleContext();
109
-
110
112
  // A getter, not a value: it is re-read reactively, so changing `lang` updates the chrome live.
111
- setLocaleContext(() => lang ?? hostLocale.locale);
112
- const locale = getLocaleContext();
113
+ const locale = layerLocaleContext(() => lang);
113
114
  const t = $derived(locale.messages);
114
115
 
115
116
  let canvas: HTMLCanvasElement;
@@ -127,6 +128,7 @@
127
128
  let updateGridScale: (() => void) | null = null;
128
129
  let updateShadowBounds: (() => void) | null = null;
129
130
  let fitToView: (() => void) | null = null;
131
+ let clearSelection: (() => void) | null = null;
130
132
  let viewerInitialized = false;
131
133
  let sceneVersion = $state(0);
132
134
  let hideButton = $state(false);
@@ -147,16 +149,23 @@
147
149
  let selectedMeshMetadata: Record<string, any> | null = $state(null);
148
150
  let selectedMeshName: string | null = $state(null);
149
151
 
150
- // Derived from LOOKS so adding a look in @selvajs/visualization shows up here with no edit; the
151
- // map only overrides the names that don't survive capitalisation ('xray' → 'X-Ray').
152
- const LOOK_LABELS: Partial<Record<Look, string>> = {
153
- xray: 'X-Ray',
154
- lineart: 'Line Art'
155
- };
156
- const STYLE_OPTIONS: { look: Look; label: string }[] = LOOKS.map((look) => ({
157
- look,
158
- label: LOOK_LABELS[look] ?? look.charAt(0).toUpperCase() + look.slice(1)
159
- }));
152
+ // Derived from LOOKS so a look added in @selvajs/visualization shows up here with no edit,
153
+ // under its capitalised name until it gets a message.
154
+ const lookLabels = $derived<Partial<Record<Look, string>>>({
155
+ technical: t.lookTechnical,
156
+ studio: t.lookStudio,
157
+ showcase: t.lookShowcase,
158
+ arctic: t.lookArctic,
159
+ xray: t.lookXray,
160
+ lineart: t.lookLineart,
161
+ wireframe: t.lookWireframe
162
+ });
163
+ const styleOptions = $derived<{ look: Look; label: string }[]>(
164
+ LOOKS.map((look) => ({
165
+ look,
166
+ label: lookLabels[look] ?? look.charAt(0).toUpperCase() + look.slice(1)
167
+ }))
168
+ );
160
169
 
161
170
  const VIEW_PRESETS: { preset: ViewPreset; label: () => string }[] = [
162
171
  { preset: 'top', label: () => t.viewTop },
@@ -192,6 +201,10 @@
192
201
  grid: { enabled: config.showToolsMenu && config.showGridToggle },
193
202
  measure: { enabled: config.showToolsMenu },
194
203
  events: {
204
+ // Fires before onMeshMetadataClicked, so picking an object without metadata closes the
205
+ // previous object's panel instead of leaving it describing the wrong thing.
206
+ onObjectSelected: closeMetadata,
207
+ onBackgroundClicked: closeMetadata,
195
208
  onMeshMetadataClicked: config.enableMeshClick
196
209
  ? (metadata: Record<string, unknown>) => {
197
210
  if (hasUsefulMetadata(metadata)) {
@@ -222,21 +235,19 @@
222
235
  updateGridScale = init.updateGridScale;
223
236
  updateShadowBounds = init.updateShadowBounds;
224
237
  fitToView = init.fitToView;
238
+ clearSelection = init.clearSelection;
225
239
  projection = init.cameraController.getProjection();
226
240
 
227
- const renderer = init.renderer;
228
-
229
241
  // Untracked so the host reading `meshes` or config inside its setup can't re-run onMount's teardown.
230
242
  const hostCleanup = untrack(() => onViewerReady?.(init));
231
243
 
232
244
  return () => {
233
245
  // Before dispose, so the host can still remove its own objects from a live scene.
234
246
  hostCleanup?.();
247
+ // Also frees the GL context: a second forceContextLoss() here hits an already-lost one.
235
248
  init.dispose();
236
- // `{#key definitionKey}` recreates the canvas + WebGLRenderer + GL context
237
- // on every definition switch; browsers cap live contexts (~16). Explicitly
238
- // drop this one so the GPU-side context is released now rather than at GC.
239
- renderer.forceContextLoss();
249
+ // Parsed geometry outlives the viewer otherwise: the cache is page-wide.
250
+ clearGeometryCache();
240
251
  };
241
252
  });
242
253
 
@@ -373,6 +384,29 @@
373
384
  }
374
385
  });
375
386
 
387
+ function closeMetadata() {
388
+ selectedMeshMetadata = null;
389
+ selectedMeshName = null;
390
+ }
391
+
392
+ function dismissMetadata() {
393
+ closeMetadata();
394
+ clearSelection?.();
395
+ invalidate?.();
396
+ }
397
+
398
+ // The panel describes an object from the previous solve once new meshes arrive.
399
+ $effect(() => {
400
+ void meshes;
401
+ untrack(closeMetadata);
402
+ });
403
+
404
+ // Dialogs portal to <body>, so one opened over the fullscreen viewer needs lifting above it.
405
+ $effect(() => {
406
+ document.body.toggleAttribute('data-viewer-fullscreen', isFullscreen);
407
+ return () => document.body.removeAttribute('data-viewer-fullscreen');
408
+ });
409
+
376
410
  function toggleFullscreen() {
377
411
  isFullscreen = !isFullscreen;
378
412
  }
@@ -402,22 +436,15 @@
402
436
  const blob = await captureImage?.();
403
437
  if (!blob) return;
404
438
 
405
- const url = URL.createObjectURL(blob);
406
- const link = document.createElement('a');
407
439
  const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
408
- link.href = url;
409
- link.download = `viewer-${timestamp}.png`;
410
- document.body.appendChild(link);
411
- link.click();
412
- document.body.removeChild(link);
413
- URL.revokeObjectURL(url);
440
+ await saveBlob(blob, `viewer-${timestamp}.png`);
414
441
  }
415
442
  </script>
416
443
 
417
444
  <div
418
445
  class="min-h-64 sm:min-h-96 lg:min-h-125 relative h-full flex-1 border {isFullscreen
419
446
  ? 'fullscreen-viewer'
420
- : 'overflow-hidden rounded-[0.625rem]'}"
447
+ : 'overflow-hidden rounded-lg'}"
421
448
  >
422
449
  <Resizable.PaneGroup direction="horizontal" class="h-full w-full">
423
450
  <!-- `defaultSize` must sum to 100 across the live panes. Panes register a frame before the
@@ -426,12 +453,12 @@
426
453
  scene pane keeps a sliver of its intended width. Hence 85 + 15, and the explicit id/order
427
454
  so a conditionally-rendered pane keeps its slot. -->
428
455
  <Resizable.Pane id="viewport" order={1} defaultSize={sceneManagerOpen ? 85 : 100} minSize={40}>
429
- <div class="relative h-full w-full" style="touch-action: none;">
456
+ <div class="relative h-full w-full touch-none">
430
457
  <!-- Mesh count of the geometry currently in the scene. The canvas itself is opaque to
431
458
  the DOM, so this is the only observable proof a solve's geometry decoded and
432
459
  rendered; e2e asserts on it. -->
433
460
  <canvas
434
- class="block h-full w-full"
461
+ class="viewer-canvas block h-full w-full select-none"
435
462
  data-testid="viewer-canvas"
436
463
  data-mesh-count={meshes.length}
437
464
  bind:this={canvas}
@@ -443,20 +470,28 @@
443
470
  : 'blur-overlay-inactive'}"
444
471
  ></div>
445
472
 
473
+ <!-- The last result stays sharp while the next one solves: blurring it on every
474
+ solve hides the model the user is adjusting. -->
446
475
  {#if isSolving}
447
476
  <div
448
- class="inset-0 absolute z-10 animate-[selva-viewer-fade-in_0.2s_ease-out] backdrop-blur-[2px] transition-all duration-300"
477
+ class="inset-x-0 top-0 h-0.5 animate-in fade-in pointer-events-none absolute z-20 overflow-hidden bg-primary/20 duration-200"
478
+ aria-hidden="true"
479
+ >
480
+ <div class="solve-bar h-full w-1/3 bg-primary"></div>
481
+ </div>
482
+ {/if}
483
+ {#if isSlow}
484
+ <div
485
+ class="inset-0 animate-in fade-in pointer-events-none absolute z-10 bg-background/25 duration-500"
449
486
  ></div>
450
487
  {/if}
451
488
 
452
489
  {#if logoUrl}
453
- <!-- Bottom offset matches the tools menu so it clears the mobile drawer handle.
454
- Always pointer-events:none so it never intercepts canvas drags. -->
490
+ <!-- pointer-events:none so it never intercepts canvas drags. -->
455
491
  <div
456
- class="right-4 {isFullscreen ? 'bottom-4' : 'bottom-16 sm:bottom-4'} absolute z-20"
457
- style={hideButton
458
- ? 'opacity: 0; pointer-events: none;'
459
- : 'opacity: 1; pointer-events: none;'}
492
+ class="right-4 bottom-4 pointer-events-none absolute z-20 {hideButton
493
+ ? 'opacity-0'
494
+ : ''}"
460
495
  >
461
496
  <img
462
497
  src={logoUrl}
@@ -466,173 +501,152 @@
466
501
  </div>
467
502
  {/if}
468
503
 
504
+ {#if selectedMeshMetadata}
505
+ <MeshMetadataPanel
506
+ metadata={selectedMeshMetadata}
507
+ meshName={selectedMeshName}
508
+ onClose={dismissMetadata}
509
+ class="top-3 right-3 left-3 sm:right-auto sm:max-h-2/3 sm:w-72 absolute z-30 max-h-1/2"
510
+ />
511
+ {/if}
512
+
469
513
  {#if config.showToolsMenu}
470
- {@const itemClass =
471
- 'gap-2 px-2 py-1.5 text-sm flex cursor-pointer select-none items-center rounded-sm outline-none transition-colors hover:bg-muted focus:bg-muted'}
472
514
  <div
473
- class="left-4 {isFullscreen
474
- ? 'bottom-4'
475
- : 'bottom-16 sm:bottom-4'} absolute z-50 flex items-center"
476
- style={hideButton
477
- ? 'opacity: 0; pointer-events: none;'
478
- : 'opacity: 1; pointer-events: auto;'}
515
+ class="left-4 bottom-4 gap-2 *:shadow-lg absolute z-50 flex items-center *:rounded-md {hideButton
516
+ ? 'pointer-events-none opacity-0'
517
+ : ''}"
479
518
  >
480
519
  <DropdownMenu.Root>
481
- <DropdownMenu.Trigger
482
- class="h-10 w-10 shadow-lg hover:shadow-xl flex items-center justify-center rounded-lg border border-border bg-card/90 transition-all hover:bg-card active:scale-95 data-[state=open]:bg-secondary/60"
483
- title={t.toolsMenu}
484
- aria-label={t.toolsMenu}
485
- >
486
- <Settings2 class="h-5 w-5 text-card-foreground" />
520
+ <DropdownMenu.Trigger>
521
+ {#snippet child({ props })}
522
+ <Button
523
+ {...props}
524
+ variant="overlay"
525
+ size="icon-lg"
526
+ title={t.toolsMenu}
527
+ aria-label={t.toolsMenu}
528
+ >
529
+ <Settings2 />
530
+ </Button>
531
+ {/snippet}
487
532
  </DropdownMenu.Trigger>
488
- <DropdownMenu.Portal>
489
- <DropdownMenu.Content
490
- sideOffset={6}
491
- align="start"
492
- class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 min-w-44 p-1 shadow-md z-10001 rounded-md border bg-popover text-popover-foreground"
493
- >
494
- <DropdownMenu.Item class={itemClass} onSelect={toggleProjection}>
495
- {#if projection === 'perspective'}
496
- <Square class="h-4 w-4" />
497
- {t.switchToOrthographic}
498
- {:else}
499
- <Box class="h-4 w-4" />
500
- {t.switchToPerspective}
501
- {/if}
502
- </DropdownMenu.Item>
503
-
504
- <DropdownMenu.Item class={itemClass} onSelect={() => fitToView?.()}>
505
- <Frame class="h-4 w-4" />
506
- {t.fitToView}
507
- </DropdownMenu.Item>
533
+ <!-- Above the fullscreen viewer, which sits at z-index 10000. -->
534
+ <DropdownMenu.Content sideOffset={6} class="min-w-44 z-10001 w-auto">
535
+ <DropdownMenu.Item onSelect={toggleProjection}>
536
+ {#if projection === 'perspective'}
537
+ <Square />
538
+ {t.switchToOrthographic}
539
+ {:else}
540
+ <Box />
541
+ {t.switchToPerspective}
542
+ {/if}
543
+ </DropdownMenu.Item>
544
+ <DropdownMenu.Item onSelect={() => fitToView?.()}>
545
+ <Frame />
546
+ {t.fitToView}
547
+ </DropdownMenu.Item>
548
+
549
+ <DropdownMenu.Sub>
550
+ <DropdownMenu.SubTrigger>
551
+ <Box />
552
+ {t.views}
553
+ </DropdownMenu.SubTrigger>
554
+ <DropdownMenu.SubContent class="z-10001">
555
+ {#each VIEW_PRESETS as { preset, label } (preset)}
556
+ <DropdownMenu.Item onSelect={() => setView(preset)}
557
+ >{label()}</DropdownMenu.Item
558
+ >
559
+ {/each}
560
+ </DropdownMenu.SubContent>
561
+ </DropdownMenu.Sub>
508
562
 
563
+ {#if config.showDisplayMenu}
509
564
  <DropdownMenu.Sub>
510
- <DropdownMenu.SubTrigger class="{itemClass} data-[state=open]:bg-muted">
511
- <Box class="h-4 w-4" />
512
- <span class="flex-1">{t.views}</span>
513
- <ChevronRight class="h-4 w-4 text-muted-foreground" />
565
+ <DropdownMenu.SubTrigger>
566
+ <Palette />
567
+ {t.display}
514
568
  </DropdownMenu.SubTrigger>
515
- <DropdownMenu.SubContent
516
- sideOffset={4}
517
- class="min-w-32 p-1 shadow-md z-10001 rounded-md border bg-popover text-popover-foreground"
518
- >
519
- {#each VIEW_PRESETS as { preset, label } (preset)}
520
- <DropdownMenu.Item
521
- class="px-2 py-1.5 text-sm flex cursor-pointer items-center rounded-sm transition-colors outline-none select-none hover:bg-muted focus:bg-muted"
522
- onSelect={() => setView(preset)}
523
- >
524
- {label()}
525
- </DropdownMenu.Item>
526
- {/each}
527
- </DropdownMenu.SubContent>
528
- </DropdownMenu.Sub>
529
-
530
- {#if config.showDisplayMenu}
531
- <DropdownMenu.Sub>
532
- <DropdownMenu.SubTrigger class="{itemClass} data-[state=open]:bg-muted">
533
- <Palette class="h-4 w-4" />
534
- <span class="flex-1">{t.display}</span>
535
- <ChevronRight class="h-4 w-4 text-muted-foreground" />
536
- </DropdownMenu.SubTrigger>
537
- <DropdownMenu.SubContent
538
- sideOffset={4}
539
- class="min-w-40 p-1 shadow-md z-10001 rounded-md border bg-popover text-popover-foreground"
569
+ <DropdownMenu.SubContent class="min-w-40 z-10001">
570
+ <DropdownMenu.RadioGroup
571
+ value={renderStyle}
572
+ onValueChange={(look) => setRenderStyle(look as typeof renderStyle)}
540
573
  >
541
- {#each STYLE_OPTIONS as { look, label } (look)}
542
- <DropdownMenu.Item
543
- closeOnSelect={false}
544
- class="{itemClass} {renderStyle === look ? 'text-primary' : ''}"
545
- onSelect={() => setRenderStyle(look)}
546
- >
547
- <span class="flex-1">{label}</span>
548
- {#if renderStyle === look}
549
- <Check class="h-4 w-4" />
550
- {/if}
551
- </DropdownMenu.Item>
574
+ {#each styleOptions as { look, label } (look)}
575
+ <DropdownMenu.RadioItem value={look} closeOnSelect={false}>
576
+ {label}
577
+ </DropdownMenu.RadioItem>
552
578
  {/each}
553
-
554
- <DropdownMenu.Separator class="my-1 h-px bg-border" />
555
-
556
- <DropdownMenu.Item
557
- closeOnSelect={false}
558
- class="{itemClass} {edgesVisible ? 'text-primary' : ''}"
559
- onSelect={toggleEdges}
560
- >
561
- <Spline class="h-4 w-4" />
562
- <span class="flex-1">{t.edges}</span>
563
- {#if edgesVisible}
564
- <Check class="h-4 w-4" />
565
- {/if}
566
- </DropdownMenu.Item>
567
- </DropdownMenu.SubContent>
568
- </DropdownMenu.Sub>
569
- {/if}
570
-
571
- <DropdownMenu.Item
572
- closeOnSelect={false}
573
- class="{itemClass} {measureActive ? 'text-primary' : ''}"
574
- onSelect={toggleMeasure}
579
+ </DropdownMenu.RadioGroup>
580
+ <DropdownMenu.Separator />
581
+ <DropdownMenu.CheckboxItem
582
+ checked={edgesVisible}
583
+ onCheckedChange={toggleEdges}
584
+ >
585
+ <Spline />
586
+ {t.edges}
587
+ </DropdownMenu.CheckboxItem>
588
+ </DropdownMenu.SubContent>
589
+ </DropdownMenu.Sub>
590
+ {/if}
591
+
592
+ <DropdownMenu.CheckboxItem checked={measureActive} onCheckedChange={toggleMeasure}>
593
+ <Ruler />
594
+ {t.measure}
595
+ </DropdownMenu.CheckboxItem>
596
+
597
+ {#if config.showGridToggle}
598
+ <DropdownMenu.CheckboxItem checked={gridVisible} onCheckedChange={toggleGrid}>
599
+ <Grid3x3 />
600
+ {t.grid}
601
+ </DropdownMenu.CheckboxItem>
602
+ {/if}
603
+
604
+ {#if config.showSceneManager || config.showScreenshotButton || config.showFullscreenButton}
605
+ <DropdownMenu.Separator />
606
+ {/if}
607
+
608
+ {#if config.showSceneManager}
609
+ <DropdownMenu.CheckboxItem
610
+ checked={sceneManagerOpen}
611
+ onCheckedChange={() => (sceneManagerOpen = !sceneManagerOpen)}
575
612
  >
576
- <Ruler class="h-4 w-4" />
577
- <span class="flex-1">{t.measure}</span>
578
- {#if measureActive}
579
- <Check class="h-4 w-4" />
580
- {/if}
613
+ <Layers />
614
+ {t.sceneManager}
615
+ </DropdownMenu.CheckboxItem>
616
+ {/if}
617
+
618
+ {#if config.showScreenshotButton}
619
+ <DropdownMenu.Item onSelect={downloadScreenshot}>
620
+ <Camera />
621
+ {t.screenshot}
581
622
  </DropdownMenu.Item>
623
+ {/if}
582
624
 
583
- {#if config.showGridToggle}
584
- <DropdownMenu.Item
585
- closeOnSelect={false}
586
- class="{itemClass} {gridVisible ? 'text-primary' : ''}"
587
- onSelect={toggleGrid}
588
- >
589
- <Grid3x3 class="h-4 w-4" />
590
- <span class="flex-1">{t.grid}</span>
591
- {#if gridVisible}
592
- <Check class="h-4 w-4" />
593
- {/if}
594
- </DropdownMenu.Item>
595
- {/if}
596
-
597
- {#if config.showSceneManager || config.showScreenshotButton || config.showFullscreenButton}
598
- <DropdownMenu.Separator class="my-1 h-px bg-border" />
599
- {/if}
600
-
601
- {#if config.showSceneManager}
602
- <DropdownMenu.Item
603
- closeOnSelect={false}
604
- class="{itemClass} {sceneManagerOpen ? 'text-primary' : ''}"
605
- onSelect={() => (sceneManagerOpen = !sceneManagerOpen)}
606
- >
607
- <Layers class="h-4 w-4" />
608
- <span class="flex-1">{t.sceneManager}</span>
609
- {#if sceneManagerOpen}
610
- <Check class="h-4 w-4" />
611
- {/if}
612
- </DropdownMenu.Item>
613
- {/if}
614
-
615
- {#if config.showScreenshotButton}
616
- <DropdownMenu.Item class={itemClass} onSelect={downloadScreenshot}>
617
- <Camera class="h-4 w-4" />
618
- {t.screenshot}
619
- </DropdownMenu.Item>
620
- {/if}
621
-
622
- {#if config.showFullscreenButton}
623
- <DropdownMenu.Item class={itemClass} onSelect={toggleFullscreen}>
624
- {#if isFullscreen}
625
- <Minimize class="h-4 w-4" />
626
- {t.exitFullscreen}
627
- {:else}
628
- <Maximize class="h-4 w-4" />
629
- {t.fullscreen}
630
- {/if}
631
- </DropdownMenu.Item>
632
- {/if}
633
- </DropdownMenu.Content>
634
- </DropdownMenu.Portal>
625
+ {#if config.showFullscreenButton}
626
+ <DropdownMenu.Item onSelect={toggleFullscreen}>
627
+ {#if isFullscreen}
628
+ <Minimize />
629
+ {t.exitFullscreen}
630
+ {:else}
631
+ <Maximize />
632
+ {t.fullscreen}
633
+ {/if}
634
+ </DropdownMenu.Item>
635
+ {/if}
636
+ </DropdownMenu.Content>
635
637
  </DropdownMenu.Root>
638
+ <!-- Touch has no F key, and fit-to-view is the way back from a framed part. -->
639
+ <div class="hidden pointer-coarse:block">
640
+ <Button
641
+ variant="overlay"
642
+ size="icon-lg"
643
+ title={t.fitToView}
644
+ aria-label={t.fitToView}
645
+ onclick={() => fitToView?.()}
646
+ >
647
+ <Frame />
648
+ </Button>
649
+ </div>
636
650
  </div>
637
651
  {/if}
638
652
  </div>
@@ -647,22 +661,12 @@
647
661
  </Resizable.PaneGroup>
648
662
  </div>
649
663
 
650
- <MeshMetadataDialog
651
- open={selectedMeshMetadata !== null}
652
- metadata={selectedMeshMetadata}
653
- meshName={selectedMeshName}
654
- {isFullscreen}
655
- onOpenChange={(open: boolean) => {
656
- if (!open) {
657
- setTimeout(() => {
658
- selectedMeshMetadata = null;
659
- selectedMeshName = null;
660
- }, 200);
661
- }
662
- }}
663
- />
664
-
665
664
  <style>
665
+ :global([data-viewer-fullscreen] [data-slot='dialog-content']),
666
+ :global([data-viewer-fullscreen] [data-slot='dialog-overlay']) {
667
+ z-index: 10001;
668
+ }
669
+
666
670
  .fullscreen-viewer {
667
671
  position: fixed;
668
672
  inset: 0;
@@ -670,6 +674,33 @@
670
674
  border-radius: 0 !important;
671
675
  }
672
676
 
677
+ /* A long press on touch otherwise selects the canvas as an image and opens the save/copy callout. */
678
+ .viewer-canvas {
679
+ -webkit-touch-callout: none;
680
+ -webkit-tap-highlight-color: transparent;
681
+ }
682
+
683
+ .solve-bar {
684
+ animation: solve-bar 1.2s cubic-bezier(0.65, 0, 0.35, 1) infinite;
685
+ }
686
+
687
+ @keyframes solve-bar {
688
+ from {
689
+ transform: translateX(-100%);
690
+ }
691
+ to {
692
+ transform: translateX(300%);
693
+ }
694
+ }
695
+
696
+ @media (prefers-reduced-motion: reduce) {
697
+ .solve-bar {
698
+ animation: none;
699
+ width: 100%;
700
+ opacity: 0.6;
701
+ }
702
+ }
703
+
673
704
  .blur-overlay {
674
705
  pointer-events: none;
675
706
  }
@@ -699,13 +730,4 @@
699
730
  backdrop-filter: blur(0px);
700
731
  }
701
732
  }
702
-
703
- @keyframes selva-viewer-fade-in {
704
- from {
705
- opacity: 0;
706
- }
707
- to {
708
- opacity: 1;
709
- }
710
- }
711
733
  </style>
@@ -15,7 +15,10 @@ export interface ViewerConfig {
15
15
  interface Props {
16
16
  meshes: any[];
17
17
  isFullscreen?: boolean;
18
+ /** Shows a progress bar along the top edge. Pass a delayed flag (`createSolvingIndicator`). */
18
19
  isSolving?: boolean;
20
+ /** Dims the stale result while a long solve runs. */
21
+ isSlow?: boolean;
19
22
  isBlurred?: boolean;
20
23
  drawerOpen?: boolean;
21
24
  viewerConfig?: ViewerConfig;
@@ -0,0 +1,8 @@
1
+ import type { SolveSettlement } from '@selvajs/solve/client';
2
+ import type { ActionButton } from '../types/actionButton';
3
+ /**
4
+ * Runs a footer-menu action. A `solvesFirst` action waits for the result for the current
5
+ * inputs and runs only on an applied one; a refused or failed solve has already shown the user
6
+ * why. A host that can't solve (`ensureSolved` undefined) runs it straight away.
7
+ */
8
+ export declare function runPanelAction(action: ActionButton, ensureSolved: (() => Promise<SolveSettlement>) | undefined): Promise<void>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Runs a footer-menu action. A `solvesFirst` action waits for the result for the current
3
+ * inputs and runs only on an applied one; a refused or failed solve has already shown the user
4
+ * why. A host that can't solve (`ensureSolved` undefined) runs it straight away.
5
+ */
6
+ export async function runPanelAction(action, ensureSolved) {
7
+ if (!action.solvesFirst || !ensureSolved)
8
+ return action.onclick();
9
+ const settled = await ensureSolved();
10
+ if (settled.status === 'applied')
11
+ await action.onclick(settled);
12
+ }