matterviz 0.4.2 → 0.4.3

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 (244) hide show
  1. package/dist/FilePicker.svelte +40 -50
  2. package/dist/api/optimade.js +10 -7
  3. package/dist/app.css +15 -0
  4. package/dist/brillouin/BrillouinZone.svelte +18 -9
  5. package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
  6. package/dist/brillouin/compute.js +10 -16
  7. package/dist/brillouin/geometry.js +2 -2
  8. package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
  9. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
  10. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
  11. package/dist/chempot-diagram/async-compute.svelte.js +12 -13
  12. package/dist/chempot-diagram/color.d.ts +21 -1
  13. package/dist/chempot-diagram/color.js +70 -1
  14. package/dist/chempot-diagram/compute.d.ts +7 -0
  15. package/dist/chempot-diagram/compute.js +85 -26
  16. package/dist/chempot-diagram/export.js +1 -6
  17. package/dist/chempot-diagram/temperature.js +9 -10
  18. package/dist/composition/BarChart.svelte +7 -9
  19. package/dist/composition/Composition.svelte +3 -8
  20. package/dist/composition/Formula.svelte +2 -2
  21. package/dist/composition/FormulaFilter.svelte +27 -93
  22. package/dist/composition/format.js +6 -8
  23. package/dist/composition/parse.js +50 -105
  24. package/dist/constants.d.ts +0 -1
  25. package/dist/constants.js +6 -17
  26. package/dist/convex-hull/ConvexHull2D.svelte +0 -2
  27. package/dist/convex-hull/ConvexHull3D.svelte +19 -47
  28. package/dist/convex-hull/ConvexHull4D.svelte +65 -95
  29. package/dist/convex-hull/StructurePopup.svelte +1 -1
  30. package/dist/convex-hull/gas-thermodynamics.js +21 -53
  31. package/dist/convex-hull/helpers.js +1 -7
  32. package/dist/convex-hull/thermodynamics.js +19 -27
  33. package/dist/coordination/CoordinationBarPlot.svelte +12 -21
  34. package/dist/element/BohrAtom.svelte +0 -1
  35. package/dist/element/ElementTile.svelte +26 -44
  36. package/dist/element/data.d.ts +2 -0
  37. package/dist/element/data.js +5 -1
  38. package/dist/element/index.d.ts +1 -1
  39. package/dist/element/index.js +1 -1
  40. package/dist/fermi-surface/FermiSurface.svelte +22 -23
  41. package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
  42. package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
  43. package/dist/fermi-surface/compute.js +22 -55
  44. package/dist/fermi-surface/parse.js +38 -70
  45. package/dist/file-viewer/JsonBrowser.svelte +1043 -0
  46. package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
  47. package/dist/file-viewer/PlotPanel.svelte +350 -0
  48. package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
  49. package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
  50. package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
  51. package/dist/file-viewer/detect.d.ts +11 -0
  52. package/dist/file-viewer/detect.js +381 -0
  53. package/dist/file-viewer/eligibility.d.ts +4 -0
  54. package/dist/file-viewer/eligibility.js +53 -0
  55. package/dist/file-viewer/host-protocol.d.ts +50 -0
  56. package/dist/file-viewer/host-transfer.d.ts +25 -0
  57. package/dist/file-viewer/host-transfer.js +42 -0
  58. package/dist/file-viewer/index.d.ts +5 -0
  59. package/dist/file-viewer/index.js +5 -0
  60. package/dist/file-viewer/main.d.ts +44 -0
  61. package/dist/file-viewer/main.js +535 -0
  62. package/dist/file-viewer/parse.d.ts +9 -0
  63. package/dist/file-viewer/parse.js +170 -0
  64. package/dist/file-viewer/plot-utils.d.ts +24 -0
  65. package/dist/file-viewer/plot-utils.js +191 -0
  66. package/dist/file-viewer/types.d.ts +4 -0
  67. package/dist/file-viewer/types.js +5 -0
  68. package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
  69. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  70. package/dist/index.d.ts +2 -2
  71. package/dist/index.js +1 -1
  72. package/dist/io/ExportPane.svelte +1 -1
  73. package/dist/io/decompress.d.ts +2 -0
  74. package/dist/io/decompress.js +3 -2
  75. package/dist/io/export.d.ts +7 -4
  76. package/dist/io/export.js +70 -18
  77. package/dist/io/file-drop.js +60 -35
  78. package/dist/io/url-drop.d.ts +2 -0
  79. package/dist/io/url-drop.js +28 -16
  80. package/dist/isosurface/Isosurface.svelte +687 -196
  81. package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
  82. package/dist/isosurface/IsosurfaceControls.svelte +564 -85
  83. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
  84. package/dist/isosurface/VolumeSlice.svelte +224 -0
  85. package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
  86. package/dist/isosurface/coloring.d.ts +20 -0
  87. package/dist/isosurface/coloring.js +118 -0
  88. package/dist/isosurface/geometry-worker-types.d.ts +38 -0
  89. package/dist/isosurface/geometry.worker.d.ts +1 -0
  90. package/dist/isosurface/geometry.worker.js +59 -0
  91. package/dist/isosurface/grid.d.ts +10 -0
  92. package/dist/isosurface/grid.js +31 -0
  93. package/dist/isosurface/index.d.ts +4 -0
  94. package/dist/isosurface/index.js +4 -0
  95. package/dist/isosurface/profile.d.ts +12 -0
  96. package/dist/isosurface/profile.js +19 -0
  97. package/dist/isosurface/sampling.d.ts +34 -0
  98. package/dist/isosurface/sampling.js +408 -0
  99. package/dist/isosurface/slice-rendering.d.ts +13 -0
  100. package/dist/isosurface/slice-rendering.js +79 -0
  101. package/dist/isosurface/slice.d.ts +22 -2
  102. package/dist/isosurface/slice.js +200 -116
  103. package/dist/isosurface/types.d.ts +24 -1
  104. package/dist/isosurface/types.js +120 -8
  105. package/dist/json-path.d.ts +4 -0
  106. package/dist/json-path.js +89 -0
  107. package/dist/labels.js +1 -1
  108. package/dist/layout/InfoCard.svelte +1 -3
  109. package/dist/layout/SettingsSection.svelte +52 -61
  110. package/dist/layout/SettingsSection.svelte.d.ts +6 -7
  111. package/dist/layout/ViewerChrome.svelte +7 -1
  112. package/dist/layout/json-tree/JsonNode.svelte +14 -48
  113. package/dist/layout/json-tree/JsonTree.svelte +48 -95
  114. package/dist/layout/json-tree/JsonValue.svelte +3 -18
  115. package/dist/layout/json-tree/utils.d.ts +0 -3
  116. package/dist/layout/json-tree/utils.js +50 -179
  117. package/dist/marching-cubes.d.ts +8 -1
  118. package/dist/marching-cubes.js +115 -48
  119. package/dist/math.d.ts +3 -2
  120. package/dist/math.js +29 -38
  121. package/dist/overlays/ContextMenu.svelte +15 -32
  122. package/dist/overlays/DraggablePane.svelte +39 -58
  123. package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
  124. package/dist/overlays/GlassChip.svelte +3 -1
  125. package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
  126. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
  127. package/dist/phase-diagram/build-diagram.js +1 -6
  128. package/dist/phase-diagram/svg-to-diagram.js +9 -14
  129. package/dist/plot/bar/BarPlot.svelte +38 -42
  130. package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
  131. package/dist/plot/box/BoxPlot.svelte +27 -23
  132. package/dist/plot/core/auto-place.d.ts +1 -1
  133. package/dist/plot/core/auto-place.js +1 -1
  134. package/dist/plot/core/data-transform.d.ts +1 -2
  135. package/dist/plot/core/data-transform.js +0 -10
  136. package/dist/plot/core/layout.d.ts +12 -5
  137. package/dist/plot/core/layout.js +37 -27
  138. package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
  139. package/dist/plot/core/pan-zoom.svelte.js +34 -17
  140. package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
  141. package/dist/plot/core/placed-tween.svelte.js +68 -21
  142. package/dist/plot/core/svg.js +0 -1
  143. package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
  144. package/dist/plot/core/utils/hierarchy-chart.js +12 -0
  145. package/dist/plot/core/utils/hierarchy-labels.js +3 -2
  146. package/dist/plot/histogram/Histogram.svelte +21 -26
  147. package/dist/plot/sankey/sankey.js +13 -9
  148. package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
  149. package/dist/plot/scatter/ScatterPlot.svelte +67 -52
  150. package/dist/plot/scatter/adaptive-density.d.ts +1 -1
  151. package/dist/plot/scatter/adaptive-density.js +36 -20
  152. package/dist/plot/sunburst/Sunburst.svelte +20 -20
  153. package/dist/plot/treemap/Treemap.svelte +175 -60
  154. package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
  155. package/dist/plot/treemap/index.d.ts +1 -0
  156. package/dist/plot/treemap/labels.d.ts +38 -0
  157. package/dist/plot/treemap/labels.js +80 -0
  158. package/dist/plot/treemap/treemap.d.ts +4 -2
  159. package/dist/plot/treemap/treemap.js +5 -2
  160. package/dist/rdf/RdfPlot.svelte +17 -35
  161. package/dist/rdf/calc-rdf.js +59 -44
  162. package/dist/rdf/index.d.ts +0 -1
  163. package/dist/sanitize.js +84 -26
  164. package/dist/scene/SceneCamera.svelte +1 -1
  165. package/dist/scene/SceneCamera.svelte.d.ts +1 -1
  166. package/dist/scene/index.d.ts +1 -2
  167. package/dist/scene/index.js +1 -1
  168. package/dist/scene/{props.js → props.svelte.js} +15 -1
  169. package/dist/settings.js +159 -482
  170. package/dist/spectral/Bands.svelte +151 -158
  171. package/dist/spectral/BrillouinBandsDos.svelte +4 -19
  172. package/dist/spectral/Dos.svelte +26 -24
  173. package/dist/spectral/helpers.js +40 -105
  174. package/dist/structure/AtomLegend.svelte +8 -13
  175. package/dist/structure/AtomLegend.svelte.d.ts +2 -3
  176. package/dist/structure/CellSelect.svelte +35 -10
  177. package/dist/structure/Structure.svelte +177 -99
  178. package/dist/structure/Structure.svelte.d.ts +1 -1
  179. package/dist/structure/StructureCarousel.svelte +10 -3
  180. package/dist/structure/StructureControls.svelte +8 -26
  181. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  182. package/dist/structure/StructureInfoPane.svelte +65 -76
  183. package/dist/structure/StructureScene.svelte +75 -109
  184. package/dist/structure/StructureScene.svelte.d.ts +3 -1
  185. package/dist/structure/StructureViewport.svelte +120 -55
  186. package/dist/structure/StructureViewport.svelte.d.ts +3 -1
  187. package/dist/structure/atom-properties.d.ts +1 -1
  188. package/dist/structure/atom-properties.js +4 -3
  189. package/dist/structure/bond-order-perception.js +3 -8
  190. package/dist/structure/bonding.d.ts +0 -1
  191. package/dist/structure/bonding.js +16 -23
  192. package/dist/structure/export.js +50 -74
  193. package/dist/structure/format-detect.js +5 -7
  194. package/dist/structure/index.js +6 -12
  195. package/dist/structure/parse.js +38 -100
  196. package/dist/structure/partial-occupancy.js +7 -4
  197. package/dist/structure/pbc.js +8 -10
  198. package/dist/structure/polyhedra.js +5 -4
  199. package/dist/structure/prop-groups.d.ts +85 -0
  200. package/dist/structure/prop-groups.js +18 -0
  201. package/dist/symmetry/SymmetryStats.svelte +0 -4
  202. package/dist/symmetry/WyckoffTable.svelte +5 -4
  203. package/dist/symmetry/cell-transform.js +5 -14
  204. package/dist/symmetry/index.js +6 -9
  205. package/dist/table/HeatmapTable.svelte +61 -103
  206. package/dist/table/ToggleMenu.svelte +166 -44
  207. package/dist/table/ToggleMenu.svelte.d.ts +1 -1
  208. package/dist/theme/embedded.d.ts +18 -0
  209. package/dist/theme/embedded.js +206 -0
  210. package/dist/theme/index.d.ts +4 -0
  211. package/dist/theme/index.js +1 -2
  212. package/dist/theme/themes.mjs +31 -60
  213. package/dist/tooltip/TooltipContent.svelte +1 -1
  214. package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
  215. package/dist/tooltip/index.d.ts +8 -1
  216. package/dist/trajectory/Trajectory.svelte +140 -120
  217. package/dist/trajectory/TrajectoryError.svelte +1 -66
  218. package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
  219. package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
  220. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
  221. package/dist/trajectory/extract.js +19 -29
  222. package/dist/trajectory/format-detect.d.ts +2 -0
  223. package/dist/trajectory/format-detect.js +7 -5
  224. package/dist/trajectory/frame-reader.js +3 -4
  225. package/dist/trajectory/helpers.js +16 -49
  226. package/dist/trajectory/index.js +2 -8
  227. package/dist/trajectory/parse/ase.js +1 -1
  228. package/dist/trajectory/parse/index.d.ts +4 -3
  229. package/dist/trajectory/parse/index.js +12 -17
  230. package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
  231. package/dist/trajectory/parse/vaspout-h5.js +1 -4
  232. package/dist/trajectory/parse/xyz.js +48 -2
  233. package/dist/trajectory/plotting.js +14 -37
  234. package/dist/xrd/XrdPlot.svelte +43 -68
  235. package/dist/xrd/calc-xrd.js +22 -58
  236. package/dist/xrd/parse.js +13 -22
  237. package/package.json +40 -8
  238. package/readme.md +6 -5
  239. package/dist/scene/types.d.ts +0 -26
  240. package/dist/tooltip/types.d.ts +0 -8
  241. package/dist/trajectory/constants.d.ts +0 -6
  242. package/dist/trajectory/constants.js +0 -11
  243. /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
  244. /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
