matterviz 0.3.2 → 0.3.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 (280) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +123 -82
  3. package/dist/Icon.svelte +18 -12
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -207
  7. package/dist/brillouin/BrillouinZone.svelte +292 -149
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
  14. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
  15. package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
  16. package/dist/brillouin/compute.js +11 -6
  17. package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
  18. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
  19. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
  20. package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
  21. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  22. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  23. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  24. package/dist/chempot-diagram/chempot-worker.js +11 -0
  25. package/dist/chempot-diagram/color.js +1 -2
  26. package/dist/chempot-diagram/compute.d.ts +10 -0
  27. package/dist/chempot-diagram/compute.js +250 -88
  28. package/dist/chempot-diagram/index.d.ts +2 -1
  29. package/dist/chempot-diagram/index.js +2 -1
  30. package/dist/chempot-diagram/temperature.js +8 -9
  31. package/dist/chempot-diagram/types.d.ts +3 -0
  32. package/dist/chempot-diagram/types.js +1 -0
  33. package/dist/colors/index.d.ts +1 -1
  34. package/dist/colors/index.js +5 -3
  35. package/dist/composition/BarChart.svelte +128 -55
  36. package/dist/composition/BubbleChart.svelte +102 -49
  37. package/dist/composition/Composition.svelte +100 -79
  38. package/dist/composition/Formula.svelte +108 -62
  39. package/dist/composition/FormulaFilter.svelte +665 -537
  40. package/dist/composition/PieChart.svelte +183 -108
  41. package/dist/composition/format.d.ts +5 -0
  42. package/dist/composition/format.js +20 -3
  43. package/dist/composition/parse.js +14 -9
  44. package/dist/convex-hull/ConvexHull.svelte +93 -40
  45. package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
  46. package/dist/convex-hull/ConvexHull2D.svelte +549 -360
  47. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
  48. package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
  49. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
  50. package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
  51. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
  52. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  53. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
  54. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  55. package/dist/convex-hull/ConvexHullStats.svelte +425 -328
  56. package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
  57. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  58. package/dist/convex-hull/StructurePopup.svelte +25 -4
  59. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  60. package/dist/convex-hull/barycentric-coords.js +13 -7
  61. package/dist/convex-hull/demo-temperature.js +8 -4
  62. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  63. package/dist/convex-hull/helpers.d.ts +9 -0
  64. package/dist/convex-hull/helpers.js +77 -34
  65. package/dist/convex-hull/thermodynamics.js +61 -56
  66. package/dist/convex-hull/types.d.ts +9 -14
  67. package/dist/convex-hull/types.js +0 -17
  68. package/dist/coordination/CoordinationBarPlot.svelte +227 -154
  69. package/dist/element/BohrAtom.svelte +55 -12
  70. package/dist/element/ElementHeading.svelte +7 -2
  71. package/dist/element/ElementPhoto.svelte +15 -9
  72. package/dist/element/ElementStats.svelte +10 -4
  73. package/dist/element/ElementTile.svelte +137 -73
  74. package/dist/element/Nucleus.svelte +39 -11
  75. package/dist/feedback/ClickFeedback.svelte +16 -5
  76. package/dist/feedback/DragOverlay.svelte +10 -2
  77. package/dist/feedback/Spinner.svelte +4 -2
  78. package/dist/feedback/StatusMessage.svelte +8 -2
  79. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  80. package/dist/fermi-surface/FermiSurface.svelte +328 -187
  81. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  82. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  83. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  84. package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
  85. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  86. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  87. package/dist/fermi-surface/compute.js +16 -20
  88. package/dist/fermi-surface/parse.js +24 -14
  89. package/dist/fermi-surface/symmetry.js +2 -7
  90. package/dist/fermi-surface/types.d.ts +3 -5
  91. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
  92. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
  93. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
  94. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
  95. package/dist/icons.js +47 -0
  96. package/dist/index.d.ts +2 -1
  97. package/dist/index.js +2 -1
  98. package/dist/io/decompress.js +1 -1
  99. package/dist/io/export.d.ts +3 -0
  100. package/dist/io/export.js +129 -143
  101. package/dist/io/is-binary.js +2 -3
  102. package/dist/io/url-drop.js +1 -2
  103. package/dist/isosurface/Isosurface.svelte +202 -148
  104. package/dist/isosurface/IsosurfaceControls.svelte +46 -28
  105. package/dist/isosurface/parse.js +34 -29
  106. package/dist/isosurface/slice.js +5 -10
  107. package/dist/isosurface/types.d.ts +2 -1
  108. package/dist/isosurface/types.js +61 -12
  109. package/dist/labels.js +11 -8
  110. package/dist/layout/FullscreenToggle.svelte +11 -2
  111. package/dist/layout/InfoCard.svelte +38 -6
  112. package/dist/layout/InfoTag.svelte +63 -32
  113. package/dist/layout/PropertyFilter.svelte +82 -37
  114. package/dist/layout/SettingsSection.svelte +85 -55
  115. package/dist/layout/SubpageGrid.svelte +10 -2
  116. package/dist/layout/json-tree/JsonNode.svelte +183 -138
  117. package/dist/layout/json-tree/JsonTree.svelte +499 -413
  118. package/dist/layout/json-tree/JsonValue.svelte +127 -99
  119. package/dist/layout/json-tree/utils.js +4 -2
  120. package/dist/marching-cubes.js +25 -2
  121. package/dist/math.d.ts +13 -17
  122. package/dist/math.js +133 -67
  123. package/dist/overlays/ContextMenu.svelte +65 -40
  124. package/dist/overlays/DraggablePane.svelte +211 -139
  125. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  126. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  127. package/dist/periodic-table/PropertySelect.svelte +25 -7
  128. package/dist/periodic-table/TableInset.svelte +8 -3
  129. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
  130. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  131. package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
  132. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  133. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
  134. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
  135. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
  136. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
  137. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  138. package/dist/phase-diagram/build-diagram.js +9 -9
  139. package/dist/phase-diagram/colors.js +1 -3
  140. package/dist/phase-diagram/parse.js +10 -9
  141. package/dist/phase-diagram/svg-to-diagram.js +53 -49
  142. package/dist/phase-diagram/utils.d.ts +1 -0
  143. package/dist/phase-diagram/utils.js +80 -25
  144. package/dist/plot/AxisLabel.svelte +28 -3
  145. package/dist/plot/BarPlot.svelte +1182 -734
  146. package/dist/plot/BarPlot.svelte.d.ts +2 -2
  147. package/dist/plot/BarPlotControls.svelte +31 -5
  148. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  149. package/dist/plot/ColorBar.svelte +479 -329
  150. package/dist/plot/ColorScaleSelect.svelte +27 -6
  151. package/dist/plot/ElementScatter.svelte +36 -15
  152. package/dist/plot/FillArea.svelte +152 -95
  153. package/dist/plot/Histogram.svelte +934 -571
  154. package/dist/plot/Histogram.svelte.d.ts +1 -1
  155. package/dist/plot/HistogramControls.svelte +53 -9
  156. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  157. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  158. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  159. package/dist/plot/Line.svelte +63 -28
  160. package/dist/plot/PlotControls.svelte +157 -114
  161. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  162. package/dist/plot/PlotLegend.svelte +174 -91
  163. package/dist/plot/PlotTooltip.svelte +45 -6
  164. package/dist/plot/PortalSelect.svelte +175 -147
  165. package/dist/plot/ReferenceLine.svelte +76 -22
  166. package/dist/plot/ReferenceLine3D.svelte +132 -107
  167. package/dist/plot/ReferencePlane.svelte +146 -121
  168. package/dist/plot/ScatterPlot.svelte +1681 -1091
  169. package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
  170. package/dist/plot/ScatterPlot3D.svelte +256 -131
  171. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  172. package/dist/plot/ScatterPlot3DControls.svelte +113 -63
  173. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  174. package/dist/plot/ScatterPlot3DScene.svelte +608 -403
  175. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  176. package/dist/plot/ScatterPlotControls.svelte +65 -25
  177. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  178. package/dist/plot/ScatterPoint.svelte +98 -26
  179. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  180. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  181. package/dist/plot/Surface3D.svelte +159 -108
  182. package/dist/plot/ZeroLines.svelte +55 -3
  183. package/dist/plot/ZoomRect.svelte +4 -2
  184. package/dist/plot/axis-utils.js +1 -3
  185. package/dist/plot/data-cleaning.js +12 -28
  186. package/dist/plot/data-transform.js +2 -1
  187. package/dist/plot/fill-utils.js +2 -0
  188. package/dist/plot/layout.d.ts +4 -1
  189. package/dist/plot/layout.js +33 -14
  190. package/dist/plot/reference-line.d.ts +2 -2
  191. package/dist/plot/reference-line.js +7 -5
  192. package/dist/plot/scales.js +24 -36
  193. package/dist/plot/types.d.ts +11 -23
  194. package/dist/plot/types.js +6 -11
  195. package/dist/plot/utils/label-placement.d.ts +32 -15
  196. package/dist/plot/utils/label-placement.js +227 -66
  197. package/dist/plot/utils/series-visibility.js +2 -3
  198. package/dist/rdf/RdfPlot.svelte +143 -91
  199. package/dist/rdf/calc-rdf.js +4 -5
  200. package/dist/sanitize.d.ts +4 -0
  201. package/dist/sanitize.js +107 -0
  202. package/dist/settings.d.ts +18 -6
  203. package/dist/settings.js +46 -16
  204. package/dist/spectral/Bands.svelte +632 -453
  205. package/dist/spectral/BandsAndDos.svelte +90 -49
  206. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  207. package/dist/spectral/Dos.svelte +389 -258
  208. package/dist/spectral/helpers.js +55 -43
  209. package/dist/state.svelte.d.ts +1 -1
  210. package/dist/state.svelte.js +3 -2
  211. package/dist/structure/Arrow.svelte +59 -20
  212. package/dist/structure/AtomLegend.svelte +215 -134
  213. package/dist/structure/Bond.svelte +73 -47
  214. package/dist/structure/CanvasTooltip.svelte +10 -2
  215. package/dist/structure/CellSelect.svelte +72 -45
  216. package/dist/structure/Cylinder.svelte +33 -17
  217. package/dist/structure/Lattice.svelte +88 -33
  218. package/dist/structure/Structure.svelte +1063 -797
  219. package/dist/structure/Structure.svelte.d.ts +1 -1
  220. package/dist/structure/StructureControls.svelte +349 -118
  221. package/dist/structure/StructureExportPane.svelte +124 -89
  222. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  223. package/dist/structure/StructureInfoPane.svelte +304 -237
  224. package/dist/structure/StructureScene.svelte +879 -443
  225. package/dist/structure/StructureScene.svelte.d.ts +15 -7
  226. package/dist/structure/atom-properties.js +8 -8
  227. package/dist/structure/bonding.js +6 -7
  228. package/dist/structure/export.js +14 -29
  229. package/dist/structure/ferrox-wasm.js +1 -1
  230. package/dist/structure/index.d.ts +13 -3
  231. package/dist/structure/index.js +83 -23
  232. package/dist/structure/measure.d.ts +2 -2
  233. package/dist/structure/measure.js +4 -44
  234. package/dist/structure/parse.js +113 -141
  235. package/dist/structure/partial-occupancy.js +7 -10
  236. package/dist/structure/pbc.d.ts +1 -0
  237. package/dist/structure/pbc.js +16 -6
  238. package/dist/structure/supercell.d.ts +2 -2
  239. package/dist/structure/supercell.js +12 -22
  240. package/dist/structure/validation.js +1 -2
  241. package/dist/symmetry/SymmetryStats.svelte +84 -41
  242. package/dist/symmetry/WyckoffTable.svelte +26 -6
  243. package/dist/symmetry/cell-transform.js +5 -3
  244. package/dist/symmetry/index.js +8 -7
  245. package/dist/symmetry/spacegroups.js +148 -148
  246. package/dist/table/HeatmapTable.svelte +790 -554
  247. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  248. package/dist/table/ToggleMenu.svelte +125 -92
  249. package/dist/table/index.js +2 -4
  250. package/dist/theme/ThemeControl.svelte +21 -12
  251. package/dist/time.js +4 -1
  252. package/dist/tooltip/TooltipContent.svelte +33 -8
  253. package/dist/trajectory/Trajectory.svelte +758 -558
  254. package/dist/trajectory/TrajectoryError.svelte +14 -3
  255. package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
  256. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  257. package/dist/trajectory/extract.js +10 -26
  258. package/dist/trajectory/format-detect.js +5 -5
  259. package/dist/trajectory/frame-reader.d.ts +1 -1
  260. package/dist/trajectory/frame-reader.js +5 -12
  261. package/dist/trajectory/helpers.d.ts +0 -1
  262. package/dist/trajectory/helpers.js +2 -17
  263. package/dist/trajectory/index.js +14 -12
  264. package/dist/trajectory/parse/ase.js +5 -4
  265. package/dist/trajectory/parse/hdf5.js +26 -18
  266. package/dist/trajectory/parse/index.js +13 -18
  267. package/dist/trajectory/parse/lammps.js +17 -7
  268. package/dist/trajectory/parse/vasp.js +5 -2
  269. package/dist/trajectory/parse/xyz.js +8 -7
  270. package/dist/trajectory/plotting.js +13 -8
  271. package/dist/utils.d.ts +1 -0
  272. package/dist/utils.js +13 -0
  273. package/dist/xrd/XrdPlot.svelte +337 -247
  274. package/dist/xrd/broadening.js +14 -9
  275. package/dist/xrd/calc-xrd.js +12 -18
  276. package/dist/xrd/parse.d.ts +1 -1
  277. package/dist/xrd/parse.js +17 -17
  278. package/package.json +99 -103
  279. package/readme.md +1 -1
  280. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -1,323 +1,480 @@
