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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/dist/components/ConfirmDialog.svelte +2 -10
  2. package/dist/components/ErrorScreen.svelte +21 -27
  3. package/dist/components/compute/AppLayout.svelte +298 -128
  4. package/dist/components/compute/AppLayout.svelte.d.ts +22 -0
  5. package/dist/components/compute/CollapsedPanelStrip.svelte +4 -2
  6. package/dist/components/compute/ComputeApp.svelte +45 -24
  7. package/dist/components/compute/ComputeApp.svelte.d.ts +19 -2
  8. package/dist/components/compute/ComputeMessagesDialog.svelte +47 -83
  9. package/dist/components/compute/ParameterPresetManager.svelte +37 -130
  10. package/dist/components/compute/ParameterPresetManager.svelte.d.ts +4 -5
  11. package/dist/components/compute/SolveMessages.svelte +131 -77
  12. package/dist/components/compute/SolveMessages.svelte.d.ts +7 -12
  13. package/dist/components/layout/AppShell.svelte +10 -6
  14. package/dist/components/layout/PageFooter.svelte +23 -11
  15. package/dist/components/layout/PageHeader.svelte +11 -7
  16. package/dist/components/layout/Pagination.svelte +2 -4
  17. package/dist/components/layout/SectionHeader.svelte +4 -2
  18. package/dist/components/layout/SideNav.svelte +56 -7
  19. package/dist/components/preview/ChartOutput.svelte +91 -48
  20. package/dist/components/preview/Group.svelte +92 -103
  21. package/dist/components/preview/Group.svelte.d.ts +1 -1
  22. package/dist/components/preview/HelpButton.svelte +36 -0
  23. package/dist/components/preview/HelpButton.svelte.d.ts +8 -0
  24. package/dist/components/preview/ImageOutput.svelte +434 -72
  25. package/dist/components/preview/InputControl.svelte +163 -90
  26. package/dist/components/preview/InputControl.svelte.d.ts +5 -1
  27. package/dist/components/preview/OutputDisplay.svelte +44 -42
  28. package/dist/components/preview/TabBar.svelte +106 -22
  29. package/dist/components/preview/TabBar.svelte.d.ts +1 -0
  30. package/dist/components/preview/TabContent.svelte +63 -57
  31. package/dist/components/preview/TabContent.svelte.d.ts +5 -1
  32. package/dist/components/preview/TabLayout.svelte +27 -5
  33. package/dist/components/preview/TabLayout.svelte.d.ts +5 -0
  34. package/dist/components/preview/inputs/CheckboxInput.svelte +7 -7
  35. package/dist/components/preview/inputs/CheckboxInput.svelte.d.ts +3 -0
  36. package/dist/components/preview/inputs/ChecklistInput.svelte +7 -2
  37. package/dist/components/preview/inputs/ChecklistInput.svelte.d.ts +2 -1
  38. package/dist/components/preview/inputs/ColorInput.svelte +4 -2
  39. package/dist/components/preview/inputs/ColorInput.svelte.d.ts +1 -0
  40. package/dist/components/preview/inputs/DropdownInput.svelte +19 -4
  41. package/dist/components/preview/inputs/DropdownInput.svelte.d.ts +2 -1
  42. package/dist/components/preview/inputs/FileInput.svelte +112 -108
  43. package/dist/components/preview/inputs/GeometryInput.svelte +128 -0
  44. package/dist/components/preview/inputs/GeometryInput.svelte.d.ts +21 -0
  45. package/dist/components/preview/inputs/NumberInput.svelte +123 -61
  46. package/dist/components/preview/inputs/NumberInput.svelte.d.ts +2 -0
  47. package/dist/components/preview/inputs/OptionGlyph.svelte +29 -0
  48. package/dist/components/preview/inputs/OptionGlyph.svelte.d.ts +8 -0
  49. package/dist/components/preview/inputs/SegmentedInput.svelte +44 -0
  50. package/dist/components/preview/inputs/SegmentedInput.svelte.d.ts +12 -0
  51. package/dist/components/preview/inputs/TextInput.svelte +8 -4
  52. package/dist/components/preview/inputs/TileInput.svelte +80 -0
  53. package/dist/components/preview/inputs/TileInput.svelte.d.ts +12 -0
  54. package/dist/components/preview/inputs/index.d.ts +3 -0
  55. package/dist/components/preview/inputs/index.js +3 -0
  56. package/dist/components/primitives/CalculateButton.svelte +56 -24
  57. package/dist/components/primitives/CalculateButton.svelte.d.ts +3 -0
  58. package/dist/components/primitives/Callout.svelte +11 -18
  59. package/dist/components/primitives/DataTable.svelte +38 -5
  60. package/dist/components/primitives/DataTable.svelte.d.ts +2 -0
  61. package/dist/components/primitives/FilterableDropdown.svelte +42 -129
  62. package/dist/components/primitives/FilterableDropdown.svelte.d.ts +1 -1
  63. package/dist/components/primitives/IssueList.svelte +56 -0
  64. package/dist/components/primitives/IssueList.svelte.d.ts +20 -0
  65. package/dist/components/primitives/SeverityIcon.svelte +15 -0
  66. package/dist/components/primitives/SeverityIcon.svelte.d.ts +8 -0
  67. package/dist/components/primitives/SolvingIndicator.svelte +43 -42
  68. package/dist/components/primitives/SolvingIndicator.svelte.d.ts +6 -0
  69. package/dist/components/primitives/StateDisplay.svelte +19 -79
  70. package/dist/components/primitives/ViewToggle.svelte +18 -33
  71. package/dist/components/primitives/alert-dialog/alert-dialog-action.svelte +9 -3
  72. package/dist/components/primitives/alert-dialog/alert-dialog-action.svelte.d.ts +6 -1
  73. package/dist/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  74. package/dist/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  75. package/dist/components/primitives/badge/badge.svelte +2 -2
  76. package/dist/components/primitives/badge/badge.svelte.d.ts +4 -4
  77. package/dist/components/primitives/button/button.svelte +6 -2
  78. package/dist/components/primitives/button/button.svelte.d.ts +10 -4
  79. package/dist/components/primitives/card/card-header.svelte +1 -1
  80. package/dist/components/primitives/card/card.svelte +21 -5
  81. package/dist/components/primitives/card/card.svelte.d.ts +25 -1
  82. package/dist/components/primitives/card/index.d.ts +1 -2
  83. package/dist/components/primitives/card/index.js +2 -3
  84. package/dist/components/primitives/checkbox/checkbox.svelte +1 -1
  85. package/dist/components/primitives/command/command-dialog.svelte +42 -0
  86. package/dist/components/primitives/command/command-dialog.svelte.d.ts +14 -0
  87. package/dist/components/primitives/command/command-empty.svelte +17 -0
  88. package/dist/components/primitives/command/command-empty.svelte.d.ts +4 -0
  89. package/dist/components/primitives/command/command-group.svelte +33 -0
  90. package/dist/components/primitives/command/command-group.svelte.d.ts +7 -0
  91. package/dist/components/primitives/command/command-input.svelte +36 -0
  92. package/dist/components/primitives/command/command-input.svelte.d.ts +4 -0
  93. package/dist/components/primitives/command/command-item.svelte +28 -0
  94. package/dist/components/primitives/command/command-item.svelte.d.ts +4 -0
  95. package/dist/components/primitives/command/command-link-item.svelte +21 -0
  96. package/dist/components/primitives/command/command-link-item.svelte.d.ts +4 -0
  97. package/dist/components/primitives/command/command-list.svelte +20 -0
  98. package/dist/components/primitives/command/command-list.svelte.d.ts +4 -0
  99. package/dist/components/primitives/command/command-loading.svelte +7 -0
  100. package/dist/components/primitives/command/command-loading.svelte.d.ts +4 -0
  101. package/dist/components/primitives/command/command-separator.svelte +17 -0
  102. package/dist/components/primitives/command/command-separator.svelte.d.ts +4 -0
  103. package/dist/components/primitives/command/command-shortcut.svelte +23 -0
  104. package/dist/components/primitives/command/command-shortcut.svelte.d.ts +5 -0
  105. package/dist/components/primitives/command/command.svelte +28 -0
  106. package/dist/components/primitives/command/command.svelte.d.ts +8 -0
  107. package/dist/components/primitives/command/index.d.ts +12 -0
  108. package/dist/components/primitives/command/index.js +14 -0
  109. package/dist/components/primitives/context-menu/context-menu-content.svelte +1 -1
  110. package/dist/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  111. package/dist/components/primitives/dialog/dialog-content.svelte +11 -3
  112. package/dist/components/primitives/dialog/dialog-content.svelte.d.ts +2 -0
  113. package/dist/components/primitives/dialog/dialog-overlay.svelte +1 -1
  114. package/dist/components/primitives/dialog/dialog-title.svelte +6 -2
  115. package/dist/components/primitives/dialog/dialog-title.svelte.d.ts +5 -1
  116. package/dist/components/primitives/drawer/Drawer.svelte +2 -2
  117. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  118. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
  119. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  120. package/dist/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
  121. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte +31 -0
  122. package/dist/components/primitives/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
  123. package/dist/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  124. package/dist/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
  125. package/dist/components/primitives/dropdown-menu/dropdown-menu-group.svelte +7 -0
  126. package/dist/components/primitives/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
  127. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte +27 -0
  128. package/dist/components/primitives/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
  129. package/dist/components/primitives/dropdown-menu/dropdown-menu-label.svelte +24 -0
  130. package/dist/components/primitives/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
  131. package/dist/components/primitives/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  132. package/dist/components/primitives/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
  133. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  134. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
  135. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  136. package/dist/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
  137. package/dist/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  138. package/dist/components/primitives/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
  139. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  140. package/dist/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
  141. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  142. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +10 -0
  143. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  144. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
  145. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  146. package/dist/components/primitives/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
  147. package/dist/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  148. package/dist/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
  149. package/dist/components/primitives/dropdown-menu/dropdown-menu.svelte +7 -0
  150. package/dist/components/primitives/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
  151. package/dist/components/primitives/dropdown-menu/index.d.ts +18 -0
  152. package/dist/components/primitives/dropdown-menu/index.js +18 -0
  153. package/dist/components/primitives/{alert/alert-description.svelte → empty/empty-content.svelte} +3 -3
  154. package/dist/components/primitives/empty/empty-content.svelte.d.ts +5 -0
  155. package/dist/components/primitives/empty/empty-description.svelte +23 -0
  156. package/dist/components/primitives/{alert/alert-description.svelte.d.ts → empty/empty-description.svelte.d.ts} +4 -4
  157. package/dist/components/primitives/{alert/alert-title.svelte → empty/empty-header.svelte} +3 -3
  158. package/dist/components/primitives/empty/empty-header.svelte.d.ts +5 -0
  159. package/dist/components/primitives/empty/empty-media.svelte +41 -0
  160. package/dist/components/primitives/empty/empty-media.svelte.d.ts +26 -0
  161. package/dist/components/primitives/{card/card-action.svelte → empty/empty-title.svelte} +2 -2
  162. package/dist/components/primitives/{card/card-action.svelte.d.ts → empty/empty-title.svelte.d.ts} +3 -3
  163. package/dist/components/primitives/empty/empty.svelte +23 -0
  164. package/dist/components/primitives/empty/empty.svelte.d.ts +5 -0
  165. package/dist/components/primitives/empty/index.d.ts +7 -0
  166. package/dist/components/primitives/empty/index.js +9 -0
  167. package/dist/components/primitives/index.d.ts +10 -1
  168. package/dist/components/primitives/index.js +10 -1
  169. package/dist/components/primitives/input/input.svelte +4 -4
  170. package/dist/components/primitives/input-group/index.d.ts +7 -0
  171. package/dist/components/primitives/input-group/index.js +9 -0
  172. package/dist/components/primitives/input-group/input-group-addon.svelte +53 -0
  173. package/dist/components/primitives/input-group/input-group-addon.svelte.d.ts +32 -0
  174. package/dist/components/primitives/input-group/input-group-button.svelte +49 -0
  175. package/dist/components/primitives/input-group/input-group-button.svelte.d.ts +32 -0
  176. package/dist/components/primitives/input-group/input-group-input.svelte +25 -0
  177. package/dist/components/primitives/input-group/input-group-input.svelte.d.ts +11 -0
  178. package/dist/components/primitives/input-group/input-group-text.svelte +22 -0
  179. package/dist/components/primitives/input-group/input-group-text.svelte.d.ts +5 -0
  180. package/dist/components/primitives/input-group/input-group-textarea.svelte +23 -0
  181. package/dist/components/primitives/input-group/input-group-textarea.svelte.d.ts +3 -0
  182. package/dist/components/primitives/input-group/input-group.svelte +24 -0
  183. package/dist/components/primitives/{alert/alert-title.svelte.d.ts → input-group/input-group.svelte.d.ts} +4 -4
  184. package/dist/components/primitives/mode-toggle/mode-toggle.svelte +6 -7
  185. package/dist/components/primitives/popover/index.d.ts +9 -0
  186. package/dist/components/primitives/popover/index.js +11 -0
  187. package/dist/components/primitives/popover/popover-close.svelte +7 -0
  188. package/dist/components/primitives/popover/popover-close.svelte.d.ts +4 -0
  189. package/dist/components/primitives/popover/popover-content.svelte +31 -0
  190. package/dist/components/primitives/popover/popover-content.svelte.d.ts +10 -0
  191. package/dist/components/primitives/popover/popover-description.svelte +20 -0
  192. package/dist/components/primitives/popover/popover-description.svelte.d.ts +5 -0
  193. package/dist/components/primitives/popover/popover-header.svelte +20 -0
  194. package/dist/components/primitives/popover/popover-header.svelte.d.ts +5 -0
  195. package/dist/components/primitives/popover/popover-portal.svelte +7 -0
  196. package/dist/components/primitives/popover/popover-portal.svelte.d.ts +3 -0
  197. package/dist/components/primitives/popover/popover-title.svelte +15 -0
  198. package/dist/components/primitives/popover/popover-title.svelte.d.ts +5 -0
  199. package/dist/components/primitives/popover/popover-trigger.svelte +17 -0
  200. package/dist/components/primitives/popover/popover-trigger.svelte.d.ts +4 -0
  201. package/dist/components/primitives/popover/popover.svelte +7 -0
  202. package/dist/components/primitives/popover/popover.svelte.d.ts +3 -0
  203. package/dist/components/primitives/resizable/resizable-handle.svelte +6 -2
  204. package/dist/components/primitives/resizable/resizable-handle.svelte.d.ts +2 -0
  205. package/dist/components/primitives/resizable/resizable-pane-group.svelte +1 -4
  206. package/dist/components/primitives/scroll-area/scroll-area.svelte +1 -1
  207. package/dist/components/primitives/search/search.svelte +3 -3
  208. package/dist/components/primitives/select/select-content.svelte +1 -1
  209. package/dist/components/primitives/select/select-trigger.svelte +1 -1
  210. package/dist/components/primitives/severity.d.ts +7 -0
  211. package/dist/components/primitives/severity.js +19 -0
  212. package/dist/components/primitives/slider/slider.svelte +8 -3
  213. package/dist/components/primitives/slider/slider.svelte.d.ts +5 -1
  214. package/dist/components/primitives/sonner/sonner.svelte +1 -1
  215. package/dist/components/primitives/spinner/index.d.ts +1 -0
  216. package/dist/components/primitives/spinner/index.js +1 -0
  217. package/dist/components/primitives/spinner/spinner.svelte +29 -0
  218. package/dist/components/primitives/spinner/spinner.svelte.d.ts +4 -0
  219. package/dist/components/primitives/switch/switch.svelte +2 -2
  220. package/dist/components/primitives/tabs/tabs-list.svelte +27 -5
  221. package/dist/components/primitives/tabs/tabs-list.svelte.d.ts +22 -1
  222. package/dist/components/primitives/tabs/tabs-trigger.svelte +23 -5
  223. package/dist/components/primitives/tabs/tabs-trigger.svelte.d.ts +22 -1
  224. package/dist/components/primitives/tabs/tabs.svelte +6 -2
  225. package/dist/components/primitives/tabs/tabs.svelte.d.ts +5 -1
  226. package/dist/components/primitives/textarea/textarea.svelte +1 -1
  227. package/dist/components/primitives/theme-switcher/ThemeSwitcher.svelte +1 -1
  228. package/dist/components/primitives/toggle/index.d.ts +3 -0
  229. package/dist/components/primitives/toggle/index.js +5 -0
  230. package/dist/components/primitives/toggle/toggle.svelte +54 -0
  231. package/dist/components/primitives/toggle/toggle.svelte.d.ts +46 -0
  232. package/dist/components/primitives/toggle-group/index.d.ts +3 -0
  233. package/dist/components/primitives/toggle-group/index.js +5 -0
  234. package/dist/components/primitives/toggle-group/toggle-group-item.svelte +35 -0
  235. package/dist/components/primitives/toggle-group/toggle-group-item.svelte.d.ts +6 -0
  236. package/dist/components/primitives/toggle-group/toggle-group.svelte +75 -0
  237. package/dist/components/primitives/toggle-group/toggle-group.svelte.d.ts +17 -0
  238. package/dist/components/viewer/MeshMetadataPanel.svelte +119 -0
  239. package/dist/components/viewer/MeshMetadataPanel.svelte.d.ts +9 -0
  240. package/dist/components/viewer/SceneManager.svelte +14 -5
  241. package/dist/components/viewer/Viewer.svelte +243 -221
  242. package/dist/components/viewer/Viewer.svelte.d.ts +3 -0
  243. package/dist/compute/run-panel-action.d.ts +8 -0
  244. package/dist/compute/run-panel-action.js +12 -0
  245. package/dist/compute/solving.svelte.d.ts +11 -7
  246. package/dist/compute/solving.svelte.js +64 -45
  247. package/dist/compute/useSolveSession.svelte.js +3 -0
  248. package/dist/constants.d.ts +6 -8
  249. package/dist/constants.js +7 -9
  250. package/dist/contexts/hostContext.svelte.d.ts +39 -0
  251. package/dist/contexts/hostContext.svelte.js +9 -0
  252. package/dist/i18n/localeContext.svelte.d.ts +10 -3
  253. package/dist/i18n/localeContext.svelte.js +27 -5
  254. package/dist/i18n/messages.d.ts +150 -2
  255. package/dist/i18n/messages.js +261 -11
  256. package/dist/index.d.ts +4 -1
  257. package/dist/index.js +3 -0
  258. package/dist/public.d.ts +1 -1
  259. package/dist/schema/host-support.d.ts +29 -0
  260. package/dist/schema/host-support.js +97 -0
  261. package/dist/schema/option-display.d.ts +12 -0
  262. package/dist/schema/option-display.js +29 -0
  263. package/dist/schema/param-exporter.d.ts +4 -1
  264. package/dist/schema/param-exporter.js +10 -6
  265. package/dist/schema/visibility-rules.js +21 -0
  266. package/dist/styles/base.css +80 -10
  267. package/dist/types/actionButton.d.ts +17 -3
  268. package/dist/types/presetLabels.d.ts +3 -2
  269. package/dist/types/presetLabels.js +9 -8
  270. package/dist/utils/file-download.js +10 -22
  271. package/dist/utils/host-files.d.ts +12 -0
  272. package/dist/utils/host-files.js +46 -0
  273. package/dist/utils/loadScript.d.ts +2 -0
  274. package/dist/utils/loadScript.js +2 -0
  275. package/dist/utils/webp-to-png.d.ts +10 -0
  276. package/dist/utils/webp-to-png.js +41 -0
  277. package/package.json +19 -15
  278. package/src/lib/components/ConfirmDialog.svelte +2 -10
  279. package/src/lib/components/ErrorScreen.svelte +21 -27
  280. package/src/lib/components/compute/AppLayout.svelte +298 -128
  281. package/src/lib/components/compute/CollapsedPanelStrip.svelte +4 -2
  282. package/src/lib/components/compute/ComputeApp.svelte +45 -24
  283. package/src/lib/components/compute/ComputeMessagesDialog.svelte +47 -83
  284. package/src/lib/components/compute/ParameterPresetManager.svelte +37 -130
  285. package/src/lib/components/compute/SolveMessages.svelte +131 -77
  286. package/src/lib/components/layout/AppShell.svelte +10 -6
  287. package/src/lib/components/layout/PageFooter.svelte +23 -11
  288. package/src/lib/components/layout/PageHeader.svelte +11 -7
  289. package/src/lib/components/layout/Pagination.svelte +2 -4
  290. package/src/lib/components/layout/SectionHeader.svelte +4 -2
  291. package/src/lib/components/layout/SideNav.svelte +56 -7
  292. package/src/lib/components/preview/ChartOutput.svelte +91 -48
  293. package/src/lib/components/preview/Group.svelte +92 -103
  294. package/src/lib/components/preview/HelpButton.svelte +36 -0
  295. package/src/lib/components/preview/ImageOutput.svelte +434 -72
  296. package/src/lib/components/preview/InputControl.svelte +163 -90
  297. package/src/lib/components/preview/OutputDisplay.svelte +44 -42
  298. package/src/lib/components/preview/TabBar.svelte +106 -22
  299. package/src/lib/components/preview/TabContent.svelte +63 -57
  300. package/src/lib/components/preview/TabLayout.svelte +27 -5
  301. package/src/lib/components/preview/inputs/CheckboxInput.svelte +7 -7
  302. package/src/lib/components/preview/inputs/ChecklistInput.svelte +7 -2
  303. package/src/lib/components/preview/inputs/ColorInput.svelte +4 -2
  304. package/src/lib/components/preview/inputs/DropdownInput.svelte +19 -4
  305. package/src/lib/components/preview/inputs/FileInput.svelte +112 -108
  306. package/src/lib/components/preview/inputs/GeometryInput.svelte +128 -0
  307. package/src/lib/components/preview/inputs/NumberInput.svelte +123 -61
  308. package/src/lib/components/preview/inputs/OptionGlyph.svelte +29 -0
  309. package/src/lib/components/preview/inputs/SegmentedInput.svelte +44 -0
  310. package/src/lib/components/preview/inputs/TextInput.svelte +8 -4
  311. package/src/lib/components/preview/inputs/TileInput.svelte +80 -0
  312. package/src/lib/components/preview/inputs/index.ts +3 -0
  313. package/src/lib/components/primitives/CalculateButton.svelte +56 -24
  314. package/src/lib/components/primitives/Callout.svelte +11 -18
  315. package/src/lib/components/primitives/DataTable.svelte +38 -5
  316. package/src/lib/components/primitives/FilterableDropdown.svelte +42 -129
  317. package/src/lib/components/primitives/IssueList.svelte +56 -0
  318. package/src/lib/components/primitives/SeverityIcon.svelte +15 -0
  319. package/src/lib/components/primitives/SolvingIndicator.svelte +43 -42
  320. package/src/lib/components/primitives/StateDisplay.svelte +19 -79
  321. package/src/lib/components/primitives/ViewToggle.svelte +18 -33
  322. package/src/lib/components/primitives/alert-dialog/alert-dialog-action.svelte +9 -3
  323. package/src/lib/components/primitives/alert-dialog/alert-dialog-content.svelte +1 -1
  324. package/src/lib/components/primitives/alert-dialog/alert-dialog-overlay.svelte +1 -1
  325. package/src/lib/components/primitives/badge/badge.svelte +2 -2
  326. package/src/lib/components/primitives/button/button.svelte +6 -2
  327. package/src/lib/components/primitives/card/card-header.svelte +1 -1
  328. package/src/lib/components/primitives/card/card.svelte +21 -5
  329. package/src/lib/components/primitives/card/index.ts +1 -4
  330. package/src/lib/components/primitives/checkbox/checkbox.svelte +1 -1
  331. package/src/lib/components/primitives/command/command-dialog.svelte +42 -0
  332. package/src/lib/components/primitives/command/command-empty.svelte +17 -0
  333. package/src/lib/components/primitives/command/command-group.svelte +33 -0
  334. package/src/lib/components/primitives/command/command-input.svelte +36 -0
  335. package/src/lib/components/primitives/command/command-item.svelte +28 -0
  336. package/src/lib/components/primitives/command/command-link-item.svelte +21 -0
  337. package/src/lib/components/primitives/command/command-list.svelte +20 -0
  338. package/src/lib/components/primitives/command/command-loading.svelte +7 -0
  339. package/src/lib/components/primitives/command/command-separator.svelte +17 -0
  340. package/src/lib/components/primitives/command/command-shortcut.svelte +23 -0
  341. package/src/lib/components/primitives/command/command.svelte +28 -0
  342. package/src/lib/components/primitives/command/index.ts +37 -0
  343. package/src/lib/components/primitives/context-menu/context-menu-content.svelte +1 -1
  344. package/src/lib/components/primitives/context-menu/context-menu-sub-content.svelte +1 -1
  345. package/src/lib/components/primitives/dialog/dialog-content.svelte +11 -3
  346. package/src/lib/components/primitives/dialog/dialog-overlay.svelte +1 -1
  347. package/src/lib/components/primitives/dialog/dialog-title.svelte +6 -2
  348. package/src/lib/components/primitives/drawer/Drawer.svelte +2 -2
  349. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  350. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-checkbox-item.svelte +46 -0
  351. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-content.svelte +31 -0
  352. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  353. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-group.svelte +7 -0
  354. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-item.svelte +27 -0
  355. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-label.svelte +24 -0
  356. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  357. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  358. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-radio-item.svelte +36 -0
  359. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  360. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
  361. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-content.svelte +31 -0
  362. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  363. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  364. package/src/lib/components/primitives/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  365. package/src/lib/components/primitives/dropdown-menu/dropdown-menu.svelte +7 -0
  366. package/src/lib/components/primitives/dropdown-menu/index.ts +54 -0
  367. package/src/lib/components/primitives/{alert/alert-description.svelte → empty/empty-content.svelte} +3 -3
  368. package/src/lib/components/primitives/empty/empty-description.svelte +23 -0
  369. package/src/lib/components/primitives/{alert/alert-title.svelte → empty/empty-header.svelte} +3 -3
  370. package/src/lib/components/primitives/empty/empty-media.svelte +41 -0
  371. package/src/lib/components/primitives/{card/card-action.svelte → empty/empty-title.svelte} +2 -2
  372. package/src/lib/components/primitives/empty/empty.svelte +23 -0
  373. package/src/lib/components/primitives/empty/index.ts +22 -0
  374. package/src/lib/components/primitives/index.ts +10 -1
  375. package/src/lib/components/primitives/input/input.svelte +4 -4
  376. package/src/lib/components/primitives/input-group/index.ts +22 -0
  377. package/src/lib/components/primitives/input-group/input-group-addon.svelte +53 -0
  378. package/src/lib/components/primitives/input-group/input-group-button.svelte +49 -0
  379. package/src/lib/components/primitives/input-group/input-group-input.svelte +25 -0
  380. package/src/lib/components/primitives/input-group/input-group-text.svelte +22 -0
  381. package/src/lib/components/primitives/input-group/input-group-textarea.svelte +23 -0
  382. package/src/lib/components/primitives/input-group/input-group.svelte +24 -0
  383. package/src/lib/components/primitives/mode-toggle/mode-toggle.svelte +6 -7
  384. package/src/lib/components/primitives/popover/index.ts +28 -0
  385. package/src/lib/components/primitives/popover/popover-close.svelte +7 -0
  386. package/src/lib/components/primitives/popover/popover-content.svelte +31 -0
  387. package/src/lib/components/primitives/popover/popover-description.svelte +20 -0
  388. package/src/lib/components/primitives/popover/popover-header.svelte +20 -0
  389. package/src/lib/components/primitives/popover/popover-portal.svelte +7 -0
  390. package/src/lib/components/primitives/popover/popover-title.svelte +15 -0
  391. package/src/lib/components/primitives/popover/popover-trigger.svelte +17 -0
  392. package/src/lib/components/primitives/popover/popover.svelte +7 -0
  393. package/src/lib/components/primitives/resizable/resizable-handle.svelte +6 -2
  394. package/src/lib/components/primitives/resizable/resizable-pane-group.svelte +1 -4
  395. package/src/lib/components/primitives/scroll-area/scroll-area.svelte +1 -1
  396. package/src/lib/components/primitives/search/search.svelte +3 -3
  397. package/src/lib/components/primitives/select/select-content.svelte +1 -1
  398. package/src/lib/components/primitives/select/select-trigger.svelte +1 -1
  399. package/src/lib/components/primitives/severity.ts +26 -0
  400. package/src/lib/components/primitives/slider/slider.svelte +8 -3
  401. package/src/lib/components/primitives/sonner/sonner.svelte +1 -1
  402. package/src/lib/components/primitives/spinner/index.ts +1 -0
  403. package/src/lib/components/primitives/spinner/spinner.svelte +29 -0
  404. package/src/lib/components/primitives/switch/switch.svelte +2 -2
  405. package/src/lib/components/primitives/tabs/tabs-list.svelte +27 -5
  406. package/src/lib/components/primitives/tabs/tabs-trigger.svelte +23 -5
  407. package/src/lib/components/primitives/tabs/tabs.svelte +6 -2
  408. package/src/lib/components/primitives/textarea/textarea.svelte +1 -1
  409. package/src/lib/components/primitives/theme-switcher/ThemeSwitcher.svelte +1 -1
  410. package/src/lib/components/primitives/toggle/index.ts +13 -0
  411. package/src/lib/components/primitives/toggle/toggle.svelte +54 -0
  412. package/src/lib/components/primitives/toggle-group/index.ts +10 -0
  413. package/src/lib/components/primitives/toggle-group/toggle-group-item.svelte +35 -0
  414. package/src/lib/components/primitives/toggle-group/toggle-group.svelte +75 -0
  415. package/src/lib/components/viewer/MeshMetadataPanel.svelte +119 -0
  416. package/src/lib/components/viewer/SceneManager.svelte +14 -5
  417. package/src/lib/components/viewer/Viewer.svelte +243 -221
  418. package/src/lib/compute/run-panel-action.ts +16 -0
  419. package/src/lib/compute/solving.svelte.ts +66 -50
  420. package/src/lib/compute/useSolveSession.svelte.ts +3 -0
  421. package/src/lib/constants.ts +7 -9
  422. package/src/lib/contexts/hostContext.svelte.ts +51 -0
  423. package/src/lib/i18n/localeContext.svelte.ts +40 -5
  424. package/src/lib/i18n/messages.ts +453 -13
  425. package/src/lib/index.ts +21 -0
  426. package/src/lib/public.ts +1 -0
  427. package/src/lib/schema/host-support.ts +129 -0
  428. package/src/lib/schema/option-display.ts +44 -0
  429. package/src/lib/schema/param-exporter.ts +10 -7
  430. package/src/lib/schema/visibility-rules.ts +23 -0
  431. package/src/lib/styles/base.css +80 -10
  432. package/src/lib/types/actionButton.ts +16 -3
  433. package/src/lib/types/presetLabels.ts +13 -11
  434. package/src/lib/utils/file-download.ts +11 -21
  435. package/src/lib/utils/host-files.ts +61 -0
  436. package/src/lib/utils/loadScript.ts +3 -0
  437. package/src/lib/utils/webp-to-png.ts +48 -0
  438. package/dist/components/primitives/alert/alert.svelte +0 -44
  439. package/dist/components/primitives/alert/alert.svelte.d.ts +0 -26
  440. package/dist/components/primitives/alert/index.d.ts +0 -5
  441. package/dist/components/primitives/alert/index.js +0 -7
  442. package/dist/components/viewer/MeshMetadataDialog.svelte +0 -126
  443. package/dist/components/viewer/MeshMetadataDialog.svelte.d.ts +0 -10
  444. package/src/lib/components/primitives/alert/alert.svelte +0 -44
  445. package/src/lib/components/primitives/alert/index.ts +0 -14
  446. package/src/lib/components/viewer/MeshMetadataDialog.svelte +0 -126