@@ -0,0 +1,381 @@
1
+ // Data type detection for JSON values -- determines which visualization component to use.
2
+ // Used by JsonBrowser and the file renderer to select visualization components.
3
+ import { build_path } from '../json-path';
4
+ import { is_optimade_raw } from '../structure/parse';
5
+ export { resolve_path } from '../json-path';
6
+ // Human-readable labels for badge display
7
+ export const TYPE_LABELS = {
8
+ structure: `Structure`,
9
+ fermi_surface: `Fermi Surface`,
10
+ band_grid: `Band Grid`,
11
+ convex_hull: `Convex Hull`,
12
+ volumetric: `Volumetric`,
13
+ phase_diagram: `Phase Diagram`,
14
+ band_structure: `Band Structure`,
15
+ dos: `DOS`,
16
+ bands_and_dos: `Bands + DOS`,
17
+ brillouin_zone: `Brillouin Zone`,
18
+ xrd: `XRD`,
19
+ table: `Table`,
20
+ plot: `Plot`,
21
+ };
22
+ // Badge colors per type (CSS color values)
23
+ export const TYPE_COLORS = {
24
+ structure: `#4fc3f7`,
25
+ fermi_surface: `#ab47bc`,
26
+ band_grid: `#7e57c2`,
27
+ convex_hull: `#66bb6a`,
28
+ volumetric: `#ffa726`,
29
+ phase_diagram: `#ef5350`,
30
+ band_structure: `#29b6f6`,
31
+ dos: `#26a69a`,
32
+ bands_and_dos: `#5c6bc0`,
33
+ brillouin_zone: `#8d6e63`,
34
+ xrd: `#ec407a`,
35
+ table: `#78909c`,
36
+ plot: `#7c3aed`,
37
+ };
38
+ // === Type Guards ===
39
+ // Narrows unknown to a non-array object record; used by every type guard below
40
+ const as_record = (obj) => obj && typeof obj === `object` && !Array.isArray(obj)
41
+ ? obj
42
+ : null;
43
+ // Check that `key` on `data` is an Array with exactly `len` elements (or any length if omitted)
44
+ const has_array = (data, key, len) => {
45
+ const val = data[key];
46
+ return Array.isArray(val) && (len === undefined || val.length === len);
47
+ };
48
+ // Structure: must have non-empty `sites` array where first site has `species` + coordinates
49
+ function is_structure(obj) {
50
+ const data = as_record(obj);
51
+ if (!data || !has_array(data, `sites`))
52
+ return false;
53
+ const sites = data.sites;
54
+ if (sites.length === 0)
55
+ return false;
56
+ const first_site = as_record(sites[0]);
57
+ if (!first_site)
58
+ return false;
59
+ const has_species = Array.isArray(first_site.species) && first_site.species.length > 0;
60
+ return has_species && (Array.isArray(first_site.abc) || Array.isArray(first_site.xyz));
61
+ }
62
+ // FermiSurfaceData: pre-computed isosurfaces with reciprocal lattice info
63
+ function is_fermi_surface(obj) {
64
+ const data = as_record(obj);
65
+ if (!data)
66
+ return false;
67
+ return (has_array(data, `isosurfaces`) &&
68
+ has_array(data, `k_lattice`, 3) &&
69
+ typeof data.fermi_energy === `number` &&
70
+ (data.reciprocal_cell === `wigner_seitz` || data.reciprocal_cell === `parallelepiped`) &&
71
+ Boolean(as_record(data.metadata)));
72
+ }
73
+ // BandGridData: raw band energies on a k-grid (needs marching cubes extraction)
74
+ function is_band_grid(obj) {
75
+ const data = as_record(obj);
76
+ if (!data)
77
+ return false;
78
+ return (has_array(data, `energies`) &&
79
+ has_array(data, `k_grid`, 3) &&
80
+ has_array(data, `k_lattice`, 3) &&
81
+ typeof data.fermi_energy === `number` &&
82
+ typeof data.n_bands === `number` &&
83
+ typeof data.n_spins === `number`);
84
+ }
85
+ // ConvexHull entries: array of objects with `composition` (object) + energy field
86
+ // Accepts `energy`, `e_form_per_atom`, or `energy_per_atom` as the energy key
87
+ function is_convex_hull_entries(obj) {
88
+ if (!Array.isArray(obj) || obj.length < 2)
89
+ return false;
90
+ // Check first few entries to avoid false positives on random arrays
91
+ return obj.slice(0, 3).every((item) => {
92
+ const entry = as_record(item);
93
+ return (entry &&
94
+ as_record(entry.composition) &&
95
+ (typeof entry.energy === `number` ||
96
+ typeof entry.e_form_per_atom === `number` ||
97
+ typeof entry.energy_per_atom === `number`));
98
+ });
99
+ }
100
+ // VolumetricData: 3D scalar grid with lattice info
101
+ function is_volumetric(obj) {
102
+ const data = as_record(obj);
103
+ if (!data || !has_array(data, `grid`))
104
+ return false;
105
+ // grid must be a 3D array (array of arrays of arrays)
106
+ const grid = data.grid;
107
+ if (grid.length === 0)
108
+ return false;
109
+ const first_slice = grid[0];
110
+ if (!Array.isArray(first_slice) || !first_slice.length || !Array.isArray(first_slice[0]))
111
+ return false;
112
+ return (has_array(data, `grid_dims`, 3) &&
113
+ has_array(data, `lattice`, 3) &&
114
+ has_array(data, `origin`, 3) &&
115
+ Boolean(as_record(data.data_range)) &&
116
+ typeof data.periodic === `boolean`);
117
+ }
118
+ // PhaseDiagramData: binary phase diagram with components, regions, boundaries
119
+ function is_phase_diagram(obj) {
120
+ const data = as_record(obj);
121
+ if (!data)
122
+ return false;
123
+ if (!has_array(data, `components`, 2))
124
+ return false;
125
+ const [comp_a, comp_b] = data.components;
126
+ if (typeof comp_a !== `string` || typeof comp_b !== `string`)
127
+ return false;
128
+ return (has_array(data, `regions`) &&
129
+ has_array(data, `boundaries`) &&
130
+ has_array(data, `temperature_range`, 2));
131
+ }
132
+ // BandStructure: normalized format (qpoints, branches, bands, nb_bands)
133
+ // or pymatgen format (kpoints, branches, bands with spin keys, labels_dict)
134
+ function is_band_structure(obj) {
135
+ const data = as_record(obj);
136
+ if (!data)
137
+ return false;
138
+ if (!has_array(data, `branches`) || data.branches.length === 0) {
139
+ return false;
140
+ }
141
+ if (!as_record(data.labels_dict))
142
+ return false;
143
+ // Normalized format
144
+ if (has_array(data, `qpoints`) &&
145
+ has_array(data, `bands`) &&
146
+ typeof data.nb_bands === `number`)
147
+ return true;
148
+ // Pymatgen format: kpoints + bands object (not array) + efermi
149
+ if (has_array(data, `kpoints`) && as_record(data.bands)) {
150
+ return typeof data.efermi === `number`;
151
+ }
152
+ return false;
153
+ }
154
+ // DOS: pymatgen CompleteDos format or normalized DosData
155
+ // CompleteDos: has energies, densities, efermi, and @class containing "Dos"
156
+ // DosData: has type ("phonon"|"electronic"), frequencies/energies, densities
157
+ function is_dos(obj) {
158
+ const data = as_record(obj);
159
+ if (!data)
160
+ return false;
161
+ const has_spectra = has_array(data, `energies`) || has_array(data, `frequencies`);
162
+ // pymatgen CompleteDos format
163
+ if (typeof data[`@class`] === `string` &&
164
+ data[`@class`].includes(`Dos`) &&
165
+ has_spectra &&
166
+ data.densities !== undefined)
167
+ return true;
168
+ // Normalized DosData format
169
+ if ((data.type === `phonon` || data.type === `electronic`) &&
170
+ has_spectra &&
171
+ has_array(data, `densities`))
172
+ return true;
173
+ return false;
174
+ }
175
+ // BandsAndDos: object containing both band_structure and dos data at the same level.
176
+ // Must be a focused wrapper (few keys), not a large object that happens to have both.
177
+ function is_bands_and_dos(obj) {
178
+ const data = as_record(obj);
179
+ if (!data)
180
+ return false;
181
+ const keys = Object.keys(data).filter((key) => !key.startsWith(`@`) && !key.startsWith(`_`));
182
+ // Wrapper format: { band_structure: {...}, dos: {...} } with few extra keys
183
+ const has_bands = as_record(data.band_structure) && is_band_structure(data.band_structure);
184
+ const has_dos_key = as_record(data.dos) && is_dos(data.dos);
185
+ if (has_bands && has_dos_key && keys.length <= 5)
186
+ return true;
187
+ // Combined-fields format: single object with both band structure and DOS fields mixed in
188
+ const has_bands_fields = has_array(data, `branches`) && as_record(data.labels_dict);
189
+ const has_dos_fields = (has_array(data, `energies`) || has_array(data, `frequencies`)) &&
190
+ data.densities !== undefined &&
191
+ (data.atom_dos !== undefined || data.spd_dos !== undefined);
192
+ return Boolean(has_bands_fields && has_dos_fields);
193
+ }
194
+ // BrillouinZone: reciprocal lattice data with optional k-path info
195
+ // Matches objects with a lattice that has reciprocal vectors (k_lattice or reciprocal_lattice)
196
+ // and optionally k-path points/labels for overlaying on the zone
197
+ function is_brillouin_zone(obj) {
198
+ const data = as_record(obj);
199
+ if (!data)
200
+ return false;
201
+ // Must have reciprocal lattice vectors (3x3 matrix)
202
+ const has_k_lattice = has_array(data, `k_lattice`, 3) || has_array(data, `reciprocal_lattice`, 3);
203
+ if (!has_k_lattice)
204
+ return false;
205
+ // Must have k-path or explicit BZ data to distinguish from Fermi surface data
206
+ // (Fermi surface also has k_lattice but additionally has isosurfaces)
207
+ if (has_array(data, `isosurfaces`))
208
+ return false; // that's a Fermi surface, not a plain BZ
209
+ // Accept if it has k_path labels/points, or a structure with lattice
210
+ return (has_array(data, `k_path`) ||
211
+ has_array(data, `k_points`) ||
212
+ Boolean(as_record(data.k_labels)) ||
213
+ Boolean(as_record(data.labels_dict)) ||
214
+ data.bz_order !== undefined);
215
+ }
216
+ // XRD pattern: x + y arrays of equal length (2-theta angles vs intensities)
217
+ // with optional hkls (Miller indices) and d_hkls (d-spacings)
218
+ function is_xrd_pattern(obj) {
219
+ const data = as_record(obj);
220
+ if (!data)
221
+ return false;
222
+ if (!has_array(data, `x`) || !has_array(data, `y`))
223
+ return false;
224
+ const x_arr = data.x;
225
+ const y_arr = data.y;
226
+ if (x_arr.length !== y_arr.length || x_arr.length === 0)
227
+ return false;
228
+ // Must have numeric x/y values
229
+ if (typeof x_arr[0] !== `number` || typeof y_arr[0] !== `number`)
230
+ return false;
231
+ // Distinguish from generic scatter data: XRD patterns typically have hkls or d_hkls,
232
+ // or have @class containing "Xrd"
233
+ return (has_array(data, `hkls`) ||
234
+ has_array(data, `d_hkls`) ||
235
+ (typeof data[`@class`] === `string` && data[`@class`].includes(`Xrd`)) ||
236
+ typeof data.wavelength === `number`);
237
+ }
238
+ // Tabular data: array of objects with consistent keys (row-based table format)
239
+ // or object with parallel arrays (column-based format)
240
+ function is_tabular_data(obj) {
241
+ // Row-based: array of objects with string keys and numeric/string values
242
+ if (Array.isArray(obj) && obj.length >= 3) {
243
+ const first = as_record(obj[0]);
244
+ const second = as_record(obj[1]);
245
+ if (!first || !second)
246
+ return false;
247
+ const first_keys = Object.keys(first);
248
+ if (first_keys.length < 2)
249
+ return false;
250
+ // Check that rows have consistent keys and mostly numeric/string values
251
+ const second_keys = Object.keys(second);
252
+ const overlap = first_keys.filter((key) => second_keys.includes(key));
253
+ if (overlap.length < first_keys.length * 0.5)
254
+ return false;
255
+ // At least some values should be numbers (not just metadata objects)
256
+ const num_count = first_keys.filter((key) => typeof first[key] === `number`).length;
257
+ return num_count >= 1;
258
+ }
259
+ // Column-based: object where multiple values are equal-length arrays
260
+ const data = as_record(obj);
261
+ if (!data)
262
+ return false;
263
+ const entries = Object.entries(data);
264
+ if (entries.length < 2)
265
+ return false;
266
+ const array_entries = entries.filter(([, val]) => Array.isArray(val) && val.length > 0);
267
+ if (array_entries.length < 2)
268
+ return false;
269
+ // Check that arrays have consistent lengths
270
+ const lengths = array_entries.map(([, val]) => val.length);
271
+ const first_len = lengths[0];
272
+ if (!lengths.every((len) => len === first_len))
273
+ return false;
274
+ // At least one column must contain numbers
275
+ return array_entries.some(([, val]) => typeof val[0] === `number`);
276
+ }
277
+ // Plottable data: tabular data with at least 2 numeric columns (enough for a scatter plot).
278
+ // Samples multiple rows/elements to handle leading nulls.
279
+ export function is_plottable_data(obj) {
280
+ if (!is_tabular_data(obj))
281
+ return false;
282
+ if (Array.isArray(obj)) {
283
+ const sample = obj.slice(0, 10).map(as_record).filter(Boolean);
284
+ if (sample.length === 0)
285
+ return false;
286
+ const all_keys = new Set(sample.flatMap(Object.keys));
287
+ const num_cols = [...all_keys].filter((key) => sample.some((row) => typeof row[key] === `number`)).length;
288
+ return num_cols >= 2;
289
+ }
290
+ const data = as_record(obj);
291
+ if (!data)
292
+ return false;
293
+ const array_entries = Object.entries(data).filter(([, val]) => Array.isArray(val) && val.length > 0);
294
+ const num_cols = array_entries.filter(([, val]) => val.some((elem) => typeof elem === `number`)).length;
295
+ return num_cols >= 2;
296
+ }
297
+ // === Main Detection Function ===
298
+ // Detect the visualization type for a given JSON value.
299
+ // Returns the type if the value matches a known format, or null if not renderable.
300
+ // Checks are ordered from most specific to least specific to minimize false positives:
301
+ // OPTIMADE before generic structure, combined bands+DOS before individual band/dos,
302
+ // structure late (structures are common building blocks inside other data types),
303
+ // and generic tabular data last.
304
+ export function detect_view_type(value) {
305
+ if (value == null)
306
+ return null;
307
+ if (as_record(value) && is_optimade_raw(value))
308
+ return `structure`;
309
+ if (is_fermi_surface(value))
310
+ return `fermi_surface`;
311
+ if (is_band_grid(value))
312
+ return `band_grid`;
313
+ if (is_phase_diagram(value))
314
+ return `phase_diagram`;
315
+ if (is_bands_and_dos(value))
316
+ return `bands_and_dos`;
317
+ if (is_band_structure(value))
318
+ return `band_structure`;
319
+ if (is_dos(value))
320
+ return `dos`;
321
+ if (is_brillouin_zone(value))
322
+ return `brillouin_zone`;
323
+ if (is_xrd_pattern(value))
324
+ return `xrd`;
325
+ if (is_volumetric(value))
326
+ return `volumetric`;
327
+ if (is_structure(value))
328
+ return `structure`;
329
+ if (is_convex_hull_entries(value))
330
+ return `convex_hull`;
331
+ if (is_tabular_data(value))
332
+ return `table`;
333
+ return null;
334
+ }
335
+ // Recursively scan a JSON object to find all paths that contain renderable data.
336
+ // Returns a Map of JSON path strings to their detected type.
337
+ // Used to show badges on JsonTree nodes indicating which subtrees contain visualizable data.
338
+ export function scan_renderable_paths(obj, prefix = ``, max_depth = 10) {
339
+ const results = new Map();
340
+ const visited = new WeakSet();
341
+ function walk(value, path, depth) {
342
+ if (depth > max_depth)
343
+ return;
344
+ if (value == null)
345
+ return;
346
+ if (typeof value !== `object`)
347
+ return;
348
+ // Circular reference protection
349
+ const obj_ref = value;
350
+ if (visited.has(obj_ref))
351
+ return;
352
+ visited.add(obj_ref);
353
+ // Check if this value itself is renderable
354
+ const detected_type = detect_view_type(value);
355
+ if (detected_type) {
356
+ results.set(path, { type: detected_type, label: TYPE_LABELS[detected_type] });
357
+ // If tabular data is also plottable, register a plot badge too
358
+ if (detected_type === `table` && is_plottable_data(value)) {
359
+ const plot_path = path ? `${path}\u0000plot` : `\u0000plot`;
360
+ results.set(plot_path, { type: `plot`, label: TYPE_LABELS.plot });
361
+ }
362
+ // Don't recurse into renderable objects -- their children are part of the data
363
+ return;
364
+ }
365
+ // Recurse into children
366
+ if (Array.isArray(value)) {
367
+ // For arrays, only scan first few elements to avoid huge arrays
368
+ const scan_count = Math.min(value.length, 20);
369
+ for (let idx = 0; idx < scan_count; idx++) {
370
+ walk(value[idx], build_path(path, idx), depth + 1);
371
+ }
372
+ }
373
+ else {
374
+ for (const [key, child_value] of Object.entries(value)) {
375
+ walk(child_value, build_path(path, key), depth + 1);
376
+ }
377
+ }
378
+ }
379
+ walk(obj, prefix, 0);
380
+ return results;
381
+ }
@@ -0,0 +1,4 @@
1
+ export declare const normalize_browser_supported_filename: (filename: string) => string | null;
2
+ export declare const should_encode_filename_as_base64: (filename: string) => boolean;
3
+ export declare const is_matterviz_filename: (filename: unknown) => boolean;
4
+ export declare const is_auto_renderable_filename: (filename: unknown) => boolean;
@@ -0,0 +1,53 @@
1
+ import { COMPRESSION_EXTENSIONS_REGEX, CONFIG_DIRS_REGEX, TRAJ_KEYWORDS_REGEX, } from '../constants';
2
+ import { FERMI_FILE_RE, VOLUMETRIC_EXT_RE, VOLUMETRIC_VASP_RE } from './types';
3
+ import { detect_compression_format, is_browser_decompressible_format, } from '../io/decompress';
4
+ import { is_structure_file } from '../structure/format-detect';
5
+ import { is_trajectory_file } from '../trajectory/format-detect';
6
+ // Return the browser-visible filename after removing one supported compression
7
+ // wrapper. Nested and unsupported wrappers are deliberately rejected because the
8
+ // parser only decompresses one layer.
9
+ export const normalize_browser_supported_filename = (filename) => {
10
+ const format = detect_compression_format(filename);
11
+ if (!format)
12
+ return filename;
13
+ if (!is_browser_decompressible_format(format))
14
+ return null;
15
+ const normalized = filename.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
16
+ return detect_compression_format(normalized) ? null : normalized;
17
+ };
18
+ export const should_encode_filename_as_base64 = (filename) => detect_compression_format(filename) !== null || /\.(?:traj|h5|hdf5)$/i.test(filename);
19
+ const normalize_eligible_filename = (filename) => {
20
+ if (typeof filename !== `string` || !filename || CONFIG_DIRS_REGEX.test(filename))
21
+ return null;
22
+ return normalize_browser_supported_filename(filename.split(/[\\/]/).pop() ?? ``);
23
+ };
24
+ // Broad: MatterViz can open/view this file (JSON/YAML structures, keyword trajs, …).
25
+ export const is_matterviz_filename = (filename) => {
26
+ const normalized = normalize_eligible_filename(filename);
27
+ if (normalized === null)
28
+ return false;
29
+ return (FERMI_FILE_RE.test(normalized) ||
30
+ VOLUMETRIC_EXT_RE.test(normalized) ||
31
+ VOLUMETRIC_VASP_RE.test(normalized) ||
32
+ is_structure_file(normalized) ||
33
+ is_trajectory_file(normalized));
34
+ };
35
+ // Conservative auto-open list: only unambiguous structure / trajectory / volumetric /
36
+ // Fermi filenames. No JSON/YAML/XML, no keyword+.log/.out/.dat/.data heuristics.
37
+ const AUTO_RENDER_EXT_RE = /\.(?:cif|mcif|mmcif|xyz|extxyz|poscar|vasp|cube|pdb|mol|mol2|sdf|lmp|dump|traj|xtc|lammpstrj)$/i;
38
+ const AUTO_RENDER_VASP_NAME_RE = /(?:^|[\\/_.-])(?:poscar|contcar|xdatcar)(?:[\\/_.-]|$)/i;
39
+ export const is_auto_renderable_filename = (filename) => {
40
+ const normalized = normalize_eligible_filename(filename);
41
+ if (normalized === null)
42
+ return false;
43
+ if (FERMI_FILE_RE.test(normalized) ||
44
+ VOLUMETRIC_EXT_RE.test(normalized) ||
45
+ VOLUMETRIC_VASP_RE.test(normalized) ||
46
+ AUTO_RENDER_VASP_NAME_RE.test(normalized) ||
47
+ AUTO_RENDER_EXT_RE.test(normalized)) {
48
+ return true;
49
+ }
50
+ // HDF5 only when the name clearly marks a trajectory
51
+ return (/\.(?:h5|hdf5)$/i.test(normalized) &&
52
+ (/vaspout/i.test(normalized) || TRAJ_KEYWORDS_REGEX.test(normalized)));
53
+ };
@@ -0,0 +1,50 @@
1
+ import type { DefaultSettings } from '../settings';
2
+ import type { ThemeName } from '../theme';
3
+ export interface FileData {
4
+ filename: string;
5
+ content: string;
6
+ is_base64: boolean;
7
+ }
8
+ export interface WebviewBootstrapData {
9
+ data: FileData;
10
+ theme: ThemeName;
11
+ defaults?: DefaultSettings;
12
+ moyo_wasm_url?: string;
13
+ }
14
+ type WatchedFileContext = {
15
+ file_path: string;
16
+ request_id?: string;
17
+ filename?: string;
18
+ frame_index?: number;
19
+ };
20
+ type HostFileRequest = {
21
+ request_id: string;
22
+ file_path: string;
23
+ };
24
+ export type FileChangeMessage = WatchedFileContext & ({
25
+ command: `fileUpdated`;
26
+ data: FileData;
27
+ theme?: ThemeName;
28
+ } | {
29
+ command: `fileDeleted`;
30
+ });
31
+ export type WebviewToHostMessage = {
32
+ command: `info` | `error`;
33
+ text: string;
34
+ } | ({
35
+ command: `request_large_file`;
36
+ } & HostFileRequest) | ({
37
+ command: `request_frame`;
38
+ frame_index: number;
39
+ } & HostFileRequest) | {
40
+ command: `saveAs`;
41
+ filename: string;
42
+ content: string;
43
+ is_binary?: boolean;
44
+ } | ({
45
+ command: `startWatching`;
46
+ } & WatchedFileContext) | {
47
+ command: `stopWatching`;
48
+ file_path: string;
49
+ };
50
+ export {};
@@ -0,0 +1,25 @@
1
+ export interface LargeFileMarker {
2
+ file_path: string;
3
+ file_size: number;
4
+ }
5
+ export declare const format_large_file_marker: ({ file_path, file_size, }: LargeFileMarker) => string;
6
+ export declare const parse_large_file_marker: (content: string) => LargeFileMarker | null;
7
+ export type HostTransferRejectReason = `file-too-large` | `unsupported-compression` | `unsupported-large-format`;
8
+ export type HostFileTransferPlan = {
9
+ kind: `inline`;
10
+ is_base64: boolean;
11
+ } | {
12
+ kind: `marker`;
13
+ content: string;
14
+ } | {
15
+ kind: `reject`;
16
+ reason: HostTransferRejectReason;
17
+ max_file_size?: number;
18
+ };
19
+ export interface HostFileTransferInput extends LargeFileMarker {
20
+ filename: string;
21
+ large_file_threshold: number;
22
+ max_file_size: number;
23
+ max_text_file_size: number;
24
+ }
25
+ export declare const plan_host_file_transfer: ({ filename, file_path, file_size, large_file_threshold, max_file_size, max_text_file_size, }: HostFileTransferInput) => HostFileTransferPlan;
@@ -0,0 +1,42 @@
1
+ import { normalize_browser_supported_filename, should_encode_filename_as_base64, } from './eligibility';
2
+ import { indexed_trajectory_format, is_indexable_trajectory_filename, } from '../trajectory/format-detect';
3
+ export const format_large_file_marker = ({ file_path, file_size, }) => {
4
+ if (!file_path || !Number.isSafeInteger(file_size) || file_size < 0)
5
+ throw new Error(`Invalid large file marker data`);
6
+ return `LARGE_FILE:${file_path}:${file_size}`;
7
+ };
8
+ export const parse_large_file_marker = (content) => {
9
+ const prefix = `LARGE_FILE:`;
10
+ if (!content.startsWith(prefix))
11
+ return null;
12
+ const separator_idx = content.lastIndexOf(`:`);
13
+ const file_path = content.slice(prefix.length, separator_idx);
14
+ const size_text = content.slice(separator_idx + 1);
15
+ const file_size = Number(size_text);
16
+ if (!file_path || !/^\d+$/.test(size_text) || !Number.isSafeInteger(file_size))
17
+ throw new Error(`Malformed large file marker`);
18
+ return { file_path, file_size };
19
+ };
20
+ export const plan_host_file_transfer = ({ filename, file_path, file_size, large_file_threshold, max_file_size, max_text_file_size, }) => {
21
+ const normalized_filename = normalize_browser_supported_filename(filename);
22
+ const is_large_file = file_size > large_file_threshold;
23
+ const trajectory_format = normalized_filename !== null && is_indexable_trajectory_filename(normalized_filename)
24
+ ? indexed_trajectory_format(normalized_filename)
25
+ : null;
26
+ const format_max_file_size = is_large_file && trajectory_format === `xyz`
27
+ ? Math.min(max_text_file_size, max_file_size)
28
+ : max_file_size;
29
+ if (file_size > format_max_file_size) {
30
+ return { kind: `reject`, reason: `file-too-large`, max_file_size: format_max_file_size };
31
+ }
32
+ if (!is_large_file)
33
+ return { kind: `inline`, is_base64: should_encode_filename_as_base64(filename) };
34
+ if (normalized_filename === null)
35
+ return { kind: `reject`, reason: `unsupported-compression` };
36
+ if (!trajectory_format)
37
+ return { kind: `reject`, reason: `unsupported-large-format` };
38
+ return {
39
+ kind: `marker`,
40
+ content: format_large_file_marker({ file_path, file_size }),
41
+ };
42
+ };
@@ -0,0 +1,5 @@
1
+ export * from './detect';
2
+ export * from './eligibility';
3
+ export { default as JsonBrowser } from './JsonBrowser.svelte';
4
+ export { default as PlotPanel } from './PlotPanel.svelte';
5
+ export * from './plot-utils';
@@ -0,0 +1,5 @@
1
+ export * from './detect';
2
+ export * from './eligibility';
3
+ export { default as JsonBrowser } from './JsonBrowser.svelte';
4
+ export { default as PlotPanel } from './PlotPanel.svelte';
5
+ export * from './plot-utils';
@@ -0,0 +1,44 @@
1
+ import '../app.css';
2
+ import '../theme/themes.mjs';
3
+ import type { FrameIndex, FrameLoader, TrajectoryFrame, TrajectoryMetadata } from '../trajectory';
4
+ import type { WebviewBootstrapData } from './host-protocol';
5
+ import type { ParseResult } from './parse';
6
+ export type MatterVizData = WebviewBootstrapData;
7
+ export interface MatterVizApp {
8
+ $on?(type: string, callback: (event: Event) => void): () => void;
9
+ $set?(props: Partial<Record<string, unknown>>): void;
10
+ }
11
+ export interface DisplayOptions {
12
+ initial_step_idx?: number;
13
+ on_step_change?: (step_idx: number, total_frames: number) => void;
14
+ }
15
+ export declare class VSCodeFrameLoader implements FrameLoader {
16
+ private readonly file_path;
17
+ private readonly vscode_api;
18
+ constructor(file_path: string, vscode_api: VSCodeAPI);
19
+ load_frame(_data: string | ArrayBuffer, frame_index: number, timeout?: number): Promise<TrajectoryFrame | null>;
20
+ get_total_frames(): Promise<number>;
21
+ build_frame_index(): Promise<FrameIndex[]>;
22
+ extract_plot_metadata(): Promise<TrajectoryMetadata[]>;
23
+ }
24
+ export interface VSCodeAPI {
25
+ postMessage(message: unknown): void;
26
+ }
27
+ declare global {
28
+ interface Window {
29
+ matterviz_data?: MatterVizData;
30
+ initializeMatterViz?: () => Promise<MatterVizApp | null>;
31
+ cleanupMatterViz?: () => Promise<void>;
32
+ download?: (data: string | Blob, filename: string) => void;
33
+ }
34
+ var matterviz_data: MatterVizData | undefined;
35
+ function acquireVsCodeApi(): VSCodeAPI;
36
+ }
37
+ export declare const setup_vscode_download: () => void;
38
+ type DisplayResult = ParseResult & {
39
+ streaming_info?: {
40
+ file_path: string;
41
+ };
42
+ };
43
+ export declare const create_display: (container: HTMLElement, result: DisplayResult, display_options?: DisplayOptions) => MatterVizApp;
44
+ export {};