matterviz 0.4.0 → 0.4.1

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 (326) hide show
  1. package/dist/brillouin/BrillouinZone.svelte +68 -145
  2. package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -14
  3. package/dist/brillouin/BrillouinZoneExportPane.svelte +43 -96
  4. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  5. package/dist/brillouin/BrillouinZoneInfoPane.svelte +9 -32
  6. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +2 -3
  7. package/dist/brillouin/BrillouinZoneScene.svelte +49 -203
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +3 -23
  9. package/dist/brillouin/ReciprocalVectors.svelte +39 -0
  10. package/dist/brillouin/ReciprocalVectors.svelte.d.ts +9 -0
  11. package/dist/brillouin/compute.d.ts +2 -0
  12. package/dist/brillouin/compute.js +80 -77
  13. package/dist/brillouin/geometry.d.ts +8 -0
  14. package/dist/brillouin/geometry.js +57 -0
  15. package/dist/brillouin/index.d.ts +2 -0
  16. package/dist/brillouin/index.js +2 -0
  17. package/dist/brillouin/types.d.ts +2 -2
  18. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +1 -1
  19. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +100 -191
  20. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +4 -1
  21. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +176 -464
  22. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +7 -1
  23. package/dist/chempot-diagram/color.d.ts +3 -6
  24. package/dist/chempot-diagram/color.js +5 -5
  25. package/dist/chempot-diagram/compute.d.ts +3 -3
  26. package/dist/chempot-diagram/compute.js +3 -1
  27. package/dist/chempot-diagram/controls-state.svelte.d.ts +10 -0
  28. package/dist/chempot-diagram/controls-state.svelte.js +42 -0
  29. package/dist/chempot-diagram/export.d.ts +47 -0
  30. package/dist/chempot-diagram/export.js +133 -0
  31. package/dist/chempot-diagram/index.d.ts +1 -0
  32. package/dist/chempot-diagram/index.js +1 -0
  33. package/dist/chempot-diagram/pointer.d.ts +0 -10
  34. package/dist/chempot-diagram/pointer.js +4 -4
  35. package/dist/chempot-diagram/types.d.ts +3 -3
  36. package/dist/colors/index.js +2 -2
  37. package/dist/composition/FormulaFilter.svelte +6 -5
  38. package/dist/composition/PieChart.svelte +5 -5
  39. package/dist/composition/chem-sys.js +3 -2
  40. package/dist/composition/format.js +3 -2
  41. package/dist/composition/parse.d.ts +0 -1
  42. package/dist/composition/parse.js +17 -19
  43. package/dist/controls.d.ts +1 -0
  44. package/dist/controls.js +0 -1
  45. package/dist/convex-hull/ConvexHull.svelte +8 -10
  46. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -4
  47. package/dist/convex-hull/ConvexHull2D.svelte +94 -175
  48. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  49. package/dist/convex-hull/ConvexHull3D.svelte +176 -680
  50. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  51. package/dist/convex-hull/ConvexHull4D.svelte +180 -680
  52. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  53. package/dist/convex-hull/ConvexHullChrome.svelte +268 -0
  54. package/dist/convex-hull/ConvexHullChrome.svelte.d.ts +30 -0
  55. package/dist/convex-hull/ConvexHullControls.svelte +88 -7
  56. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +7 -6
  57. package/dist/convex-hull/ConvexHullInfoPane.svelte +18 -5
  58. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +6 -5
  59. package/dist/convex-hull/ConvexHullStats.svelte +29 -168
  60. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +3 -1
  61. package/dist/convex-hull/ConvexHullTooltip.svelte +11 -2
  62. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +2 -1
  63. package/dist/convex-hull/barycentric-coords.d.ts +2 -4
  64. package/dist/convex-hull/barycentric-coords.js +6 -33
  65. package/dist/convex-hull/canvas-interactions.svelte.d.ts +79 -0
  66. package/dist/convex-hull/canvas-interactions.svelte.js +278 -0
  67. package/dist/convex-hull/helpers.d.ts +39 -7
  68. package/dist/convex-hull/helpers.js +154 -69
  69. package/dist/convex-hull/hull-state.svelte.d.ts +44 -0
  70. package/dist/convex-hull/hull-state.svelte.js +124 -0
  71. package/dist/convex-hull/index.d.ts +9 -7
  72. package/dist/convex-hull/index.js +7 -2
  73. package/dist/convex-hull/thermodynamics.js +91 -920
  74. package/dist/convex-hull/types.d.ts +12 -4
  75. package/dist/convex-hull/types.js +12 -0
  76. package/dist/coordination/CoordinationBarPlot.svelte +4 -11
  77. package/dist/element/BohrAtom.svelte +2 -1
  78. package/dist/element/ElementTile.svelte.d.ts +1 -1
  79. package/dist/element/index.d.ts +4 -0
  80. package/dist/element/index.js +18 -0
  81. package/dist/feedback/DragOverlay.svelte +3 -1
  82. package/dist/feedback/DragOverlay.svelte.d.ts +1 -0
  83. package/dist/feedback/StatusMessage.svelte +13 -3
  84. package/dist/fermi-surface/FermiSurface.svelte +67 -146
  85. package/dist/fermi-surface/FermiSurface.svelte.d.ts +5 -14
  86. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  87. package/dist/fermi-surface/FermiSurfaceScene.svelte +72 -224
  88. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +3 -23
  89. package/dist/fermi-surface/compute.js +11 -10
  90. package/dist/fermi-surface/export.js +4 -15
  91. package/dist/fermi-surface/index.d.ts +0 -1
  92. package/dist/fermi-surface/index.js +0 -1
  93. package/dist/fermi-surface/parse.d.ts +1 -1
  94. package/dist/fermi-surface/parse.js +64 -75
  95. package/dist/fermi-surface/types.d.ts +2 -2
  96. package/dist/heatmap-matrix/HeatmapMatrix.svelte +55 -40
  97. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +4 -3
  98. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +3 -2
  99. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +5 -5
  100. package/dist/heatmap-matrix/index.d.ts +3 -2
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/io/ExportPane.svelte +166 -0
  104. package/dist/io/ExportPane.svelte.d.ts +17 -0
  105. package/dist/io/decompress.js +1 -2
  106. package/dist/io/export.d.ts +5 -1
  107. package/dist/io/export.js +32 -28
  108. package/dist/io/fetch.d.ts +2 -1
  109. package/dist/io/file-drop.d.ts +7 -0
  110. package/dist/io/file-drop.js +13 -0
  111. package/dist/io/index.d.ts +2 -0
  112. package/dist/io/index.js +10 -0
  113. package/dist/io/types.d.ts +13 -0
  114. package/dist/isosurface/parse.js +46 -44
  115. package/dist/labels.js +1 -1
  116. package/dist/layout/FullscreenButton.svelte +33 -0
  117. package/dist/layout/FullscreenButton.svelte.d.ts +10 -0
  118. package/dist/layout/FullscreenToggle.svelte +8 -14
  119. package/dist/layout/ViewerChrome.svelte +116 -0
  120. package/dist/layout/ViewerChrome.svelte.d.ts +17 -0
  121. package/dist/layout/fullscreen.d.ts +4 -0
  122. package/dist/layout/fullscreen.svelte.d.ts +8 -0
  123. package/dist/layout/fullscreen.svelte.js +37 -0
  124. package/dist/layout/index.d.ts +3 -0
  125. package/dist/layout/index.js +3 -0
  126. package/dist/math.d.ts +7 -3
  127. package/dist/math.js +18 -21
  128. package/dist/overlays/index.d.ts +4 -0
  129. package/dist/periodic-table/PeriodicTable.svelte +9 -8
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  131. package/dist/phase-diagram/PhaseDiagramControls.svelte +3 -2
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +4 -3
  133. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  134. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +2 -3
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +47 -132
  136. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +3 -4
  137. package/dist/phase-diagram/colors.js +1 -1
  138. package/dist/phase-diagram/parse.d.ts +2 -1
  139. package/dist/plot/bar/BarPlot.svelte +79 -316
  140. package/dist/plot/bar/BarPlot.svelte.d.ts +7 -15
  141. package/dist/plot/bar/BarPlotControls.svelte.d.ts +1 -1
  142. package/dist/plot/bar/SpacegroupBarPlot.svelte +2 -1
  143. package/dist/plot/box/BoxPlot.svelte +76 -246
  144. package/dist/plot/box/BoxPlot.svelte.d.ts +4 -3
  145. package/dist/plot/box/BoxPlotControls.svelte.d.ts +1 -1
  146. package/dist/plot/box/Violin.svelte.d.ts +1 -1
  147. package/dist/plot/box/box-plot.d.ts +3 -2
  148. package/dist/plot/box/box-plot.js +5 -2
  149. package/dist/plot/box/kde.d.ts +2 -1
  150. package/dist/plot/box/kde.js +4 -4
  151. package/dist/plot/core/auto-place.d.ts +1 -1
  152. package/dist/plot/core/auto-place.js +4 -1
  153. package/dist/plot/core/components/ColorBar.svelte +5 -5
  154. package/dist/plot/core/components/ColorBar.svelte.d.ts +5 -4
  155. package/dist/plot/core/components/Line.svelte +3 -2
  156. package/dist/plot/core/components/Line.svelte.d.ts +3 -2
  157. package/dist/plot/core/components/PlotAxis.svelte +2 -1
  158. package/dist/plot/core/components/PlotAxis.svelte.d.ts +2 -1
  159. package/dist/plot/core/components/PlotControls.svelte.d.ts +1 -1
  160. package/dist/plot/core/components/ReferenceLine3D.svelte +2 -2
  161. package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +4 -4
  162. package/dist/plot/core/components/ReferencePlane.svelte +2 -2
  163. package/dist/plot/core/components/ReferencePlane.svelte.d.ts +4 -4
  164. package/dist/plot/core/data-cleaning.js +18 -18
  165. package/dist/plot/core/fill-utils.d.ts +4 -3
  166. package/dist/plot/core/fill-utils.js +6 -3
  167. package/dist/plot/core/interactions.d.ts +5 -1
  168. package/dist/plot/core/interactions.js +14 -0
  169. package/dist/plot/core/pan-zoom.svelte.d.ts +35 -0
  170. package/dist/plot/core/pan-zoom.svelte.js +221 -0
  171. package/dist/plot/core/placed-tween.svelte.d.ts +21 -0
  172. package/dist/plot/core/placed-tween.svelte.js +68 -0
  173. package/dist/plot/core/reference-line.d.ts +10 -10
  174. package/dist/plot/core/reference-line.js +6 -6
  175. package/dist/plot/core/scales.d.ts +17 -25
  176. package/dist/plot/core/scales.js +10 -8
  177. package/dist/plot/core/svg.d.ts +2 -1
  178. package/dist/plot/core/types.d.ts +18 -7
  179. package/dist/plot/core/utils/label-placement.d.ts +1 -1
  180. package/dist/plot/core/utils/label-placement.js +3 -3
  181. package/dist/plot/core/utils.d.ts +2 -1
  182. package/dist/plot/histogram/Histogram.svelte +77 -314
  183. package/dist/plot/histogram/HistogramControls.svelte.d.ts +1 -1
  184. package/dist/plot/sankey/Sankey.svelte +2 -5
  185. package/dist/plot/sankey/Sankey.svelte.d.ts +1 -1
  186. package/dist/plot/sankey/sankey.js +3 -1
  187. package/dist/plot/scatter/BinnedScatterPlot.svelte +3 -5
  188. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +4 -4
  189. package/dist/plot/scatter/ScatterPlot.svelte +160 -450
  190. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +7 -15
  191. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
  192. package/dist/plot/scatter/binned-scatter-types.d.ts +4 -11
  193. package/dist/plot/scatter/index.d.ts +1 -1
  194. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +15 -26
  195. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +6 -14
  196. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +9 -10
  197. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +5 -5
  198. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +122 -121
  199. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +5 -14
  200. package/dist/plot/scatter-3d/Surface3D.svelte +6 -5
  201. package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +4 -3
  202. package/dist/plot/sunburst/Sunburst.svelte +16 -20
  203. package/dist/plot/sunburst/Sunburst.svelte.d.ts +4 -3
  204. package/dist/plot/sunburst/SunburstControls.svelte.d.ts +1 -1
  205. package/dist/plot/sunburst/sunburst.js +4 -1
  206. package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
  207. package/dist/sanitize.js +13 -2
  208. package/dist/scene/SceneCamera.svelte +62 -0
  209. package/dist/scene/SceneCamera.svelte.d.ts +19 -0
  210. package/dist/scene/bind-renderer.svelte.d.ts +2 -0
  211. package/dist/scene/bind-renderer.svelte.js +14 -0
  212. package/dist/scene/index.d.ts +4 -0
  213. package/dist/scene/index.js +5 -0
  214. package/dist/scene/props.js +52 -0
  215. package/dist/scene/types.d.ts +26 -0
  216. package/dist/scene/types.js +1 -0
  217. package/dist/settings.d.ts +14 -2
  218. package/dist/settings.js +59 -1
  219. package/dist/spectral/Bands.svelte +8 -7
  220. package/dist/spectral/Bands.svelte.d.ts +3 -2
  221. package/dist/spectral/BandsAndDos.svelte +22 -24
  222. package/dist/spectral/BrillouinBandsDos.svelte +3 -3
  223. package/dist/spectral/Dos.svelte +5 -4
  224. package/dist/spectral/Dos.svelte.d.ts +2 -1
  225. package/dist/spectral/helpers.d.ts +6 -6
  226. package/dist/spectral/helpers.js +43 -37
  227. package/dist/state.svelte.d.ts +0 -7
  228. package/dist/state.svelte.js +0 -6
  229. package/dist/structure/Arrow.svelte +2 -4
  230. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  231. package/dist/structure/CanvasTooltip.svelte +1 -0
  232. package/dist/structure/CellSelect.svelte +11 -3
  233. package/dist/structure/CellSelect.svelte.d.ts +2 -1
  234. package/dist/structure/Lattice.svelte +2 -2
  235. package/dist/structure/Structure.svelte +291 -355
  236. package/dist/structure/Structure.svelte.d.ts +5 -15
  237. package/dist/structure/StructureControls.svelte +217 -2
  238. package/dist/structure/StructureControls.svelte.d.ts +5 -3
  239. package/dist/structure/StructureExportPane.svelte +54 -156
  240. package/dist/structure/StructureExportPane.svelte.d.ts +4 -5
  241. package/dist/structure/StructureInfoPane.svelte +5 -3
  242. package/dist/structure/StructureInfoPane.svelte.d.ts +5 -5
  243. package/dist/structure/StructureScene.svelte +365 -198
  244. package/dist/structure/StructureScene.svelte.d.ts +22 -20
  245. package/dist/structure/{label-placement.d.ts → atom-label-placement.d.ts} +3 -3
  246. package/dist/structure/{label-placement.js → atom-label-placement.js} +12 -2
  247. package/dist/structure/atom-properties.d.ts +1 -1
  248. package/dist/structure/atom-properties.js +11 -16
  249. package/dist/structure/bond-order-perception.js +2 -4
  250. package/dist/structure/bonding.d.ts +3 -0
  251. package/dist/structure/bonding.js +91 -48
  252. package/dist/structure/export.d.ts +24 -4
  253. package/dist/structure/export.js +64 -122
  254. package/dist/structure/index.d.ts +2 -0
  255. package/dist/structure/index.js +2 -0
  256. package/dist/structure/parse.d.ts +3 -2
  257. package/dist/structure/parse.js +333 -370
  258. package/dist/structure/partial-occupancy.d.ts +0 -1
  259. package/dist/structure/partial-occupancy.js +1 -1
  260. package/dist/structure/pbc.d.ts +1 -1
  261. package/dist/structure/pbc.js +186 -13
  262. package/dist/structure/polyhedra.d.ts +41 -0
  263. package/dist/structure/polyhedra.js +602 -0
  264. package/dist/structure/site.d.ts +4 -0
  265. package/dist/structure/site.js +1 -0
  266. package/dist/structure/supercell.js +3 -2
  267. package/dist/structure/validation.js +5 -6
  268. package/dist/symmetry/SymmetryElementControls.svelte +69 -0
  269. package/dist/symmetry/SymmetryElementControls.svelte.d.ts +9 -0
  270. package/dist/symmetry/SymmetryElements.svelte +354 -0
  271. package/dist/symmetry/SymmetryElements.svelte.d.ts +24 -0
  272. package/dist/symmetry/SymmetryStats.svelte +111 -6
  273. package/dist/symmetry/WyckoffTable.svelte +68 -7
  274. package/dist/symmetry/WyckoffTable.svelte.d.ts +3 -0
  275. package/dist/symmetry/cell-transform.js +7 -14
  276. package/dist/symmetry/index.d.ts +14 -4
  277. package/dist/symmetry/index.js +301 -80
  278. package/dist/symmetry/spacegroups.d.ts +5 -1
  279. package/dist/symmetry/spacegroups.js +15 -1
  280. package/dist/symmetry/symmetry-elements.d.ts +33 -0
  281. package/dist/symmetry/symmetry-elements.js +521 -0
  282. package/dist/symmetry/wyckoff-db.d.ts +9 -0
  283. package/dist/symmetry/wyckoff-db.js +87 -0
  284. package/dist/table/HeatmapTable.svelte +4 -15
  285. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  286. package/dist/trajectory/Trajectory.svelte +58 -61
  287. package/dist/trajectory/Trajectory.svelte.d.ts +10 -22
  288. package/dist/trajectory/TrajectoryExportPane.svelte +15 -24
  289. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +4 -5
  290. package/dist/trajectory/TrajectoryInfoPane.svelte +3 -2
  291. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +3 -2
  292. package/dist/trajectory/constants.js +6 -2
  293. package/dist/trajectory/extract.js +17 -37
  294. package/dist/trajectory/format-detect.d.ts +0 -1
  295. package/dist/trajectory/format-detect.js +3 -9
  296. package/dist/trajectory/frame-reader.d.ts +0 -1
  297. package/dist/trajectory/frame-reader.js +62 -128
  298. package/dist/trajectory/helpers.d.ts +10 -2
  299. package/dist/trajectory/helpers.js +56 -36
  300. package/dist/trajectory/parse/ase.d.ts +9 -1
  301. package/dist/trajectory/parse/ase.js +47 -32
  302. package/dist/trajectory/parse/diagnostics.d.ts +3 -0
  303. package/dist/trajectory/parse/diagnostics.js +14 -0
  304. package/dist/trajectory/parse/index.d.ts +1 -1
  305. package/dist/trajectory/parse/index.js +54 -102
  306. package/dist/trajectory/parse/lammps.d.ts +0 -2
  307. package/dist/trajectory/parse/lammps.js +8 -6
  308. package/dist/trajectory/parse/pymatgen.d.ts +2 -0
  309. package/dist/trajectory/parse/pymatgen.js +74 -0
  310. package/dist/trajectory/parse/vasp.js +4 -3
  311. package/dist/trajectory/parse/xyz.d.ts +9 -21
  312. package/dist/trajectory/parse/xyz.js +28 -33
  313. package/dist/trajectory/plotting.d.ts +0 -1
  314. package/dist/trajectory/plotting.js +3 -100
  315. package/dist/utils.d.ts +1 -0
  316. package/dist/utils.js +1 -1
  317. package/dist/xrd/XrdPlot.svelte +14 -29
  318. package/dist/xrd/broadening.d.ts +2 -1
  319. package/dist/xrd/calc-xrd.js +6 -11
  320. package/dist/xrd/index.d.ts +2 -2
  321. package/package.json +29 -16
  322. package/dist/element/data.json +0 -11864
  323. package/dist/fermi-surface/marching-cubes.d.ts +0 -2
  324. package/dist/fermi-surface/marching-cubes.js +0 -2
  325. package/dist/plot/core/hover-lock.svelte.d.ts +0 -14
  326. package/dist/plot/core/hover-lock.svelte.js +0 -45