1
- <script lang="ts">import {} from '../colors';
2
- import { get_hill_formula } from '../composition/format';
3
- import { extract_formula_elements } from '../composition/parse';
4
- import TemperatureSlider from '../convex-hull/TemperatureSlider.svelte';
5
- import { export_svg_as_png, export_svg_as_svg } from '../io/export';
6
- import { download } from '../io/fetch';
7
- import DraggablePane from '../overlays/DraggablePane.svelte';
8
- import { ColorBar, ScatterPlot } from '../plot';
9
- import { onDestroy } from 'svelte';
10
- import { SvelteMap } from 'svelte/reactivity';
11
- import { get_chempot_color_bar_config, make_chempot_color_scale } from './color';
12
- import { apply_element_padding, best_form_energy_for_formula, build_axis_ranges, compute_chempot_diagram, formula_key_from_composition, get_energy_per_atom, get_min_entries_and_el_refs, orthonormal_2d, pad_domain_points, } from './compute';
13
- import { with_hover_pointer } from './pointer';
14
- import { get_temp_filter_payload, get_valid_temperature } from './temperature';
15
- import { CHEMPOT_DEFAULTS } from './types';
16
- let { entries = [], config = {}, width = $bindable(800), height = $bindable(600),
17
- // Auto-corrected to a valid available temperature when needed.
18
- temperature = $bindable(undefined), interpolate_temperature = CHEMPOT_DEFAULTS.interpolate_temperature, max_interpolation_gap = CHEMPOT_DEFAULTS.max_interpolation_gap, hover_info = $bindable(null), render_local_tooltip = true, } = $props();
19
- let container_width = $state(0);
20
- const base_aspect_ratio = $derived(height > 0 && width > 0 ? height / width : 1);
21
- const render_width = $derived(container_width > 0 ? container_width : width);
22
- const render_height = $derived(Math.round(render_width * base_aspect_ratio));
23
- // === Control overrides ===
24
- let formal_chempots_override = $state(null);
25
- let label_stable_override = $state(null);
26
- let element_padding_override = $state(null);
27
- let default_min_limit_override = $state(null);
28
- const formal_chempots = $derived(formal_chempots_override ??
29
- (config.formal_chempots ?? CHEMPOT_DEFAULTS.formal_chempots));
30
- const label_stable = $derived(label_stable_override ?? (config.label_stable ?? CHEMPOT_DEFAULTS.label_stable));
31
- const element_padding = $derived(element_padding_override ??
32
- (config.element_padding ?? CHEMPOT_DEFAULTS.element_padding));
33
- const default_min_limit = $derived(default_min_limit_override ??
34
- (config.default_min_limit ?? CHEMPOT_DEFAULTS.default_min_limit));
35
- let color_mode_override = $state(null);
36
- let color_scale_override = $state(null);
37
- let reverse_color_scale_override = $state(null);
38
- const color_mode = $derived(color_mode_override ?? (config.color_mode ?? CHEMPOT_DEFAULTS.color_mode));
39
- const color_scale = $derived(color_scale_override ?? (config.color_scale ?? CHEMPOT_DEFAULTS.color_scale));
40
- const reverse_color_scale = $derived(reverse_color_scale_override ??
41
- (config.reverse_color_scale ?? CHEMPOT_DEFAULTS.reverse_color_scale));
42
- const arity_colors = [`#3498db`, `#2ecc71`, `#e67e22`, `#9b59b6`];
43
- const show_tooltip = $derived(config.show_tooltip ?? CHEMPOT_DEFAULTS.show_tooltip);
44
- const effective_config = $derived({
1
+ <script lang="ts">
2
+ import { type D3InterpolateName } from '../colors'
3
+ import { get_electro_neg_formula } from '../composition/format'
4
+ import { extract_formula_elements } from '../composition/parse'
5
+ import TemperatureSlider from '../convex-hull/TemperatureSlider.svelte'
6
+ import type { PhaseData } from '../convex-hull/types'
7
+ import Spinner from '../feedback/Spinner.svelte'
8
+ import { export_svg_as_png, export_svg_as_svg } from '../io/export'
9
+ import { download } from '../io/fetch'
10
+ import DraggablePane from '../overlays/DraggablePane.svelte'
11
+ import { ColorBar, ScatterPlot } from '../plot'
12
+ import { constrain_tooltip_position } from '../plot/layout'
13
+ import type { DataSeries, UserContentProps } from '../plot/types'
14
+ import { sanitize_html } from '../sanitize'
15
+ import { onDestroy } from 'svelte'
16
+ import { SvelteMap } from 'svelte/reactivity'
17
+ import { compute_chempot_async } from './async-compute.svelte'
18
+ import { get_chempot_color_bar_config, make_chempot_color_scale } from './color'
19
+ import {
20
+ apply_element_padding,
21
+ best_form_energy_for_formula,
22
+ build_axis_ranges,
23
+ formula_key_from_composition,
24
+ get_energy_per_atom,
25
+ get_min_entries_and_el_refs,
26
+ orthonormal_2d,
27
+ pad_domain_points,
28
+ } from './compute'
29
+ import { with_hover_pointer } from './pointer'
30
+ import { get_temp_filter_payload, get_valid_temperature } from './temperature'
31
+ import type { ChemPotDiagramConfig, ChemPotDiagramData, ChemPotHoverInfo } from './types'
32
+ import { CHEMPOT_DEFAULTS } from './types'
33
+
34
+ let {
35
+ entries = [],
36
+ config = {},
37
+ width = $bindable(800),
38
+ height = $bindable(600),
39
+ // Auto-corrected to a valid available temperature when needed.
40
+ temperature = $bindable<number | undefined>(undefined),
41
+ interpolate_temperature = CHEMPOT_DEFAULTS.interpolate_temperature,
42
+ max_interpolation_gap = CHEMPOT_DEFAULTS.max_interpolation_gap,
43
+ hover_info = $bindable<ChemPotHoverInfo | null>(null),
44
+ render_local_tooltip = true,
45
+ }: {
46
+ entries: PhaseData[]
47
+ config?: ChemPotDiagramConfig
48
+ width?: number
49
+ height?: number
50
+ temperature?: number
51
+ interpolate_temperature?: boolean
52
+ max_interpolation_gap?: number
53
+ hover_info?: ChemPotHoverInfo | null
54
+ render_local_tooltip?: boolean
55
+ } = $props()
56
+ let container_width = $state(0)
57
+ const base_aspect_ratio = $derived(height > 0 && width > 0 ? height / width : 1)
58
+ const render_width = $derived(container_width > 0 ? container_width : width)
59
+ const render_height = $derived(Math.round(render_width * base_aspect_ratio))
60
+
61
+ // === Control overrides ===
62
+ let formal_chempots_override = $state<boolean | null>(null)
63
+ let label_stable_override = $state<boolean | null>(null)
64
+ let element_padding_override = $state<number | null>(null)
65
+ let default_min_limit_override = $state<number | null>(null)
66
+ const formal_chempots = $derived(
67
+ formal_chempots_override ??
68
+ (config.formal_chempots ?? CHEMPOT_DEFAULTS.formal_chempots),
69
+ )
70
+ const label_stable = $derived(
71
+ label_stable_override ?? (config.label_stable ?? CHEMPOT_DEFAULTS.label_stable),
72
+ )
73
+ const element_padding = $derived(
74
+ element_padding_override ??
75
+ (config.element_padding ?? CHEMPOT_DEFAULTS.element_padding),
76
+ )
77
+ const default_min_limit = $derived(
78
+ default_min_limit_override ??
79
+ (config.default_min_limit ?? CHEMPOT_DEFAULTS.default_min_limit),
80
+ )
81
+ let color_mode_override = $state<
82
+ NonNullable<ChemPotDiagramConfig[`color_mode`]> | null
83
+ >(
84
+ null,
85
+ )
86
+ let color_scale_override = $state<D3InterpolateName | null>(null)
87
+ let reverse_color_scale_override = $state<boolean | null>(null)
88
+ const color_mode = $derived(
89
+ color_mode_override ?? (config.color_mode ?? CHEMPOT_DEFAULTS.color_mode),
90
+ )
91
+ const color_scale = $derived(
92
+ color_scale_override ?? (config.color_scale ?? CHEMPOT_DEFAULTS.color_scale),
93
+ )
94
+ const reverse_color_scale = $derived(
95
+ reverse_color_scale_override ??
96
+ (config.reverse_color_scale ?? CHEMPOT_DEFAULTS.reverse_color_scale),
97
+ )
98
+ const arity_colors = [`#3498db`, `#2ecc71`, `#e67e22`, `#9b59b6`] as const
99
+ const show_tooltip = $derived(config.show_tooltip ?? CHEMPOT_DEFAULTS.show_tooltip)
100
+ const effective_config = $derived({
45
101
  ...config,
46
102
  formal_chempots,
47
103
  label_stable,
48
104
  element_padding,
49
105
  default_min_limit,
50
- });
51
- const { has_temp_data, available_temperatures, temp_filtered_entries } = $derived(get_temp_filter_payload(entries, temperature, config, {
52
- interpolate_temperature,
53
- max_interpolation_gap,
54
- }));
55
- $effect(() => {
106
+ })
107
+ const { has_temp_data, available_temperatures, temp_filtered_entries } = $derived(
108
+ get_temp_filter_payload(entries, temperature, config, {
109
+ interpolate_temperature,
110
+ max_interpolation_gap,
111
+ }),
112
+ )
113
+
114
+ $effect(() => {
56
115
  // Keep bound temperature aligned with available data points.
57
- const next_temperature = get_valid_temperature(temperature, has_temp_data, available_temperatures);
58
- if (next_temperature !== temperature)
59
- temperature = next_temperature;
60
- });
61
- const show_temperature_slider = $derived(has_temp_data && available_temperatures.length > 0);
62
- function reset_controls() {
63
- formal_chempots_override = null;
64
- label_stable_override = null;
65
- element_padding_override = null;
66
- default_min_limit_override = null;
67
- color_mode_override = null;
68
- color_scale_override = null;
69
- reverse_color_scale_override = null;
70
- }
71
- // === Diagram computation ===
72
- const diagram_data = $derived.by(() => {
73
- if (temp_filtered_entries.length < 2)
74
- return null;
75
- try {
76
- return compute_chempot_diagram(temp_filtered_entries, effective_config);
77
- }
78
- catch (err) {
79
- console.error(`ChemPotDiagram2D:`, err);
80
- return null;
116
+ const next_temperature = get_valid_temperature(
117
+ temperature,
118
+ has_temp_data,
119
+ available_temperatures,
120
+ )
121
+ if (next_temperature !== temperature) temperature = next_temperature
122
+ })
123
+
124
+ const show_temperature_slider = $derived(
125
+ has_temp_data && available_temperatures.length > 0,
126
+ )
127
+
128
+ function reset_controls(): void {
129
+ formal_chempots_override = null
130
+ label_stable_override = null
131
+ element_padding_override = null
132
+ default_min_limit_override = null
133
+ color_mode_override = null
134
+ color_scale_override = null
135
+ reverse_color_scale_override = null
136
+ }
137
+
138
+ // === Diagram computation (off main thread via Web Worker) ===
139
+ let diagram_data = $state<ChemPotDiagramData | null>(null)
140
+ let diagram_computing = $state(false)
141
+ $effect(() => {
142
+ if (temp_filtered_entries.length < 2) {
143
+ diagram_data = null
144
+ diagram_computing = false
145
+ return
81
146
  }
82
- });
83
- const plot_elements = $derived((diagram_data?.elements ?? config.elements ?? []).slice(0, 2));
84
- const draw_domains = $derived.by(() => {
85
- if (!diagram_data || plot_elements.length < 2)
86
- return {};
87
- const indices = [0, 1];
147
+ let cancelled = false
148
+ diagram_computing = true
149
+ compute_chempot_async(temp_filtered_entries, effective_config)
150
+ .then((data) => {
151
+ if (cancelled) return
152
+ diagram_data = data
153
+ diagram_computing = false
154
+ })
155
+ .catch((err) => {
156
+ if (cancelled) return
157
+ console.error(`ChemPotDiagram2D:`, err)
158
+ diagram_data = null
159
+ diagram_computing = false
160
+ })
161
+ return () => { cancelled = true }
162
+ })
163
+
164
+ const plot_elements = $derived(
165
+ (diagram_data?.elements ?? config.elements ?? []).slice(0, 2),
166
+ )
167
+
168
+ const draw_domains = $derived.by(() => {
169
+ if (!diagram_data || plot_elements.length < 2) return {}
170
+ const indices = [0, 1]
88
171
  if (element_padding <= 0) {
89
- return Object.fromEntries(Object.entries(diagram_data.domains).filter(([, pts]) => pts.length > 0));
172
+ return Object.fromEntries(
173
+ Object.entries(diagram_data.domains).filter(([, pts]) => pts.length > 0),
174
+ )
90
175
  }
91
- const new_lims = apply_element_padding(diagram_data.domains, indices, element_padding, default_min_limit);
92
- const result = {};
176
+ const new_lims = apply_element_padding(
177
+ diagram_data.domains,
178
+ indices,
179
+ element_padding,
180
+ default_min_limit,
181
+ )
182
+ const result: Record<string, number[][]> = {}
93
183
  for (const [formula, pts] of Object.entries(diagram_data.domains)) {
94
- const padded = pad_domain_points(pts, indices, new_lims, default_min_limit, element_padding);
95
- if (padded.length > 0)
96
- result[formula] = padded;
184
+ const padded = pad_domain_points(
185
+ pts,
186
+ indices,
187
+ new_lims,
188
+ default_min_limit,
189
+ element_padding,
190
+ )
191
+ if (padded.length > 0) result[formula] = padded
97
192
  }
98
- return result;
99
- });
100
- const domain_entries = $derived(Object.entries(draw_domains));
101
- const domain_formulas = $derived(Object.keys(draw_domains));
102
- const raw_el_refs = $derived(get_min_entries_and_el_refs(temp_filtered_entries).el_refs);
103
- const entry_energy_stats_by_formula = $derived.by(() => {
104
- const stats = new SvelteMap();
105
- for (const entry of temp_filtered_entries) {
106
- const formula_key = formula_key_from_composition(entry.composition);
107
- const epa = get_energy_per_atom(entry);
108
- const prev_stats = stats.get(formula_key);
193
+ return result
194
+ })
195
+ const domain_entries = $derived(Object.entries(draw_domains))
196
+ const domain_formulas = $derived(Object.keys(draw_domains))
197
+
198
+ interface FormulaEnergyStats {
199
+ matching_entry_count: number
200
+ min_energy_per_atom: number | null
201
+ }
202
+ type NumericColorMode = Exclude<
203
+ NonNullable<ChemPotDiagramConfig[`color_mode`]>,
204
+ `none` | `arity`
205
+ >
206
+
207
+ const raw_el_refs = $derived(
208
+ get_min_entries_and_el_refs(temp_filtered_entries).el_refs,
209
+ )
210
+ const entry_energy_stats_by_formula = $derived.by(
211
+ (): SvelteMap<string, FormulaEnergyStats> => {
212
+ const stats = new SvelteMap<string, FormulaEnergyStats>()
213
+ for (const entry of temp_filtered_entries) {
214
+ const formula_key = formula_key_from_composition(entry.composition)
215
+ const epa = get_energy_per_atom(entry)
216
+ const prev_stats = stats.get(formula_key)
109
217
  if (!prev_stats) {
110
- stats.set(formula_key, {
111
- matching_entry_count: 1,
112
- min_energy_per_atom: epa,
113
- });
114
- continue;
218
+ stats.set(formula_key, {
219
+ matching_entry_count: 1,
220
+ min_energy_per_atom: epa,
221
+ })
222
+ continue
115
223
  }
116
224
  stats.set(formula_key, {
117
- matching_entry_count: prev_stats.matching_entry_count + 1,
118
- min_energy_per_atom: Math.min(prev_stats.min_energy_per_atom ?? epa, epa),
119
- });
120
- }
121
- return stats;
122
- });
123
- const color_mode_labels = {
225
+ matching_entry_count: prev_stats.matching_entry_count + 1,
226
+ min_energy_per_atom: Math.min(prev_stats.min_energy_per_atom ?? epa, epa),
227
+ })
228
+ }
229
+ return stats
230
+ },
231
+ )
232
+ const color_mode_labels: Record<NumericColorMode, string> = {
124
233
  energy: `Energy per atom (eV)`,
125
234
  formation_energy: `Formation energy (eV/atom)`,
126
235
  entries: `Entry count`,
127
- };
128
- function get_numeric_color_value(formula, active_color_mode) {
236
+ }
237
+ function get_numeric_color_value(
238
+ formula: string,
239
+ active_color_mode: NumericColorMode,
240
+ ): number | null {
129
241
  if (active_color_mode === `energy`) {
130
- return entry_energy_stats_by_formula.get(formula)?.min_energy_per_atom ?? null;
242
+ return entry_energy_stats_by_formula.get(formula)?.min_energy_per_atom ?? null
131
243
  }
132
244
  if (active_color_mode === `formation_energy`) {
133
- return best_form_energy_for_formula(temp_filtered_entries, formula, raw_el_refs) ?? null;
134
- }
135
- return entry_energy_stats_by_formula.get(formula)?.matching_entry_count ?? 0;
136
- }
137
- const domain_color_values = $derived.by(() => {
138
- if (color_mode === `none` || color_mode === `arity`)
139
- return null;
140
- const active_color_mode = color_mode;
141
- const value_by_formula = new SvelteMap();
142
- const values = [];
143
- for (const formula of domain_formulas) {
144
- const value = get_numeric_color_value(formula, active_color_mode);
145
- if (value == null || !Number.isFinite(value))
146
- continue;
147
- values.push(value);
148
- value_by_formula.set(formula, value);
245
+ return best_form_energy_for_formula(
246
+ temp_filtered_entries,
247
+ formula,
248
+ raw_el_refs,
249
+ ) ?? null
149
250
  }
150
- return { value_by_formula, values };
151
- });
152
- const domain_colors = $derived.by(() => {
153
- const colors = new SvelteMap();
154
- if (color_mode === `none`)
155
- return colors;
251
+ return entry_energy_stats_by_formula.get(formula)?.matching_entry_count ?? 0
252
+ }
253
+ const domain_color_values = $derived.by(
254
+ (): { value_by_formula: SvelteMap<string, number>; values: number[] } | null => {
255
+ if (color_mode === `none` || color_mode === `arity`) return null
256
+ const active_color_mode = color_mode as NumericColorMode
257
+ const value_by_formula = new SvelteMap<string, number>()
258
+ const values: number[] = []
259
+ for (const formula of domain_formulas) {
260
+ const value = get_numeric_color_value(formula, active_color_mode)
261
+ if (value == null || !Number.isFinite(value)) continue
262
+ values.push(value)
263
+ value_by_formula.set(formula, value)
264
+ }
265
+ return { value_by_formula, values }
266
+ },
267
+ )
268
+ const domain_colors = $derived.by((): SvelteMap<string, string> => {
269
+ const colors = new SvelteMap<string, string>()
270
+ if (color_mode === `none`) return colors
156
271
  if (color_mode === `arity`) {
157
- for (const formula of domain_formulas) {
158
- const n_elements = extract_formula_elements(formula).length;
159
- const color_idx = Math.min(n_elements, arity_colors.length) - 1;
160
- colors.set(formula, arity_colors[Math.max(0, color_idx)]);
161
- }
162
- return colors;
272
+ for (const formula of domain_formulas) {
273
+ const n_elements = extract_formula_elements(formula).length
274
+ const color_idx = Math.min(n_elements, arity_colors.length) - 1
275
+ colors.set(formula, arity_colors[Math.max(0, color_idx)])
276
+ }
277
+ return colors
163
278
  }
164
- const values_payload = domain_color_values;
165
- const scale = make_chempot_color_scale(values_payload?.values ?? [], color_scale, reverse_color_scale);
279
+ const values_payload = domain_color_values
280
+ const scale = make_chempot_color_scale(
281
+ values_payload?.values ?? [],
282
+ color_scale,
283
+ reverse_color_scale,
284
+ )
166
285
  for (const formula of domain_formulas) {
167
- const color_val = values_payload?.value_by_formula.get(formula);
168
- colors.set(formula, color_val != null && scale ? scale(color_val) : `#999`);
286
+ const color_val = values_payload?.value_by_formula.get(formula)
287
+ colors.set(formula, color_val != null && scale ? scale(color_val) : `#999`)
169
288
  }
170
- return colors;
171
- });
172
- const color_range = $derived.by(() => {
173
- const values = domain_color_values?.values ?? [];
174
- if (values.length === 0)
175
- return null;
176
- let min_val = values[0], max_val = values[0];
177
- for (let idx = 1; idx < values.length; idx++) {
178
- if (values[idx] < min_val)
179
- min_val = values[idx];
180
- if (values[idx] > max_val)
181
- max_val = values[idx];
182
- }
183
- return {
289
+ return colors
290
+ })
291
+ const color_range = $derived.by(
292
+ (): { min: number; max: number; label: string } | null => {
293
+ const values = domain_color_values?.values ?? []
294
+ if (values.length === 0) return null
295
+ let min_val = values[0], max_val = values[0]
296
+ for (let idx = 1; idx < values.length; idx++) {
297
+ if (values[idx] < min_val) min_val = values[idx]
298
+ if (values[idx] > max_val) max_val = values[idx]
299
+ }
300
+ return {
184
301
  min: min_val,
185
302
  max: Math.max(max_val, min_val + 1e-6),
186
303
  label: color_mode === `none` || color_mode === `arity`
187
- ? ``
188
- : color_mode_labels[color_mode],
189
- };
190
- });
191
- // === Convert domains to ScatterPlot DataSeries ===
192
- const series = $derived(domain_entries.map(([formula, pts]) => ({
193
- id: formula,
194
- label: formula,
195
- x: pts.map((pt) => pt[0]),
196
- y: pts.map((pt) => pt[1]),
197
- markers: `line+points`,
198
- line_style: { stroke: domain_colors.get(formula) ?? `black`, stroke_width: 3 },
199
- point_style: { fill: domain_colors.get(formula) ?? `black`, radius: 3 },
200
- })));
201
- // Axis label text
202
- function axis_label(element) {
203
- if (formal_chempots)
204
- return `\u0394\u03BC(${element}) (eV)`;
205
- return `\u03BC(${element}) (eV)`;
206
- }
207
- let x_axis = $state({ label: `` });
208
- let y_axis = $state({ label: `` });
209
- $effect(() => {
210
- const next_x_label = axis_label(plot_elements[0] ?? ``);
211
- const next_y_label = axis_label(plot_elements[1] ?? ``);
212
- if (x_axis.label !== next_x_label)
213
- x_axis = { ...x_axis, label: next_x_label };
214
- if (y_axis.label !== next_y_label)
215
- y_axis = { ...y_axis, label: next_y_label };
216
- });
217
- // === Domain label annotations (in data coordinates) ===
218
- const annotations = $derived.by(() => {
219
- if (!label_stable)
220
- return [];
221
- const result = [];
304
+ ? ``
305
+ : color_mode_labels[color_mode],
306
+ }
307
+ },
308
+ )
309
+
310
+ // === Convert domains to ScatterPlot DataSeries ===
311
+ const series = $derived<DataSeries[]>(
312
+ domain_entries.map(([formula, pts]) => ({
313
+ id: formula,
314
+ label: formula,
315
+ x: pts.map((pt) => pt[0]),
316
+ y: pts.map((pt) => pt[1]),
317
+ markers: `line+points` as const,
318
+ line_style: { stroke: domain_colors.get(formula) ?? `black`, stroke_width: 3 },
319
+ point_style: { fill: domain_colors.get(formula) ?? `black`, radius: 3 },
320
+ })),
321
+ )
322
+
323
+ // Axis label text
324
+ function axis_label(element: string): string {
325
+ const prefix = formal_chempots ? `\u0394` : ``
326
+ return `${prefix}\u03BC<sub>${element}</sub> <span style="font-weight:300;opacity:0.7">(eV)</span>`
327
+ }
328
+
329
+ let x_axis = $state({ label: ``, label_shift: { y: -45 } })
330
+ let y_axis = $state({ label: `` })
331
+
332
+ $effect(() => {
333
+ const next_x_label = axis_label(plot_elements[0] ?? ``)
334
+ const next_y_label = axis_label(plot_elements[1] ?? ``)
335
+ if (x_axis.label !== next_x_label) x_axis = { ...x_axis, label: next_x_label }
336
+ if (y_axis.label !== next_y_label) y_axis = { ...y_axis, label: next_y_label }
337
+ })
338
+
339
+ // === Domain label annotations (in data coordinates) ===
340
+ const annotations = $derived.by(() => {
341
+ if (!label_stable) return []
342
+ const result: { formula: string; data_x: number; data_y: number }[] = []
222
343
  for (const [formula, pts] of Object.entries(draw_domains)) {
223
- if (pts.length === 0)
224
- continue;
225
- const center_x = pts.reduce((s, p) => s + p[0], 0) / pts.length;
226
- const center_y = pts.reduce((s, p) => s + p[1], 0) / pts.length;
227
- let offset_x = 0;
228
- let offset_y = 0;
229
- if (pts.length >= 2) {
230
- const [nx, ny] = orthonormal_2d(pts);
231
- offset_x = nx * 0.25;
232
- offset_y = ny * 0.25;
233
- }
234
- result.push({
235
- formula,
236
- data_x: center_x + offset_x,
237
- data_y: center_y + offset_y,
238
- });
344
+ if (pts.length === 0) continue
345
+ const center_x = pts.reduce((s, p) => s + p[0], 0) / pts.length
346
+ const center_y = pts.reduce((s, p) => s + p[1], 0) / pts.length
347
+ let offset_x = 0
348
+ let offset_y = 0
349
+ if (pts.length >= 2) {
350
+ const [nx, ny] = orthonormal_2d(pts)
351
+ offset_x = nx * 0.25
352
+ offset_y = ny * 0.25
353
+ }
354
+ result.push({
355
+ formula,
356
+ data_x: center_x + offset_x,
357
+ data_y: center_y + offset_y,
358
+ })
239
359
  }
240
- return result;
241
- });
242
- // === Hover info for external consumers ===
243
- let locked_hover_formula = $state(null);
244
- function set_hover_info(formula, pts, event) {
245
- const bounds = scatter_wrapper?.getBoundingClientRect();
246
- hover_info = with_hover_pointer({
360
+ return result
361
+ })
362
+
363
+ // === Hover info for external consumers ===
364
+ let locked_hover_formula = $state<string | null>(null)
365
+
366
+ function set_hover_info(
367
+ formula: string,
368
+ pts: number[][],
369
+ event: MouseEvent,
370
+ ): void {
371
+ const bounds = scatter_wrapper?.getBoundingClientRect()
372
+ hover_info = with_hover_pointer<ChemPotHoverInfo>(
373
+ {
247
374
  formula,
248
375
  view: `2d`,
249
376
  n_points: pts.length,
250
377
  axis_ranges: build_axis_ranges(pts, plot_elements),
251
- }, event, bounds);
252
- }
253
- function clear_hover_lock() {
254
- locked_hover_formula = null;
255
- hover_info = null;
256
- }
257
- function handle_hover(data) {
378
+ },
379
+ event,
380
+ bounds,
381
+ )
382
+ }
383
+
384
+ function clear_hover_lock(): void {
385
+ locked_hover_formula = null
386
+ hover_info = null
387
+ }
388
+
389
+ function handle_hover(
390
+ data: { point: { series_idx: number }; event: MouseEvent } | null,
391
+ ): void {
258
392
  if (!data) {
259
- if (!locked_hover_formula)
260
- hover_info = null;
261
- return;
393
+ if (!locked_hover_formula) hover_info = null
394
+ return
262
395
  }
263
- const entry = domain_entries[data.point.series_idx];
264
- if (!entry)
265
- return;
266
- const [formula, pts] = entry;
267
- if (locked_hover_formula && locked_hover_formula !== formula)
268
- return;
269
- set_hover_info(formula, pts, data.event);
270
- }
271
- function handle_click(data) {
272
- const entry = domain_entries[data.point.series_idx];
273
- if (!entry)
274
- return;
275
- const [formula, pts] = entry;
396
+ const entry = domain_entries[data.point.series_idx]
397
+ if (!entry) return
398
+ const [formula, pts] = entry
399
+ if (locked_hover_formula && locked_hover_formula !== formula) return
400
+ set_hover_info(formula, pts, data.event)
401
+ }
402
+
403
+ function handle_click(
404
+ data: { point: { series_idx: number }; event: MouseEvent },
405
+ ): void {
406
+ const entry = domain_entries[data.point.series_idx]
407
+ if (!entry) return
408
+ const [formula, pts] = entry
276
409
  if (locked_hover_formula === formula) {
277
- clear_hover_lock();
278
- return;
410
+ clear_hover_lock()
411
+ return
279
412
  }
280
- locked_hover_formula = formula;
281
- set_hover_info(formula, pts, data.event);
282
- }
283
- // === Export ===
284
- let scatter_wrapper = $state();
285
- let export_pane_open = $state(false);
286
- let copy_status = $state(false);
287
- let copy_timeout_id = null;
288
- function get_svg_element() {
289
- return scatter_wrapper?.querySelector(`svg`) ?? null;
290
- }
291
- function get_json_string() {
292
- return JSON.stringify({
413
+ locked_hover_formula = formula
414
+ set_hover_info(formula, pts, data.event)
415
+ }
416
+
417
+ let tooltip_el = $state<HTMLElement>()
418
+ const tooltip_pos = $derived.by(() => {
419
+ const pointer = hover_info?.pointer
420
+ if (!pointer) return { x: 4, y: 4 }
421
+ return constrain_tooltip_position(
422
+ pointer.x, pointer.y,
423
+ tooltip_el?.offsetWidth ?? 150,
424
+ tooltip_el?.offsetHeight ?? 40,
425
+ render_width, render_height,
426
+ { offset: 0 },
427
+ )
428
+ })
429
+
430
+ // === Export ===
431
+ let scatter_wrapper = $state<HTMLDivElement>()
432
+ let export_pane_open = $state(false)
433
+ let copy_status = $state(false)
434
+ let copy_timeout_id: ReturnType<typeof setTimeout> | null = null
435
+
436
+ function get_svg_element(): SVGSVGElement | null {
437
+ return scatter_wrapper?.querySelector<SVGSVGElement>(`svg`) ?? null
438
+ }
439
+
440
+ function get_json_string(): string {
441
+ return JSON.stringify(
442
+ {
293
443
  elements: diagram_data?.elements ?? [],
294
444
  domains: draw_domains,
295
445
  lims: diagram_data?.lims ?? [],
296
- }, null, 2);
297
- }
298
- function export_json_file() {
299
- download(get_json_string(), `chempot-${plot_elements.join(`-`)}.json`, `application/json`);
300
- }
301
- async function copy_json() {
446
+ },
447
+ null,
448
+ 2,
449
+ )
450
+ }
451
+
452
+ function export_json_file(): void {
453
+ download(
454
+ get_json_string(),
455
+ `chempot-${plot_elements.join(`-`)}.json`,
456
+ `application/json`,
457
+ )
458
+ }
459
+
460
+ async function copy_json(): Promise<void> {
302
461
  try {
303
- await navigator.clipboard.writeText(get_json_string());
304
- copy_status = true;
462
+ await navigator.clipboard.writeText(get_json_string())
463
+ copy_status = true
464
+ } catch (err) {
465
+ copy_status = false
466
+ console.error(`Failed to copy JSON to clipboard:`, err)
305
467
  }
306
- catch (err) {
307
- copy_status = false;
308
- console.error(`Failed to copy JSON to clipboard:`, err);
309
- }
310
- if (copy_timeout_id !== null)
311
- clearTimeout(copy_timeout_id);
468
+ if (copy_timeout_id !== null) clearTimeout(copy_timeout_id)
312
469
  copy_timeout_id = setTimeout(() => {
313
- copy_status = false;
314
- copy_timeout_id = null;
315
- }, 1000);
316
- }
317
- onDestroy(() => {
318
- if (copy_timeout_id !== null)
319
- clearTimeout(copy_timeout_id);
320
- });
470
+ copy_status = false
471
+ copy_timeout_id = null
472
+ }, 1000)
473
+ }
474
+
475
+ onDestroy(() => {
476
+ if (copy_timeout_id !== null) clearTimeout(copy_timeout_id)
477
+ })
321
478
  </script>
