matterviz 0.3.1 → 0.3.2

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 (257) hide show
  1. package/dist/FilePicker.svelte +37 -20
  2. package/dist/Icon.svelte +2 -2
  3. package/dist/app.css +29 -0
  4. package/dist/brillouin/BrillouinZone.svelte +19 -61
  5. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  6. package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
  7. package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
  8. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  9. package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
  10. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  11. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
  12. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  13. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
  14. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  15. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
  16. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  17. package/dist/chempot-diagram/color.d.ts +10 -0
  18. package/dist/chempot-diagram/color.js +33 -0
  19. package/dist/chempot-diagram/compute.d.ts +38 -0
  20. package/dist/chempot-diagram/compute.js +650 -0
  21. package/dist/chempot-diagram/index.d.ts +5 -0
  22. package/dist/chempot-diagram/index.js +5 -0
  23. package/dist/chempot-diagram/pointer.d.ts +16 -0
  24. package/dist/chempot-diagram/pointer.js +40 -0
  25. package/dist/chempot-diagram/temperature.d.ts +15 -0
  26. package/dist/chempot-diagram/temperature.js +37 -0
  27. package/dist/chempot-diagram/types.d.ts +83 -0
  28. package/dist/chempot-diagram/types.js +27 -0
  29. package/dist/colors/index.d.ts +3 -1
  30. package/dist/colors/index.js +4 -0
  31. package/dist/composition/BarChart.svelte +13 -22
  32. package/dist/composition/BubbleChart.svelte +5 -3
  33. package/dist/composition/FormulaFilter.svelte +586 -94
  34. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  35. package/dist/composition/PieChart.svelte +43 -18
  36. package/dist/composition/PieChart.svelte.d.ts +1 -1
  37. package/dist/convex-hull/ConvexHull.svelte +4 -2
  38. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  39. package/dist/convex-hull/ConvexHull2D.svelte +13 -44
  40. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  41. package/dist/convex-hull/ConvexHull3D.svelte +16 -7
  42. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  43. package/dist/convex-hull/ConvexHull4D.svelte +17 -7
  44. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  45. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHullStats.svelte +701 -226
  47. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  48. package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
  49. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  50. package/dist/convex-hull/demo-temperature.js +36 -0
  51. package/dist/convex-hull/helpers.d.ts +1 -1
  52. package/dist/convex-hull/helpers.js +2 -4
  53. package/dist/convex-hull/index.d.ts +1 -0
  54. package/dist/convex-hull/index.js +1 -0
  55. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  56. package/dist/convex-hull/thermodynamics.js +106 -17
  57. package/dist/convex-hull/types.d.ts +5 -0
  58. package/dist/convex-hull/types.js +5 -0
  59. package/dist/coordination/CoordinationBarPlot.svelte +29 -46
  60. package/dist/element/BohrAtom.svelte +1 -1
  61. package/dist/element/data.js +2 -14
  62. package/dist/element/data.json.gz +0 -0
  63. package/dist/element/types.d.ts +1 -0
  64. package/dist/fermi-surface/FermiSurface.svelte +20 -64
  65. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  66. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  67. package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
  68. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  69. package/dist/fermi-surface/parse.js +16 -22
  70. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
  71. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  72. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
  73. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
  74. package/dist/heatmap-matrix/index.d.ts +53 -0
  75. package/dist/heatmap-matrix/index.js +100 -0
  76. package/dist/heatmap-matrix/shared.d.ts +2 -0
  77. package/dist/heatmap-matrix/shared.js +4 -0
  78. package/dist/icons.d.ts +111 -0
  79. package/dist/icons.js +111 -0
  80. package/dist/index.d.ts +3 -1
  81. package/dist/index.js +3 -1
  82. package/dist/io/export.js +15 -3
  83. package/dist/io/file-drop.d.ts +7 -0
  84. package/dist/io/file-drop.js +43 -0
  85. package/dist/io/index.d.ts +2 -2
  86. package/dist/io/index.js +2 -112
  87. package/dist/io/types.d.ts +1 -0
  88. package/dist/io/url-drop.d.ts +2 -0
  89. package/dist/io/url-drop.js +118 -0
  90. package/dist/isosurface/Isosurface.svelte +101 -45
  91. package/dist/isosurface/IsosurfaceControls.svelte +19 -0
  92. package/dist/isosurface/parse.js +73 -30
  93. package/dist/isosurface/slice.d.ts +2 -1
  94. package/dist/isosurface/slice.js +3 -3
  95. package/dist/isosurface/types.d.ts +13 -1
  96. package/dist/isosurface/types.js +98 -0
  97. package/dist/labels.d.ts +2 -1
  98. package/dist/labels.js +1 -0
  99. package/dist/layout/InfoTag.svelte +62 -62
  100. package/dist/layout/SubpageGrid.svelte +74 -0
  101. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  102. package/dist/layout/index.d.ts +1 -0
  103. package/dist/layout/index.js +1 -0
  104. package/dist/layout/json-tree/JsonNode.svelte +83 -85
  105. package/dist/layout/json-tree/JsonTree.svelte +20 -19
  106. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  107. package/dist/layout/json-tree/JsonValue.svelte +196 -116
  108. package/dist/layout/json-tree/types.d.ts +10 -2
  109. package/dist/layout/json-tree/utils.d.ts +2 -0
  110. package/dist/layout/json-tree/utils.js +33 -0
  111. package/dist/math.d.ts +7 -0
  112. package/dist/math.js +358 -7
  113. package/dist/overlays/ContextMenu.svelte +3 -2
  114. package/dist/overlays/DraggablePane.svelte +163 -58
  115. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  116. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
  117. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  118. package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
  119. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  120. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
  121. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  122. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
  123. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
  124. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
  125. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  126. package/dist/phase-diagram/index.d.ts +2 -0
  127. package/dist/phase-diagram/index.js +2 -0
  128. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  129. package/dist/phase-diagram/svg-to-diagram.js +865 -0
  130. package/dist/phase-diagram/types.d.ts +10 -0
  131. package/dist/phase-diagram/utils.d.ts +7 -4
  132. package/dist/phase-diagram/utils.js +149 -59
  133. package/dist/plot/AxisLabel.svelte +26 -0
  134. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  135. package/dist/plot/BarPlot.svelte +473 -228
  136. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  137. package/dist/plot/BarPlotControls.svelte +3 -2
  138. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  139. package/dist/plot/ColorBar.svelte +54 -54
  140. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  141. package/dist/plot/ColorScaleSelect.svelte +1 -1
  142. package/dist/plot/ElementScatter.svelte +3 -2
  143. package/dist/plot/FillArea.svelte +4 -1
  144. package/dist/plot/Histogram.svelte +320 -230
  145. package/dist/plot/Histogram.svelte.d.ts +2 -2
  146. package/dist/plot/HistogramControls.svelte +29 -10
  147. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  148. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
  149. package/dist/plot/PlotControls.svelte +109 -27
  150. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  151. package/dist/plot/PlotLegend.svelte +1 -1
  152. package/dist/plot/PortalSelect.svelte +2 -1
  153. package/dist/plot/ReferenceLine.svelte +2 -1
  154. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  155. package/dist/plot/ReferencePlane.svelte +1 -3
  156. package/dist/plot/ScatterPlot.svelte +343 -209
  157. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  158. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  159. package/dist/plot/ScatterPlot3DControls.svelte +203 -250
  160. package/dist/plot/ScatterPlot3DScene.svelte +4 -7
  161. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  162. package/dist/plot/ScatterPlotControls.svelte +95 -55
  163. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  164. package/dist/plot/ZeroLines.svelte +44 -0
  165. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  166. package/dist/plot/ZoomRect.svelte +21 -0
  167. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  168. package/dist/plot/axis-utils.d.ts +1 -1
  169. package/dist/plot/index.d.ts +6 -2
  170. package/dist/plot/index.js +6 -2
  171. package/dist/plot/interactions.d.ts +8 -10
  172. package/dist/plot/interactions.js +2 -3
  173. package/dist/plot/layout.d.ts +7 -1
  174. package/dist/plot/layout.js +12 -4
  175. package/dist/plot/reference-line.d.ts +4 -21
  176. package/dist/plot/reference-line.js +7 -81
  177. package/dist/plot/types.d.ts +42 -17
  178. package/dist/plot/types.js +10 -0
  179. package/dist/plot/utils/label-placement.js +13 -10
  180. package/dist/plot/utils.d.ts +1 -0
  181. package/dist/plot/utils.js +14 -0
  182. package/dist/rdf/RdfPlot.svelte +55 -66
  183. package/dist/settings.d.ts +3 -0
  184. package/dist/settings.js +17 -3
  185. package/dist/spectral/Bands.svelte +515 -143
  186. package/dist/spectral/Bands.svelte.d.ts +22 -2
  187. package/dist/spectral/helpers.d.ts +23 -1
  188. package/dist/spectral/helpers.js +65 -9
  189. package/dist/spectral/types.d.ts +2 -0
  190. package/dist/structure/AtomLegend.svelte +29 -8
  191. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  192. package/dist/structure/CellSelect.svelte +92 -22
  193. package/dist/structure/Structure.svelte +108 -118
  194. package/dist/structure/Structure.svelte.d.ts +1 -1
  195. package/dist/structure/StructureControls.svelte +25 -22
  196. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  197. package/dist/structure/StructureInfoPane.svelte +7 -1
  198. package/dist/structure/StructureScene.svelte +104 -66
  199. package/dist/structure/StructureScene.svelte.d.ts +2 -1
  200. package/dist/structure/atom-properties.d.ts +6 -2
  201. package/dist/structure/atom-properties.js +38 -25
  202. package/dist/structure/export.js +10 -7
  203. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  204. package/dist/structure/ferrox-wasm-types.js +0 -3
  205. package/dist/structure/ferrox-wasm.d.ts +3 -2
  206. package/dist/structure/ferrox-wasm.js +1 -2
  207. package/dist/structure/index.d.ts +6 -0
  208. package/dist/structure/index.js +22 -0
  209. package/dist/structure/parse.js +19 -16
  210. package/dist/structure/partial-occupancy.d.ts +25 -0
  211. package/dist/structure/partial-occupancy.js +102 -0
  212. package/dist/structure/validation.js +6 -3
  213. package/dist/symmetry/SymmetryStats.svelte +18 -4
  214. package/dist/symmetry/WyckoffTable.svelte +18 -10
  215. package/dist/symmetry/index.d.ts +7 -4
  216. package/dist/symmetry/index.js +83 -18
  217. package/dist/table/HeatmapTable.svelte +425 -65
  218. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  219. package/dist/table/ToggleMenu.svelte +2 -0
  220. package/dist/table/index.d.ts +2 -0
  221. package/dist/trajectory/Trajectory.svelte +147 -145
  222. package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
  223. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  224. package/dist/trajectory/constants.d.ts +6 -0
  225. package/dist/trajectory/constants.js +7 -0
  226. package/dist/trajectory/extract.js +3 -5
  227. package/dist/trajectory/format-detect.d.ts +9 -0
  228. package/dist/trajectory/format-detect.js +76 -0
  229. package/dist/trajectory/frame-reader.d.ts +17 -0
  230. package/dist/trajectory/frame-reader.js +339 -0
  231. package/dist/trajectory/helpers.d.ts +15 -0
  232. package/dist/trajectory/helpers.js +187 -0
  233. package/dist/trajectory/index.d.ts +1 -0
  234. package/dist/trajectory/index.js +11 -4
  235. package/dist/trajectory/parse/ase.d.ts +2 -0
  236. package/dist/trajectory/parse/ase.js +76 -0
  237. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  238. package/dist/trajectory/parse/hdf5.js +121 -0
  239. package/dist/trajectory/parse/index.d.ts +12 -0
  240. package/dist/trajectory/parse/index.js +304 -0
  241. package/dist/trajectory/parse/lammps.d.ts +5 -0
  242. package/dist/trajectory/parse/lammps.js +169 -0
  243. package/dist/trajectory/parse/vasp.d.ts +2 -0
  244. package/dist/trajectory/parse/vasp.js +65 -0
  245. package/dist/trajectory/parse/xyz.d.ts +2 -0
  246. package/dist/trajectory/parse/xyz.js +109 -0
  247. package/dist/trajectory/types.d.ts +11 -0
  248. package/dist/trajectory/types.js +1 -0
  249. package/dist/utils.d.ts +2 -0
  250. package/dist/utils.js +4 -0
  251. package/dist/xrd/XrdPlot.svelte +6 -4
  252. package/dist/xrd/calc-xrd.js +0 -1
  253. package/package.json +30 -24
  254. package/readme.md +4 -4
  255. package/dist/trajectory/parse.d.ts +0 -42
  256. package/dist/trajectory/parse.js +0 -1267
  257. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
