matterviz 0.3.7 → 0.4.0

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 (324) hide show
  1. package/dist/Icon.svelte +7 -4
  2. package/dist/MillerIndexInput.svelte +1 -1
  3. package/dist/api/optimade.js +32 -26
  4. package/dist/app.css +0 -3
  5. package/dist/brillouin/BrillouinZone.svelte +8 -3
  6. package/dist/brillouin/BrillouinZone.svelte.d.ts +2 -1
  7. package/dist/brillouin/BrillouinZoneScene.svelte +52 -6
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -0
  9. package/dist/brillouin/BrillouinZoneTooltip.svelte +16 -25
  10. package/dist/brillouin/compute.js +10 -14
  11. package/dist/chempot-diagram/ChemPotDiagram.svelte +14 -13
  12. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +12 -15
  13. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +8 -10
  14. package/dist/chempot-diagram/async-compute.svelte.js +3 -1
  15. package/dist/chempot-diagram/chempot-worker.js +2 -1
  16. package/dist/chempot-diagram/compute.d.ts +1 -1
  17. package/dist/chempot-diagram/compute.js +17 -19
  18. package/dist/colors/index.js +6 -5
  19. package/dist/composition/FormulaFilter.svelte +12 -6
  20. package/dist/composition/PieChart.svelte +6 -5
  21. package/dist/composition/chem-sys.d.ts +8 -0
  22. package/dist/composition/chem-sys.js +85 -0
  23. package/dist/composition/format.js +4 -2
  24. package/dist/composition/index.d.ts +1 -0
  25. package/dist/composition/index.js +1 -0
  26. package/dist/composition/parse.js +25 -13
  27. package/dist/convex-hull/ConvexHull2D.svelte +12 -10
  28. package/dist/convex-hull/ConvexHull3D.svelte +5 -5
  29. package/dist/convex-hull/ConvexHull4D.svelte +5 -9
  30. package/dist/convex-hull/ConvexHullStats.svelte +12 -12
  31. package/dist/convex-hull/GasPressureControls.svelte +4 -4
  32. package/dist/convex-hull/TemperatureSlider.svelte +2 -2
  33. package/dist/convex-hull/demo-temperature.d.ts +1 -1
  34. package/dist/convex-hull/demo-temperature.js +20 -22
  35. package/dist/convex-hull/gas-thermodynamics.d.ts +2 -2
  36. package/dist/convex-hull/gas-thermodynamics.js +22 -30
  37. package/dist/convex-hull/helpers.d.ts +3 -0
  38. package/dist/convex-hull/helpers.js +17 -9
  39. package/dist/convex-hull/index.d.ts +1 -1
  40. package/dist/convex-hull/thermodynamics.js +83 -78
  41. package/dist/convex-hull/types.d.ts +1 -1
  42. package/dist/coordination/CoordinationBarPlot.svelte +23 -23
  43. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
  44. package/dist/element/ElementTile.svelte.d.ts +1 -1
  45. package/dist/fermi-surface/FermiSlice.svelte +13 -5
  46. package/dist/fermi-surface/FermiSurface.svelte +11 -5
  47. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  48. package/dist/fermi-surface/FermiSurfaceControls.svelte +1 -1
  49. package/dist/fermi-surface/FermiSurfaceScene.svelte +3 -0
  50. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +8 -34
  51. package/dist/fermi-surface/compute.js +59 -59
  52. package/dist/fermi-surface/export.js +3 -2
  53. package/dist/fermi-surface/parse.js +7 -4
  54. package/dist/fermi-surface/types.d.ts +1 -0
  55. package/dist/heatmap-matrix/HeatmapMatrix.svelte +23 -21
  56. package/dist/heatmap-matrix/index.js +1 -1
  57. package/dist/io/decompress.js +4 -2
  58. package/dist/io/export.d.ts +4 -4
  59. package/dist/io/export.js +47 -25
  60. package/dist/io/fetch.js +5 -1
  61. package/dist/io/file-drop.d.ts +1 -1
  62. package/dist/io/file-drop.js +35 -36
  63. package/dist/io/url-drop.js +64 -33
  64. package/dist/isosurface/parse.js +6 -7
  65. package/dist/isosurface/slice.js +5 -4
  66. package/dist/isosurface/types.js +1 -1
  67. package/dist/keyboard.d.ts +3 -0
  68. package/dist/keyboard.js +23 -0
  69. package/dist/labels.d.ts +1 -1
  70. package/dist/labels.js +8 -7
  71. package/dist/layout/PropertyFilter.svelte +3 -2
  72. package/dist/layout/SettingsSection.svelte +1 -1
  73. package/dist/layout/json-tree/JsonNode.svelte +1 -1
  74. package/dist/layout/json-tree/JsonTree.svelte +2 -2
  75. package/dist/layout/json-tree/utils.js +5 -4
  76. package/dist/marching-cubes.js +8 -13
  77. package/dist/math.d.ts +5 -1
  78. package/dist/math.js +24 -9
  79. package/dist/overlays/DraggablePane.svelte +4 -4
  80. package/dist/periodic-table/PeriodicTable.svelte +20 -9
  81. package/dist/periodic-table/PropertySelect.svelte +1 -0
  82. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +9 -3
  83. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  84. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  85. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
  86. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +1 -1
  87. package/dist/phase-diagram/build-diagram.js +2 -2
  88. package/dist/phase-diagram/parse.js +6 -5
  89. package/dist/phase-diagram/types.d.ts +1 -1
  90. package/dist/phase-diagram/utils.d.ts +3 -3
  91. package/dist/phase-diagram/utils.js +8 -12
  92. package/dist/plot/{BarPlot.svelte → bar/BarPlot.svelte} +229 -587
  93. package/dist/plot/{BarPlot.svelte.d.ts → bar/BarPlot.svelte.d.ts} +5 -5
  94. package/dist/plot/{BarPlotControls.svelte → bar/BarPlotControls.svelte} +6 -5
  95. package/dist/plot/{BarPlotControls.svelte.d.ts → bar/BarPlotControls.svelte.d.ts} +3 -3
  96. package/dist/plot/{SpacegroupBarPlot.svelte → bar/SpacegroupBarPlot.svelte} +6 -6
  97. package/dist/plot/{SpacegroupBarPlot.svelte.d.ts → bar/SpacegroupBarPlot.svelte.d.ts} +1 -1
  98. package/dist/plot/bar/data.d.ts +40 -0
  99. package/dist/plot/bar/data.js +154 -0
  100. package/dist/plot/bar/geometry.d.ts +39 -0
  101. package/dist/plot/bar/geometry.js +60 -0
  102. package/dist/plot/bar/index.d.ts +3 -0
  103. package/dist/plot/bar/index.js +3 -0
  104. package/dist/plot/box/BoxPlot.svelte +1462 -0
  105. package/dist/plot/box/BoxPlot.svelte.d.ts +94 -0
  106. package/dist/plot/box/BoxPlotControls.svelte +109 -0
  107. package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
  108. package/dist/plot/box/Violin.svelte +14 -0
  109. package/dist/plot/box/Violin.svelte.d.ts +70 -0
  110. package/dist/plot/box/box-plot.d.ts +55 -0
  111. package/dist/plot/box/box-plot.js +126 -0
  112. package/dist/plot/box/index.d.ts +5 -0
  113. package/dist/plot/box/index.js +5 -0
  114. package/dist/plot/box/kde.d.ts +16 -0
  115. package/dist/plot/box/kde.js +160 -0
  116. package/dist/plot/box/quantile.d.ts +3 -0
  117. package/dist/plot/box/quantile.js +53 -0
  118. package/dist/plot/{auto-place.js → core/auto-place.js} +2 -2
  119. package/dist/plot/core/axis-utils.d.ts +46 -0
  120. package/dist/plot/core/axis-utils.js +110 -0
  121. package/dist/plot/{AxisLabel.svelte → core/components/AxisLabel.svelte} +2 -2
  122. package/dist/plot/{AxisLabel.svelte.d.ts → core/components/AxisLabel.svelte.d.ts} +1 -1
  123. package/dist/plot/{ColorBar.svelte → core/components/ColorBar.svelte} +36 -33
  124. package/dist/plot/{ColorBar.svelte.d.ts → core/components/ColorBar.svelte.d.ts} +2 -2
  125. package/dist/plot/{ColorScaleSelect.svelte → core/components/ColorScaleSelect.svelte} +4 -3
  126. package/dist/plot/{ColorScaleSelect.svelte.d.ts → core/components/ColorScaleSelect.svelte.d.ts} +2 -2
  127. package/dist/plot/core/components/ControlPane.svelte +46 -0
  128. package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
  129. package/dist/plot/{FillArea.svelte → core/components/FillArea.svelte} +17 -6
  130. package/dist/plot/{FillArea.svelte.d.ts → core/components/FillArea.svelte.d.ts} +1 -1
  131. package/dist/plot/{InteractiveAxisLabel.svelte → core/components/InteractiveAxisLabel.svelte} +3 -3
  132. package/dist/plot/{InteractiveAxisLabel.svelte.d.ts → core/components/InteractiveAxisLabel.svelte.d.ts} +2 -2
  133. package/dist/plot/{Line.svelte → core/components/Line.svelte} +30 -13
  134. package/dist/plot/{PlotAxis.svelte → core/components/PlotAxis.svelte} +7 -5
  135. package/dist/plot/{PlotAxis.svelte.d.ts → core/components/PlotAxis.svelte.d.ts} +3 -2
  136. package/dist/plot/{PlotControls.svelte → core/components/PlotControls.svelte} +17 -29
  137. package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
  138. package/dist/plot/{PlotLegend.svelte → core/components/PlotLegend.svelte} +21 -10
  139. package/dist/plot/{PlotLegend.svelte.d.ts → core/components/PlotLegend.svelte.d.ts} +3 -2
  140. package/dist/plot/{PlotTooltip.svelte → core/components/PlotTooltip.svelte} +17 -1
  141. package/dist/plot/{PlotTooltip.svelte.d.ts → core/components/PlotTooltip.svelte.d.ts} +8 -0
  142. package/dist/plot/{PortalSelect.svelte → core/components/PortalSelect.svelte} +11 -7
  143. package/dist/plot/{ReferenceLine.svelte → core/components/ReferenceLine.svelte} +3 -3
  144. package/dist/plot/{ReferenceLine.svelte.d.ts → core/components/ReferenceLine.svelte.d.ts} +1 -1
  145. package/dist/plot/{ReferenceLine3D.svelte → core/components/ReferenceLine3D.svelte} +4 -4
  146. package/dist/plot/{ReferenceLine3D.svelte.d.ts → core/components/ReferenceLine3D.svelte.d.ts} +2 -2
  147. package/dist/plot/{ReferencePlane.svelte → core/components/ReferencePlane.svelte} +7 -7
  148. package/dist/plot/{ReferencePlane.svelte.d.ts → core/components/ReferencePlane.svelte.d.ts} +2 -2
  149. package/dist/plot/{ZeroLines.svelte → core/components/ZeroLines.svelte} +3 -3
  150. package/dist/plot/{ZeroLines.svelte.d.ts → core/components/ZeroLines.svelte.d.ts} +3 -3
  151. package/dist/plot/{ZoomRect.svelte → core/components/ZoomRect.svelte} +1 -1
  152. package/dist/plot/{ZoomRect.svelte.d.ts → core/components/ZoomRect.svelte.d.ts} +1 -1
  153. package/dist/plot/core/components/index.d.ts +17 -0
  154. package/dist/plot/core/components/index.js +17 -0
  155. package/dist/plot/{data-cleaning.d.ts → core/data-cleaning.d.ts} +71 -1
  156. package/dist/plot/{data-cleaning.js → core/data-cleaning.js} +3 -5
  157. package/dist/plot/{data-transform.d.ts → core/data-transform.d.ts} +2 -2
  158. package/dist/plot/{data-transform.js → core/data-transform.js} +3 -3
  159. package/dist/plot/core/fill-utils.d.ts +33 -0
  160. package/dist/plot/core/fill-utils.js +388 -0
  161. package/dist/plot/{hover-lock.svelte.js → core/hover-lock.svelte.js} +5 -6
  162. package/dist/plot/core/index.d.ts +10 -0
  163. package/dist/plot/core/index.js +11 -0
  164. package/dist/plot/core/interactions.d.ts +35 -0
  165. package/dist/plot/core/interactions.js +195 -0
  166. package/dist/plot/{layout.d.ts → core/layout.d.ts} +1 -0
  167. package/dist/plot/{layout.js → core/layout.js} +16 -8
  168. package/dist/plot/{reference-line.d.ts → core/reference-line.d.ts} +1 -1
  169. package/dist/plot/{reference-line.js → core/reference-line.js} +23 -36
  170. package/dist/plot/{scales.d.ts → core/scales.d.ts} +2 -2
  171. package/dist/plot/{scales.js → core/scales.js} +84 -85
  172. package/dist/plot/core/svg.d.ts +2 -0
  173. package/dist/plot/core/svg.js +41 -0
  174. package/dist/plot/{types.d.ts → core/types.d.ts} +19 -79
  175. package/dist/plot/{types.js → core/types.js} +1 -1
  176. package/dist/plot/{utils → core/utils}/label-placement.d.ts +2 -2
  177. package/dist/plot/core/utils/series-visibility.d.ts +26 -0
  178. package/dist/plot/{utils → core/utils}/series-visibility.js +29 -2
  179. package/dist/plot/core/utils.d.ts +11 -0
  180. package/dist/plot/core/utils.js +27 -0
  181. package/dist/plot/{Histogram.svelte → histogram/Histogram.svelte} +154 -294
  182. package/dist/plot/{Histogram.svelte.d.ts → histogram/Histogram.svelte.d.ts} +2 -2
  183. package/dist/plot/{HistogramControls.svelte → histogram/HistogramControls.svelte} +6 -6
  184. package/dist/plot/{HistogramControls.svelte.d.ts → histogram/HistogramControls.svelte.d.ts} +4 -4
  185. package/dist/plot/histogram/index.d.ts +2 -0
  186. package/dist/plot/histogram/index.js +2 -0
  187. package/dist/plot/index.d.ts +8 -41
  188. package/dist/plot/index.js +10 -39
  189. package/dist/plot/sankey/Sankey.svelte +700 -0
  190. package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
  191. package/dist/plot/sankey/SankeyControls.svelte +98 -0
  192. package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
  193. package/dist/plot/sankey/index.d.ts +4 -0
  194. package/dist/plot/sankey/index.js +3 -0
  195. package/dist/plot/sankey/sankey-types.d.ts +42 -0
  196. package/dist/plot/sankey/sankey-types.js +4 -0
  197. package/dist/plot/sankey/sankey.d.ts +52 -0
  198. package/dist/plot/sankey/sankey.js +187 -0
  199. package/dist/plot/{BinnedScatterPlot.svelte → scatter/BinnedScatterPlot.svelte} +61 -59
  200. package/dist/plot/{BinnedScatterPlot.svelte.d.ts → scatter/BinnedScatterPlot.svelte.d.ts} +4 -4
  201. package/dist/plot/{ElementScatter.svelte → scatter/ElementScatter.svelte} +6 -6
  202. package/dist/plot/{ElementScatter.svelte.d.ts → scatter/ElementScatter.svelte.d.ts} +2 -2
  203. package/dist/plot/{ScatterPlot.svelte → scatter/ScatterPlot.svelte} +221 -642
  204. package/dist/plot/{ScatterPlot.svelte.d.ts → scatter/ScatterPlot.svelte.d.ts} +7 -7
  205. package/dist/plot/{ScatterPlotControls.svelte → scatter/ScatterPlotControls.svelte} +6 -5
  206. package/dist/plot/{ScatterPlotControls.svelte.d.ts → scatter/ScatterPlotControls.svelte.d.ts} +1 -1
  207. package/dist/plot/{ScatterPoint.svelte → scatter/ScatterPoint.svelte} +7 -7
  208. package/dist/plot/{ScatterPoint.svelte.d.ts → scatter/ScatterPoint.svelte.d.ts} +3 -3
  209. package/dist/plot/{adaptive-density.d.ts → scatter/adaptive-density.d.ts} +14 -4
  210. package/dist/plot/{adaptive-density.js → scatter/adaptive-density.js} +46 -20
  211. package/dist/plot/{binned-scatter-types.d.ts → scatter/binned-scatter-types.d.ts} +3 -3
  212. package/dist/plot/scatter/index.d.ts +7 -0
  213. package/dist/plot/scatter/index.js +5 -0
  214. package/dist/plot/scatter/scatter-data.d.ts +19 -0
  215. package/dist/plot/scatter/scatter-data.js +212 -0
  216. package/dist/plot/{ScatterPlot3D.svelte → scatter-3d/ScatterPlot3D.svelte} +12 -10
  217. package/dist/plot/{ScatterPlot3D.svelte.d.ts → scatter-3d/ScatterPlot3D.svelte.d.ts} +7 -7
  218. package/dist/plot/{ScatterPlot3DControls.svelte → scatter-3d/ScatterPlot3DControls.svelte} +5 -4
  219. package/dist/plot/{ScatterPlot3DControls.svelte.d.ts → scatter-3d/ScatterPlot3DControls.svelte.d.ts} +2 -2
  220. package/dist/plot/{ScatterPlot3DScene.svelte → scatter-3d/ScatterPlot3DScene.svelte} +11 -11
  221. package/dist/plot/{ScatterPlot3DScene.svelte.d.ts → scatter-3d/ScatterPlot3DScene.svelte.d.ts} +3 -3
  222. package/dist/plot/{Surface3D.svelte → scatter-3d/Surface3D.svelte} +1 -1
  223. package/dist/plot/{Surface3D.svelte.d.ts → scatter-3d/Surface3D.svelte.d.ts} +1 -1
  224. package/dist/plot/scatter-3d/index.d.ts +4 -0
  225. package/dist/plot/scatter-3d/index.js +4 -0
  226. package/dist/plot/sunburst/Sunburst.svelte +1045 -0
  227. package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -0
  228. package/dist/plot/sunburst/SunburstControls.svelte +200 -0
  229. package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
  230. package/dist/plot/sunburst/index.d.ts +4 -0
  231. package/dist/plot/sunburst/index.js +4 -0
  232. package/dist/plot/sunburst/render.d.ts +34 -0
  233. package/dist/plot/sunburst/render.js +122 -0
  234. package/dist/plot/sunburst/sunburst.d.ts +62 -0
  235. package/dist/plot/sunburst/sunburst.js +266 -0
  236. package/dist/rdf/RdfPlot.svelte +2 -1
  237. package/dist/rdf/calc-rdf.js +11 -24
  238. package/dist/sanitize.js +1 -1
  239. package/dist/settings.d.ts +65 -1
  240. package/dist/settings.js +262 -0
  241. package/dist/spectral/Bands.svelte +39 -29
  242. package/dist/spectral/Bands.svelte.d.ts +3 -4
  243. package/dist/spectral/BandsAndDos.svelte +1 -1
  244. package/dist/spectral/BrillouinBandsDos.svelte +39 -27
  245. package/dist/spectral/Dos.svelte +10 -19
  246. package/dist/spectral/Dos.svelte.d.ts +2 -2
  247. package/dist/spectral/helpers.d.ts +3 -1
  248. package/dist/spectral/helpers.js +95 -29
  249. package/dist/structure/AtomLegend.svelte +8 -9
  250. package/dist/structure/CellSelect.svelte +1 -2
  251. package/dist/structure/Cylinder.svelte +12 -8
  252. package/dist/structure/Cylinder.svelte.d.ts +4 -1
  253. package/dist/structure/Structure.svelte +78 -72
  254. package/dist/structure/Structure.svelte.d.ts +1 -1
  255. package/dist/structure/StructureInfoPane.svelte +5 -6
  256. package/dist/structure/StructureScene.svelte +11 -10
  257. package/dist/structure/atom-properties.js +6 -6
  258. package/dist/structure/bond-order-perception.js +1 -1
  259. package/dist/structure/bonding.d.ts +1 -0
  260. package/dist/structure/bonding.js +43 -15
  261. package/dist/structure/export.js +27 -23
  262. package/dist/structure/index.d.ts +2 -4
  263. package/dist/structure/index.js +1 -3
  264. package/dist/structure/label-placement.js +4 -4
  265. package/dist/structure/measure.d.ts +3 -2
  266. package/dist/structure/measure.js +6 -5
  267. package/dist/structure/parse.js +121 -103
  268. package/dist/structure/pbc.js +4 -0
  269. package/dist/symmetry/SymmetryStats.svelte +2 -2
  270. package/dist/symmetry/index.d.ts +1 -1
  271. package/dist/symmetry/index.js +22 -24
  272. package/dist/symmetry/spacegroups.d.ts +7 -0
  273. package/dist/symmetry/spacegroups.js +48 -13
  274. package/dist/table/HeatmapTable.svelte +63 -11
  275. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  276. package/dist/table/index.d.ts +1 -3
  277. package/dist/table/index.js +1 -1
  278. package/dist/theme/index.js +8 -8
  279. package/dist/tooltip/KCoords.svelte +45 -0
  280. package/dist/tooltip/KCoords.svelte.d.ts +8 -0
  281. package/dist/tooltip/index.d.ts +1 -0
  282. package/dist/tooltip/index.js +1 -0
  283. package/dist/trajectory/Trajectory.svelte +66 -40
  284. package/dist/trajectory/Trajectory.svelte.d.ts +2 -1
  285. package/dist/trajectory/TrajectoryExportPane.svelte +2 -1
  286. package/dist/trajectory/TrajectoryInfoPane.svelte +2 -1
  287. package/dist/trajectory/format-detect.d.ts +1 -0
  288. package/dist/trajectory/format-detect.js +25 -11
  289. package/dist/trajectory/frame-reader.js +17 -50
  290. package/dist/trajectory/helpers.js +1 -1
  291. package/dist/trajectory/index.js +1 -1
  292. package/dist/trajectory/parse/hdf5.js +1 -1
  293. package/dist/trajectory/parse/index.js +14 -6
  294. package/dist/trajectory/parse/vasp.js +36 -17
  295. package/dist/trajectory/parse/xyz.d.ts +24 -0
  296. package/dist/trajectory/parse/xyz.js +102 -89
  297. package/dist/trajectory/plotting.d.ts +1 -1
  298. package/dist/trajectory/plotting.js +15 -15
  299. package/dist/utils.d.ts +1 -0
  300. package/dist/utils.js +6 -4
  301. package/dist/xrd/XrdPlot.svelte +2 -1
  302. package/dist/xrd/calc-xrd.js +15 -12
  303. package/dist/xrd/parse.js +2 -2
  304. package/package.json +22 -18
  305. package/dist/plot/PlotControls.svelte.d.ts +0 -4
  306. package/dist/plot/axis-utils.d.ts +0 -19
  307. package/dist/plot/axis-utils.js +0 -78
  308. package/dist/plot/defaults.d.ts +0 -19
  309. package/dist/plot/defaults.js +0 -9
  310. package/dist/plot/fill-utils.d.ts +0 -46
  311. package/dist/plot/fill-utils.js +0 -322
  312. package/dist/plot/interactions.d.ts +0 -12
  313. package/dist/plot/interactions.js +0 -101
  314. package/dist/plot/svg.d.ts +0 -1
  315. package/dist/plot/svg.js +0 -11
  316. package/dist/plot/utils/series-visibility.d.ts +0 -15
  317. package/dist/plot/utils.d.ts +0 -1
  318. package/dist/plot/utils.js +0 -14
  319. /package/dist/plot/{auto-place.d.ts → core/auto-place.d.ts} +0 -0
  320. /package/dist/plot/{Line.svelte.d.ts → core/components/Line.svelte.d.ts} +0 -0
  321. /package/dist/plot/{PortalSelect.svelte.d.ts → core/components/PortalSelect.svelte.d.ts} +0 -0
  322. /package/dist/plot/{hover-lock.svelte.d.ts → core/hover-lock.svelte.d.ts} +0 -0
  323. /package/dist/plot/{utils → core/utils}/label-placement.js +0 -0
  324. /package/dist/plot/{binned-scatter-types.js → scatter/binned-scatter-types.js} +0 -0
