@selvajs/ui 6.6.0-beta.2 → 6.6.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +0 -3
  2. package/dist/components/ErrorScreen.svelte +7 -3
  3. package/dist/components/compute/AppLayout.svelte +307 -123
  4. package/dist/components/compute/AppLayout.svelte.d.ts +28 -0
  5. package/dist/components/compute/CollapsedPanelStrip.svelte +4 -2
  6. package/dist/components/compute/ComputeApp.svelte +46 -22
  7. package/dist/components/compute/ComputeApp.svelte.d.ts +20 -3
  8. package/dist/components/compute/ComputeMessagesDialog.svelte +31 -41
  9. package/dist/components/compute/ParameterPresetManager.svelte +16 -65
  10. package/dist/components/compute/ParameterPresetManager.svelte.d.ts +4 -5
  11. package/dist/components/compute/SolveMessages.svelte +128 -121
  12. package/dist/components/compute/SolveMessages.svelte.d.ts +12 -12
  13. package/dist/components/layout/AppShell.svelte +10 -6
  14. package/dist/components/layout/FooterItemRenderer.svelte +2 -2
  15. package/dist/components/layout/FooterItemRenderer.svelte.d.ts +1 -1
  16. package/dist/components/layout/PageContent.svelte +3 -2
  17. package/dist/components/layout/PageFooter.svelte +26 -14
  18. package/dist/components/layout/PageHeader.svelte +14 -10
  19. package/dist/components/layout/SectionHeader.svelte +4 -2
  20. package/dist/components/layout/SideNav.svelte +57 -8
  21. package/dist/components/layout/SubNav.svelte +2 -2
  22. package/dist/components/preview/ChartOutput.svelte +131 -65
  23. package/dist/components/preview/Group.svelte +42 -22
  24. package/dist/components/preview/HelpButton.svelte +5 -3
  25. package/dist/components/preview/ImageOutput.svelte +435 -71
  26. package/dist/components/preview/InputControl.svelte +125 -51
  27. package/dist/components/preview/InputControl.svelte.d.ts +5 -1
  28. package/dist/components/preview/OutputDisplay.svelte +42 -28
  29. package/dist/components/preview/TabBar.svelte +8 -6
  30. package/dist/components/preview/TabBar.svelte.d.ts +2 -0
  31. package/dist/components/preview/TabContent.svelte +66 -31
  32. package/dist/components/preview/TabContent.svelte.d.ts +5 -1
  33. package/dist/components/preview/TabLayout.svelte +29 -8
  34. package/dist/components/preview/TabLayout.svelte.d.ts +5 -0
  35. package/dist/components/preview/inputs/CheckboxInput.svelte +1 -1
  36. package/dist/components/preview/inputs/ChecklistInput.svelte +11 -6
  37. package/dist/components/preview/inputs/ChecklistInput.svelte.d.ts +2 -1
  38. package/dist/components/preview/inputs/ColorInput.svelte +357 -13
  39. package/dist/components/preview/inputs/ColorInput.svelte.d.ts +2 -0
  40. package/dist/components/preview/inputs/DropdownInput.svelte +20 -5
  41. package/dist/components/preview/inputs/DropdownInput.svelte.d.ts +2 -1
  42. package/dist/components/preview/inputs/FileInput.svelte +115 -103
  43. package/dist/components/preview/inputs/GeometryInput.svelte +128 -0
  44. package/dist/components/preview/inputs/GeometryInput.svelte.d.ts +21 -0
  45. package/dist/components/preview/inputs/NumberInput.svelte +98 -16
  46. package/dist/components/preview/inputs/NumberInput.svelte.d.ts +2 -0
  47. package/dist/components/preview/inputs/OptionGlyph.svelte +29 -0
  48. package/dist/components/preview/inputs/OptionGlyph.svelte.d.ts +8 -0
  49. package/dist/components/preview/inputs/SegmentedInput.svelte +45 -0
  50. package/dist/components/preview/inputs/SegmentedInput.svelte.d.ts +12 -0
  51. package/dist/components/preview/inputs/TextInput.svelte +11 -6
  52. package/dist/components/preview/inputs/TileInput.svelte +83 -0
  53. package/dist/components/preview/inputs/TileInput.svelte.d.ts +12 -0
  54. package/dist/components/preview/inputs/index.d.ts +3 -0
  55. package/dist/components/preview/inputs/index.js +3 -0
  56. package/dist/components/primitives/CalculateButton.svelte +56 -24
  57. package/dist/components/primitives/CalculateButton.svelte.d.ts +3 -0
  58. package/dist/components/primitives/Callout.svelte +8 -6
  59. package/dist/components/primitives/Callout.svelte.d.ts +4 -4
  60. package/dist/components/primitives/DataTable.svelte +38 -4
  61. package/dist/components/primitives/DataTable.svelte.d.ts +2 -0
  62. package/dist/components/primitives/FilterableDropdown.svelte +13 -1
  63. package/dist/components/primitives/FilterableDropdown.svelte.d.ts +5 -0
  64. package/dist/components/primitives/ImageUploadField.svelte +1 -1
  65. package/dist/components/primitives/SolvingIndicator.svelte +245 -15
  66. package/dist/components/primitives/SolvingIndicator.svelte.d.ts +15 -0
  67. package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  68. package/dist/components/primitives/badge/badge.svelte +4 -4
  69. package/dist/components/primitives/badge/badge.svelte.d.ts +10 -10
  70. package/dist/components/primitives/button/button.svelte +10 -8
  71. package/dist/components/primitives/button/button.svelte.d.ts +20 -17
  72. package/dist/components/primitives/card/card.svelte +1 -1
  73. package/dist/components/primitives/card/card.svelte.d.ts +1 -1
  74. package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
  75. package/dist/components/primitives/command/command-input.svelte +1 -1
  76. package/dist/components/primitives/command/command-item.svelte +1 -1
  77. package/dist/components/primitives/command/command-link-item.svelte +1 -1
  78. package/dist/components/primitives/command/command.svelte +1 -1
  79. package/dist/components/primitives/context-menu/context-menu-checkbox-item.svelte +1 -1
  80. package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
  81. package/dist/components/primitives/context-menu/context-menu-item.svelte +1 -1
  82. package/dist/components/primitives/context-menu/context-menu-radio-item.svelte +1 -1
  83. package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  84. package/dist/components/primitives/context-menu/context-menu-sub-trigger.svelte +1 -1
  85. package/dist/components/primitives/dialog/dialog-content.svelte +7 -3
  86. package/dist/components/primitives/drawer/Drawer.svelte +1 -1
  87. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +31 -11
  88. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +2 -0
  89. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +5 -1
  90. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +2 -0
  91. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +4 -2
  92. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +2 -1
  93. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
  94. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
  95. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +1 -1
  96. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
  97. package/dist/components/primitives/index.d.ts +1 -1
  98. package/dist/components/primitives/index.js +1 -1
  99. package/dist/components/primitives/input/input.svelte +4 -4
  100. package/dist/components/primitives/input-group/input-group-input.svelte.d.ts +1 -1
  101. package/dist/components/primitives/input-group/input-group.svelte +1 -1
  102. package/dist/components/primitives/mode-toggle/mode-toggle.svelte +5 -2
  103. package/dist/components/primitives/popover/popover-content.svelte +1 -1
  104. package/dist/components/primitives/resizable/resizable-handle.svelte +2 -2
  105. package/dist/components/primitives/scroll-area/scroll-area.svelte +1 -1
  106. package/dist/components/primitives/search/search.svelte +2 -2
  107. package/dist/components/primitives/select/select-content.svelte +1 -1
  108. package/dist/components/primitives/select/select-item.svelte +1 -1
  109. package/dist/components/primitives/select/select-trigger.svelte +1 -1
  110. package/dist/components/primitives/slider/slider.svelte +8 -3
  111. package/dist/components/primitives/slider/slider.svelte.d.ts +5 -1
  112. package/dist/components/primitives/switch/switch.svelte +1 -1
  113. package/dist/components/primitives/tabs/tabs-list.svelte +2 -2
  114. package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +6 -6
  115. package/dist/components/primitives/tabs/tabs-trigger.svelte +3 -3
  116. package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +7 -7
  117. package/dist/components/primitives/textarea/textarea.svelte +1 -1
  118. package/dist/components/primitives/toggle/toggle.svelte +11 -3
  119. package/dist/components/primitives/toggle/toggle.svelte.d.ts +13 -4
  120. package/dist/components/primitives/toggle-group/toggle-group-item.svelte +1 -1
  121. package/dist/components/primitives/toggle-group/toggle-group.svelte +1 -1
  122. package/dist/components/primitives/tooltip/index.d.ts +6 -0
  123. package/dist/components/primitives/tooltip/index.js +8 -0
  124. package/dist/components/primitives/tooltip/tooltip-content.svelte +34 -0
  125. package/dist/components/primitives/tooltip/tooltip-content.svelte.d.ts +10 -0
  126. package/dist/components/primitives/tooltip/tooltip-portal.svelte +7 -0
  127. package/dist/components/primitives/tooltip/tooltip-portal.svelte.d.ts +4 -0
  128. package/dist/components/primitives/tooltip/tooltip-provider.svelte +7 -0
  129. package/dist/components/primitives/tooltip/tooltip-provider.svelte.d.ts +4 -0
  130. package/dist/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
  131. package/dist/components/primitives/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  132. package/dist/components/primitives/tooltip/tooltip.svelte +7 -0
  133. package/dist/components/primitives/tooltip/tooltip.svelte.d.ts +4 -0
  134. package/dist/components/viewer/MeshMetadataPanel.svelte +119 -0
  135. package/dist/components/viewer/MeshMetadataPanel.svelte.d.ts +9 -0
  136. package/dist/components/viewer/SceneManager.svelte +32 -21
  137. package/dist/components/viewer/SceneManager.svelte.d.ts +3 -1
  138. package/dist/components/viewer/Viewer.svelte +195 -207
  139. package/dist/components/viewer/Viewer.svelte.d.ts +7 -2
  140. package/dist/components/viewer/ViewerDock.svelte +404 -0
  141. package/dist/components/viewer/ViewerDock.svelte.d.ts +42 -0
  142. package/dist/composables/useFooterItem.svelte.d.ts +1 -1
  143. package/dist/composables/useFooterItem.svelte.js +1 -1
  144. package/dist/compute/liveProgress.svelte.d.ts +16 -0
  145. package/dist/compute/liveProgress.svelte.js +90 -0
  146. package/dist/compute/run-panel-action.d.ts +8 -0
  147. package/dist/compute/run-panel-action.js +12 -0
  148. package/dist/compute/solving.svelte.d.ts +11 -7
  149. package/dist/compute/solving.svelte.js +64 -45
  150. package/dist/compute/useSolveSession.svelte.js +3 -0
  151. package/dist/constants.d.ts +6 -8
  152. package/dist/constants.js +8 -11
  153. package/dist/contexts/hostContext.svelte.d.ts +39 -0
  154. package/dist/contexts/hostContext.svelte.js +9 -0
  155. package/dist/features/chart/plotly-compat.d.ts +15 -0
  156. package/dist/features/chart/plotly-compat.js +124 -0
  157. package/dist/i18n/localeContext.svelte.d.ts +10 -3
  158. package/dist/i18n/localeContext.svelte.js +27 -5
  159. package/dist/i18n/messages.d.ts +164 -2
  160. package/dist/i18n/messages.js +281 -11
  161. package/dist/index.d.ts +6 -1
  162. package/dist/index.js +5 -0
  163. package/dist/public.d.ts +2 -1
  164. package/dist/public.js +1 -0
  165. package/dist/schema/host-support.d.ts +29 -0
  166. package/dist/schema/host-support.js +97 -0
  167. package/dist/schema/option-display.d.ts +12 -0
  168. package/dist/schema/option-display.js +29 -0
  169. package/dist/schema/param-exporter.d.ts +4 -1
  170. package/dist/schema/param-exporter.js +10 -6
  171. package/dist/schema/visibility-rules.d.ts +7 -1
  172. package/dist/schema/visibility-rules.js +38 -0
  173. package/dist/styles/base.css +116 -83
  174. package/dist/types/actionButton.d.ts +17 -3
  175. package/dist/types/presetLabels.d.ts +3 -2
  176. package/dist/types/presetLabels.js +9 -8
  177. package/dist/utils/color-space.d.ts +21 -0
  178. package/dist/utils/color-space.js +90 -0
  179. package/dist/utils/file-download.js +10 -22
  180. package/dist/utils/host-files.d.ts +12 -0
  181. package/dist/utils/host-files.js +46 -0
  182. package/dist/utils/loadScript.d.ts +2 -0
  183. package/dist/utils/loadScript.js +2 -0
  184. package/dist/utils/webp-to-png.d.ts +10 -0
  185. package/dist/utils/webp-to-png.js +41 -0
  186. package/package.json +30 -22
  187. package/src/lib/components/ErrorScreen.svelte +7 -3
  188. package/src/lib/components/compute/AppLayout.svelte +307 -123
  189. package/src/lib/components/compute/CollapsedPanelStrip.svelte +4 -2
  190. package/src/lib/components/compute/ComputeApp.svelte +46 -22
  191. package/src/lib/components/compute/ComputeMessagesDialog.svelte +32 -42
  192. package/src/lib/components/compute/ParameterPresetManager.svelte +16 -65
  193. package/src/lib/components/compute/SolveMessages.svelte +130 -123
  194. package/src/lib/components/layout/AppShell.svelte +10 -6
  195. package/src/lib/components/layout/FooterItemRenderer.svelte +2 -2
  196. package/src/lib/components/layout/PageContent.svelte +3 -2
  197. package/src/lib/components/layout/PageFooter.svelte +26 -14
  198. package/src/lib/components/layout/PageHeader.svelte +14 -10
  199. package/src/lib/components/layout/SectionHeader.svelte +4 -2
  200. package/src/lib/components/layout/SideNav.svelte +57 -8
  201. package/src/lib/components/layout/SubNav.svelte +2 -2
  202. package/src/lib/components/preview/ChartOutput.svelte +131 -65
  203. package/src/lib/components/preview/Group.svelte +42 -22
  204. package/src/lib/components/preview/HelpButton.svelte +5 -3
  205. package/src/lib/components/preview/ImageOutput.svelte +435 -71
  206. package/src/lib/components/preview/InputControl.svelte +125 -51
  207. package/src/lib/components/preview/OutputDisplay.svelte +42 -28
  208. package/src/lib/components/preview/TabBar.svelte +8 -6
  209. package/src/lib/components/preview/TabContent.svelte +66 -31
  210. package/src/lib/components/preview/TabLayout.svelte +29 -8
  211. package/src/lib/components/preview/inputs/CheckboxInput.svelte +1 -1
  212. package/src/lib/components/preview/inputs/ChecklistInput.svelte +11 -6
  213. package/src/lib/components/preview/inputs/ColorInput.svelte +357 -13
  214. package/src/lib/components/preview/inputs/DropdownInput.svelte +20 -5
  215. package/src/lib/components/preview/inputs/FileInput.svelte +115 -103
  216. package/src/lib/components/preview/inputs/GeometryInput.svelte +128 -0
  217. package/src/lib/components/preview/inputs/NumberInput.svelte +98 -16
  218. package/src/lib/components/preview/inputs/OptionGlyph.svelte +29 -0
  219. package/src/lib/components/preview/inputs/SegmentedInput.svelte +45 -0
  220. package/src/lib/components/preview/inputs/TextInput.svelte +11 -6
  221. package/src/lib/components/preview/inputs/TileInput.svelte +83 -0
  222. package/src/lib/components/preview/inputs/index.ts +3 -0
  223. package/src/lib/components/primitives/CalculateButton.svelte +56 -24
  224. package/src/lib/components/primitives/Callout.svelte +9 -7
  225. package/src/lib/components/primitives/DataTable.svelte +38 -4
  226. package/src/lib/components/primitives/FilterableDropdown.svelte +14 -2
  227. package/src/lib/components/primitives/ImageUploadField.svelte +1 -1
  228. package/src/lib/components/primitives/IssueList.svelte +1 -1
  229. package/src/lib/components/primitives/SeverityIcon.svelte +1 -1
  230. package/src/lib/components/primitives/SolvingIndicator.svelte +245 -15
  231. package/src/lib/components/primitives/StateDisplay.svelte +1 -1
  232. package/src/lib/components/primitives/alert-dialog/alert-dialog-action.svelte +2 -2
  233. package/src/lib/components/primitives/alert-dialog/alert-dialog-cancel.svelte +2 -2
  234. package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +2 -2
  235. package/src/lib/components/primitives/alert-dialog/alert-dialog-description.svelte +1 -1
  236. package/src/lib/components/primitives/alert-dialog/alert-dialog-footer.svelte +1 -1
  237. package/src/lib/components/primitives/alert-dialog/alert-dialog-header.svelte +1 -1
  238. package/src/lib/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  239. package/src/lib/components/primitives/alert-dialog/alert-dialog-title.svelte +1 -1
  240. package/src/lib/components/primitives/badge/badge.svelte +5 -5
  241. package/src/lib/components/primitives/button/button.svelte +10 -8
  242. package/src/lib/components/primitives/button-group/button-group-separator.svelte +2 -2
  243. package/src/lib/components/primitives/button-group/button-group-text.svelte +1 -1
  244. package/src/lib/components/primitives/button-group/button-group.svelte +1 -1
  245. package/src/lib/components/primitives/card/card-content.svelte +1 -1
  246. package/src/lib/components/primitives/card/card-description.svelte +1 -1
  247. package/src/lib/components/primitives/card/card-footer.svelte +1 -1
  248. package/src/lib/components/primitives/card/card-header.svelte +1 -1
  249. package/src/lib/components/primitives/card/card-title.svelte +1 -1
  250. package/src/lib/components/primitives/card/card.svelte +2 -2
  251. package/src/lib/components/primitives/checkbox/checkbox.svelte +2 -2
  252. package/src/lib/components/primitives/command/command-dialog.svelte +2 -2
  253. package/src/lib/components/primitives/command/command-empty.svelte +1 -1
  254. package/src/lib/components/primitives/command/command-group.svelte +1 -1
  255. package/src/lib/components/primitives/command/command-input.svelte +3 -3
  256. package/src/lib/components/primitives/command/command-item.svelte +2 -2
  257. package/src/lib/components/primitives/command/command-link-item.svelte +2 -2
  258. package/src/lib/components/primitives/command/command-list.svelte +1 -1
  259. package/src/lib/components/primitives/command/command-separator.svelte +1 -1
  260. package/src/lib/components/primitives/command/command-shortcut.svelte +1 -1
  261. package/src/lib/components/primitives/command/command.svelte +2 -2
  262. package/src/lib/components/primitives/context-menu/context-menu-checkbox-item.svelte +2 -2
  263. package/src/lib/components/primitives/context-menu/context-menu-content.svelte +2 -2
  264. package/src/lib/components/primitives/context-menu/context-menu-group-heading.svelte +1 -1
  265. package/src/lib/components/primitives/context-menu/context-menu-item.svelte +2 -2
  266. package/src/lib/components/primitives/context-menu/context-menu-label.svelte +1 -1
  267. package/src/lib/components/primitives/context-menu/context-menu-radio-item.svelte +2 -2
  268. package/src/lib/components/primitives/context-menu/context-menu-separator.svelte +1 -1
  269. package/src/lib/components/primitives/context-menu/context-menu-shortcut.svelte +1 -1
  270. package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +2 -2
  271. package/src/lib/components/primitives/context-menu/context-menu-sub-trigger.svelte +2 -2
  272. package/src/lib/components/primitives/dialog/dialog-content.svelte +8 -4
  273. package/src/lib/components/primitives/dialog/dialog-description.svelte +1 -1
  274. package/src/lib/components/primitives/dialog/dialog-footer.svelte +1 -1
  275. package/src/lib/components/primitives/dialog/dialog-header.svelte +1 -1
  276. package/src/lib/components/primitives/dialog/dialog-overlay.svelte +1 -1
  277. package/src/lib/components/primitives/dialog/dialog-title.svelte +1 -1
  278. package/src/lib/components/primitives/drawer/Drawer.svelte +1 -1
  279. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +32 -12
  280. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +6 -2
  281. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +1 -1
  282. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +5 -3
  283. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-label.svelte +1 -1
  284. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +2 -2
  285. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +1 -1
  286. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +2 -2
  287. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +2 -2
  288. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +2 -2
  289. package/src/lib/components/primitives/empty/empty-content.svelte +1 -1
  290. package/src/lib/components/primitives/empty/empty-description.svelte +1 -1
  291. package/src/lib/components/primitives/empty/empty-header.svelte +1 -1
  292. package/src/lib/components/primitives/empty/empty-media.svelte +1 -1
  293. package/src/lib/components/primitives/empty/empty-title.svelte +1 -1
  294. package/src/lib/components/primitives/empty/empty.svelte +1 -1
  295. package/src/lib/components/primitives/field/field-content.svelte +1 -1
  296. package/src/lib/components/primitives/field/field-description.svelte +1 -1
  297. package/src/lib/components/primitives/field/field-error.svelte +1 -1
  298. package/src/lib/components/primitives/field/field-group.svelte +1 -1
  299. package/src/lib/components/primitives/field/field-label.svelte +2 -2
  300. package/src/lib/components/primitives/field/field-legend.svelte +1 -1
  301. package/src/lib/components/primitives/field/field-separator.svelte +2 -2
  302. package/src/lib/components/primitives/field/field-set.svelte +1 -1
  303. package/src/lib/components/primitives/field/field-title.svelte +1 -1
  304. package/src/lib/components/primitives/field/field.svelte +1 -1
  305. package/src/lib/components/primitives/index.ts +1 -1
  306. package/src/lib/components/primitives/input/input.svelte +5 -5
  307. package/src/lib/components/primitives/input-group/input-group-addon.svelte +1 -1
  308. package/src/lib/components/primitives/input-group/input-group-button.svelte +2 -2
  309. package/src/lib/components/primitives/input-group/input-group-input.svelte +2 -2
  310. package/src/lib/components/primitives/input-group/input-group-text.svelte +1 -1
  311. package/src/lib/components/primitives/input-group/input-group-textarea.svelte +2 -2
  312. package/src/lib/components/primitives/input-group/input-group.svelte +2 -2
  313. package/src/lib/components/primitives/label/label.svelte +1 -1
  314. package/src/lib/components/primitives/mode-toggle/mode-toggle.svelte +5 -2
  315. package/src/lib/components/primitives/popover/popover-content.svelte +2 -2
  316. package/src/lib/components/primitives/popover/popover-description.svelte +1 -1
  317. package/src/lib/components/primitives/popover/popover-header.svelte +1 -1
  318. package/src/lib/components/primitives/popover/popover-title.svelte +1 -1
  319. package/src/lib/components/primitives/popover/popover-trigger.svelte +1 -1
  320. package/src/lib/components/primitives/resizable/resizable-handle.svelte +3 -3
  321. package/src/lib/components/primitives/resizable/resizable-pane-group.svelte +1 -1
  322. package/src/lib/components/primitives/scroll-area/scroll-area-scrollbar.svelte +1 -1
  323. package/src/lib/components/primitives/scroll-area/scroll-area.svelte +2 -2
  324. package/src/lib/components/primitives/search/search.svelte +3 -3
  325. package/src/lib/components/primitives/select/select-content.svelte +2 -2
  326. package/src/lib/components/primitives/select/select-group-heading.svelte +1 -1
  327. package/src/lib/components/primitives/select/select-item.svelte +2 -2
  328. package/src/lib/components/primitives/select/select-label.svelte +1 -1
  329. package/src/lib/components/primitives/select/select-scroll-down-button.svelte +1 -1
  330. package/src/lib/components/primitives/select/select-scroll-up-button.svelte +1 -1
  331. package/src/lib/components/primitives/select/select-separator.svelte +2 -2
  332. package/src/lib/components/primitives/select/select-trigger.svelte +2 -2
  333. package/src/lib/components/primitives/separator/separator.svelte +1 -1
  334. package/src/lib/components/primitives/slider/slider.svelte +9 -4
  335. package/src/lib/components/primitives/spinner/spinner.svelte +1 -1
  336. package/src/lib/components/primitives/switch/switch.svelte +2 -2
  337. package/src/lib/components/primitives/tabs/tabs-content.svelte +1 -1
  338. package/src/lib/components/primitives/tabs/tabs-list.svelte +3 -3
  339. package/src/lib/components/primitives/tabs/tabs-trigger.svelte +4 -4
  340. package/src/lib/components/primitives/tabs/tabs.svelte +1 -1
  341. package/src/lib/components/primitives/textarea/textarea.svelte +2 -2
  342. package/src/lib/components/primitives/toggle/toggle.svelte +12 -4
  343. package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +3 -3
  344. package/src/lib/components/primitives/toggle-group/toggle-group.svelte +3 -3
  345. package/src/lib/components/primitives/tooltip/index.ts +19 -0
  346. package/src/lib/components/primitives/tooltip/tooltip-content.svelte +34 -0
  347. package/src/lib/components/primitives/tooltip/tooltip-portal.svelte +7 -0
  348. package/src/lib/components/primitives/tooltip/tooltip-provider.svelte +7 -0
  349. package/src/lib/components/primitives/tooltip/tooltip-trigger.svelte +7 -0
  350. package/src/lib/components/primitives/tooltip/tooltip.svelte +7 -0
  351. package/src/lib/components/viewer/MeshMetadataPanel.svelte +119 -0
  352. package/src/lib/components/viewer/SceneManager.svelte +32 -21
  353. package/src/lib/components/viewer/Viewer.svelte +196 -208
  354. package/src/lib/components/viewer/ViewerDock.svelte +404 -0
  355. package/src/lib/composables/useFooterItem.svelte.ts +1 -1
  356. package/src/lib/compute/liveProgress.svelte.ts +103 -0
  357. package/src/lib/compute/run-panel-action.ts +16 -0
  358. package/src/lib/compute/solving.svelte.ts +66 -50
  359. package/src/lib/compute/useSolveSession.svelte.ts +3 -0
  360. package/src/lib/constants.ts +8 -11
  361. package/src/lib/contexts/hostContext.svelte.ts +51 -0
  362. package/src/lib/features/chart/plotly-compat.ts +141 -0
  363. package/src/lib/i18n/localeContext.svelte.ts +40 -5
  364. package/src/lib/i18n/messages.ts +487 -13
  365. package/src/lib/index.ts +23 -0
  366. package/src/lib/public.ts +2 -0
  367. package/src/lib/schema/host-support.ts +129 -0
  368. package/src/lib/schema/option-display.ts +44 -0
  369. package/src/lib/schema/param-exporter.ts +10 -7
  370. package/src/lib/schema/visibility-rules.ts +50 -1
  371. package/src/lib/styles/base.css +116 -83
  372. package/src/lib/types/actionButton.ts +16 -3
  373. package/src/lib/types/presetLabels.ts +13 -11
  374. package/src/lib/utils/color-space.ts +107 -0
  375. package/src/lib/utils/file-download.ts +11 -21
  376. package/src/lib/utils/host-files.ts +61 -0
  377. package/src/lib/utils/loadScript.ts +3 -0
  378. package/src/lib/utils/webp-to-png.ts +48 -0
  379. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
  380. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte.d.ts +0 -3
  381. package/dist/components/primitives/theme-switcher/index.d.ts +0 -1
  382. package/dist/components/primitives/theme-switcher/index.js +0 -1
  383. package/dist/components/viewer/MeshMetadataDialog.svelte +0 -126
  384. package/dist/components/viewer/MeshMetadataDialog.svelte.d.ts +0 -10
  385. package/dist/styles/themes/cyberpunk.css +0 -98
  386. package/dist/styles/themes/neutral.css +0 -72
  387. package/dist/styles/themes/ocean.css +0 -75
  388. package/dist/styles/themes/selva.css +0 -105
  389. package/dist/theme/themeStore.svelte.d.ts +0 -12
  390. package/dist/theme/themeStore.svelte.js +0 -40
  391. package/dist/theme/themes.d.ts +0 -5
  392. package/dist/theme/themes.js +0 -13
  393. package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +0 -39
  394. package/src/lib/components/primitives/theme-switcher/index.ts +0 -1
  395. package/src/lib/components/viewer/MeshMetadataDialog.svelte +0 -126
  396. package/src/lib/styles/themes/cyberpunk.css +0 -98
  397. package/src/lib/styles/themes/neutral.css +0 -72
  398. package/src/lib/styles/themes/ocean.css +0 -75
  399. package/src/lib/styles/themes/selva.css +0 -105
  400. package/src/lib/theme/themeStore.svelte.ts +0 -50
  401. package/src/lib/theme/themes.ts +0 -18