@@ -17,6 +17,7 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
17
17
  default_num_format?: string;
18
18
  show_heatmap?: boolean;
19
19
  heatmap_class?: string;
20
+ onrowclick?: (event: MouseEvent | KeyboardEvent, row: RowData) => void;
20
21
  onrowdblclick?: (event: MouseEvent, row: RowData) => void;
21
22
  column_order?: string[];
22
23
  export_data?: ExportData;
@@ -27,12 +28,22 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
27
28
  selected_rows?: RowData[];
28
29
  hidden_columns?: string[];
29
30
  scroll_style?: string;
31
+ root_style?: string;
30
32
  onsort?: (column: string, dir: `asc` | `desc`) => Promise<RowData[]>;
31
33
  onsorterror?: (error: unknown, column: string, dir: `asc` | `desc`) => void;
32
34
  loading?: boolean;
33
35
  sort_data?: boolean;
34
36
  heatmap_opacity?: number;
37
+ empty_message?: string;
38
+ show_row_numbers?: boolean;
39
+ allow_better_toggle?: boolean;
40
+ show_controls?: boolean;
41
+ controls_open?: boolean;
42
+ header_cell?: Snippet<[{
43
+ col: Label;
44
+ }]>;
45
+ footer?: Snippet;
35
46
  };