@@ -40,24 +40,20 @@ export function spacegroup_num_to_crystal_sys(spacegroup) {
40
40
  }
41
41
  // Convert space group (number or symbol) to crystal system
42
42
  export function spacegroup_to_crystal_sys(spacegroup) {
43
- if (typeof spacegroup === `number`)
44
- return spacegroup_num_to_crystal_sys(spacegroup);
45
- // Try to parse as symbol
46
- const number = SPACEGROUP_SYMBOL_TO_NUM[spacegroup];
47
- if (number !== undefined)
48
- return spacegroup_num_to_crystal_sys(number);
49
- // Try to parse string as number
50
- const parsed = parseInt(spacegroup, 10);
51
- if (!isNaN(parsed))
52
- return spacegroup_num_to_crystal_sys(parsed);
53
- return null;
43
+ const num = normalize_spacegroup(spacegroup);
44
+ return num == null ? null : spacegroup_num_to_crystal_sys(num);
54
45
  }
55
- // Normalize space group input to number
46
+ // Normalize space group input (number, Hermann-Mauguin symbol, or numeric string
47
+ // like "225") to a space group number in [1, 230], or null if invalid
56
48
  export function normalize_spacegroup(spacegroup) {
57
49
  if (typeof spacegroup === `number`) {
58
50
  return spacegroup >= 1 && spacegroup <= 230 ? spacegroup : null;
59
51
  }
60
- return SPACEGROUP_SYMBOL_TO_NUM[spacegroup] ?? null;
52
+ const from_symbol = SPACEGROUP_SYMBOL_TO_NUM[spacegroup];
53
+ if (from_symbol !== undefined)
54
+ return from_symbol;
55
+ const parsed = parseInt(spacegroup, 10);
56
+ return isNaN(parsed) ? null : normalize_spacegroup(parsed);
61
57
  }