@@ -1,13 +1,12 @@
1
1
  <script lang="ts">
2
2
  import type { ColorSchemeName } from '../colors'
3
3
  import { ELEMENT_COLOR_SCHEMES } from '../colors'
4
- import type { ShowControlsProp } from '../controls'
5
- import { normalize_show_controls } from '../controls'
6
- import type { ElementSymbol } from '../element'
4
+ import { normalize_show_controls, type ShowControlsProp } from '../controls'
5
+ import { coerce_elem_symbol, type ElementSymbol } from '../element'
7
6
  import { StatusMessage } from '../feedback'
8
7
  import Spinner from '../feedback/Spinner.svelte'
9
8
  import Icon from '../Icon.svelte'
10
- import { create_file_drop_handler, load_from_url } from '../io'
9
+ import { create_file_drop_handler, drag_over_handlers, load_from_url } from '../io'
11
10
  import { forward_window_keydown, handle_and_prevent } from '../keyboard'
12
11
  import { parse_volumetric_file } from '../isosurface/parse'
13
12
  import type { IsosurfaceSettings, VolumetricData } from '../isosurface/types'
@@ -16,8 +15,8 @@
16
15
  DEFAULT_ISOSURFACE_SETTINGS,
17
16
  tile_volumetric_data,
18
17
  } from '../isosurface/types'