36
- declare const HeatmapTable: import("svelte").Component<$$ComponentProps, {}, "sort" | "data" | "loading" | "show_heatmap" | "column_order" | "selected_rows" | "hidden_columns" | "heatmap_opacity">;
47
+ declare const HeatmapTable: import("svelte").Component<$$ComponentProps, {}, "sort" | "data" | "show_controls" | "controls_open" | "loading" | "show_heatmap" | "column_order" | "selected_rows" | "hidden_columns" | "heatmap_opacity">;
37
48
  type HeatmapTable = ReturnType<typeof HeatmapTable>;
38
49
  export default HeatmapTable;
@@ -69,6 +69,8 @@ function toggle_section(name) {
69
69
  : [...collapsed_sections, name];
70
70
  }
71
71
  function toggle_column_visibility(col, event) {
72
+ if (!(event.target instanceof HTMLInputElement))
73
+ return;
72
74
  col.visible = event.target.checked;
73
75
  columns = [...columns]; // trigger reactivity on parent binding
74
76
  }
@@ -22,6 +22,7 @@ export type Label = {
22
22
  sticky?: boolean;
23
23
  visible?: boolean;
24
24
  sortable?: boolean;
25
+ show_sort_indicator?: boolean;
25
26
  disabled?: boolean;
26
27
  style?: string;
27
28
  cell_style?: string;
@@ -51,6 +52,7 @@ export type InitialSort = string | {
51
52
  };
52
53
  export type Pagination = boolean | {
53
54
  page_size?: number;
55
+ page_sizes?: number[];
54
56
  };
55
57
  export type Search = boolean | {
56
58
  placeholder?: string;
@@ -140,8 +140,12 @@ let extended_config = $derived.by(() => {
140
140
  });
141
141
  // Plot series state (not derived so we can update on legend toggle)
142
142
  let plot_series = $state([]);
143
+ // Prevent circular updates when syncing legend toggles back to bindable visible_properties.
144
+ let syncing_visible_properties = false;
143
145
  // Regenerate plot series when trajectory, config, or visible_properties change
144
146
  $effect(() => {
147
+ if (syncing_visible_properties)
148
+ return;
145
149
  const keys_set = visible_properties ? new Set(visible_properties) : undefined;
146
150
  if (trajectory?.plot_metadata) {
147
151
  plot_series = generate_streaming_plot_series(trajectory.plot_metadata, {
@@ -164,20 +168,21 @@ $effect(() => {
164
168
  if (!plot_series.length)
165
169
  return;
166
170
  // Extract property keys from visible series metadata
167
- const visible_keys = plot_series
168
- .filter((srs) => srs.visible)
169
- // Get property key from series metadata (stored during series generation)
170
- .map((srs) => {
171
+ const visible_keys = plot_series.flatMap((srs) => {
172
+ if (!srs.visible)
173
+ return [];
171
174
  const metadata = Array.isArray(srs.metadata) ? srs.metadata[0] : srs.metadata;
172
- return metadata?.property_key;
173
- })
174
- .filter((key) => Boolean(key));
175
+ const key = metadata?.property_key;
176
+ return key ? [key] : [];
177
+ });
175
178
  // Only update if changed (use untrack to avoid circular dependency)
176
179
  const current = untrack(() => visible_properties) || [];
177
180
  const has_changed = visible_keys.length !== current.length ||
178
181
  !visible_keys.every((key, idx) => key === current[idx]);
179
182
  if (has_changed) {
183
+ syncing_visible_properties = true;
180
184
  visible_properties = visible_keys;
185
+ queueMicrotask(() => (syncing_visible_properties = false));
181
186
  }
182
187
  });
183
188
  // Handler for legend toggle - updates plot_series state
@@ -194,7 +199,7 @@ let y_axis_labels = $derived(generate_axis_labels(plot_series));
194
199
  let y_axis = $derived({
195
200
  label: y_axis_labels.y1,
196
201
  format: `.2~s`,
197
- label_shift: { y: 20 },
202
+ label_shift: { y: 10 },
198
203
  });
199
204
  let y2_axis = $derived({
200
205
  label: y_axis_labels.y2,
@@ -960,7 +965,7 @@ let fullscreen = $state(false);
960
965
  controls={scatter_controls}
961
966
  current_x_value={current_step_idx}
962
967
  change={plot_skimming ? handle_plot_change : undefined}
963
- padding={{ t: 20, b: 60, l: 100, r: has_y2_series ? 100 : 20 }}
968
+ padding={{ t: 20, b: 60, l: 52, r: has_y2_series ? 100 : 20 }}
964
969
  range_padding={0}
965
970
  style="height: 100%"
966
971
  {...scatter_props}
@@ -973,14 +978,10 @@ let fullscreen = $state(false);
973
978
  }}
974
979
  class="plot {scatter_props.class ?? ``}"
975
980
  >
976
- {#snippet tooltip({ x, y, metadata })}
977
- {#if metadata?.series_label}
978
- Step: {Math.round(x)}<br />
979
- {@html metadata.series_label}: {typeof y === `number` ? format_num(y) : y}
980
- {:else}
981
- Step: {Math.round(x)}<br />
982
- Value: {typeof y === `number` ? format_num(y) : y}
983
- {/if}
981
+ {#snippet tooltip({ x, y, metadata, label })}
982
+ {@const formatted_y = typeof y === `number` ? format_num(y) : y}
983
+ Step: {Math.round(x)}<br />
984
+ {@html metadata?.series_label || label || `Value`}: {formatted_y}
984
985
  {/snippet}
985
986
  </ScatterPlot>
986
987
  {:else if display_mode === `histogram` || display_mode === `structure+histogram`}
@@ -1004,9 +1005,9 @@ let fullscreen = $state(false);
1004
1005
  --ctrl-btn-top="6ex"
1005
1006
  >
1006
1007
  {#snippet tooltip({ value, count, property })}
1008
+ {#if property}<div><strong>{property}</strong></div>{/if}
1007
1009
  <div>Value: {format_num(value)}</div>
1008
1010
  <div>Count: {count}</div>
1009
- <div>{property}</div>
1010
1011
  {/snippet}
1011
1012
  </Histogram>
1012
1013
  {/if}
@@ -1049,61 +1050,55 @@ let fullscreen = $state(false);
1049
1050
  contain: layout;
1050
1051
  z-index: var(--traj-z-index, 1);
1051
1052
  container-type: size; /* enable cqh for panes if explicit height is set */
1052
- }
1053
- .trajectory :global(.plot) {
1054
- background: var(--surface-bg);
1055
- }
1056
- .trajectory.active {
1057
- z-index: 2; /* needed so info/control panes from an active viewer overlay those of the next (if there is one) */
1058
- }
1059
- .trajectory.active .trajectory-controls {
1060
- z-index: 5; /* needed so info/control panes from an active viewer its own plot when active, not sure why needed */
1061
- }
1062
- .trajectory:fullscreen {
1063
- height: 100vh !important;
1064
- width: 100vw !important;
1065
- border-radius: 0 !important;
1066
- background: var(--surface-bg);
1067
- overflow: hidden;
1053
+ :global(.plot) {
1054
+ background: var(--surface-bg);
1055
+ }
1056
+ &.active {
1057
+ z-index: 2; /* needed so info/control panes from an active viewer overlay those of the next (if there is one) */
1058
+ .trajectory-controls {
1059
+ z-index: 5; /* needed so info/control panes from an active viewer its own plot when active, not sure why needed */
1060
+ }
1061
+ }
1062
+ &:fullscreen {
1063
+ height: 100vh !important;
1064
+ width: 100vw !important;
1065
+ border-radius: 0 !important;
1066
+ background: var(--surface-bg);
1067
+ overflow: hidden;
1068
+ }
1069
+ &.horizontal .content-area {
1070
+ grid-template-columns: 1fr 1fr;
1071
+ grid-template-rows: 1fr;
1072
+ }
1073
+ &.vertical .content-area {
1074
+ grid-template-columns: 1fr;
1075
+ grid-template-rows: 1fr 1fr;
1076
+ }
1077
+ /* Display mode specific layouts */
1078
+ &:is(.horizontal, .vertical) .content-area:is(.show-structure-only, .show-plot-only) {
1079
+ grid-template-columns: 1fr !important;
1080
+ grid-template-rows: 1fr !important;
1081
+ }
1082
+ &.dragover {
1083
+ background-color: var(--traj-dragover-bg, var(--dragover-bg));
1084
+ border: var(--traj-dragover-border, var(--dragover-border));
1085
+ }
1086
+ /* Mode: hover - controls visible on component hover */
1087
+ &:hover .trajectory-controls.hover-visible {
1088
+ opacity: 1;
1089
+ pointer-events: auto;
1090
+ }
1068
1091
  }
1069
1092
  /* Content area - grid container for equal sizing */
1070
1093
  .content-area {
1071
1094
  display: grid;
1072
1095
  flex: 1;
1073
1096
  min-height: 0; /* important for tall structure viewers not to overflow */
1074
- }
1075
- .trajectory.horizontal .content-area {
1076
- grid-template-columns: 1fr 1fr;
1077
- grid-template-rows: 1fr;
1078
- }
1079
- .trajectory.vertical .content-area {
1080
- grid-template-columns: 1fr;
1081
- grid-template-rows: 1fr 1fr;
1082
- }
1083
- /* When plot is hidden, structure takes full space */
1084
- .content-area.hide-plot {
1085
- grid-template-columns: 1fr !important;
1086
- grid-template-rows: 1fr !important;
1087
- }
1088
- /* When structure is hidden, plot takes full space */
1089
- .content-area.hide-structure {
1090
- grid-template-columns: 1fr !important;
1091
- grid-template-rows: 1fr !important;
1092
- }
1093
- /* Display mode specific layouts */
1094
- .trajectory.horizontal .content-area.show-structure-only,
1095
- .trajectory.vertical .content-area.show-structure-only {
1096
- grid-template-columns: 1fr !important;
1097
- grid-template-rows: 1fr !important;
1098
- }
1099
- .trajectory.horizontal .content-area.show-plot-only,
1100
- .trajectory.vertical .content-area.show-plot-only {
1101
- grid-template-columns: 1fr !important;
1102
- grid-template-rows: 1fr !important;
1103
- }
1104
- .trajectory.dragover {
1105
- background-color: var(--traj-dragover-bg, var(--dragover-bg));
1106
- border: var(--traj-dragover-border, var(--dragover-border));
1097
+ /* When plot or structure is hidden, the other takes full space */
1098
+ &:is(.hide-plot, .hide-structure) {
1099
+ grid-template-columns: 1fr !important;
1100
+ grid-template-rows: 1fr !important;
1101
+ }
1107
1102
  }
1108
1103
  .trajectory-controls {
1109
1104
  display: flex;
@@ -1117,27 +1112,29 @@ let fullscreen = $state(false);
1117
1112
  opacity: 0;
1118
1113
  pointer-events: none;
1119
1114
  transition: opacity 0.2s ease;
1120
- }
1121
- /* Mode: always - controls always visible */
1122
- .trajectory-controls.always-visible {
1123
- opacity: 1;
1124
- pointer-events: auto;
1125
- }
1126
- /* Mode: hover - controls visible on component hover */
1127
- .trajectory:hover .trajectory-controls.hover-visible {
1128
- opacity: 1;
1129
- pointer-events: auto;
1130
- }
1131
- /* Mode: never - stays hidden (default state, no additional CSS needed) */
1132
- .trajectory-controls:focus-within {
1133
- z-index: var(--traj-controls-z-index, 999999999);
1134
- }
1135
- .trajectory-controls button {
1136
- background: var(--btn-bg);
1137
- font-size: clamp(0.8rem, 2cqw, 1rem);
1138
- }
1139
- .trajectory-controls button:hover:not(:disabled) {
1140
- background: var(--btn-bg-hover);
1115
+ /* Mode: always - controls always visible */
1116
+ &.always-visible {
1117
+ opacity: 1;
1118
+ pointer-events: auto;
1119
+ }
1120
+ /* Mode: never - stays hidden (default state, no additional CSS needed) */
1121
+ &:focus-within {
1122
+ z-index: var(--traj-controls-z-index, 999999999);
1123
+ }
1124
+ button {
1125
+ background: var(--btn-bg);
1126
+ font-size: clamp(0.8rem, 2cqw, 1rem);
1127
+ &:hover:not(:disabled) {
1128
+ background: var(--btn-bg-hover);
1129
+ }
1130
+ }
1131
+ input[type='number'] {
1132
+ &::-webkit-outer-spin-button,
1133
+ &::-webkit-inner-spin-button {
1134
+ -webkit-appearance: none;
1135
+ margin: 0;
1136
+ }
1137
+ }
1141
1138
  }
1142
1139
  .nav-section {
1143
1140
  display: flex;
@@ -1210,68 +1207,73 @@ let fullscreen = $state(false);
1210
1207
  .fullscreen-button {
1211
1208
  background: transparent !important;
1212
1209
  padding: 0;
1213
- }
1214
- .fullscreen-button:hover:not(:disabled) {
1215
- background: var(--border-color);
1210
+ &:hover:not(:disabled) {
1211
+ background: var(--border-color);
1212
+ }
1216
1213
  }
1217
1214
  .info-section {
1218
1215
  display: flex;
1219
1216
  align-items: center;
1220
- gap: clamp(6pt, 1cqw, 1.5ex);
1217
+ gap: clamp(3pt, 0.6cqw, 1ex);
1221
1218
  position: relative;
1222
1219
  }
1220
+ .info-section :global(:is(.trajectory-info-toggle, .trajectory-export-toggle)) {
1221
+ font-size: clamp(1rem, 2.2cqw, 1.1rem);
1222
+ }
1223
1223
  .play-button {
1224
1224
  min-width: clamp(32px, 4cqw, 36px);
1225
- }
1226
- .play-button:hover:not(:disabled) {
1227
- background: var(--traj-play-btn-bg-hover, var(--btn-bg-hover, rgba(0, 0, 0, 0.2)));
1228
- }
1229
- .play-button.playing {
1230
- background: var(--traj-pause-btn-bg, var(--btn-bg, rgba(0, 0, 0, 0.1)));
1231
- }
1232
- .play-button.playing:hover:not(:disabled) {
1233
- background: var(--traj-pause-btn-bg-hover, var(--btn-bg-hover, rgba(0, 0, 0, 0.1)));
1225
+ &:hover:not(:disabled) {
1226
+ background: var(--traj-play-btn-bg-hover, var(--btn-bg-hover, rgba(0, 0, 0, 0.2)));
1227
+ }
1228
+ &.playing {
1229
+ background: var(--traj-pause-btn-bg, var(--btn-bg, rgba(0, 0, 0, 0.1)));
1230
+ &:hover:not(:disabled) {
1231
+ background: var(
1232
+ --traj-pause-btn-bg-hover,
1233
+ var(--btn-bg-hover, rgba(0, 0, 0, 0.1))
1234
+ );
1235
+ }
1236
+ }
1234
1237
  }
1235
1238
  :global(.trajectory-empty-state) {
1236
1239
  padding: 2rem;
1237
1240
  border-radius: var(--border-radius, 3pt);
1238
1241
  background: var(--dropzone-bg);
1242
+ :where(p, ul) {
1243
+ color: var(--text-color-muted);
1244
+ }
1245
+ :where(ul, li, strong) {
1246
+ max-width: var(--trajectory-empty-state-max-width, 500px);
1247
+ margin-inline: auto;
1248
+ }
1239
1249
  }
1240
- :global(.trajectory-empty-state) :where(p, ul) {
1241
- color: var(--text-color-muted);
1242
- }
1243
- :global(.trajectory-empty-state) :where(ul, li, strong) {
1244
- max-width: var(--trajectory-empty-state-max-width, 500px);
1245
- margin-inline: auto;
1246
- }
1247
- button:hover:not(:disabled) {
1248
- background: var(--border-color);
1249
- }
1250
- button:disabled {
1251
- background: var(--btn-disabled-bg);
1252
- color: var(--text-color-muted);
1253
- cursor: not-allowed;
1254
- }
1255
- .trajectory-controls input[type='number']::-webkit-outer-spin-button,
1256
- .trajectory-controls input[type='number']::-webkit-inner-spin-button {
1257
- -webkit-appearance: none;
1258
- margin: 0;
1250
+ button {
1251
+ &:hover:not(:disabled) {
1252
+ background: var(--border-color);
1253
+ }
1254
+ &:disabled {
1255
+ background: var(--btn-disabled-bg);
1256
+ color: var(--text-color-muted);
1257
+ cursor: not-allowed;
1258
+ }
1259
1259
  }
1260
1260
  /* Responsive design */
1261
1261
  @media (orientation: portrait) {
1262
- /* Fallback class for browsers without :has() support */
1263
- .trajectory.show-both-views {
1264
- min-height: calc(var(--min-height) * 2);
1265
- }
1266
- /* Modern browsers: use :has() for same effect */
1267
- @supports selector(:has(.content-area)) {
1268
- .trajectory:has(.content-area.show-both:not(.hide-plot):not(.hide-structure)) {
1262
+ .trajectory {
1263
+ /* Fallback class for browsers without :has() support */
1264
+ &.show-both-views {
1269
1265
  min-height: calc(var(--min-height) * 2);
1270
1266
  }
1271
- }
1272
- .trajectory .content-area.show-both:not(.hide-plot):not(.hide-structure) {
1273
- grid-template-columns: 1fr !important;
1274
- grid-template-rows: 1fr 1fr !important;
1267
+ /* Modern browsers: use :has() for same effect */
1268
+ @supports selector(:has(.content-area)) {
1269
+ &:has(.content-area.show-both:not(.hide-plot):not(.hide-structure)) {
1270
+ min-height: calc(var(--min-height) * 2);
1271
+ }
1272
+ }
1273
+ .content-area.show-both:not(.hide-plot):not(.hide-structure) {
1274
+ grid-template-columns: 1fr !important;
1275
+ grid-template-rows: 1fr 1fr !important;
1276
+ }
1275
1277
  }
1276
1278
  }
1277
1279
  .view-mode-dropdown-wrapper {
@@ -1297,19 +1299,19 @@ let fullscreen = $state(false);
1297
1299
  border-radius: 0;
1298
1300
  text-align: left;
1299
1301
  transition: background-color 0.15s ease;
1300
- }
1301
- .view-mode-option:first-child {
1302
- border-top-left-radius: 3px;
1303
- border-top-right-radius: 3px;
1304
- }
1305
- .view-mode-option.selected {
1306
- color: var(--accent-color);
1307
- }
1308
- .view-mode-option span {
1309
- font-weight: 500;
1310
- white-space: nowrap;
1311
- overflow: hidden;
1312
- text-overflow: ellipsis;
1313
- flex: 1;
1302
+ &:first-child {
1303
+ border-top-left-radius: 3px;
1304
+ border-top-right-radius: 3px;
1305
+ }
1306
+ &.selected {
1307
+ color: var(--accent-color);
1308
+ }
1309
+ span {
1310
+ font-weight: 500;
1311
+ white-space: nowrap;
1312
+ overflow: hidden;
1313
+ text-overflow: ellipsis;
1314
+ flex: 1;
1315
+ }
1314
1316
  }
1315
1317
  </style>
@@ -1,6 +1,6 @@
1
- <script lang="ts">import SettingsSection from '../layout/SettingsSection.svelte';
1
+ <script lang="ts">import { export_trajectory_video, get_ffmpeg_conversion_command, } from '../io/export';
2
+ import SettingsSection from '../layout/SettingsSection.svelte';
2
3
  import DraggablePane from '../overlays/DraggablePane.svelte';
3
- import { export_trajectory_video, get_ffmpeg_conversion_command, } from '../io/export';
4
4
  import { tooltip } from 'svelte-multiselect/attachments';
5
5
  let { export_pane_open = $bindable(false), trajectory = undefined, wrapper = undefined, filename = `trajectory`, video_fps = $bindable(30), resolution_multiplier = $bindable(1), on_step_change = undefined, pane_props = {}, toggle_props = {}, ...rest } = $props();
6
6
  let is_exporting = $state(false);
@@ -242,8 +242,9 @@ $effect(() => {
242
242
  <style>
243
243
  .field-label {
244
244
  display: flex;
245
- flex-direction: column;
246
- gap: 2pt;
245
+ align-items: center;
246
+ gap: 6pt;
247
+ white-space: nowrap;
247
248
  }
248
249
  .warning, .error-message {
249
250
  padding: 1ex;
@@ -275,13 +276,16 @@ $effect(() => {
275
276
  color: var(--text-color-muted);
276
277
  }
277
278
  .resolution-buttons {
278
- display: flex;
279
- gap: 6pt;
280
- margin: 4pt;
279
+ display: inline-flex;
280
+ gap: 3pt;
281
+ margin: 0;
282
+ margin-left: auto;
283
+ white-space: nowrap;
281
284
  }
282
285
  .resolution-buttons button {
283
- flex: 1;
284
- padding: 1pt 4pt;
286
+ flex: 0 0 auto;
287
+ min-width: 2.8em;
288
+ padding: 1pt 3pt;
285
289
  border: 1px solid var(--border-color, rgba(255, 255, 255, 0.2));
286
290
  background: var(--btn-bg, rgba(255, 255, 255, 0.1));
287
291
  color: var(--text-color);
@@ -12,6 +12,6 @@ type $$ComponentProps = {
12
12
  pane_props?: ComponentProps<typeof DraggablePane>[`pane_props`];
13
13
  toggle_props?: ComponentProps<typeof DraggablePane>[`toggle_props`];
14
14
  };
15
- declare const TrajectoryExportPane: import("svelte").Component<$$ComponentProps, {}, "resolution_multiplier" | "export_pane_open" | "video_fps">;
15
+ declare const TrajectoryExportPane: import("svelte").Component<$$ComponentProps, {}, "export_pane_open" | "resolution_multiplier" | "video_fps">;
16
16
  type TrajectoryExportPane = ReturnType<typeof TrajectoryExportPane>;
17
17
  export default TrajectoryExportPane;
@@ -0,0 +1,6 @@
1
+ export declare const MAX_SAFE_STRING_LENGTH: number;
2
+ export declare const MAX_METADATA_SIZE: number;
3
+ export declare const LARGE_FILE_THRESHOLD: number;
4
+ export declare const INDEX_SAMPLE_RATE = 100;
5
+ export declare const MAX_BIN_FILE_SIZE: number;
6
+ export declare const MAX_TEXT_FILE_SIZE: number;
@@ -0,0 +1,7 @@
1
+ // Constants for trajectory parsing and large file handling
2
+ export const MAX_SAFE_STRING_LENGTH = 0x1fffffe8 * 0.5; // 50% of JS max string length as safety
3
+ export const MAX_METADATA_SIZE = 50 * 1024 * 1024; // 50MB limit for metadata
4
+ export const LARGE_FILE_THRESHOLD = 400 * 1024 * 1024; // 400MB
5
+ export const INDEX_SAMPLE_RATE = 100; // Default sample rate for frame indexing
6
+ export const MAX_BIN_FILE_SIZE = 100 * 1024 * 1024; // 100MB default for ArrayBuffer files
7
+ export const MAX_TEXT_FILE_SIZE = 50 * 1024 * 1024; // 50MB default for string files
@@ -136,11 +136,9 @@ function property_varies(trajectory, property_key, tolerance = 1e-10) {
136
136
  // Check both direct structure properties and metadata
137
137
  let value;
138
138
  if (`lattice` in frame.structure) {
139
- const lattice = frame.structure.lattice;
140
- if (property_key in lattice &&
141
- typeof lattice[property_key] === `number`) {
142
- value = lattice[property_key];
143
- }
139
+ const lattice_value = frame.structure.lattice[property_key];
140
+ if (typeof lattice_value === `number`)
141
+ value = lattice_value;
144
142
  }
145
143
  if (value === undefined &&
146
144
  frame.metadata &&
@@ -0,0 +1,9 @@
1
+ export declare function strip_compression_extensions(filename: string): string;
2
+ export declare const FORMAT_PATTERNS: {
3
+ readonly ase: (data: unknown, filename?: string) => boolean;
4
+ readonly hdf5: (data: unknown, filename?: string) => boolean;
5
+ readonly vasp: (data: string, filename?: string) => boolean;
6
+ readonly xyz_multi: (data: string, filename?: string) => boolean;
7
+ readonly lammpstrj: (data: string, filename?: string) => boolean;
8
+ };
9
+ export declare function is_trajectory_file(filename: string, content?: string): boolean;
@@ -0,0 +1,76 @@
1
+ // Format detection for trajectory files
2
+ import { COMPRESSION_EXTENSIONS_REGEX, CONFIG_DIRS_REGEX, MD_SIM_EXCLUDE_REGEX, TRAJ_EXTENSIONS_REGEX, TRAJ_FALLBACK_EXTENSIONS_REGEX, TRAJ_KEYWORDS_SIMPLE_REGEX, XDATCAR_REGEX, } from '../constants';
3
+ import { count_xyz_frames } from './helpers';
4
+ export function strip_compression_extensions(filename) {
5
+ let base_name = filename.toLowerCase();
6
+ while (COMPRESSION_EXTENSIONS_REGEX.test(base_name)) {
7
+ base_name = base_name.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
8
+ }
9
+ return base_name;
10
+ }
11
+ // Unified format detection
12
+ export const FORMAT_PATTERNS = {
13
+ ase: (data, filename) => {
14
+ const base_name = filename ? strip_compression_extensions(filename) : undefined;
15
+ if (!base_name?.endsWith(`.traj`) || !(data instanceof ArrayBuffer)) {
16
+ return false;
17
+ }
18
+ const view = new Uint8Array(data.slice(0, 24));
19
+ return [0x2d, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x6c, 0x6d].every((byte, idx) => view[idx] === byte);
20
+ },
21
+ hdf5: (data, filename) => {
22
+ const base_name = filename ? strip_compression_extensions(filename) : undefined;
23
+ const has_ext = base_name?.match(/\.(h5|hdf5)$/);
24
+ if (!has_ext || !(data instanceof ArrayBuffer) || data.byteLength < 8)
25
+ return false;
26
+ const signature = new Uint8Array(data.slice(0, 8));
27
+ return [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a].every((b, idx) => signature[idx] === b);
28
+ },
29
+ vasp: (data, filename) => {
30
+ const basename = filename?.toLowerCase().split(`/`).pop() || ``;
31
+ if (basename === `xdatcar` || basename.startsWith(`xdatcar`))
32
+ return true;
33
+ const lines = data.trim().split(/\r?\n/);
34
+ return lines.length >= 10 &&
35
+ lines.some((line) => line.includes(`Direct configuration=`)) &&
36
+ !isNaN(parseFloat(lines[1])) &&
37
+ lines.slice(2, 5).every((line) => line.trim().split(/\s+/).length === 3);
38
+ },
39
+ xyz_multi: (data, filename) => {
40
+ const base = filename ? strip_compression_extensions(filename) : ``;
41
+ if (!/\.(xyz|extxyz)$/.test(base))
42
+ return false;
43
+ return count_xyz_frames(data) >= 2;
44
+ },
45
+ lammpstrj: (data, filename) => {
46
+ const base = filename ? strip_compression_extensions(filename) : ``;
47
+ if (!/\.lammpstrj$/.test(base))
48
+ return false;
49
+ return data.includes(`ITEM: TIMESTEP`) && data.includes(`ITEM: ATOMS`);
50
+ },
51
+ };
52
+ // Check if file is a trajectory (supports both filename-only and content-based detection)
53
+ export function is_trajectory_file(filename, content) {
54
+ if (CONFIG_DIRS_REGEX.test(filename))
55
+ return false;
56
+ const base_name = strip_compression_extensions(filename);
57
+ // For xyz/extxyz files, use content-based detection if available
58
+ if (/\.(xyz|extxyz)$/i.test(base_name)) {
59
+ if (content)
60
+ return count_xyz_frames(content) >= 2;
61
+ return TRAJ_KEYWORDS_SIMPLE_REGEX.test(base_name);
62
+ }
63
+ // Always detect these specific trajectory formats
64
+ if (TRAJ_EXTENSIONS_REGEX.test(base_name) || XDATCAR_REGEX.test(base_name))
65
+ return true;
66
+ // Special exclusion for generic md_simulation pattern with certain extensions
67
+ if (MD_SIM_EXCLUDE_REGEX.test(base_name))
68
+ return false;
69
+ // For .h5/.hdf5 files, require trajectory keywords
70
+ if (/\.(h5|hdf5)$/i.test(base_name)) {
71
+ return TRAJ_KEYWORDS_SIMPLE_REGEX.test(base_name);
72
+ }
73
+ // For other extensions, require both keywords and specific extensions
74
+ return TRAJ_KEYWORDS_SIMPLE_REGEX.test(base_name) &&
75
+ TRAJ_FALLBACK_EXTENSIONS_REGEX.test(base_name);
76
+ }
@@ -0,0 +1,17 @@
1
+ import type { FrameIndex, FrameLoader, ParseProgress, TrajectoryFrame, TrajectoryMetadata } from './index';
2
+ export declare class TrajFrameReader implements FrameLoader {
3
+ private format;
4
+ private global_numbers?;
5
+ constructor(filename: string);
6
+ get_total_frames(data: string | ArrayBuffer): Promise<number>;
7
+ build_frame_index(data: string | ArrayBuffer, sample_rate: number, on_progress?: (progress: ParseProgress) => void): Promise<FrameIndex[]>;
8
+ load_frame(data: string | ArrayBuffer, frame_number: number): Promise<TrajectoryFrame | null>;
9
+ extract_plot_metadata(data: string | ArrayBuffer, options?: {
10
+ sample_rate?: number;
11
+ properties?: string[];
12
+ }, on_progress?: (progress: ParseProgress) => void): Promise<TrajectoryMetadata[]>;
13
+ private load_xyz_frame;
14
+ private load_ase_frame;
15
+ private parse_xyz_metadata;
16
+ private parse_ase_metadata;
17
+ }