62
58
  export const SPACEGROUP_SYMBOL_TO_NUM = {
63
59
  // Triclinic
@@ -392,3 +388,42 @@ export const SPACEGROUP_NUM_TO_SYMBOL = Object.entries(SPACEGROUP_SYMBOL_TO_NUM)
392
388
  acc[num] = symbol;
393
389
  return acc;
394
390
  }, {});
391
+ // Build crystal-system -> spacegroup hierarchy data for the Sunburst component from a
392
+ // list of spacegroup numbers or symbols (one entry per occurrence; counts become leaf
393
+ // values). Leaf ids follow the pymatviz spacegroup_sunburst scheme, e.g. "cubic/225".
394
+ export function spacegroup_sunburst_data(spacegroups) {
395
+ const counts = new Map();
396
+ let n_invalid = 0;
397
+ for (const spacegroup of spacegroups) {
398
+ const num = normalize_spacegroup(spacegroup); // numbers, symbols, numeric strings
399
+ if (num == null) {
400
+ n_invalid += 1;
401
+ continue;
402
+ }
403
+ counts.set(num, (counts.get(num) ?? 0) + 1);
404
+ }
405
+ if (n_invalid > 0) {
406
+ console.warn(`spacegroup_sunburst_data: skipped ${n_invalid} invalid spacegroup(s) (expected numbers 1-230 or Hermann-Mauguin symbols)`);
407
+ }
408
+ // Emit crystal systems in canonical (space group number) order, only those present
409
+ return CRYSTAL_SYSTEMS.flatMap((system) => {
410
+ const nums = [...counts.keys()]
411
+ .filter((num) => spacegroup_num_to_crystal_sys(num) === system)
412
+ .sort((num_a, num_b) => num_a - num_b);
413
+ if (nums.length === 0)
414
+ return [];
415
+ return [
416
+ {
417
+ id: system,
418
+ label: system,
419
+ color: CRYSTAL_SYSTEM_COLORS[system],
420
+ children: nums.map((num) => ({
421
+ id: `${system}/${num}`,
422
+ label: SPACEGROUP_NUM_TO_SYMBOL[num] ?? `${num}`,
423
+ value: counts.get(num),
424
+ metadata: { spacegroup: num, crystal_system: system },
425
+ })),
426
+ },
427
+ ];
428
+ });
429
+ }
@@ -32,6 +32,57 @@
32
32
  const is_invalid = (val: unknown) =>