322
479
 
323
480
  {#snippet domain_labels(props: UserContentProps)}
@@ -328,7 +485,7 @@ onDestroy(() => {
328
485
  text-anchor="middle"
329
486
  class="domain-label"
330
487
  >
331
- {get_hill_formula(formula, true, ``)}
488
+ {get_electro_neg_formula(formula, true, ``, `.3~s`)}
332
489
  </text>
333
490
  {/each}
334
491
  {/snippet}
@@ -496,7 +653,11 @@ onDestroy(() => {
496
653
  <button type="button" onclick={reset_controls}>Reset defaults</button>
497
654
  {/snippet}
498
655
 
499
- {#if !diagram_data}
656
+ {#if diagram_computing}
657
+ <div class="computing-state">
658
+ <Spinner text="Computing chemical potential domains..." style="--spinner-size: 1.2em" />
659
+ </div>
660
+ {:else if !diagram_data}
500
661
  <div class="error-state" role="alert" aria-live="polite">
501
662
  <p>Cannot compute chemical potential diagram.</p>
502
663
  <p>Need at least 2 elements with elemental reference entries.</p>
@@ -564,11 +725,14 @@ onDestroy(() => {
564
725
  {/if}
565
726
  {#if render_local_tooltip && show_tooltip && hover_info?.view === `2d`}
566
727
  <aside
728
+ bind:this={tooltip_el}
567
729
  class="tooltip"
568
- style:left="{hover_info.pointer?.x ?? 4}px"
569
- style:top="{hover_info.pointer?.y ?? 4}px"
730
+ style:left="{tooltip_pos.x}px"
731
+ style:top="{tooltip_pos.y}px"
570
732
  >
571
- <strong>{@html get_hill_formula(hover_info.formula, false, ``)}</strong>
733
+ <strong>
734
+ {@html sanitize_html(get_electro_neg_formula(hover_info.formula, false, ``, `.3~s`))}
735
+ </strong>
572
736
  {#if locked_hover_formula === hover_info.formula}
573
737
  <div>Pinned · Press Esc to unlock</div>
574
738
  {/if}
@@ -625,6 +789,12 @@ onDestroy(() => {
625
789
  gap: 4pt;
626
790
  margin-left: 4pt;
627
791
  }
792
+ .computing-state {
793
+ display: flex;
794
+ align-items: center;
795
+ justify-content: center;
796
+ min-height: 200px;
797
+ }
628
798
  .error-state {
629
799
  display: flex;
630
800
  flex-direction: column;