19
- import { ELEM_SYMBOLS } from '../labels'
20
- import { set_fullscreen_bg, toggle_fullscreen } from '../layout'
18
+ import { type FullscreenToggleProp, toggle_fullscreen, ViewerChrome } from '../layout'
19
+ import { sync_fullscreen } from '../layout/fullscreen.svelte'
21
20
  import type { Vec3 } from '../math'
22
21
  import { create_cart_to_frac, create_frac_to_cart } from '../math'
23
22
  import { DEFAULTS } from '../settings'
@@ -189,15 +188,13 @@
189
188
  structure?: AnyStructure
190
189
  bonds?: StructureBond[]
191
190
  scene_props?: ComponentProps<typeof StructureScene>
192
- /**
193
- * Controls visibility configuration.
194
- * - 'always': controls always visible
195
- * - 'hover': controls visible on component hover (default)
196
- * - 'never': controls never visible
197
- * - object: { mode, hidden, style } for fine-grained control
198
- *
199
- * Control names: 'reset-camera', 'fullscreen', 'measure-mode', 'info-pane', 'export-pane', 'controls'
200
- */
191
+ // Controls visibility configuration.
192
+ // - 'always': controls always visible
193
+ // - 'hover': controls visible on component hover (default)
194
+ // - 'never': controls never visible
195
+ // - object: { mode, hidden, style } for fine-grained control
196
+ //
197
+ // Control names: 'reset-camera', 'fullscreen', 'measure-mode', 'info-pane', 'export-pane', 'controls'
201
198
  show_controls?: ShowControlsProp