@@ -74,14 +74,16 @@
74
74
 
75
75
  const rootClass = $derived(
76
76
  _mode === 'fixed'
77
- ? `flex flex-col h-screen overflow-hidden bg-background ${className}`
78
- : `flex flex-col min-h-screen bg-background ${className}`
77
+ ? // dvh, not vh: iOS sizes 100vh to the viewport without its toolbar, which hides the
78
+ // footer and the phone drawer's handle behind the toolbar.
79
+ `flex flex-col h-dvh overflow-hidden bg-background ${className}`
80
+ : `flex flex-col min-h-dvh bg-background ${className}`
79
81
  );
80
82
 
81
83
  const bodyShellClass = $derived(
82
84
  _mode === 'fixed'
83
- ? `flex flex-1 min-h-0 ${sidenav ? 'flex-row' : 'flex-col'} ${bodyClass}`.trim()
84
- : `flex-1 ${sidenav ? 'flex flex-row' : ''} ${bodyClass}`.trim()
85
+ ? `flex flex-1 min-h-0 ${sidenav ? 'flex-col sm:flex-row' : 'flex-col'} ${bodyClass}`.trim()
86
+ : `flex-1 ${sidenav ? 'flex flex-col sm:flex-row' : ''} ${bodyClass}`.trim()
85
87
  );