33
33
  val == null || (typeof val === `number` && Number.isNaN(val))
34
34
 
35
+ // tooltip() wires [title]/[aria-label]/[data-title] elements once when it runs.
36
+ // Table cells are replaced when the table re-renders (sort, filter, data or
37
+ // pagination changes), which would silently drop their tooltips. Observe the
38
+ // container and incrementally wire newly added elements / unwire removed ones,
39
+ // instead of tearing down and rebuilding every tooltip on each unrelated DOM
40
+ // mutation (dropdowns, panes, pagination, context menu).
41
+ const tooltip_selector = `[title], [aria-label], [data-title]`
42
+ function table_tooltips(node: HTMLElement) {
43
+ const options = { allow_html: true } as const
44
+ // Per-element cleanups so individual nodes can be unwired as they leave the DOM.
45
+ const wired = new SvelteMap<Element, () => void>()
46
+
47
+ const wire = (root: Element) => {
48
+ const targets = root.matches(tooltip_selector)
49
+ ? [root, ...root.querySelectorAll(tooltip_selector)]
50
+ : [...root.querySelectorAll(tooltip_selector)]
51
+ for (const el of targets) {
52
+ if (!(el instanceof HTMLElement) || wired.has(el)) continue
53
+ // tooltip() only mutates attributes (title -> data-original-title), never
54
+ // childList, so wiring here can't re-trigger the childList observer below.
55
+ const cleanup = tooltip(options)(el)
56
+ if (cleanup) wired.set(el, cleanup)
57
+ }
58
+ }
59
+
60
+ wire(node)
61
+ const observer = new MutationObserver((mutations) => {
62
+ // Unwire elements that left the DOM. isConnected stays true for moved nodes
63
+ // (e.g. row reordering on sort), so those keep their tooltips without churn.
64
+ // Deleting the current entry mid-iteration is safe for Map.
65
+ for (const [el, cleanup] of wired) {
66
+ if (!el.isConnected) {
67
+ cleanup()
68
+ wired.delete(el)
69
+ }
70
+ }
71
+ // Wire only the freshly added subtrees, not the whole container.
72
+ for (const { addedNodes } of mutations) {
73
+ for (const added of addedNodes) {
74
+ if (added instanceof Element) wire(added)
75
+ }
76
+ }
77
+ })
78
+ observer.observe(node, { childList: true, subtree: true })
79
+ return () => {
80
+ observer.disconnect()
81
+ for (const cleanup of wired.values()) cleanup()
82
+ wired.clear()
83
+ }
84
+ }
85
+
35
86
  const NUMERIC_WITH_ERROR_RE =