@@ -15,31 +15,18 @@
15
15
  type Grid,
16
16
  type ThreeViewer
17
17
  } from '@selvajs/visualization/render';
18
- import {
19
- Maximize,
20
- Minimize,
21
- Camera,
22
- Layers,
23
- Settings2,
24
- Box,
25
- Square,
26
- Frame,
27
- Ruler,
28
- Grid3x3,
29
- Palette,
30
- Spline
31
- } from '@lucide/svelte';
32
18
  import { SvelteSet } from 'svelte/reactivity';
33
19
  import type * as THREE from 'three';
34
20
  import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
35
21
  import { createSceneOutliner, type SceneOutliner } from '@selvajs/visualization/scene';
22
+ import { clearGeometryCache } from '@selvajs/visualization/parse';
36
23
  import SceneManager from './SceneManager.svelte';
37
- import MeshMetadataDialog from './MeshMetadataDialog.svelte';
38
- import * as Resizable from '$lib/components/primitives/resizable/index.js';
39
- import * as DropdownMenu from '$lib/components/primitives/dropdown-menu/index.js';
40
- import { Button } from '$lib/components/primitives/button/index.js';
41
- import type { Locale } from '$lib/i18n/messages';
42
- import { setLocaleContext, getLocaleContext } from '$lib/i18n/localeContext.svelte';
24
+ import ViewerDock from './ViewerDock.svelte';
25
+ import MeshMetadataPanel from './MeshMetadataPanel.svelte';
26
+ import * as Resizable from '#lib/components/primitives/resizable/index.js';
27
+ import type { Locale } from '#lib/i18n/messages.js';
28
+ import { layerLocaleContext } from '#lib/i18n/localeContext.svelte.js';
29
+ import { saveBlob } from '#lib/utils/host-files.js';
43
30
 