202
199
  fullscreen?: boolean
203
200
  // bindable width of the canvas
@@ -218,7 +215,7 @@
218
215
  bond_edit_mode?: BondEditMode
219
216
  bond_edit_order?: BondOrder
220
217
  info_pane_open?: boolean
221
- fullscreen_toggle?: Snippet<[{ fullscreen: boolean }]> | boolean
218
+ fullscreen_toggle?: FullscreenToggleProp
222
219
  bottom_left?: Snippet<[{ structure?: AnyStructure }]>
223
220
  top_right_controls?: Snippet // Additional controls to render at the end of the control buttons row
224
221
  data_url?: string // URL to load structure from (alternative to providing structure directly)
@@ -643,6 +640,10 @@
643
640
  emit_bonds(edited_bonds)
644
641
  })
645
642
 
643
+ // Elements currently anchoring polyhedra (written by StructureScene, read by
644
+ // StructureControls so per-element toggles reflect actual render state)
645
+ let polyhedra_rendered_elements = $state<string[]>([])
646
+
646
647
  // === Edit-atoms mode state ===
647
648
  let dragging_atoms = $state(false)
648
649
  let undo_stack = $state<AnyStructure[]>([])
@@ -682,9 +683,8 @@
682
683
 
683
684
  // Normalize and validate element symbol (e.g. "fe" → "Fe", "Xx" → null)
684
685
  function normalize_element(input: string): ElementSymbol | null {
685
- const normalized = (input.charAt(0).toUpperCase() +
686
- input.slice(1).toLowerCase()) as ElementSymbol
687
- return ELEM_SYMBOLS.includes(normalized) ? normalized : null
686
+ const normalized = input.charAt(0).toUpperCase() + input.slice(1).toLowerCase()
687
+ return coerce_elem_symbol(normalized) ?? null
688
688
  }
689
689
 
690
690
  function clear_selection() {
@@ -1479,25 +1479,15 @@
1479
1479
  }
1480
1480
  })
1481
1481
 
1482
- $effect(() => { // fullscreen and background
1483
- if (typeof window !== `undefined`) {
1484
- if (fullscreen && !document.fullscreenElement && wrapper) {
1485
- wrapper.requestFullscreen().catch(console.error)
1486
- } else if (!fullscreen && document.fullscreenElement) {
1487
- document.exitFullscreen()
1488
- }
1489
- }
1490
- set_fullscreen_bg(wrapper, fullscreen, `--struct-bg-fullscreen`)
1482
+ sync_fullscreen({
1483
+ get_wrapper: () => wrapper,
1484
+ get_fullscreen: () => fullscreen,
1485
+ set_fullscreen: (val) => (fullscreen = val),
1486
+ bg_css_var: `--struct-bg-fullscreen`,
1487
+ on_change: (val) => on_fullscreen_change?.({ structure, fullscreen: val }),
1491
1488
  })
1492
1489
  </script>
1493
1490
 
1494
- <svelte:document
1495
- onfullscreenchange={() => {
1496
- fullscreen = Boolean(document.fullscreenElement)
1497
- on_fullscreen_change?.({ structure, fullscreen })
1498
- }}
1499
- />
1500
-
1501
1491
  <!-- Forward shortcuts to the hovered viewer when focus is on <body> (see
1502
1492
  forward_window_keydown). Edit modes are excluded so destructive keys
1503
1493
  (delete/undo) still require focus, not just a hovering mouse. -->
@@ -1535,15 +1525,7 @@
1535
1525
  reset_camera()
1536
1526
  }}
1537
1527
  ondrop={handle_file_drop}