36
87
  /^([-+−]?(?:\d+\.?\d*|\d*\.\d+)(?:[eE][-+−]?\d+)?)\s*(?:±|\+[-−]|\()/
37
88
 
@@ -484,7 +535,7 @@
484
535
 
485
536
  // Push invalid values to bottom
486
537
  if (is_invalid(val1) || is_invalid(val2)) {
487
- return +is_invalid(val1) - +is_invalid(val2)
538
+ return Number(is_invalid(val1)) - Number(is_invalid(val2))
488
539
  }
489
540
 
490
541
  const sort_val1 = get_sort_val(val1)
@@ -497,10 +548,11 @@
497
548
  sensitivity: `base`,
498
549
  })
499
550
  if (cmp !== 0) return cmp * modifier
500
- } else {
501
- if (sort_val1 !== sort_val2) {
502
- return (sort_val1 ?? 0) < (sort_val2 ?? 0) ? -modifier : modifier
503
- }
551
+ } else if (typeof sort_val1 !== typeof sort_val2) {
552
+ // number<string is false both ways, breaking the comparator: numbers sort first
553
+ return (typeof sort_val1 === `number` ? -1 : 1) * modifier
554
+ } else if (sort_val1 !== sort_val2) {
555
+ return (sort_val1 ?? 0) < (sort_val2 ?? 0) ? -modifier : modifier
504
556
  }
505
557
  }
506
558
  return 0
@@ -558,7 +610,7 @@
558
610
  // Shift+click for multi-column sort