44
31
  export interface ViewerConfig {
45
32
  showScreenshotButton?: boolean;
@@ -50,6 +37,8 @@
50
37
  showDisplayMenu?: boolean;
51
38
  enableMeshClick?: boolean;
52
39
  backgroundColor?: string;
40
+ /** The look to open in. Default `technical`. */
41
+ look?: Look;
53
42
  /** Initial state of the edge overlay and its display-menu checkmark. Default false. */
54
43
  showEdges?: boolean;
55
44
  }
@@ -57,7 +46,10 @@
57
46
  interface Props {
58
47
  meshes: any[];
59
48
  isFullscreen?: boolean;
49
+ /** @deprecated Ignored: the panel's solve status shows a running solve. */
60
50
  isSolving?: boolean;
51
+ /** @deprecated Ignored, like `isSolving`. */
52
+ isSlow?: boolean;
61
53
  isBlurred?: boolean;
62
54
  drawerOpen?: boolean;
63
55
  viewerConfig?: ViewerConfig;
@@ -86,13 +78,13 @@
86
78
  showDisplayMenu: true,
87
79
  enableMeshClick: true,
88
80
  backgroundColor: '#E6E6E6',
81
+ look: 'technical',
89
82
  showEdges: false
90
83
  };
91
84
 
92
85
  let {
93
86
  meshes,
94
87
  isFullscreen = $bindable(false),
95
- isSolving = false,
96
88
  isBlurred = false,
97
89
  drawerOpen = false,
98
90
  viewerConfig = {},
@@ -103,12 +95,8 @@
103
95
 
104
96
  const config = $derived({ ...defaultViewerConfig, ...viewerConfig });
105
97
 
106
- // Read the host's locale before overriding it for our subtree.
107
- const hostLocale = getLocaleContext();
108
-
109
98
  // A getter, not a value: it is re-read reactively, so changing `lang` updates the chrome live.
110
- setLocaleContext(() => lang ?? hostLocale.locale);
111
- const locale = getLocaleContext();
99
+ const locale = layerLocaleContext(() => lang);
112
100
  const t = $derived(locale.messages);
113
101
 
114
102
  let canvas: HTMLCanvasElement;
@@ -122,10 +110,14 @@
122
110
  let clearEdges: ThreeViewer['clearEdges'] | null = null;
123
111
  let invalidate: (() => void) | null = null;
124
112
  let captureImage: ThreeViewer['captureImage'] | null = null;
125
- let setLook: ((look: Look) => void) | null = null;
113
+ let setLook: ThreeViewer['setLook'] | null = null;
114
+ let syncEmissiveLights: (() => void) | null = null;
126
115
  let updateGridScale: (() => void) | null = null;
127
116
  let updateShadowBounds: (() => void) | null = null;
128
117
  let fitToView: (() => void) | null = null;
118
+ let clearSelection: (() => void) | null = null;
119
+ let selectEntries: ThreeViewer['selectEntries'] | null = null;
120
+ let setHover: ThreeViewer['setHover'] | null = null;
129
121
  let viewerInitialized = false;
130
122
  let sceneVersion = $state(0);
131
123
  let hideButton = $state(false);
@@ -140,22 +132,42 @@
140
132
  let projection: CameraProjection = $state('perspective');
141
133
  let measureActive = $state(false);
142
134
  let gridVisible = $state(false);
143
- let renderStyle: Look = $state('technical');
135
+ // A saved schema can name a look this build doesn't have, and an unknown look throws on mount.
136
+ const openingLook = untrack(() => {
137
+ const look = viewerConfig.look;
138
+ return look && LOOKS.includes(look) ? look : defaultViewerConfig.look;
139
+ });
140
+ const opensAsLineDrawing = LOOK_PRESETS[openingLook].requiresEdges === true;
141
+ const configuredEdges = untrack(() => viewerConfig.showEdges) ?? defaultViewerConfig.showEdges;
142
+ let renderStyle: Look = $state(openingLook);
144
143
  // Seeded once, then owned by the menu toggle.
145
- let edgesVisible = $state(untrack(() => viewerConfig.showEdges) ?? defaultViewerConfig.showEdges);
144
+ let edgesVisible = $state(opensAsLineDrawing || configuredEdges);
146
145
  let selectedMeshMetadata: Record<string, any> | null = $state(null);
147
146
  let selectedMeshName: string | null = $state(null);
148
147
 
149
- // Derived from LOOKS so adding a look in @selvajs/visualization shows up here with no edit; the
150
- // map only overrides the names that don't survive capitalisation ('xray' → 'X-Ray').
151
- const LOOK_LABELS: Partial<Record<Look, string>> = {
152
- xray: 'X-Ray',
153
- lineart: 'Line Art'
154
- };
155
- const STYLE_OPTIONS: { look: Look; label: string }[] = LOOKS.map((look) => ({
156
- look,
157
- label: LOOK_LABELS[look] ?? look.charAt(0).toUpperCase() + look.slice(1)
158
- }));
148
+ // Derived from LOOKS so a look added in @selvajs/visualization shows up here with no edit,
149
+ // under its capitalised name until it gets a message.
150
+ const lookLabels = $derived<Partial<Record<Look, string>>>({
151
+ technical: t.lookTechnical,
152
+ studio: t.lookStudio,
153
+ showcase: t.lookShowcase,
154
+ arctic: t.lookArctic,
155
+ xray: t.lookXray,
156
+ lineart: t.lookLineart,
157
+ wireframe: t.lookWireframe,
158
+ night: t.lookNight
159
+ });
160
+ // Night has its own toggle, so it stays out of the radio list.
161
+ const styleOptions = $derived<{ look: Look; label: string }[]>(
162
+ LOOKS.filter((look) => look !== 'night').map((look) => ({
163
+ look,
164
+ label: lookLabels[look] ?? look.charAt(0).toUpperCase() + look.slice(1)
165
+ }))
166
+ );
167
+ const nightOn = $derived(renderStyle === 'night');
168
+
169
+ // What the night toggle returns to.
170
+ let dayLook: Look = $state(openingLook === 'night' ? defaultViewerConfig.look : openingLook);
159
171
 
160
172
  const VIEW_PRESETS: { preset: ViewPreset; label: () => string }[] = [
161
173
  { preset: 'top', label: () => t.viewTop },
@@ -191,6 +203,10 @@
191
203
  grid: { enabled: config.showToolsMenu && config.showGridToggle },
192
204
  measure: { enabled: config.showToolsMenu },
193
205
  events: {
206
+ // Fires before onMeshMetadataClicked, so picking an object without metadata closes the
207
+ // previous object's panel instead of leaving it describing the wrong thing.
208
+ onObjectSelected: dropPanelSelection,
209
+ onBackgroundClicked: dropPanelSelection,
194
210
  onMeshMetadataClicked: config.enableMeshClick
195
211
  ? (metadata: Record<string, unknown>) => {
196
212
  if (hasUsefulMetadata(metadata)) {
@@ -218,24 +234,25 @@
218
234
  invalidate = init.invalidate;
219
235
  captureImage = init.captureImage;
220
236
  setLook = init.setLook;
237
+ syncEmissiveLights = init.syncEmissiveLights;
221
238
  updateGridScale = init.updateGridScale;
222
239
  updateShadowBounds = init.updateShadowBounds;
223
240
  fitToView = init.fitToView;
241
+ clearSelection = init.clearSelection;
242
+ selectEntries = init.selectEntries;
243
+ setHover = init.setHover;
224
244
  projection = init.cameraController.getProjection();
225
245
 
226
- const renderer = init.renderer;
227
-
228
246
  // Untracked so the host reading `meshes` or config inside its setup can't re-run onMount's teardown.
229
247
  const hostCleanup = untrack(() => onViewerReady?.(init));
230
248
 
231
249
  return () => {
232
250
  // Before dispose, so the host can still remove its own objects from a live scene.
233
251
  hostCleanup?.();
252
+ // Also frees the GL context: a second forceContextLoss() here hits an already-lost one.
234
253
  init.dispose();
235
- // `{#key definitionKey}` recreates the canvas + WebGLRenderer + GL context
236
- // on every definition switch; browsers cap live contexts (~16). Explicitly
237
- // drop this one so the GPU-side context is released now rather than at GC.
238
- renderer.forceContextLoss();
254
+ // Parsed geometry outlives the viewer otherwise: the cache is page-wide.
255
+ clearGeometryCache();
239
256
  };
240
257
  });
241
258
 
@@ -263,12 +280,13 @@
263
280
 
264
281
  // Edges the lineart look switched on, so leaving it can put the user's own choice back rather
265
282
  // than stranding them with overlays they never asked for.
266
- let edgesBeforeLineDrawing: boolean | null = null;
283
+ let edgesBeforeLineDrawing: boolean | null = opensAsLineDrawing ? configuredEdges : null;
267
284
 
268
285
  function setRenderStyle(look: Look) {
269
286
  if (!setLook || look === renderStyle) return;
270
287
  const wasLineDrawing = LOOK_PRESETS[renderStyle].requiresEdges === true;
271
288
  renderStyle = look;
289
+ if (look !== 'night') dayLook = look;
272
290
  setLook(look);
273
291
 
274
292
  // A look never drives the overlay itself; honouring `requiresEdges` is the host's call. Without
@@ -334,6 +352,10 @@
334
352
  else clearEdges?.(scene);
335
353
  }
336
354
 
355
+ function toggleNight() {
356
+ setRenderStyle(nightOn ? dayLook : 'night');
357
+ }
358
+
337
359
  function toggleEdges() {
338
360
  edgesVisible = !edgesVisible;
339
361
  // Toggling by hand inside lineart overrides what that look forced, so the pre-look value is
@@ -350,7 +372,8 @@
350
372
  untrack(() => {
351
373
  // The new meshes carry the materials the parser built, so a look that overrides them
352
374
  // (arctic, x-ray) has to be re-applied or the solve silently reverts to shaded.
353
- setLook?.(renderStyle);
375
+ // Emissive lights wait for `applyTo` below, or a hidden lamp would count as lit.
376
+ setLook?.(renderStyle, { deferEmissiveLights: true });
354
377
  // updateScene discarded the previous solve's overlays along with its content.
355
378
  if (edgesVisible) applyEdges?.(scene!, edgeOverrides());
356
379
  // Rescale the grid so cells and fade match the new content's extent.
@@ -361,6 +384,7 @@
361
384
  // The rebuild un-hid everything; the outliner keys hidden state on Grasshopper
362
385
  // identity, not on the instances just discarded, so it can re-hide it.
363
386
  outliner?.applyTo();
387
+ syncEmissiveLights?.();
364
388
  sceneVersion++;
365
389
  // Repaint now rather than on the render loop's safety interval.
366
390
  invalidate?.();
@@ -372,10 +396,72 @@
372
396
  }
373
397
  });
374
398
 
399
+ function closeMetadata() {
400
+ selectedMeshMetadata = null;
401
+ selectedMeshName = null;
402
+ }
403
+
404
+ // A click on the canvas replaces whatever the panel selected: one selection at a time.
405
+ function dropPanelSelection() {
406
+ closeMetadata();
407
+ selectedObjects.clear();
408
+ }
409
+
410
+ // Mirrors the panel's selection into the viewport. The size check skips walking the entries for
411
+ // the common empty case, which also keeps `has()` from subscribing to every key.
412
+ $effect(() => {
413
+ if (selectedObjects.size === 0) {
414
+ selectEntries?.([]);
415
+ return;
416
+ }
417
+ const chosen = outliner?.entries().filter((entry) => selectedObjects.has(entry.key)) ?? [];
418
+ closeMetadata();
419
+ selectEntries?.(chosen);
420
+ });
421
+
422
+ function dismissMetadata() {
423
+ closeMetadata();
424
+ clearSelection?.();
425
+ invalidate?.();
426
+ }
427
+
428
+ // The panel describes an object from the previous solve once new meshes arrive.
429
+ $effect(() => {
430
+ void meshes;
431
+ untrack(closeMetadata);
432
+ });
433
+
434
+ // Dialogs portal to <body>, so one opened over the fullscreen viewer needs lifting above it.
435
+ $effect(() => {
436
+ document.body.toggleAttribute('data-viewer-fullscreen', isFullscreen);
437
+ return () => document.body.removeAttribute('data-viewer-fullscreen');
438
+ });
439
+
375
440
  function toggleFullscreen() {
376
441
  isFullscreen = !isFullscreen;
377
442
  }
378
443
 
444
+ // Single letters; F (fit) is handled by the canvas itself. Held back while typing, e.g. in the
445
+ // scene manager's search box.
446
+ const SHORTCUTS: Record<string, () => void> = {
447
+ o: toggleProjection,
448
+ m: toggleMeasure,
449
+ g: toggleGrid,
450
+ n: toggleNight,
451
+ e: toggleEdges,
452
+ s: () => (sceneManagerOpen = !sceneManagerOpen)
453
+ };
454
+
455
+ function handleShortcut(event: KeyboardEvent) {
456
+ if (event.ctrlKey || event.metaKey || event.altKey || event.repeat) return;
457
+ const target = event.target as HTMLElement;
458
+ if (target.closest('input, textarea, select, [contenteditable]')) return;
459
+ const action = SHORTCUTS[event.key.toLowerCase()];
460
+ if (!action) return;
461
+ event.preventDefault();
462
+ action();
463
+ }
464
+
379
465
  function hasUsefulMetadata(metadata: Record<string, any>): boolean {
380
466
  if (!metadata) return false;
381
467
 
@@ -401,22 +487,18 @@
401
487
  const blob = await captureImage?.();
402
488
  if (!blob) return;
403
489
 
404
- const url = URL.createObjectURL(blob);
405
- const link = document.createElement('a');
406
490
  const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
407
- link.href = url;
408
- link.download = `viewer-${timestamp}.png`;
409
- document.body.appendChild(link);
410
- link.click();
411
- document.body.removeChild(link);
412
- URL.revokeObjectURL(url);
491
+ await saveBlob(blob, `viewer-${timestamp}.png`);
413
492
  }
414
493
  </script>
415
494
 
495
+ <!-- Key events bubble up from the focused canvas or any control inside; the div itself is not interactive. -->
496
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
416
497
  <div
417
498
  class="min-h-64 sm:min-h-96 lg:min-h-125 relative h-full flex-1 border {isFullscreen
418
499
  ? 'fullscreen-viewer'
419
500
  : 'overflow-hidden rounded-lg'}"
501
+ onkeydown={handleShortcut}
420
502
  >
421
503
  <Resizable.PaneGroup direction="horizontal" class="h-full w-full">
422
504
  <!-- `defaultSize` must sum to 100 across the live panes. Panes register a frame before the
@@ -430,7 +512,7 @@
430
512
  the DOM, so this is the only observable proof a solve's geometry decoded and
431
513
  rendered; e2e asserts on it. -->
432
514
  <canvas
433
- class="block h-full w-full"
515
+ class="viewer-canvas block h-full w-full outline-none select-none"
434
516
  data-testid="viewer-canvas"
435
517
  data-mesh-count={meshes.length}
436
518
  bind:this={canvas}
@@ -442,17 +524,10 @@
442
524
  : 'blur-overlay-inactive'}"
443
525
  ></div>
444
526
 
445
- {#if isSolving}
446
- <div class="inset-0 animate-in fade-in backdrop-blur-xs absolute z-10 duration-200"></div>
447
- {/if}
448
-
449
527
  {#if logoUrl}
450
- <!-- Bottom offset matches the tools menu so it clears the mobile drawer handle.
451
- Always pointer-events:none so it never intercepts canvas drags. -->
528
+ <!-- pointer-events:none so it never intercepts canvas drags. -->
452
529
  <div
453
- class="right-4 {isFullscreen
454
- ? 'bottom-4'
455
- : 'bottom-16 sm:bottom-4'} pointer-events-none absolute z-20 {hideButton
530
+ class="right-4 bottom-4 pointer-events-none absolute z-20 {hideButton
456
531
  ? 'opacity-0'
457
532
  : ''}"
458
533
  >
@@ -464,134 +539,46 @@
464
539
  </div>
465
540
  {/if}
466
541
 
542
+ {#if selectedMeshMetadata}
543
+ <MeshMetadataPanel
544
+ metadata={selectedMeshMetadata}
545
+ meshName={selectedMeshName}
546
+ onClose={dismissMetadata}
547
+ 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"
548
+ />
549
+ {/if}
550
+
467
551
  {#if config.showToolsMenu}
468
- <div
469
- class="left-4 {isFullscreen
470
- ? 'bottom-4'
471
- : 'bottom-16 sm:bottom-4'} shadow-lg absolute z-50 flex items-center rounded-lg {hideButton
472
- ? 'pointer-events-none opacity-0'
473
- : ''}"
474
- >
475
- <DropdownMenu.Root>
476
- <DropdownMenu.Trigger>
477
- {#snippet child({ props })}
478
- <Button
479
- {...props}
480
- variant="outline"
481
- size="icon-lg"
482
- title={t.toolsMenu}
483
- aria-label={t.toolsMenu}
484
- >
485
- <Settings2 />
486
- </Button>
487
- {/snippet}
488
- </DropdownMenu.Trigger>
489
- <!-- Above the fullscreen viewer, which sits at z-index 10000. -->
490
- <DropdownMenu.Content sideOffset={6} class="min-w-44 z-10001 w-auto">
491
- <DropdownMenu.Item onSelect={toggleProjection}>
492
- {#if projection === 'perspective'}
493
- <Square />
494
- {t.switchToOrthographic}
495
- {:else}
496
- <Box />
497
- {t.switchToPerspective}
498
- {/if}
499
- </DropdownMenu.Item>
500
- <DropdownMenu.Item onSelect={() => fitToView?.()}>
501
- <Frame />
502
- {t.fitToView}
503
- </DropdownMenu.Item>
504
-
505
- <DropdownMenu.Sub>
506
- <DropdownMenu.SubTrigger>
507
- <Box />
508
- {t.views}
509
- </DropdownMenu.SubTrigger>
510
- <DropdownMenu.SubContent class="z-10001">
511
- {#each VIEW_PRESETS as { preset, label } (preset)}
512
- <DropdownMenu.Item onSelect={() => setView(preset)}
513
- >{label()}</DropdownMenu.Item
514
- >
515
- {/each}
516
- </DropdownMenu.SubContent>
517
- </DropdownMenu.Sub>
518
-
519
- {#if config.showDisplayMenu}
520
- <DropdownMenu.Sub>
521
- <DropdownMenu.SubTrigger>
522
- <Palette />
523
- {t.display}
524
- </DropdownMenu.SubTrigger>
525
- <DropdownMenu.SubContent class="min-w-40 z-10001">
526
- <DropdownMenu.RadioGroup
527
- value={renderStyle}
528
- onValueChange={(look) => setRenderStyle(look as typeof renderStyle)}
529
- >
530
- {#each STYLE_OPTIONS as { look, label } (look)}
531
- <DropdownMenu.RadioItem value={look} closeOnSelect={false}>
532
- {label}
533
- </DropdownMenu.RadioItem>
534
- {/each}
535
- </DropdownMenu.RadioGroup>
536
- <DropdownMenu.Separator />
537
- <DropdownMenu.CheckboxItem
538
- checked={edgesVisible}
539
- onCheckedChange={toggleEdges}
540
- >
541
- <Spline />
542
- {t.edges}
543
- </DropdownMenu.CheckboxItem>
544
- </DropdownMenu.SubContent>
545
- </DropdownMenu.Sub>
546
- {/if}
547
-
548
- <DropdownMenu.CheckboxItem checked={measureActive} onCheckedChange={toggleMeasure}>
549
- <Ruler />
550
- {t.measure}
551
- </DropdownMenu.CheckboxItem>
552
-
553
- {#if config.showGridToggle}
554
- <DropdownMenu.CheckboxItem checked={gridVisible} onCheckedChange={toggleGrid}>
555
- <Grid3x3 />
556
- {t.grid}
557
- </DropdownMenu.CheckboxItem>
558
- {/if}
559
-
560
- {#if config.showSceneManager || config.showScreenshotButton || config.showFullscreenButton}
561
- <DropdownMenu.Separator />
562
- {/if}
563
-
564
- {#if config.showSceneManager}
565
- <DropdownMenu.CheckboxItem
566
- checked={sceneManagerOpen}
567
- onCheckedChange={() => (sceneManagerOpen = !sceneManagerOpen)}
568
- >
569
- <Layers />
570
- {t.sceneManager}
571
- </DropdownMenu.CheckboxItem>
572
- {/if}
573
-
574
- {#if config.showScreenshotButton}
575
- <DropdownMenu.Item onSelect={downloadScreenshot}>
576
- <Camera />
577
- {t.screenshot}
578
- </DropdownMenu.Item>
579
- {/if}
580
-
581
- {#if config.showFullscreenButton}
582
- <DropdownMenu.Item onSelect={toggleFullscreen}>
583
- {#if isFullscreen}
584
- <Minimize />
585
- {t.exitFullscreen}
586
- {:else}
587
- <Maximize />
588
- {t.fullscreen}
589
- {/if}
590
- </DropdownMenu.Item>
591
- {/if}
592
- </DropdownMenu.Content>
593
- </DropdownMenu.Root>
594
- </div>
552
+ <ViewerDock
553
+ {t}
554
+ hidden={hideButton}
555
+ showDisplay={config.showDisplayMenu}
556
+ showGrid={config.showGridToggle}
557
+ showSceneManager={config.showSceneManager}
558
+ showScreenshot={config.showScreenshotButton}
559
+ showFullscreen={config.showFullscreenButton}
560
+ {styleOptions}
561
+ viewPresets={VIEW_PRESETS.map(({ preset, label }) => ({ preset, label: label() }))}
562
+ {renderStyle}
563
+ {nightOn}
564
+ {edgesVisible}
565
+ {measureActive}
566
+ {gridVisible}
567
+ {sceneManagerOpen}
568
+ {projection}
569
+ {isFullscreen}
570
+ onSetLook={setRenderStyle}
571
+ onToggleNight={toggleNight}
572
+ onToggleEdges={toggleEdges}
573
+ onToggleMeasure={toggleMeasure}
574
+ onToggleGrid={toggleGrid}
575
+ onToggleSceneManager={() => (sceneManagerOpen = !sceneManagerOpen)}
576
+ onToggleProjection={toggleProjection}
577
+ onSetView={setView}
578
+ onFit={() => fitToView?.()}
579
+ onScreenshot={downloadScreenshot}
580
+ onToggleFullscreen={toggleFullscreen}
581
+ />
595
582
  {/if}
596
583
  </div>
597
584
  </Resizable.Pane>
@@ -599,28 +586,23 @@
599
586
  {#if sceneManagerOpen && scene && outliner}
600
587
  <Resizable.Handle withHandle />
601
588
  <Resizable.Pane id="scene-manager" order={2} defaultSize={15} minSize={8} maxSize={30}>
602
- <SceneManager {outliner} {sceneVersion} onVisibilityChange={() => invalidate?.()} />
589
+ <SceneManager
590
+ {outliner}
591
+ {sceneVersion}
592
+ onVisibilityChange={() => syncEmissiveLights?.()}
593
+ onHover={(entry) => setHover?.(entry?.object ?? null, entry?.memberIndex ?? null)}
594
+ />
603
595
  </Resizable.Pane>
604
596
  {/if}
605
597
  </Resizable.PaneGroup>
606
598
  </div>
607
599
 
608
- <MeshMetadataDialog
609
- open={selectedMeshMetadata !== null}
610
- metadata={selectedMeshMetadata}
611
- meshName={selectedMeshName}
612
- {isFullscreen}
613
- onOpenChange={(open: boolean) => {
614
- if (!open) {
615
- setTimeout(() => {
616
- selectedMeshMetadata = null;
617
- selectedMeshName = null;
618
- }, 200);
619
- }
620
- }}
621
- />
622
-
623
600
  <style>
601
+ :global([data-viewer-fullscreen] [data-slot='dialog-content']),
602
+ :global([data-viewer-fullscreen] [data-slot='dialog-overlay']) {
603
+ z-index: 10001;
604
+ }
605
+
624
606
  .fullscreen-viewer {
625
607
  position: fixed;
626
608
  inset: 0;
@@ -628,6 +610,12 @@
628
610
  border-radius: 0 !important;
629
611
  }
630
612
 
613
+ /* A long press on touch otherwise selects the canvas as an image and opens the save/copy callout. */
614
+ .viewer-canvas {
615
+ -webkit-touch-callout: none;
616
+ -webkit-tap-highlight-color: transparent;
617
+ }
618
+
631
619
  .blur-overlay {
632
620
  pointer-events: none;
633
621
  }