86
88
 
87
89
  const bodyShellStyle = '';
@@ -92,13 +94,15 @@
92
94
  const sidenavWrapClass = $derived(
93
95
  _mode === 'fixed'
94
96
  ? 'flex shrink-0'
95
- : 'flex shrink-0 self-start sticky top-(--header-h) max-h-[calc(100svh-var(--header-h))]'
97
+ : 'flex shrink-0 sm:self-start sm:sticky sm:top-(--header-h) sm:max-h-(--below-header-h)'
96
98
  );
97
99
 
98
100
  // The sticky wrapper is only as tall as its content, so in scroll mode the divider comes
99
101
  // from the main column's left edge — that one spans the full body height.
100
102
  const mainClass = $derived(
101
- _mode === 'fixed' ? 'flex-1 overflow-y-auto' : 'flex-1 overflow-y-auto border-l border-border'
103
+ _mode === 'fixed'
104
+ ? 'min-h-0 flex-1 overflow-y-auto'
105
+ : 'min-w-0 flex-1 overflow-y-auto border-border sm:border-l'
102
106
  );
103
107
  </script>
104
108
 
@@ -5,6 +5,8 @@
5
5
  import { useFooter } from '../../contexts/footerContext.svelte';
6
6
  import ComputeMessagesDialog from '../compute/ComputeMessagesDialog.svelte';