559
611
  if (event.shiftKey) {
560
612
  const existing_idx = multi_sort.findIndex((sort_entry) => sort_entry.column === col_id)
561
- if (existing_idx >= 0) {
613
+ if (existing_idx !== -1) {
562
614
  // Toggle direction or remove if clicked again
563
615
  const existing = multi_sort[existing_idx]
564
616
  if (existing.ascending === (col.better === `lower`)) {
@@ -684,7 +736,7 @@
684
736
 
685
737
  // Check multi-sort first
686
738
  const multi_idx = multi_sort.findIndex((sort_entry) => sort_entry.column === col_id)
687
- if (multi_idx >= 0) {
739
+ if (multi_idx !== -1) {
688
740
  const arrow = multi_sort[multi_idx].ascending ? `↓` : `↑`
689
741
  const badge = multi_sort.length > 1 ? `<sup>${multi_idx + 1}</sup>` : ``
690
742
  return `<span style="font-size: 0.8em;">${arrow}${badge}</span>`
@@ -716,7 +768,7 @@
716
768
  function toggle_row_select(row: RowData) {
717
769
  const row_id = get_row_id(row)
718
770
  const idx = selected_rows.findIndex((selected_row) => get_row_id(selected_row) === row_id)
719
- if (idx >= 0) {
771
+ if (idx !== -1) {
720
772
  selected_rows = selected_rows.filter((_, i) => i !== idx)
721
773
  } else {
722
774
  selected_rows = [...selected_rows, row]
@@ -755,7 +807,7 @@
755
807
  const quote = (str: string) => {
756
808
  if (!csv_quote) return str
757
809
  if (str.includes(`,`) || str.includes(`"`) || str.includes(`\n`)) {
758
- return `"${str.replace(/"/g, `""`)}"`
810
+ return `"${str.replaceAll('"', `""`)}"`
759
811
  }
760
812
  return str
761
813
  }
@@ -799,7 +851,7 @@
799
851
  const link = document.createElement(`a`)
800
852
  link.href = url
801
853
  link.download = filename
802
- document.body.appendChild(link)
854
+ document.body.append(link)
803
855
  link.click()
804
856
  document.body.removeChild(link)
805
857
  URL.revokeObjectURL(url)
@@ -869,7 +921,7 @@
869
921
  {/snippet}
870
922
 
871
923
  <div
872
- {@attach tooltip({ allow_html: true })}
924
+ {@attach table_tooltips}
873
925
  {...rest_props}
874
926
  bind:this={container_el}
875
927
  class="table-container {rest_props.class ?? ``}"
@@ -44,6 +44,6 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
44
44
  }]>;
45
45
  footer?: Snippet;
46
46
  };
47
- declare const HeatmapTable: import("svelte").Component<$$ComponentProps, {}, "sort" | "data" | "show_controls" | "loading" | "controls_open" | "show_heatmap" | "column_order" | "selected_rows" | "hidden_columns" | "heatmap_opacity">;
47
+ declare const HeatmapTable: import("svelte").Component<$$ComponentProps, {}, "sort" | "data" | "show_controls" | "controls_open" | "show_heatmap" | "column_order" | "selected_rows" | "hidden_columns" | "loading" | "heatmap_opacity">;
48
48
  type HeatmapTable = ReturnType<typeof HeatmapTable>;
49
49
  export default HeatmapTable;
@@ -2,9 +2,7 @@ import * as d3sc from 'd3-scale-chromatic';
2
2
  import type { Snippet } from 'svelte';
3
3
  export { default as HeatmapTable } from './HeatmapTable.svelte';
4
4
  export { default as ToggleMenu } from './ToggleMenu.svelte';
5
- export type CellVal = string | number | boolean | undefined | null | Record<string, unknown> | {
6
- [key: string]: string | number | null | undefined | boolean;
7
- }[];
5
+ export type CellVal = string | number | boolean | undefined | null | Record<string, unknown> | Record<string, string | number | null | undefined | boolean>[];
8
6
  export type RowData = {
9
7
  style?: string;
10
8
  class?: string;
@@ -5,7 +5,7 @@ import * as d3sc from 'd3-scale-chromatic';
5
5
  export { default as HeatmapTable } from './HeatmapTable.svelte';
6
6
  export { default as ToggleMenu } from './ToggleMenu.svelte';
7
7
  // Strip HTML tags from a string (for search, export, etc.)
8
- export const strip_html = (str) => str.replace(/<[^>]*>/g, ``);
8
+ export const strip_html = (str) => str.replaceAll(/<[^>]*>/g, ``);
9
9
  // Calculate table cell background color based on its value and column config
10
10
  export function calc_cell_color(val, // cell value
11
11
  all_values, // all values in the column
@@ -33,7 +33,7 @@ export const get_theme_preference = () => {
33
33
  return AUTO_THEME;
34
34
  try {
35
35
  const saved = localStorage[storage_key];
36
- return is_valid_theme_mode(saved || ``) ? saved : AUTO_THEME;
36
+ return is_valid_theme_mode(saved ?? ``) ? saved : AUTO_THEME;
37
37
  }
38
38
  catch {
39
39
  return AUTO_THEME;
@@ -57,8 +57,8 @@ export const apply_theme_to_dom = (mode) => {
57
57
  if (!resolved || !(resolved in THEME_TYPE)) {
58
58
  throw new Error(`Invalid theme mode: ${resolved}`);
59
59
  }
60
- const theme = globalThis.MATTERVIZ_THEMES?.[resolved] || {};
61
- const css_vars = globalThis.MATTERVIZ_CSS_MAP || {};
60
+ const theme = globalThis.MATTERVIZ_THEMES?.[resolved] ?? {};
61
+ const css_vars = globalThis.MATTERVIZ_CSS_MAP ?? {};
62
62
  const root = document.documentElement;
63
63
  Object.entries(theme).forEach(([key, value]) => {
64
64
  const css_var = css_vars[key];
@@ -72,8 +72,8 @@ export const apply_theme_to_dom = (mode) => {
72
72
  root.style.setProperty(`color-scheme`, color_scheme);
73
73
  };
74
74
  // Theme getters
75
- export const light_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.light] || {};
76
- export const dark_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.dark] || {};
77
- export const white_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.white] || {};
78
- export const black_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.black] || {};
79
- export const get_theme_by_name = (name) => globalThis.MATTERVIZ_THEMES?.[name] || {};
75
+ export const light_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.light] ?? {};
76
+ export const dark_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.dark] ?? {};
77
+ export const white_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.white] ?? {};
78
+ export const black_theme = () => globalThis.MATTERVIZ_THEMES?.[COLOR_THEMES.black] ?? {};
79
+ export const get_theme_by_name = (name) => globalThis.MATTERVIZ_THEMES?.[name] ?? {};
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ // Shared k-point coordinate rows for hover tooltips (Brillouin zone + Fermi surface)
3
+ import { format_vec3 } from '../labels'
4
+ import type { Vec3 } from '../math'
5
+
6
+ let { cartesian, fractional = null }: {
7
+ cartesian: Vec3
8
+ fractional?: Vec3 | null
9
+ } = $props()
10
+ </script>
11
+
12
+ <div class="k-coord-row">
13
+ <span class="k-coord-label"><span class="vec">k</span> (Å⁻¹):</span>
14
+ <span>{format_vec3(cartesian)}</span>
15
+ </div>
16
+ {#if fractional}
17
+ <div class="k-coord-row">
18
+ <span class="k-coord-label"><span class="vec">k</span> (frac):</span>
19
+ <span>{format_vec3(fractional)}</span>
20
+ </div>
21
+ {/if}
22
+
23
+ <style>
24
+ .k-coord-row {
25
+ display: flex;
26
+ gap: 4px;
27
+ }
28
+ .k-coord-label {
29
+ opacity: 0.8;
30
+ min-width: 50px;
31
+ }
32
+ .vec {
33
+ position: relative;
34
+ font-style: italic;
35
+ }
36
+ .vec::after {
37
+ content: '→';
38
+ position: absolute;
39
+ left: 50%;
40
+ top: -0.85em;
41
+ transform: translateX(-50%);
42
+ font-size: 0.6em;
43
+ font-style: normal;
44
+ }
45
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Vec3 } from '../math';
2
+ type $$ComponentProps = {
3
+ cartesian: Vec3;
4
+ fractional?: Vec3 | null;
5
+ };
6
+ declare const KCoords: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type KCoords = ReturnType<typeof KCoords>;
8
+ export default KCoords;
@@ -1,2 +1,3 @@
1
+ export { default as KCoords } from './KCoords.svelte';
1
2
  export { default as TooltipContent } from './TooltipContent.svelte';
2
3
  export type * from './types';
@@ -1 +1,2 @@
1
+ export { default as KCoords } from './KCoords.svelte';
1
2
  export { default as TooltipContent } from './TooltipContent.svelte';
@@ -6,13 +6,14 @@
6
6
  import Spinner from '../feedback/Spinner.svelte'
7
7
  import Icon from '../Icon.svelte'
8
8
  import { handle_url_drop, load_from_url } from '../io'
9
+ import { forward_window_keydown, handle_and_prevent } from '../keyboard'
9
10
  import { format_num, trajectory_property_config } from '../labels'
10
11
  import { sanitize_html } from '../sanitize'
11
12
  import { toggle_fullscreen } from '../layout'
12
13
  import type { ControlsConfig, DataSeries, Orientation, Point } from '../plot'
13
- import type { ScatterHandlerProps } from '../plot/types'
14
+ import type { ScatterHandlerProps } from '../plot/core/types'
14
15
  import { Histogram, ScatterPlot } from '../plot'
15
- import { toggle_series_visibility } from '../plot/utils/series-visibility'
16
+ import { toggle_series_visibility } from '../plot/core/utils/series-visibility'
16
17
  import { DEFAULTS } from '../settings'
17
18
  import Structure from '../structure/Structure.svelte'
18
19
  import { scaleLinear } from 'd3-scale'
@@ -31,7 +32,6 @@
31
32
  import { TrajectoryError, TrajectoryExportPane, TrajectoryInfoPane } from './index'
32
33
  import type { AtomTypeMapping, LoadingOptions } from './parse'
33
34
  import {
34
- create_frame_loader,
35
35
  get_unsupported_format_message,
36
36
  MAX_BIN_FILE_SIZE,
37
37
  MAX_TEXT_FILE_SIZE,
@@ -98,6 +98,7 @@
98
98
  loading_options = {},
99
99
  atom_type_mapping,
100
100
  plot_skimming = true,
101
+ hovered = $bindable(false),
101
102
  ...rest
102
103
  }: EventHandlers & HTMLAttributes<HTMLDivElement> & {
103
104
  // trajectory data - can be provided directly or loaded from file
@@ -184,6 +185,8 @@
184
185
  atom_type_mapping?: AtomTypeMapping
185
186
  // Disable plot skimming (mouse over plot doesn't update structure/step slider)
186
187
  plot_skimming?: boolean
188
+ // bindable: true while the pointer is over the viewer (drives hover-scoped shortcuts)
189
+ hovered?: boolean
187
190
  } = $props()
188
191
 
189
192
  let dragover = $state(false)
@@ -341,19 +344,28 @@
341
344
  // Prevent circular updates when syncing legend toggles back to bindable visible_properties.
342
345
  let syncing_visible_properties = false
343
346
 
344
- // Regenerate plot series when trajectory, config, or visible_properties change
347
+ // Regenerate plot series when trajectory, config, or visible_properties change.
348
+ // Read ALL reactive deps before the syncing guard can return: a guarded run that
349
+ // reads no dependencies leaves the effect dep-less, and Svelte permanently unlinks
350
+ // dep-less effects - trajectory changes would then never regenerate the plot.
345
351
  $effect(() => {
352
+ const [traj, extractor, config, keys] = [
353
+ trajectory,
354
+ data_extractor,
355
+ extended_config,
356
+ visible_properties,
357
+ ]
346
358
  if (syncing_visible_properties) return
347
- const keys_set = visible_properties ? new Set(visible_properties) : undefined
359
+ const keys_set = keys ? new Set(keys) : undefined
348
360
 
349
- if (trajectory?.plot_metadata) {
350
- plot_series = generate_streaming_plot_series(trajectory.plot_metadata, {
351
- property_config: extended_config,
361
+ if (traj?.plot_metadata) {
362
+ plot_series = generate_streaming_plot_series(traj.plot_metadata, {
363
+ property_config: config,
352
364
  default_visible_properties: keys_set,
353
365
  })
354
- } else if (trajectory) {
355
- plot_series = generate_plot_series(trajectory, data_extractor, {
356
- property_config: extended_config,
366
+ } else if (traj) {
367
+ plot_series = generate_plot_series(traj, extractor, {
368
+ property_config: config,
357
369
  default_visible_properties: keys_set,
358
370
  })
359
371
  } else {
@@ -363,7 +375,7 @@
363
375
 
364
376
  // Update visible_properties binding when user toggles series visibility in legend
365
377
  $effect(() => {
366
- if (!plot_series.length) return
378
+ if (plot_series.length === 0) return
367
379
 
368
380
  // Extract property keys from visible series metadata
369
381
  const visible_keys = plot_series.flatMap((srs) => {
@@ -477,8 +489,8 @@
477
489
  }
478
490
 
479
491
  // Helper function to read file content
480
- async function read_file_content(file: File): Promise<string | ArrayBuffer> {
481
- return new Promise((resolve, reject) => {
492
+ const read_file_content = (file: File): Promise<string | ArrayBuffer> =>
493
+ new Promise((resolve, reject) => {
482
494
  const reader = new FileReader()
483
495
  reader.addEventListener(`load`, () => resolve(reader.result as string | ArrayBuffer))
484
496
  reader.addEventListener(`error`, () => reject(new Error(`Failed to read file`)))
@@ -488,7 +500,6 @@
488
500
  reader.readAsText(file)
489
501
  } else reader.readAsArrayBuffer(file)
490
502
  })
491
- }
492
503
 
493
504
  // Play/pause functionality
494
505
  function toggle_play() {
@@ -506,7 +517,7 @@
506
517
  is_playing = false
507
518
  if (trajectory) {
508
519
  on_pause?.({
509
- trajectory: trajectory,
520
+ trajectory,
510
521
  step_idx: current_step_idx,
511
522
  frame_count: total_frames,
512
523
  })
@@ -517,9 +528,10 @@
517
528
  const playing = is_playing
518
529
  const rate_ms = 1000 / fps
519
530
 
531
+ // Read current interval once (untrack to avoid circular dependency)
532
+ const current_interval = untrack(() => play_interval)
520
533
  if (playing) {
521
- // Clear existing interval if it exists - use untrack to avoid circular dependency
522
- const current_interval = untrack(() => play_interval)
534
+ // Clear existing interval if it exists
523
535
  if (current_interval !== undefined) clearInterval(current_interval)
524
536
 
525
537
  // Create new interval with current frame rate
@@ -539,13 +551,10 @@
539
551
  }
540
552
  } else next_step()
541
553
  }, rate_ms)
542
- } else {
543
- // Clear interval when not playing - use untrack to avoid circular dependency
544
- const current_interval = untrack(() => play_interval)
545
- if (current_interval !== undefined) {
546
- clearInterval(current_interval)
547
- play_interval = undefined
548
- }
554
+ } else if (current_interval !== undefined) {
555
+ // Clear interval when not playing
556
+ clearInterval(current_interval)
557
+ play_interval = undefined
549
558
  }
550
559
  })
551
560
 
@@ -624,6 +633,9 @@
624
633
  // Read file content directly
625
634
  const content = await read_file_content(file)
626
635
  await load_trajectory_data(content, file.name)
636
+ // Don't fall through: drops from IDEs/file managers often also carry a
637
+ // text/plain payload (the file path) which would clobber the loaded data
638
+ return
627
639
  }
628
640
 
629
641
  // Check for plain text data (fallback)
@@ -631,7 +643,6 @@
631
643
  if (text_data) {
632
644
  file_size = new Blob([text_data]).size // Calculate byte size of text data
633
645
  await load_trajectory_data(text_data, `trajectory.json`)
634
- return
635
646
  }
636
647
  } catch (error) {
637
648
  console.error(`File drop failed:`, error)
@@ -674,7 +685,7 @@
674
685
 
675
686
  // Watch for frame rate changes
676
687
  $effect(() => {
677
- on_frame_rate_change?.({ trajectory, fps: fps })
688
+ on_frame_rate_change?.({ trajectory, fps })
678
689
  })
679
690
 
680
691
  async function load_trajectory_data(data: string | ArrayBuffer, filename: string) {
@@ -751,9 +762,10 @@
751
762
  parsing_progress = progress
752
763
  }, merged_options)
753
764
 
754
- // Attach frame loader and original data directly to trajectory for unified access
755
- orig_data = data
756
- trajectory.frame_loader = create_frame_loader(filename)
765
+ // Keep original data for on-demand frame loads only when indexed parsing attached a
766
+ // frame_loader. Direct-parse fallbacks (e.g. large JSON or extensionless blob:
767
+ // filenames) load all frames upfront, so retaining a full duplicate payload wastes memory.
768
+ orig_data = trajectory?.frame_loader ? data : null
757
769
  } catch (error) {
758
770
  console.error(`Indexed loading failed:`, error)
759
771
  throw error
@@ -783,9 +795,10 @@
783
795
  }
784
796
  }
785
797
 
786
- // Handle keyboard shortcuts
787
- function onkeydown(event: KeyboardEvent) {
788
- if (!trajectory) return
798
+ // Handle keyboard shortcuts. Returns true if the key was handled, so the caller
799
+ // (handle_and_prevent / forward_window_keydown) can suppress the browser default.
800
+ function onkeydown(event: KeyboardEvent): boolean {
801
+ if (!trajectory) return false
789
802
 
790
803
  // Don't handle shortcuts if user is typing in an input field (but allow if it's our step input and not focused)
791
804
  const target = event.target instanceof HTMLElement ? event.target : null
@@ -794,18 +807,25 @@
794
807
  target?.tagName === `INPUT` || target?.tagName === `TEXTAREA`
795
808
 
796
809
  // Skip if typing in an input that's not our step input
797
- if (is_input_focused && !is_step_input) return
810
+ if (is_input_focused && !is_step_input) return false
798
811
 
799
812
  // If typing in step input, only handle certain navigation keys
800
813
  if (is_step_input && is_input_focused) {
801
814
  // Allow normal typing, but handle special navigation keys
802
815
  if ([`Escape`, `Enter`].includes(event.key)) target?.blur() // Remove focus from input
803
- return
816
+ return false
804
817
  }
805
818
 
806
819
  const is_cmd_or_ctrl = event.metaKey || event.ctrlKey
807
820
 
808
- // Navigation shortcuts
821
+ // Only the Arrow keys intentionally use Cmd/Ctrl (jump to first/last). For any
822
+ // other key a Cmd/Ctrl combo is a browser/OS shortcut (find, tab switch, zoom)
823
+ // — bail so we neither hijack it nor preventDefault the browser's own action.
824
+ if (is_cmd_or_ctrl && event.key !== `ArrowLeft` && event.key !== `ArrowRight`) return false
825
+
826
+ // Track whether a shortcut fired so callers suppress browser defaults (page
827
+ // scroll on Space/arrows/PageUp-Down/Home/End) only when we handled the key.
828
+ let handled = true
809
829
  if (event.key === ` `) toggle_play()
810
830
  else if (event.key === `ArrowLeft`) {
811
831
  if (is_cmd_or_ctrl) go_to_step(0)
@@ -829,10 +849,10 @@
829
849
  // Playback speed shortcuts (only when playing)
830
850
  else if ((event.key === `=` || event.key === `+`) && is_playing) {
831
851
  fps = Math.min(fps_range[1], fps + 0.2)
832
- on_frame_rate_change?.({ trajectory, fps: fps })
852
+ on_frame_rate_change?.({ trajectory, fps })
833
853
  } else if (event.key === `-` && is_playing) {
834
854
  fps = Math.max(fps_range[0], fps - 0.2)
835
- on_frame_rate_change?.({ trajectory, fps: fps })
855
+ on_frame_rate_change?.({ trajectory, fps })
836
856
  } // System shortcuts
837
857
  else if (event.key === `Escape`) {
838
858
  if (document.fullscreenElement) document.exitFullscreen()
@@ -841,7 +861,9 @@
841
861
  } // Number keys 0-9 - jump to percentage of trajectory
842
862
  else if (event.key >= `0` && event.key <= `9`) {
843
863
  go_to_step(Math.floor((parseInt(event.key, 10) / 10) * (total_frames - 1)))
844
- }
864
+ } else handled = false
865
+
866
+ return handled
845
867
  }
846
868
 
847
869
  // Separate state variables for each pane to match component prop types
@@ -859,6 +881,8 @@
859
881
  }}
860
882
  />
861
883
 
884
+ <svelte:window onkeydown={forward_window_keydown(() => hovered, onkeydown)} />
885
+
862
886
  <div
863
887
  class:dragover
864
888
  class:active={is_playing || structure_info_open || structure_controls_open ||
@@ -869,6 +893,8 @@
869
893
  role="button"
870
894
  tabindex="0"
871
895
  aria-label="Drop trajectory file here to load"
896
+ onmouseenter={() => (hovered = true)}
897
+ onmouseleave={() => (hovered = false)}
872
898
  ondrop={handle_file_drop}
873
899
  ondragover={(event) => {
874
900
  event.preventDefault()
@@ -880,7 +906,7 @@
880
906
  dragover = false
881
907
  }}
882
908
  onclick={handle_click_outside}
883
- {onkeydown}
909
+ onkeydown={handle_and_prevent(onkeydown)}
884
910
  {...rest}
885
911
  class="trajectory {actual_layout} {rest.class ?? ``}"
886
912
  class:show-both-views={[`structure+scatter`, `structure+histogram`].includes(display_mode) &&
@@ -71,7 +71,8 @@ type $$ComponentProps = EventHandlers & HTMLAttributes<HTMLDivElement> & {
71
71
  loading_options?: LoadingOptions;
72
72
  atom_type_mapping?: AtomTypeMapping;
73
73
  plot_skimming?: boolean;
74
+ hovered?: boolean;
74
75
  };
75
- declare const Trajectory: import("svelte").Component<$$ComponentProps, {}, "trajectory" | "fps" | "display_mode" | "current_step_idx" | "visible_properties">;
76
+ declare const Trajectory: import("svelte").Component<$$ComponentProps, {}, "trajectory" | "hovered" | "fps" | "display_mode" | "current_step_idx" | "visible_properties">;
76
77
  type Trajectory = ReturnType<typeof Trajectory>;
77
78
  export default Trajectory;
@@ -8,6 +8,7 @@
8
8
  import type { TrajectoryType } from './'
9
9
  import type { ComponentProps } from 'svelte'
10
10
  import { tooltip } from 'svelte-multiselect/attachments'
11
+ import { to_error } from '../utils'
11
12
 
12
13
  let {
13
14
  export_pane_open = $bindable(false),
@@ -123,7 +124,7 @@
123
124
  }, 1000)
124
125
  } catch (error) {
125
126
  console.error(`Export failed:`, error)
126
- export_error = error instanceof Error ? error.message : String(error)
127
+ export_error = to_error(error).message
127
128
  is_exporting = false
128
129
  export_progress = 0
129
130
  }
@@ -45,7 +45,7 @@
45
45
  frames.map((frame) => frame.metadata?.[prop]).filter(is_valid_number)
46
46
 
47
47
  const format_range = (values: number[], unit = ``, decimals = `.2~f`) => {
48
- if (!values.length) return null
48
+ if (values.length === 0) return null
49
49
  if (values.length === 1) {
50
50
  return `${format_num(values[0], decimals)} ${unit}`.trim()
51
51
  }
@@ -61,6 +61,7 @@
61
61
  tooltip?: string,
62
62
  ): InfoItem | null => value ? { label, value, key, tooltip } : null
63
63
 
64
+ // oxlint-disable-next-line eslint-plugin-unicorn/prefer-native-coercion-functions -- type predicate needed for narrowing
64
65
  const is_info_item = (item: unknown): item is InfoItem => Boolean(item)
65
66
 
66
67
  const safe_formula = (structure: AnyStructure) => {