1538
- ondragover={(event) => {
1539
- event.preventDefault()
1540
- if (!allow_file_drop) return
1541
- dragover = true
1542
- }}
1543
- ondragleave={(event) => {
1544
- event.preventDefault()
1545
- dragover = false
1546
- }}
1528
+ {...drag_over_handlers({ allow: () => allow_file_drop, set_dragover: (over) => dragover = over })}
1547
1529
  onkeydown={handle_and_prevent(handle_keydown)}
1548
1530
  {...rest}
1549
1531
  class="structure {rest.class ?? ``}"
@@ -1558,294 +1540,282 @@
1558
1540
  {:else if error_msg}
1559
1541
  <StatusMessage bind:message={error_msg} type="error" dismissible />
1560
1542
  {:else if (structure?.sites?.length ?? 0) > 0}
1561
- <section
1562
- class="control-buttons {controls_config.class}"
1563
- style={controls_config.style}
1543
+ {#snippet reset_camera_btn()}
1544
+ {#if camera_has_moved && controls_config.visible(`reset-camera`)}
1545
+ <button class="reset-camera" onclick={reset_camera} title={reset_text}>
1546
+ <!-- Target/Focus icon for reset camera -->
1547
+ <Icon icon="Reset" />
1548
+ </button>
1549
+ {/if}
1550
+ {/snippet}
1551
+ <ViewerChrome
1552
+ {controls_config}
1553
+ {fullscreen}
1554
+ {fullscreen_toggle}
1555
+ fullscreen_btn_style="padding: 0 3px"
1556
+ {wrapper}
1557
+ before={reset_camera_btn}
1558
+ style="--viewer-buttons-gap: 4pt; --viewer-buttons-btn-padding: 1px 6px; --viewer-buttons-align: stretch"
1564
1559
  >
1565
- {#if controls_config.mode !== `never`}
1566
- {#if camera_has_moved && controls_config.visible(`reset-camera`)}
1567
- <button class="reset-camera" onclick={reset_camera} title={reset_text}>
1568
- <!-- Target/Focus icon for reset camera -->
1569
- <Icon icon="Reset" />
1570
- </button>
1571
- {/if}
1572
- {#if fullscreen_toggle && controls_config.visible(`fullscreen`)}
1560
+ {#if enable_measure_mode && controls_config.visible(`measure-mode`)}
1561
+ <div
1562
+ class="measure-mode-dropdown"
1563
+ {@attach click_outside({ callback: () => measure_menu_open = false })}
1564
+ >
1573
1565
  <button
1574
- type="button"
1575
- onclick={() => fullscreen_toggle && toggle_fullscreen(wrapper)}
1576
- title="{fullscreen ? `Exit` : `Enter`} fullscreen"
1577
- aria-pressed={fullscreen}
1578
- class="fullscreen-toggle"
1579
- style="padding: 0 3px"
1580
- {@attach tooltip()}
1566
+ onclick={() => (measure_menu_open = !measure_menu_open)}
1567
+ title="Measure / Edit"
1568
+ class="view-mode-button"
1569
+ class:active={measure_menu_open}
1570
+ aria-expanded={measure_menu_open}
1571
+ style="transform: scale(1.2)"
1581
1572
  >
1582
- {#if typeof fullscreen_toggle === `function`}
1583
- {@render fullscreen_toggle({ fullscreen })}
1573
+ {#if show_measure_selection_limit}
1574
+ <span class="selection-limit-text">
1575
+ {measured_sites.length}/{MAX_SELECTED_SITES}
1576
+ </span>
1584
1577
  {:else}
1585
- <Icon icon="{fullscreen ? `Exit` : ``}Fullscreen" />
1578
+ <Icon
1579
+ icon={({
1580
+ distance: `Ruler`,
1581
+ angle: `Angle`,
1582
+ 'edit-bonds': `Link`,
1583
+ 'edit-atoms': `Edit`,
1584
+ } as const)[measure_mode]}
1585
+ />
1586
1586
  {/if}
1587
+ <Icon
1588
+ icon="Arrow{measure_menu_open ? `Up` : `Down`}"
1589
+ style="margin-left: -2px"
1590
+ />
1587
1591
  </button>
1588
- {/if}
1589
-
1590
- {#if enable_measure_mode && controls_config.visible(`measure-mode`)}
1591
- <div
1592
- class="measure-mode-dropdown"
1593
- {@attach click_outside({ callback: () => measure_menu_open = false })}
1594
- >
1592
+ {#if show_selection_reset}
1595
1593
  <button
1596
- onclick={() => (measure_menu_open = !measure_menu_open)}
1597
- title="Measure / Edit"
1598
- class="view-mode-button"
1599
- class:active={measure_menu_open}
1600
- aria-expanded={measure_menu_open}
1601
- style="transform: scale(1.2)"
1594
+ type="button"
1595
+ aria-label="Reset selection and bond edits"
1596
+ onclick={() => {
1597
+ clear_selection()
1598
+ clear_bond_edits()
1599
+ }}
1602
1600
  >
1603
- {#if show_measure_selection_limit}
1604
- <span class="selection-limit-text">
1605
- {measured_sites.length}/{MAX_SELECTED_SITES}
1606
- </span>
1607
- {:else}
1608
- <Icon
1609
- icon={({
1610
- distance: `Ruler`,
1611
- angle: `Angle`,
1612
- 'edit-bonds': `Link`,
1613
- 'edit-atoms': `Edit`,
1614
- } as const)[measure_mode]}
1615
- />
1616
- {/if}
1617
- <Icon
1618
- icon="Arrow{measure_menu_open ? `Up` : `Down`}"
1619
- style="margin-left: -2px"
1620
- />
1601
+ <Icon icon="Reset" style="margin-left: -4px" />
1621
1602
  </button>
1622
- {#if show_selection_reset}
1623
- <button
1624
- type="button"
1625
- aria-label="Reset selection and bond edits"
1626
- onclick={() => {
1627
- clear_selection()
1628
- clear_bond_edits()
1629
- }}
1630
- >
1631
- <Icon icon="Reset" style="margin-left: -4px" />
1632
- </button>
1633
- {/if}
1634
- {#if measure_menu_open}
1635
- <div class="view-mode-dropdown">
1636
- {#each [
1637
- { mode: `distance`, icon: `Ruler`, label: `Distance`, scale: 1.1 },
1638
- { mode: `angle`, icon: `Angle`, label: `Angle`, scale: 1.3 },
1639
- { mode: `edit-atoms`, icon: `Edit`, label: `Edit Atoms`, scale: 1.0 },
1640
- { mode: `edit-bonds`, icon: `Link`, label: `Edit Bonds`, scale: 1.0 },
1641
- ] as const as { mode, icon, label, scale } (mode)}
1642
- <button
1643
- class="view-mode-option"
1644
- class:selected={measure_mode === mode}
1645
- disabled={mode === `edit-bonds` && !bond_edits_enabled}
1646
- title={mode === `edit-bonds` && !bond_edits_enabled
1647
- ? `Bond editing is only available for the original 1x1x1 cell`
1648
- : label}
1649
- onclick={() => {
1650
- if (mode === `edit-bonds` && !bond_edits_enabled) return
1651
- ;[measure_mode, measure_menu_open] = [mode, false]
1652
- }}
1653
- >
1654
- <Icon {icon} style="transform: scale({scale})" />
1655
- <span>{label}</span>
1656
- </button>
1657
- {/each}
1658
- </div>
1659
- {/if}
1660
- </div>
1661
-
1662
- <!-- Undo/redo buttons (only in edit-atoms mode) -->
1663
- {#if measure_mode === `edit-atoms`}
1664
- <div class="undo-redo-container">
1665
- <button
1666
- type="button"
1667
- aria-label="Undo (Cmd/Ctrl+Z)"
1668
- disabled={undo_stack.length === 0}
1669
- onclick={undo}
1670
- title="Undo (Cmd/Ctrl+Z)"
1671
- class="undo-redo-btn"
1672
- >
1673
- <Icon icon="Undo" />
1674
- {#if undo_stack.length > 0}
1675
- <span class="history-count">{undo_stack.length}</span>
1676
- {/if}
1677
- </button>
1678
- <button
1679
- type="button"
1680
- aria-label="Redo (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1681
- disabled={redo_stack.length === 0}
1682
- onclick={redo}
1683
- title="Redo (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1684
- class="undo-redo-btn"
1685
- >
1686
- <Icon icon="Redo" />
1687
- {#if redo_stack.length > 0}
1688
- <span class="history-count">{redo_stack.length}</span>
1689
- {/if}
1690
- </button>
1691
- </div>
1692
1603
  {/if}
1693
-
1694
- {#if measure_mode === `edit-bonds`}
1695
- <div class="bond-edit-toolbar" aria-label="Bond editing controls">
1696
- {#if bond_edit_mode === `add`}
1697
- <label>
1698
- <span>Bond order</span>
1699
- <select bind:value={bond_edit_order}>
1700
- {#each BOND_ORDER_OPTIONS as { order, label } (label)}
1701
- <option value={order}>{label}</option>
1702
- {/each}
1703
- </select>
1704
- </label>
1705
- {/if}
1706
- <div class="bond-edit-mode-toggle">
1707
- {#each [
1708
- { mode: `add`, label: `Add`, title: `Add: click two atoms` },
1709
- { mode: `delete`, label: `Delete`, title: `Delete: click a bond` },
1710
- ] as const as { mode, label, title } (mode)}
1711
- <button
1712
- type="button"
1713
- class:selected={bond_edit_mode === mode}
1714
- aria-pressed={bond_edit_mode === mode}
1715
- title="{title} ({label[0]})"
1716
- onclick={() => (bond_edit_mode = mode)}
1717
- >
1718
- {label}
1719
- </button>
1720
- {/each}
1721
- </div>
1722
- </div>
1723
- <div class="undo-redo-container">
1724
- <button
1725
- type="button"
1726
- aria-label="Undo bond edit (Cmd/Ctrl+Z)"
1727
- disabled={bond_undo_stack.length === 0}
1728
- onclick={undo_bond_edit}
1729
- title="Undo bond edit (Cmd/Ctrl+Z)"
1730
- class="undo-redo-btn"
1731
- >
1732
- <Icon icon="Undo" />
1733
- {#if bond_undo_stack.length > 0}
1734
- <span class="history-count">{bond_undo_stack.length}</span>
1735
- {/if}
1736
- </button>
1737
- <button
1738
- type="button"
1739
- aria-label="Redo bond edit (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1740
- disabled={bond_redo_stack.length === 0}
1741
- onclick={redo_bond_edit}
1742
- title="Redo bond edit (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1743
- class="undo-redo-btn"
1744
- >
1745
- <Icon icon="Redo" />
1746
- {#if bond_redo_stack.length > 0}
1747
- <span class="history-count">{bond_redo_stack.length}</span>
1748
- {/if}
1749
- </button>
1604
+ {#if measure_menu_open}
1605
+ <div class="view-mode-dropdown">
1606
+ {#each [
1607
+ { mode: `distance`, icon: `Ruler`, label: `Distance`, scale: 1.1 },
1608
+ { mode: `angle`, icon: `Angle`, label: `Angle`, scale: 1.3 },
1609
+ { mode: `edit-atoms`, icon: `Edit`, label: `Edit Atoms`, scale: 1.0 },
1610
+ { mode: `edit-bonds`, icon: `Link`, label: `Edit Bonds`, scale: 1.0 },
1611
+ ] as const as { mode, icon, label, scale } (mode)}
1612
+ <button
1613
+ class="view-mode-option"
1614
+ class:selected={measure_mode === mode}
1615
+ disabled={mode === `edit-bonds` && !bond_edits_enabled}
1616
+ title={mode === `edit-bonds` && !bond_edits_enabled
1617
+ ? `Bond editing is only available for the original 1x1x1 cell`
1618
+ : label}
1619
+ onclick={() => {
1620
+ if (mode === `edit-bonds` && !bond_edits_enabled) return
1621
+ ;[measure_mode, measure_menu_open] = [mode, false]
1622
+ }}
1623
+ >
1624
+ <Icon {icon} style="transform: scale({scale})" />
1625
+ <span>{label}</span>
1626
+ </button>
1627
+ {/each}
1750
1628
  </div>
1751
1629
  {/if}
1630
+ </div>
1752
1631
 
1753
- <!-- Add-atom element input (shown when add_atom_mode is active) -->
1754
- {#if measure_mode === `edit-atoms` && add_atom_mode}
1755
- <div class="add-atom-input">
1756
- <label>
1757
- <span>Element:</span>
1758
- <input
1759
- type="text"
1760
- bind:value={add_element}
1761
- maxlength="2"
1762
- placeholder="C"
1763
- style="width: 3em; text-align: center"
1764
- />
1765
- </label>
1766
- <span style="font-size: 0.75em; opacity: 0.7">Click to place</span>
1767
- </div>
1768
- {/if}
1632
+ <!-- Undo/redo buttons (only in edit-atoms mode) -->
1633
+ {#if measure_mode === `edit-atoms`}
1634
+ <div class="undo-redo-container">
1635
+ <button
1636
+ type="button"
1637
+ aria-label="Undo (Cmd/Ctrl+Z)"
1638
+ disabled={undo_stack.length === 0}
1639
+ onclick={undo}
1640
+ title="Undo (Cmd/Ctrl+Z)"
1641
+ class="undo-redo-btn"
1642
+ >
1643
+ <Icon icon="Undo" />
1644
+ {#if undo_stack.length > 0}
1645
+ <span class="history-count">{undo_stack.length}</span>
1646
+ {/if}
1647
+ </button>
1648
+ <button
1649
+ type="button"
1650
+ aria-label="Redo (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1651
+ disabled={redo_stack.length === 0}
1652
+ onclick={redo}
1653
+ title="Redo (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1654
+ class="undo-redo-btn"
1655
+ >
1656
+ <Icon icon="Redo" />
1657
+ {#if redo_stack.length > 0}
1658
+ <span class="history-count">{redo_stack.length}</span>
1659
+ {/if}
1660
+ </button>
1661
+ </div>
1662
+ {/if}
1769
1663
 
1770
- <!-- Change-element input (shown when 'e' pressed with selection) -->
1771
- {#if measure_mode === `edit-atoms` && change_element_mode &&
1772
- selected_sites.length > 0}
1773
- <div class="add-atom-input">
1664
+ {#if measure_mode === `edit-bonds`}
1665
+ <div class="bond-edit-toolbar" aria-label="Bond editing controls">
1666
+ {#if bond_edit_mode === `add`}
1774
1667
  <label>
1775
- <span>New element:</span>
1776
- <input
1777
- type="text"
1778
- bind:value={change_element_value}
1779
- maxlength="2"
1780
- placeholder="Fe"
1781
- style="width: 3em; text-align: center"
1782
- onkeydown={(event: KeyboardEvent) => {
1783
- if (event.key === `Enter`) {
1784
- handle_change_element(change_element_value)
1785
- } else if (event.key === `Escape`) {
1786
- change_element_mode = false
1787
- }
1788
- event.stopPropagation()
1789
- }}
1790
- {@attach (node: HTMLInputElement) => {
1791
- node.focus()
1792
- }}
1793
- />
1668
+ <span>Bond order</span>
1669
+ <select bind:value={bond_edit_order}>
1670
+ {#each BOND_ORDER_OPTIONS as { order, label } (label)}
1671
+ <option value={order}>{label}</option>
1672
+ {/each}
1673
+ </select>
1794
1674
  </label>
1795
- <span style="font-size: 0.75em; opacity: 0.7">Enter to apply</span>
1675
+ {/if}
1676
+ <div class="bond-edit-mode-toggle">
1677
+ {#each [
1678
+ { mode: `add`, label: `Add`, title: `Add: click two atoms` },
1679
+ { mode: `delete`, label: `Delete`, title: `Delete: click a bond` },
1680
+ ] as const as { mode, label, title } (mode)}
1681
+ <button
1682
+ type="button"
1683
+ class:selected={bond_edit_mode === mode}
1684
+ aria-pressed={bond_edit_mode === mode}
1685
+ title="{title} ({label[0]})"
1686
+ onclick={() => (bond_edit_mode = mode)}
1687
+ >
1688
+ {label}
1689
+ </button>
1690
+ {/each}
1796
1691
  </div>
1797
- {/if}
1692
+ </div>
1693
+ <div class="undo-redo-container">
1694
+ <button
1695
+ type="button"
1696
+ aria-label="Undo bond edit (Cmd/Ctrl+Z)"
1697
+ disabled={bond_undo_stack.length === 0}
1698
+ onclick={undo_bond_edit}
1699
+ title="Undo bond edit (Cmd/Ctrl+Z)"
1700
+ class="undo-redo-btn"
1701
+ >
1702
+ <Icon icon="Undo" />
1703
+ {#if bond_undo_stack.length > 0}
1704
+ <span class="history-count">{bond_undo_stack.length}</span>
1705
+ {/if}
1706
+ </button>
1707
+ <button
1708
+ type="button"
1709
+ aria-label="Redo bond edit (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1710
+ disabled={bond_redo_stack.length === 0}
1711
+ onclick={redo_bond_edit}
1712
+ title="Redo bond edit (Cmd/Ctrl+Y or Cmd+Shift+Z)"
1713
+ class="undo-redo-btn"
1714
+ >
1715
+ <Icon icon="Redo" />
1716
+ {#if bond_redo_stack.length > 0}
1717
+ <span class="history-count">{bond_redo_stack.length}</span>
1718
+ {/if}
1719
+ </button>
1720
+ </div>
1798
1721
  {/if}
1799
1722
 
1800
- {#if enable_info_pane && normalized_structure &&
1801
- controls_config.visible(`info-pane`)}
1802
- <StructureInfoPane
1803
- structure={normalized_structure}
1804
- bind:pane_open={info_pane_open}
1805
- bind:highlighted_sites
1806
- bind:hovered_site_idx
1807
- bind:selected_sites
1808
- {sym_data}
1809
- {@attach tooltip({ content: `Structure info pane` })}
1810
- />
1723
+ <!-- Add-atom element input (shown when add_atom_mode is active) -->
1724
+ {#if measure_mode === `edit-atoms` && add_atom_mode}
1725
+ <div class="add-atom-input">
1726
+ <label>
1727
+ <span>Element:</span>
1728
+ <input
1729
+ type="text"
1730
+ bind:value={add_element}
1731
+ maxlength="2"
1732
+ placeholder="C"
1733
+ style="width: 3em; text-align: center"
1734
+ />
1735
+ </label>
1736
+ <span style="font-size: 0.75em; opacity: 0.7">Click to place</span>
1737
+ </div>
1811
1738
  {/if}
1812
1739
 
1813
- {#if controls_config.visible(`export-pane`)}
1814
- <StructureExportPane
1815
- bind:export_pane_open
1816
- structure={normalized_structure}
1817
- {wrapper}
1818
- {scene}
1819
- {camera}
1820
- bind:png_dpi
1821
- pane_props={{ style: `max-height: calc(${height}px - 50px)` }}
1822
- />
1740
+ <!-- Change-element input (shown when 'e' pressed with selection) -->
1741
+ {#if measure_mode === `edit-atoms` && change_element_mode &&
1742
+ selected_sites.length > 0}
1743
+ <div class="add-atom-input">
1744
+ <label>
1745
+ <span>New element:</span>
1746
+ <input
1747
+ type="text"
1748
+ bind:value={change_element_value}
1749
+ maxlength="2"
1750
+ placeholder="Fe"
1751
+ style="width: 3em; text-align: center"
1752
+ onkeydown={(event: KeyboardEvent) => {
1753
+ if (event.key === `Enter`) {
1754
+ handle_change_element(change_element_value)
1755
+ } else if (event.key === `Escape`) {
1756
+ change_element_mode = false
1757
+ }
1758
+ event.stopPropagation()
1759
+ }}
1760
+ {@attach (node: HTMLInputElement) => {
1761
+ node.focus()
1762
+ }}
1763
+ />
1764
+ </label>
1765
+ <span style="font-size: 0.75em; opacity: 0.7">Enter to apply</span>
1766
+ </div>
1823
1767
  {/if}
1768
+ {/if}
1824
1769
 
1825
- {#if controls_config.visible(`controls`)}
1826
- <StructureControls
1827
- bind:controls_open
1828
- bind:scene_props
1829
- bind:lattice_props
1830
- bind:show_image_atoms
1831
- bind:supercell_scaling
1832
- bind:background_color
1833
- bind:background_opacity
1834
- bind:color_scheme
1835
- bind:atom_color_config
1836
- bind:cell_type
1837
- bind:volumetric_data
1838
- bind:isosurface_settings
1839
- bind:active_volume_idx
1840
- {structure}
1841
- {supercell_loading}
1842
- {sym_data}
1843
- />
1844
- {/if}
1770
+ {#if enable_info_pane && normalized_structure &&
1771
+ controls_config.visible(`info-pane`)}
1772
+ <StructureInfoPane
1773
+ structure={normalized_structure}
1774
+ bind:pane_open={info_pane_open}
1775
+ bind:highlighted_sites
1776
+ bind:hovered_site_idx
1777
+ bind:selected_sites
1778
+ {sym_data}
1779
+ {@attach tooltip({ content: `Structure info pane` })}
1780
+ />
1781
+ {/if}
1782
+
1783
+ {#if controls_config.visible(`export-pane`)}
1784
+ <StructureExportPane
1785
+ bind:export_pane_open
1786
+ structure={normalized_structure}
1787
+ {wrapper}
1788
+ {scene}
1789
+ {camera}
1790
+ bind:png_dpi
1791
+ pane_props={{ style: `max-height: calc(${height}px - 50px)` }}
1792
+ />
1793
+ {/if}
1845
1794
 
1846
- {@render top_right_controls?.()}
1795
+ {#if controls_config.visible(`controls`)}
1796
+ <StructureControls
1797
+ bind:controls_open
1798
+ bind:scene_props
1799
+ bind:lattice_props
1800
+ bind:show_image_atoms
1801
+ bind:supercell_scaling
1802
+ bind:background_color
1803
+ bind:background_opacity
1804
+ bind:color_scheme
1805
+ bind:atom_color_config
1806
+ bind:cell_type
1807
+ bind:volumetric_data
1808
+ bind:isosurface_settings
1809
+ bind:active_volume_idx
1810
+ {structure}
1811
+ {supercell_loading}
1812
+ {sym_data}
1813
+ {polyhedra_rendered_elements}
1814
+ />
1847
1815
  {/if}
1848
- </section>
1816
+
1817
+ {@render top_right_controls?.()}
1818
+ </ViewerChrome>
1849
1819
 
1850
1820
  <AtomLegend
1851
1821
  bind:atom_color_config
@@ -1861,15 +1831,18 @@
1861
1831
  show_mode_toggle={viewer_active}
1862
1832
  {sym_data}
1863
1833
  >
1864
- {#if structure && `lattice` in structure}
1865
- <CellSelect
1866
- bind:supercell_scaling
1867
- bind:cell_type
1868
- {sym_data}
1869
- loading={supercell_loading}
1870
- direction="up"
1871
- />
1872
- {/if}
1834
+ {#snippet children({ mode_menu_open })}
1835
+ {#if structure && `lattice` in structure}
1836
+ <CellSelect
1837
+ bind:supercell_scaling
1838
+ bind:cell_type
1839
+ {sym_data}
1840
+ loading={supercell_loading}
1841
+ direction="up"
1842
+ suppress_hover={mode_menu_open}
1843
+ />
1844
+ {/if}
1845
+ {/snippet}
1873
1846
  </AtomLegend>
1874
1847
 
1875
1848
  <!-- prevent from rendering in vitest runner since WebGLRenderingContext not available -->
@@ -1918,6 +1891,7 @@
1918
1891
  bind:add_element
1919
1892
  bind:cursor={canvas_cursor}
1920
1893
  bind:dragging_atoms
1894
+ bind:polyhedra_rendered_elements
1921
1895
  />
1922
1896
  </Canvas>
1923
1897
  </div>
@@ -1986,7 +1960,7 @@
1986
1960
  }
1987
1961
  /* Avoid accidental text selection while interacting with the viewer */
1988
1962
  .structure :global(canvas),
1989
- .structure section.control-buttons,
1963
+ .structure :global(section.control-buttons),
1990
1964
  .structure .bottom-left {
1991
1965
  user-select: none;
1992
1966
  }
@@ -1997,44 +1971,6 @@
1997
1971
  font-size: var(--struct-bottom-left-font-size, 1.2em);
1998
1972
  padding: var(--struct-bottom-left-padding, 1pt 5pt);
1999
1973
  }
2000
- section.control-buttons {
2001
- position: absolute;
2002
- display: flex;
2003
- top: var(--struct-buttons-top, var(--ctrl-btn-top, 1ex));
2004
- right: var(--struct-buttons-right, var(--ctrl-btn-right, 1ex));
2005
- gap: 4pt;
2006
- /* buttons need higher z-index than AtomLegend to make info/controls panes occlude legend */
2007
- /* we also need crazy high z-index to make info/control pane occlude threlte/extras' <HTML> elements for site labels */
2008
- z-index: var(
2009
- --struct-buttons-z-index,
2010
- var(--z-index-overlay-controls, 100000000)
2011
- );
2012
- opacity: 0;
2013
- pointer-events: none;
2014
- transition: opacity 0.2s ease;
2015
- }
2016
- /* Mode: always - controls always visible */
2017
- section.control-buttons.always-visible {
2018
- opacity: 1;
2019
- pointer-events: auto;
2020
- }
2021
- /* Mode: hover - controls visible on component hover */
2022
- .structure:hover section.control-buttons.hover-visible,
2023
- .structure:focus-within section.control-buttons.hover-visible {
2024
- opacity: 1;
2025
- pointer-events: auto;
2026
- }
2027
- /* Mode: never - stays hidden (default state, no additional CSS needed) */
2028
- section.control-buttons > :global(button) {
2029
- background-color: transparent;
2030
- display: flex;
2031
- padding: 1px 6px;
2032
- border-radius: var(--border-radius, 3pt);
2033
- font-size: clamp(0.85em, 2cqmin, 1.3em);
2034
- }
2035
- section.control-buttons :global(button:hover) {
2036
- background-color: color-mix(in srgb, currentColor 8%, transparent);
2037
- }
2038
1974
  /* Match Trajectory dropdown UI */
2039
1975
  .view-mode-dropdown {
2040
1976
  position: absolute;