7
7
  import FooterItemRenderer from './FooterItemRenderer.svelte';
8
+ import { getLocaleContext } from '../../i18n/localeContext.svelte';
9
+ import { formatCount, formatMessage } from '../../i18n/messages';
8
10
 
9
11
  interface Props {
10
12
  errors?: string[];
@@ -26,6 +28,9 @@
26
28
  children
27
29
  }: Props = $props();
28
30
 
31
+ const locale = getLocaleContext();
32
+ const t = $derived(locale.messages);
33
+
29
34
  let footerStore = (() => {
30
35
  try {
31
36
  return useFooter();
@@ -66,16 +71,15 @@
66
71
  const hasMessages = $derived(totalCount > 0);
67
72
 
68
73
  const copyrightLine = $derived(
69
- footerText
70
- ? footerText.replace('{name}', copyrightName).replace('{year}', String(_currentYear))
71
- : `by ${copyrightName} © ${_currentYear}`
74
+ formatMessage(footerText ?? t.copyright, { name: copyrightName, year: _currentYear })
72
75
  );
73
76
  </script>
74
77
 
78
+ <!-- One line at any width: the message counts keep their size and the copyright truncates. -->
75
79
  <footer
76
- class="px-4 gap-4 text-xs h-7 flex shrink-0 items-center justify-between border-t border-border bg-background text-muted-foreground select-none"
80
+ class="px-4 gap-4 text-xs h-7 flex shrink-0 items-center justify-between border-t border-border bg-background whitespace-nowrap text-muted-foreground select-none"
77
81
  >
78
- <div class="gap-4 flex items-center">
82
+ <div class="gap-4 flex shrink-0 items-center">
79
83
  {#each leftItems as item (item.id)}
80
84
  <FooterItemRenderer {item} />
81
85
  {/each}
@@ -90,11 +94,13 @@
90
94
  : warningCount > 0
91
95
  ? 'text-warning hover:bg-warning/10'
92
96
  : 'hover:bg-muted'}"
93
- title={`${totalCount} ${totalCount === 1 ? 'message' : 'messages'} from the last solve`}
97
+ title={formatCount(totalCount, t.lastSolveMessagesOne, t.lastSolveMessagesOther)}
94
98
  >
95
99
  {#if errorCount > 0}
96
100
  <CircleAlert class="h-3.5 w-3.5" />
97
- <span class="font-medium">{errorCount} Error{errorCount !== 1 ? 's' : ''}</span>
101
+ <span class="font-medium"
102
+ >{formatCount(errorCount, t.errorCountOne, t.errorCountOther)}</span
103
+ >
98
104
  {/if}
99
105
 
100
106
  {#if warningCount > 0}
@@ -102,12 +108,16 @@
102
108
  <span class="text-border">•</span>
103
109
  {/if}
104
110
  <TriangleAlert class="h-3.5 w-3.5" />
105
- <span class="font-medium">{warningCount} Warning{warningCount !== 1 ? 's' : ''}</span>
111
+ <span class="font-medium"
112
+ >{formatCount(warningCount, t.warningCountOne, t.warningCountOther)}</span
113
+ >
106
114
  {/if}
107
115
 
108
116
  {#if errorCount === 0 && warningCount === 0}
109
117
  <Info class="h-3.5 w-3.5" />
110
- <span class="font-medium">{remarkCount} Note{remarkCount !== 1 ? 's' : ''}</span>
118
+ <span class="font-medium"
119
+ >{formatCount(remarkCount, t.noteCountOne, t.noteCountOther)}</span
120
+ >
111
121
  {/if}
112
122
  </div>
113
123
  {/snippet}
@@ -121,11 +131,13 @@
121
131
  {/if}
122
132
  </div>
123
133
 
124
- <div class="gap-4 ml-auto flex items-center">
134
+ <div class="gap-4 min-w-0 ml-auto flex items-center">
125
135
  {#each rightItems as item (item.id)}
126
136
  <FooterItemRenderer {item} />
127
137
  {/each}
128
138
 
129
- <p>{copyrightLine}</p>
139
+ {#if copyrightLine}
140
+ <p class="truncate">{copyrightLine}</p>
141
+ {/if}
130
142
  </div>
131
143
  </footer>
@@ -31,27 +31,31 @@
31
31
  <header
32
32
  class={`top-0 backdrop-blur-sm sticky z-40 border-b border-border bg-background/90 ${className}`}
33
33
  >
34
- <div class="gap-5 flex h-(--header-h) items-center px-(--page-px)">
34
+ <div class="gap-2 sm:gap-5 flex h-(--header-h) items-center px-(--page-px)">
35
35
  <!-- Logo -->
36
- <a href={homeUrl} class="gap-2 flex shrink-0 items-center">
36
+ <a href={homeUrl} class="gap-2 flex shrink-0 items-center" aria-label={brandName}>
37
37
  <img src={logo} alt="" aria-hidden="true" class="h-5 w-5" />
38
- <span class="font-semibold text-sm tracking-tight">{brandName}</span>
38
+ <!-- On a phone the page title needs the room more than the brand does. -->
39
+ <span
40
+ class="font-semibold text-sm tracking-tight {title !== undefined ? 'sm:inline hidden' : ''}"
41
+ >{brandName}</span
42
+ >
39
43
  </a>
40
44
 
41
45
  {#if title !== undefined}
42
- <span class="text-sm text-border">/</span>
43
- <span class="text-sm font-medium text-muted-foreground">{title ?? ''}</span>
46
+ <span class="text-sm shrink-0 text-border">/</span>
47
+ <span class="text-sm font-medium min-w-0 truncate text-muted-foreground">{title ?? ''}</span>
44
48
  {/if}
45
49
 
46
50
  <!-- Primary nav next to logo -->
47
51
  {#if navItems}
48
- <nav class="ml-5 gap-1 flex">
52
+ <nav class="sm:ml-5 gap-1 flex shrink-0">
49
53
  {@render navItems()}
50
54
  </nav>
51
55
  {/if}
52
56
 
53
57
  <!-- Right cluster: identity + utilities, anchored flush-right -->
54
- <div class="gap-2 ml-auto flex items-center">
58
+ <div class="gap-2 ml-auto flex shrink-0 items-center">
55
59
  {#if rightContent}
56
60
  {@render rightContent()}
57
61
  {/if}
@@ -40,23 +40,21 @@
40
40
  </p>
41
41
  <div class="gap-1 flex items-center">
42
42
  <Button
43
- size="sm"
43
+ size="icon-sm"
44
44
  variant="outline"
45
45
  disabled={page <= 1}
46
46
  onclick={() => (page -= 1)}
47
47
  aria-label="Previous page"
48
- class="h-8 w-8 p-0"
49
48
  >
50
49
  <ChevronLeft class="h-4 w-4" />
51
50
  </Button>
52
51
  <span class="px-2 font-mono text-xs text-muted-foreground">{page} / {pageCount}</span>
53
52
  <Button
54
- size="sm"
53
+ size="icon-sm"
55
54
  variant="outline"
56
55
  disabled={page >= pageCount}
57
56
  onclick={() => (page += 1)}
58
57
  aria-label="Next page"
59
- class="h-8 w-8 p-0"
60
58
  >
61
59
  <ChevronRight class="h-4 w-4" />
62
60
  </Button>
@@ -18,7 +18,9 @@
18
18
  }: SectionHeaderProps = $props();
19
19
  </script>
20
20
 
21
- <div class={`mb-6 gap-5 flex items-start justify-between ${className}`}>
21
+ <div
22
+ class={`mb-6 gap-3 sm:gap-5 sm:flex-row sm:items-start sm:justify-between flex flex-col ${className}`}
23
+ >
22
24
  <div class="min-w-0">
23
25
  {#if eyebrow}
24
26
  <div class="mb-1.5 text-xs font-medium tracking-wider text-muted-foreground uppercase">
@@ -31,7 +33,7 @@
31
33
  {/if}
32
34
  </div>
33
35
  {#if actions}
34
- <div class="gap-2 flex shrink-0 items-center">
36
+ <div class="gap-2 flex shrink-0 flex-wrap items-center">
35
37
  {@render actions()}
36
38
  </div>
37
39
  {/if}
@@ -13,6 +13,9 @@
13
13
  <script lang="ts">
14
14
  import type { Snippet } from 'svelte';
15
15
  import { page } from '$app/state';
16
+ import ChevronsUpDown from '@lucide/svelte/icons/chevrons-up-down';
17
+ import Check from '@lucide/svelte/icons/check';
18
+ import * as DropdownMenu from '../primitives/dropdown-menu/index.js';
16
19
 
17
20
  interface SideNavProps {
18
21
  items: SideNavItem[];
@@ -41,15 +44,19 @@
41
44
  if (item.match === 'prefix') return path.startsWith(item.href);
42
45
  return path === item.href;
43
46
  }
47
+
48
+ const activeItem = $derived(items.find(isActive));
44
49
  </script>
45
50
 
51
+ <!-- On a phone a 240px column would leave the page ~150px, so the nav collapses to one switcher
52
+ naming the current section; the header and footer drop out. -->
46
53
  <aside
47
- class={`w-60 flex h-full shrink-0 flex-col overflow-y-auto bg-background ${
48
- border ? 'border-r border-border' : ''
54
+ class={`sm:h-full sm:w-60 sm:overflow-y-auto sm:border-b-0 flex w-full shrink-0 flex-col border-b border-border bg-background ${
55
+ border ? 'sm:border-r' : ''
49
56
  } ${className}`}
50
57
  >
51
58
  {#if eyebrow}
52
- <div class="px-4 pt-5 pb-3">
59
+ <div class="px-4 pt-5 pb-3 sm:block hidden">
53
60
  <span class="text-xs font-medium tracking-wider text-muted-foreground uppercase">
54
61
  {eyebrow}
55
62
  </span>
@@ -57,17 +64,59 @@
57
64
  {/if}
58
65
 
59
66
  {#if header}
60
- <div class="px-3 pb-3">
67
+ <div class="px-3 pb-3 sm:block hidden">
61
68
  {@render header()}
62
69
  </div>
63
70
  {/if}
64
71
 
65
- <nav class="pb-4 pr-2 flex-1 space-y-px">
72
+ <div class="py-2 sm:hidden px-(--page-px)">
73
+ <DropdownMenu.Root>
74
+ <DropdownMenu.Trigger>
75
+ {#snippet child({ props })}
76
+ <button
77
+ {...props}
78
+ class="min-h-11 gap-3 px-3 text-sm font-medium flex w-full items-center rounded-md border border-border bg-card text-left"
79
+ >
80
+ {#if activeItem?.icon}
81
+ {@const Icon = activeItem.icon}
82
+ <Icon class="size-4 shrink-0 opacity-70" />
83
+ {/if}
84
+ <span class="min-w-0 flex-1 truncate">
85
+ {#if eyebrow}<span class="text-muted-foreground">{eyebrow} /</span>{/if}
86
+ {activeItem?.label ?? ''}
87
+ </span>
88
+ <ChevronsUpDown class="size-4 shrink-0 opacity-60" />
89
+ </button>
90
+ {/snippet}
91
+ </DropdownMenu.Trigger>
92
+ <DropdownMenu.Content sideOffset={4}>
93
+ {#each items as item (item.href)}
94
+ {@const active = isActive(item)}
95
+ {@const Icon = item.icon}
96
+ <DropdownMenu.Item class="min-h-11">
97
+ {#snippet child({ props })}
98
+ <a href={item.href} aria-current={active ? 'page' : undefined} {...props}>
99
+ {#if Icon}<Icon class="size-4 opacity-70" />{/if}
100
+ <span class="flex-1">{item.label}</span>
101
+ {#if item.badge !== undefined}
102
+ <span class="font-mono text-2xs tabular-nums opacity-60">{item.badge}</span>
103
+ {/if}
104
+ {#if active}<Check class="size-4" aria-hidden="true" />{/if}
105
+ </a>
106
+ {/snippet}
107
+ </DropdownMenu.Item>
108
+ {/each}
109
+ </DropdownMenu.Content>
110
+ </DropdownMenu.Root>
111
+ </div>
112
+
113
+ <nav class="pb-4 pr-2 sm:block hidden flex-1 space-y-px">
66
114
  {#each items as item (item.href)}
67
115
  {@const active = isActive(item)}
68
116
  {@const Icon = item.icon}
69
117
  <a
70
118
  href={item.href}
119
+ aria-current={active ? 'page' : undefined}
71
120
  class={`group gap-2 py-1.5 pr-2.5 relative flex w-full items-center text-left transition-colors ${
72
121
  active
73
122
  ? 'pl-4 rounded-r-md bg-accent text-accent-foreground'
@@ -82,14 +131,14 @@
82
131
  {/if}
83
132
  <span class="text-sm font-medium flex-1 truncate">{item.label}</span>
84
133
  {#if item.badge !== undefined}
85
- <span class="font-mono shrink-0 text-[11px] tabular-nums opacity-60">{item.badge}</span>
134
+ <span class="font-mono shrink-0 text-2xs tabular-nums opacity-60">{item.badge}</span>
86
135
  {/if}
87
136
  </a>
88
137
  {/each}
89
138
  </nav>
90
139
 
91
140
  {#if footer}
92
- <div class="px-3 py-3 border-t border-border/60">
141
+ <div class="px-3 py-3 sm:block hidden border-t border-border/60">
93
142
  {@render footer()}
94
143
  </div>
95
144
  {/if}
@@ -1,8 +1,11 @@
1
1
  <script lang="ts">
2
2
  import type { OutputChartLayoutItem } from '@selvajs/schemas';
3
- import { onMount } from 'svelte';
3
+ import { onMount, tick } from 'svelte';
4
+ import { mode } from 'mode-watcher';
4
5
  import { Loader, Maximize, Minimize } from '@lucide/svelte';
5
6
  import { loadScript } from '../../utils/loadScript';
7
+ import { getLocaleContext } from '../../i18n/localeContext.svelte';
8
+ import { formatMessage } from '../../i18n/messages';
6
9
 
7
10
  interface Props {
8
11
  item: OutputChartLayoutItem;
@@ -11,6 +14,9 @@
11
14
 
12
15
  let { item: _item, value }: Props = $props();
13
16
 
17
+ const locale = getLocaleContext();
18
+ const t = $derived(locale.messages);
19
+
14
20
  const MAP_TYPE_SET = new Set(['scattermap', 'scattermapbox', 'choropleth', 'scattergeo']);
15
21
 
16
22
  type PlotlyTrace = Record<string, unknown>;
@@ -40,20 +46,40 @@
40
46
  // @ts-expect-error — Plotly is loaded dynamically onto window
41
47
  if (window.Plotly) return window.Plotly;
42
48
 
43
- await loadScript('https://cdn.plot.ly/plotly-3.4.0.min.js', { crossOrigin: 'anonymous' });
49
+ // Pinned by hash: the plugin's web UI runs this on the origin that holds its bridge secret.
50
+ await loadScript('https://cdn.plot.ly/plotly-3.4.0.min.js', {
51
+ crossOrigin: 'anonymous',
52
+ integrity: 'sha384-pFOQ7loGBChmUyS4Tszy+he8OWeYkwTFjc2xii4aJUWWiGwf2u2OEc04T5iDRpMp'
53
+ });
44
54
  // @ts-expect-error — Plotly is loaded dynamically onto window
45
55
  return window.Plotly;
46
56
  }
47
57
 
58
+ // Some embedded WebViews (WKWebView in the Rhino panel) ship without the Fullscreen API at all.
59
+ const fullscreenSupported = typeof document !== 'undefined' && !!document.fullscreenEnabled;
60
+
61
+ // `Plots.resize()`/`relayout({autosize: true})` ask Plotly to re-measure the container itself,
62
+ // which it silently fails to do across a Fullscreen transition — _fullLayout keeps the
63
+ // pre-toggle width/height even though clientWidth/clientHeight are already correct. Passing
64
+ // the measured size explicitly is the only call that actually takes.
65
+ function resizeChart() {
66
+ // @ts-expect-error — Plotly is loaded dynamically onto window
67
+ if (!containerEl || !window.Plotly) return;
68
+ const { width, height } = containerEl.getBoundingClientRect();
69
+ if (width === 0 || height === 0) return;
70
+ // @ts-expect-error — Plotly is loaded dynamically onto window
71
+ window.Plotly.relayout(containerEl, { width, height });
72
+ }
73
+
48
74
  function toggleFullscreen() {
49
75
  if (!wrapperEl) return;
50
76
  if (!document.fullscreenElement) {
51
77
  wrapperEl.requestFullscreen().catch((err) => {
52
- error = `Fullscreen unavailable: ${err.message}`;
78
+ error = formatMessage(t.fullscreenUnavailable, { error: err.message });
53
79
  });
54
80
  } else {
55
81
  document.exitFullscreen().catch((err) => {
56
- error = `Exit fullscreen failed: ${err.message}`;
82
+ error = formatMessage(t.exitFullscreenFailed, { error: err.message });
57
83
  });
58
84
  }
59
85
  }
@@ -61,26 +87,41 @@
61
87
  $effect(() => {
62
88
  function onFullscreenChange() {
63
89
  isFullscreen = !!document.fullscreenElement;
64
- Promise.resolve().then(() => {
65
- // @ts-expect-error — Plotly is loaded dynamically onto window
66
- if (containerEl && window.Plotly) {
67
- // @ts-expect-error — Plotly is loaded dynamically onto window
68
- window.Plotly.Plots.resize(containerEl);
69
- }
70
- });
90
+ // `tick()` waits for the h-dvh/h-95 class swap this state drives to actually reach the
91
+ // DOM before resizeChart measures it.
92
+ tick().then(resizeChart);
71
93
  }
72
94
  document.addEventListener('fullscreenchange', onFullscreenChange);
73
95
  return () => document.removeEventListener('fullscreenchange', onFullscreenChange);
74
96
  });
75
97
 
98
+ // Chromium's getComputedStyle returns color values in whatever function the CSS used to
99
+ // declare them — oklch() here, since base.css's theme tokens are oklch(). Plotly's own color
100
+ // parser doesn't recognize oklch() and silently falls back to its #444 default instead of
101
+ // erroring, so every chart read dark axis text even in dark mode. `fillStyle` alone just
102
+ // echoes the oklch() string back unconverted; actually rasterizing a pixel and reading its
103
+ // bytes is what forces a real rgb() value Plotly can parse.
104
+ function toRgb(cssColor: string): string {
105
+ const canvas = document.createElement('canvas');
106
+ canvas.width = 1;
107
+ canvas.height = 1;
108
+ const ctx = canvas.getContext('2d');
109
+ if (!ctx) return cssColor;
110
+ ctx.fillStyle = cssColor;
111
+ ctx.fillRect(0, 0, 1, 1);
112
+ const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
113
+ return a === 255 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${(a / 255).toFixed(3)})`;
114
+ }
115
+
76
116
  function getThemeColors(): { textColor: string; gridColor: string } {
77
117
  if (!containerEl) return { textColor: '#333', gridColor: '#eee' };
78
118
  const style = getComputedStyle(containerEl);
79
- const textColor = style.getPropertyValue('color').trim() || '#333';
80
- const gridColor =
119
+ const textColor = toRgb(style.getPropertyValue('color').trim() || '#333');
120
+ const gridColor = toRgb(
81
121
  style.getPropertyValue('--border')?.trim() ||
82
- style.getPropertyValue('--color-border')?.trim() ||
83
- 'rgba(128,128,128,0.2)';
122
+ style.getPropertyValue('--color-border')?.trim() ||
123
+ 'rgba(128,128,128,0.2)'
124
+ );
84
125
  return { textColor, gridColor };
85
126
  }
86
127
 
@@ -113,11 +154,15 @@
113
154
  font: { color: textColor },
114
155
  paper_bgcolor: layoutRest.paper_bgcolor ?? 'transparent',
115
156
  plot_bgcolor: layoutRest.plot_bgcolor ?? 'transparent',
116
- ...(!isMapFigure && layoutRest.xaxis
117
- ? { xaxis: { ...(layoutRest.xaxis as object), gridcolor: gridColor, color: textColor } }
118
- : {}),
119
- ...(!isMapFigure && layoutRest.yaxis
120
- ? { yaxis: { ...(layoutRest.yaxis as object), gridcolor: gridColor, color: textColor } }
157
+ // Plotly's axis tick/label color doesn't inherit the top-level `font.color`, so this
158
+ // has to be set even when the figure's own JSON never declared an xaxis/yaxis —
159
+ // which the mock's figures don't. Gating on `layoutRest.xaxis` existing left every
160
+ // axis on Plotly's built-in #444 default, light or dark mode.
161
+ ...(!isMapFigure
162
+ ? {
163
+ xaxis: { ...(layoutRest.xaxis as object), gridcolor: gridColor, color: textColor },
164
+ yaxis: { ...(layoutRest.yaxis as object), gridcolor: gridColor, color: textColor }
165
+ }
121
166
  : {})
122
167
  };
123
168
 
@@ -129,7 +174,7 @@
129
174
  ...figData.config
130
175
  });
131
176
  } catch (e) {
132
- error = e instanceof Error ? e.message : 'Failed to render chart';
177
+ error = e instanceof Error ? e.message : t.chartRenderFailed;
133
178
  } finally {
134
179
  loading = false;
135
180
  }
@@ -142,11 +187,7 @@
142
187
  const ro = new ResizeObserver(() => {
143
188
  if (resizeTimer) clearTimeout(resizeTimer);
144
189
  resizeTimer = setTimeout(() => {
145
- // @ts-expect-error — Plotly is loaded dynamically onto window
146
- if (containerEl && window.Plotly) {
147
- // @ts-expect-error — Plotly is loaded dynamically onto window
148
- window.Plotly.Plots.resize(containerEl);
149
- }
190
+ resizeChart();
150
191
  resizeTimer = null;
151
192
  }, 150);
152
193
  });
@@ -163,6 +204,10 @@
163
204
  let renderTimeout: ReturnType<typeof setTimeout> | null = null;
164
205
 
165
206
  $effect(() => {
207
+ // Re-reads mode.current so a theme toggle re-renders with the new axis/font colors —
208
+ // getThemeColors() only reads the DOM once, at render time, and nothing else repaints an
209
+ // already-drawn chart when the page's color scheme changes under it.
210
+ void mode.current;
166
211
  if (!value || !mounted || !containerEl) return;
167
212
  if (renderTimeout) clearTimeout(renderTimeout);
168
213
  renderTimeout = setTimeout(() => {
@@ -177,25 +222,20 @@
177
222
 
178
223
  <div
179
224
  bind:this={wrapperEl}
180
- class="group rounded relative overflow-hidden border border-border {isFullscreen
225
+ class="group relative overflow-hidden rounded-lg border border-border {isFullscreen
181
226
  ? 'bg-background'
182
227
  : ''}"
183
228
  >
184
229
  {#if error}
185
230
  <div class="px-4 py-3 text-sm text-destructive">{error}</div>
186
- {:else if !figData && value}
187
- <div class="px-4 py-8 text-sm text-center text-muted-foreground">Receiving chart data...</div>
188
231
  {:else if !figData}
189
- <div class="px-4 py-8 text-sm text-center text-muted-foreground">Waiting for chart data...</div>
232
+ <!-- The layout hides this output while it has no data, so this is data that doesn't parse yet. -->
233
+ <div class="px-4 py-8 text-sm text-center text-muted-foreground">{t.chartReceiving}</div>
190
234
  {:else}
191
- <div
192
- bind:this={containerEl}
193
- class="w-full"
194
- style="height: {isFullscreen ? '100vh' : '380px'};"
195
- ></div>
235
+ <div bind:this={containerEl} class="w-full {isFullscreen ? 'h-dvh' : 'h-95'}"></div>
196
236
 
197
237
  <div
198
- class="gap-1 left-2 top-2 absolute z-10 flex items-center opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100 {isFullscreen
238
+ class="gap-1 left-2 top-2 absolute z-10 flex items-center opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100 pointer-coarse:opacity-100 {isFullscreen
199
239
  ? 'opacity-100'
200
240
  : ''}"
201
241
  >
@@ -207,18 +247,21 @@
207
247
  </div>
208
248
  {/if}
209
249
 
210
- <button
211
- onclick={toggleFullscreen}
212
- disabled={loading}
213
- title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
214
- class="rounded p-1.5 backdrop-blur-sm flex items-center border border-border bg-background/80 text-foreground transition-colors hover:bg-background disabled:cursor-not-allowed disabled:opacity-50"
215
- >
216
- {#if isFullscreen}
217
- <Minimize size={14} />
218
- {:else}
219
- <Maximize size={14} />
220
- {/if}
221
- </button>
250
+ {#if fullscreenSupported}
251
+ <button
252
+ onclick={toggleFullscreen}
253
+ disabled={loading}
254
+ title={isFullscreen ? t.exitFullscreen : t.fullscreen}
255
+ aria-label={isFullscreen ? t.exitFullscreen : t.fullscreen}
256
+ class="rounded p-1.5 backdrop-blur-sm flex items-center border border-border bg-background/80 text-foreground transition-colors hover:bg-background disabled:cursor-not-allowed disabled:opacity-50"
257
+ >
258
+ {#if isFullscreen}
259
+ <Minimize size={14} />
260
+ {:else}
261
+ <Maximize size={14} />
262
+ {/if}
263
+ </button>
264
+ {/if}
222
265
  </div>
223
266
  {/if}
224
267
  </div>