matterviz 0.3.1 → 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 (358) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +154 -96
  3. package/dist/Icon.svelte +20 -14
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -178
  7. package/dist/brillouin/BrillouinZone.svelte +299 -198
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +74 -55
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +277 -165
  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 +327 -0
  18. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  19. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +847 -0
  20. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  21. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3194 -0
  22. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  23. package/dist/chempot-diagram/ChemPotScene3D.svelte +11 -0
  24. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  25. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  26. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  27. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  28. package/dist/chempot-diagram/chempot-worker.js +11 -0
  29. package/dist/chempot-diagram/color.d.ts +10 -0
  30. package/dist/chempot-diagram/color.js +32 -0
  31. package/dist/chempot-diagram/compute.d.ts +48 -0
  32. package/dist/chempot-diagram/compute.js +812 -0
  33. package/dist/chempot-diagram/index.d.ts +6 -0
  34. package/dist/chempot-diagram/index.js +6 -0
  35. package/dist/chempot-diagram/pointer.d.ts +16 -0
  36. package/dist/chempot-diagram/pointer.js +40 -0
  37. package/dist/chempot-diagram/temperature.d.ts +15 -0
  38. package/dist/chempot-diagram/temperature.js +36 -0
  39. package/dist/chempot-diagram/types.d.ts +86 -0
  40. package/dist/chempot-diagram/types.js +28 -0
  41. package/dist/colors/index.d.ts +3 -1
  42. package/dist/colors/index.js +9 -3
  43. package/dist/composition/BarChart.svelte +141 -77
  44. package/dist/composition/BubbleChart.svelte +107 -52
  45. package/dist/composition/Composition.svelte +100 -79
  46. package/dist/composition/Formula.svelte +108 -62
  47. package/dist/composition/FormulaFilter.svelte +973 -353
  48. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  49. package/dist/composition/PieChart.svelte +199 -99
  50. package/dist/composition/PieChart.svelte.d.ts +1 -1
  51. package/dist/composition/format.d.ts +5 -0
  52. package/dist/composition/format.js +20 -3
  53. package/dist/composition/parse.js +14 -9
  54. package/dist/convex-hull/ConvexHull.svelte +93 -38
  55. package/dist/convex-hull/ConvexHull2D.svelte +551 -393
  56. package/dist/convex-hull/ConvexHull3D.svelte +1303 -825
  57. package/dist/convex-hull/ConvexHull4D.svelte +1012 -686
  58. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  59. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  60. package/dist/convex-hull/ConvexHullStats.svelte +821 -249
  61. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  62. package/dist/convex-hull/ConvexHullTooltip.svelte +41 -16
  63. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  64. package/dist/convex-hull/StructurePopup.svelte +25 -4
  65. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  66. package/dist/convex-hull/barycentric-coords.js +13 -7
  67. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  68. package/dist/convex-hull/demo-temperature.js +40 -0
  69. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  70. package/dist/convex-hull/helpers.d.ts +10 -1
  71. package/dist/convex-hull/helpers.js +79 -38
  72. package/dist/convex-hull/index.d.ts +1 -0
  73. package/dist/convex-hull/index.js +1 -0
  74. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  75. package/dist/convex-hull/thermodynamics.js +163 -69
  76. package/dist/convex-hull/types.d.ts +12 -12
  77. package/dist/convex-hull/types.js +0 -12
  78. package/dist/coordination/CoordinationBarPlot.svelte +232 -176
  79. package/dist/element/BohrAtom.svelte +56 -13
  80. package/dist/element/ElementHeading.svelte +7 -2
  81. package/dist/element/ElementPhoto.svelte +15 -9
  82. package/dist/element/ElementStats.svelte +10 -4
  83. package/dist/element/ElementTile.svelte +137 -73
  84. package/dist/element/Nucleus.svelte +39 -11
  85. package/dist/element/data.js +2 -14
  86. package/dist/element/data.json.gz +0 -0
  87. package/dist/element/types.d.ts +1 -0
  88. package/dist/feedback/ClickFeedback.svelte +16 -5
  89. package/dist/feedback/DragOverlay.svelte +10 -2
  90. package/dist/feedback/Spinner.svelte +4 -2
  91. package/dist/feedback/StatusMessage.svelte +8 -2
  92. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  93. package/dist/fermi-surface/FermiSurface.svelte +336 -239
  94. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  95. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  96. package/dist/fermi-surface/FermiSurfaceScene.svelte +536 -343
  97. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  98. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  99. package/dist/fermi-surface/compute.js +16 -20
  100. package/dist/fermi-surface/parse.js +37 -33
  101. package/dist/fermi-surface/symmetry.js +2 -7
  102. package/dist/fermi-surface/types.d.ts +3 -5
  103. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1527 -0
  104. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  105. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  106. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  107. package/dist/heatmap-matrix/index.d.ts +53 -0
  108. package/dist/heatmap-matrix/index.js +100 -0
  109. package/dist/heatmap-matrix/shared.d.ts +2 -0
  110. package/dist/heatmap-matrix/shared.js +4 -0
  111. package/dist/icons.d.ts +111 -0
  112. package/dist/icons.js +158 -0
  113. package/dist/index.d.ts +5 -2
  114. package/dist/index.js +5 -2
  115. package/dist/io/decompress.js +1 -1
  116. package/dist/io/export.d.ts +3 -0
  117. package/dist/io/export.js +138 -140
  118. package/dist/io/file-drop.d.ts +7 -0
  119. package/dist/io/file-drop.js +43 -0
  120. package/dist/io/index.d.ts +2 -2
  121. package/dist/io/index.js +2 -112
  122. package/dist/io/is-binary.js +2 -3
  123. package/dist/io/types.d.ts +1 -0
  124. package/dist/io/url-drop.d.ts +2 -0
  125. package/dist/io/url-drop.js +117 -0
  126. package/dist/isosurface/Isosurface.svelte +220 -110
  127. package/dist/isosurface/IsosurfaceControls.svelte +65 -28
  128. package/dist/isosurface/parse.js +104 -56
  129. package/dist/isosurface/slice.d.ts +2 -1
  130. package/dist/isosurface/slice.js +8 -13
  131. package/dist/isosurface/types.d.ts +14 -1
  132. package/dist/isosurface/types.js +152 -5
  133. package/dist/labels.d.ts +2 -1
  134. package/dist/labels.js +12 -8
  135. package/dist/layout/FullscreenToggle.svelte +11 -2
  136. package/dist/layout/InfoCard.svelte +38 -6
  137. package/dist/layout/InfoTag.svelte +125 -94
  138. package/dist/layout/PropertyFilter.svelte +82 -37
  139. package/dist/layout/SettingsSection.svelte +85 -55
  140. package/dist/layout/SubpageGrid.svelte +82 -0
  141. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  142. package/dist/layout/index.d.ts +1 -0
  143. package/dist/layout/index.js +1 -0
  144. package/dist/layout/json-tree/JsonNode.svelte +266 -223
  145. package/dist/layout/json-tree/JsonTree.svelte +516 -429
  146. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  147. package/dist/layout/json-tree/JsonValue.svelte +281 -173
  148. package/dist/layout/json-tree/types.d.ts +10 -2
  149. package/dist/layout/json-tree/utils.d.ts +2 -0
  150. package/dist/layout/json-tree/utils.js +37 -2
  151. package/dist/marching-cubes.js +25 -2
  152. package/dist/math.d.ts +20 -17
  153. package/dist/math.js +474 -57
  154. package/dist/overlays/ContextMenu.svelte +66 -40
  155. package/dist/overlays/DraggablePane.svelte +331 -154
  156. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  157. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  158. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  159. package/dist/periodic-table/PropertySelect.svelte +25 -7
  160. package/dist/periodic-table/TableInset.svelte +8 -3
  161. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +559 -267
  162. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  163. package/dist/phase-diagram/PhaseDiagramControls.svelte +131 -51
  164. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  165. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  166. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  167. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +160 -110
  168. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +8 -1
  169. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +217 -86
  170. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  171. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  172. package/dist/phase-diagram/build-diagram.js +9 -9
  173. package/dist/phase-diagram/colors.js +1 -3
  174. package/dist/phase-diagram/index.d.ts +2 -0
  175. package/dist/phase-diagram/index.js +2 -0
  176. package/dist/phase-diagram/parse.js +10 -9
  177. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  178. package/dist/phase-diagram/svg-to-diagram.js +869 -0
  179. package/dist/phase-diagram/types.d.ts +10 -0
  180. package/dist/phase-diagram/utils.d.ts +8 -4
  181. package/dist/phase-diagram/utils.js +219 -74
  182. package/dist/plot/AxisLabel.svelte +51 -0
  183. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  184. package/dist/plot/BarPlot.svelte +1461 -768
  185. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  186. package/dist/plot/BarPlotControls.svelte +33 -6
  187. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  188. package/dist/plot/ColorBar.svelte +533 -383
  189. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  190. package/dist/plot/ColorScaleSelect.svelte +28 -7
  191. package/dist/plot/ElementScatter.svelte +38 -16
  192. package/dist/plot/FillArea.svelte +152 -92
  193. package/dist/plot/Histogram.svelte +1162 -709
  194. package/dist/plot/Histogram.svelte.d.ts +1 -1
  195. package/dist/plot/HistogramControls.svelte +81 -18
  196. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  197. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  198. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  199. package/dist/plot/Line.svelte +63 -28
  200. package/dist/plot/PlotControls.svelte +221 -96
  201. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  202. package/dist/plot/PlotLegend.svelte +174 -91
  203. package/dist/plot/PlotTooltip.svelte +45 -6
  204. package/dist/plot/PortalSelect.svelte +175 -146
  205. package/dist/plot/ReferenceLine.svelte +77 -22
  206. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  207. package/dist/plot/ReferenceLine3D.svelte +132 -107
  208. package/dist/plot/ReferencePlane.svelte +146 -123
  209. package/dist/plot/ScatterPlot.svelte +1880 -1156
  210. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  211. package/dist/plot/ScatterPlot3D.svelte +256 -131
  212. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  213. package/dist/plot/ScatterPlot3DControls.svelte +300 -297
  214. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  215. package/dist/plot/ScatterPlot3DScene.svelte +608 -406
  216. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  217. package/dist/plot/ScatterPlotControls.svelte +150 -70
  218. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  219. package/dist/plot/ScatterPoint.svelte +98 -26
  220. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  221. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  222. package/dist/plot/Surface3D.svelte +159 -108
  223. package/dist/plot/ZeroLines.svelte +96 -0
  224. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  225. package/dist/plot/ZoomRect.svelte +23 -0
  226. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  227. package/dist/plot/axis-utils.d.ts +1 -1
  228. package/dist/plot/axis-utils.js +1 -3
  229. package/dist/plot/data-cleaning.js +12 -28
  230. package/dist/plot/data-transform.js +2 -1
  231. package/dist/plot/fill-utils.js +2 -0
  232. package/dist/plot/index.d.ts +6 -2
  233. package/dist/plot/index.js +6 -2
  234. package/dist/plot/interactions.d.ts +8 -10
  235. package/dist/plot/interactions.js +2 -3
  236. package/dist/plot/layout.d.ts +11 -2
  237. package/dist/plot/layout.js +44 -17
  238. package/dist/plot/reference-line.d.ts +5 -22
  239. package/dist/plot/reference-line.js +12 -84
  240. package/dist/plot/scales.js +24 -36
  241. package/dist/plot/types.d.ts +53 -40
  242. package/dist/plot/types.js +12 -7
  243. package/dist/plot/utils/label-placement.d.ts +32 -15
  244. package/dist/plot/utils/label-placement.js +227 -63
  245. package/dist/plot/utils/series-visibility.js +2 -3
  246. package/dist/plot/utils.d.ts +1 -0
  247. package/dist/plot/utils.js +14 -0
  248. package/dist/rdf/RdfPlot.svelte +173 -132
  249. package/dist/rdf/calc-rdf.js +4 -5
  250. package/dist/sanitize.d.ts +4 -0
  251. package/dist/sanitize.js +107 -0
  252. package/dist/settings.d.ts +21 -6
  253. package/dist/settings.js +63 -19
  254. package/dist/spectral/Bands.svelte +963 -412
  255. package/dist/spectral/Bands.svelte.d.ts +22 -2
  256. package/dist/spectral/BandsAndDos.svelte +90 -49
  257. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  258. package/dist/spectral/Dos.svelte +389 -258
  259. package/dist/spectral/helpers.d.ts +23 -1
  260. package/dist/spectral/helpers.js +119 -51
  261. package/dist/spectral/types.d.ts +2 -0
  262. package/dist/state.svelte.d.ts +1 -1
  263. package/dist/state.svelte.js +3 -2
  264. package/dist/structure/Arrow.svelte +59 -20
  265. package/dist/structure/AtomLegend.svelte +231 -129
  266. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  267. package/dist/structure/Bond.svelte +73 -47
  268. package/dist/structure/CanvasTooltip.svelte +10 -2
  269. package/dist/structure/CellSelect.svelte +148 -51
  270. package/dist/structure/Cylinder.svelte +33 -17
  271. package/dist/structure/Lattice.svelte +88 -33
  272. package/dist/structure/Structure.svelte +1077 -821
  273. package/dist/structure/Structure.svelte.d.ts +1 -1
  274. package/dist/structure/StructureControls.svelte +373 -139
  275. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  276. package/dist/structure/StructureExportPane.svelte +124 -89
  277. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  278. package/dist/structure/StructureInfoPane.svelte +304 -231
  279. package/dist/structure/StructureScene.svelte +919 -445
  280. package/dist/structure/StructureScene.svelte.d.ts +16 -7
  281. package/dist/structure/atom-properties.d.ts +6 -2
  282. package/dist/structure/atom-properties.js +42 -29
  283. package/dist/structure/bonding.js +6 -7
  284. package/dist/structure/export.js +22 -34
  285. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  286. package/dist/structure/ferrox-wasm-types.js +0 -3
  287. package/dist/structure/ferrox-wasm.d.ts +3 -2
  288. package/dist/structure/ferrox-wasm.js +2 -3
  289. package/dist/structure/index.d.ts +16 -0
  290. package/dist/structure/index.js +88 -6
  291. package/dist/structure/measure.d.ts +2 -2
  292. package/dist/structure/measure.js +4 -44
  293. package/dist/structure/parse.js +130 -155
  294. package/dist/structure/partial-occupancy.d.ts +25 -0
  295. package/dist/structure/partial-occupancy.js +99 -0
  296. package/dist/structure/pbc.d.ts +1 -0
  297. package/dist/structure/pbc.js +16 -6
  298. package/dist/structure/supercell.d.ts +2 -2
  299. package/dist/structure/supercell.js +12 -22
  300. package/dist/structure/validation.js +5 -3
  301. package/dist/symmetry/SymmetryStats.svelte +94 -37
  302. package/dist/symmetry/WyckoffTable.svelte +42 -14
  303. package/dist/symmetry/cell-transform.js +5 -3
  304. package/dist/symmetry/index.d.ts +7 -4
  305. package/dist/symmetry/index.js +87 -21
  306. package/dist/symmetry/spacegroups.js +148 -148
  307. package/dist/table/HeatmapTable.svelte +1112 -516
  308. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  309. package/dist/table/ToggleMenu.svelte +125 -90
  310. package/dist/table/index.d.ts +2 -0
  311. package/dist/table/index.js +2 -4
  312. package/dist/theme/ThemeControl.svelte +21 -12
  313. package/dist/time.js +4 -1
  314. package/dist/tooltip/TooltipContent.svelte +33 -8
  315. package/dist/trajectory/Trajectory.svelte +889 -687
  316. package/dist/trajectory/TrajectoryError.svelte +14 -3
  317. package/dist/trajectory/TrajectoryExportPane.svelte +148 -90
  318. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  319. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  320. package/dist/trajectory/constants.d.ts +6 -0
  321. package/dist/trajectory/constants.js +7 -0
  322. package/dist/trajectory/extract.js +13 -31
  323. package/dist/trajectory/format-detect.d.ts +9 -0
  324. package/dist/trajectory/format-detect.js +76 -0
  325. package/dist/trajectory/frame-reader.d.ts +17 -0
  326. package/dist/trajectory/frame-reader.js +332 -0
  327. package/dist/trajectory/helpers.d.ts +14 -0
  328. package/dist/trajectory/helpers.js +172 -0
  329. package/dist/trajectory/index.d.ts +1 -0
  330. package/dist/trajectory/index.js +23 -14
  331. package/dist/trajectory/parse/ase.d.ts +2 -0
  332. package/dist/trajectory/parse/ase.js +77 -0
  333. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  334. package/dist/trajectory/parse/hdf5.js +129 -0
  335. package/dist/trajectory/parse/index.d.ts +12 -0
  336. package/dist/trajectory/parse/index.js +299 -0
  337. package/dist/trajectory/parse/lammps.d.ts +5 -0
  338. package/dist/trajectory/parse/lammps.js +179 -0
  339. package/dist/trajectory/parse/vasp.d.ts +2 -0
  340. package/dist/trajectory/parse/vasp.js +68 -0
  341. package/dist/trajectory/parse/xyz.d.ts +2 -0
  342. package/dist/trajectory/parse/xyz.js +110 -0
  343. package/dist/trajectory/plotting.js +13 -8
  344. package/dist/trajectory/types.d.ts +11 -0
  345. package/dist/trajectory/types.js +1 -0
  346. package/dist/utils.d.ts +3 -0
  347. package/dist/utils.js +17 -0
  348. package/dist/xrd/XrdPlot.svelte +337 -245
  349. package/dist/xrd/broadening.js +14 -9
  350. package/dist/xrd/calc-xrd.js +12 -19
  351. package/dist/xrd/parse.d.ts +1 -1
  352. package/dist/xrd/parse.js +17 -17
  353. package/package.json +103 -101
  354. package/readme.md +4 -4
  355. package/dist/trajectory/parse.d.ts +0 -42
  356. package/dist/trajectory/parse.js +0 -1267
  357. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
  358. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -0,0 +1,847 @@
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({
101
+ ...config,
102
+ formal_chempots,
103
+ label_stable,
104
+ element_padding,
105
+ default_min_limit,
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(() => {
115
+ // Keep bound temperature aligned with available data points.
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
146
+ }
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]
171
+ if (element_padding <= 0) {
172
+ return Object.fromEntries(
173
+ Object.entries(diagram_data.domains).filter(([, pts]) => pts.length > 0),
174
+ )
175
+ }
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[][]> = {}
183
+ for (const [formula, pts] of Object.entries(diagram_data.domains)) {
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
192
+ }
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)
217
+ if (!prev_stats) {
218
+ stats.set(formula_key, {
219
+ matching_entry_count: 1,
220
+ min_energy_per_atom: epa,
221
+ })
222
+ continue
223
+ }
224
+ stats.set(formula_key, {
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> = {
233
+ energy: `Energy per atom (eV)`,
234
+ formation_energy: `Formation energy (eV/atom)`,
235
+ entries: `Entry count`,
236
+ }
237
+ function get_numeric_color_value(
238
+ formula: string,
239
+ active_color_mode: NumericColorMode,
240
+ ): number | null {
241
+ if (active_color_mode === `energy`) {
242
+ return entry_energy_stats_by_formula.get(formula)?.min_energy_per_atom ?? null
243
+ }
244
+ if (active_color_mode === `formation_energy`) {
245
+ return best_form_energy_for_formula(
246
+ temp_filtered_entries,
247
+ formula,
248
+ raw_el_refs,
249
+ ) ?? null
250
+ }
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
271
+ if (color_mode === `arity`) {
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
278
+ }
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
+ )
285
+ for (const formula of domain_formulas) {
286
+ const color_val = values_payload?.value_by_formula.get(formula)
287
+ colors.set(formula, color_val != null && scale ? scale(color_val) : `#999`)
288
+ }
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 {
301
+ min: min_val,
302
+ max: Math.max(max_val, min_val + 1e-6),
303
+ label: color_mode === `none` || color_mode === `arity`
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 }[] = []
343
+ for (const [formula, pts] of Object.entries(draw_domains)) {
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
+ })
359
+ }
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
+ {
374
+ formula,
375
+ view: `2d`,
376
+ n_points: pts.length,
377
+ axis_ranges: build_axis_ranges(pts, plot_elements),
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 {
392
+ if (!data) {
393
+ if (!locked_hover_formula) hover_info = null
394
+ return
395
+ }
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
409
+ if (locked_hover_formula === formula) {
410
+ clear_hover_lock()
411
+ return
412
+ }
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
+ {
443
+ elements: diagram_data?.elements ?? [],
444
+ domains: draw_domains,
445
+ lims: diagram_data?.lims ?? [],
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> {
461
+ try {
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)
467
+ }
468
+ if (copy_timeout_id !== null) clearTimeout(copy_timeout_id)
469
+ copy_timeout_id = setTimeout(() => {
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
+ })
478
+ </script>
479
+
480
+ {#snippet domain_labels(props: UserContentProps)}
481
+ {#each annotations as { formula, data_x, data_y } (formula)}
482
+ <text
483
+ x={props.x_scale_fn(data_x)}
484
+ y={props.y_scale_fn(data_y)}
485
+ text-anchor="middle"
486
+ class="domain-label"
487
+ >
488
+ {get_electro_neg_formula(formula, true, ``, `.3~s`)}
489
+ </text>
490
+ {/each}
491
+ {/snippet}
492
+
493
+ {#snippet export_toggle()}
494
+ <DraggablePane
495
+ bind:show={export_pane_open}
496
+ open_icon="Cross"
497
+ closed_icon="Export"
498
+ pane_props={{ class: `chempot-export-pane` }}
499
+ toggle_props={{
500
+ class: `chempot-export-toggle`,
501
+ title: `Export chemical potential diagram`,
502
+ style:
503
+ `position: absolute; top: var(--ctrl-btn-top, 5pt); right: 36px; z-index: 10`,
504
+ }}
505
+ >
506
+ <h4 id="export-image">Export Image</h4>
507
+ <div class="export-row">
508
+ <label>
509
+ SVG
510
+ <button
511
+ type="button"
512
+ onclick={() => {
513
+ const svg = get_svg_element()
514
+ if (svg) {
515
+ export_svg_as_svg(svg, `chempot-${plot_elements.join(`-`)}.svg`)
516
+ }
517
+ }}
518
+ aria-label="Download SVG"
519
+ >
520
+
521
+ </button>
522
+ </label>
523
+ <label>
524
+ PNG
525
+ <button
526
+ type="button"
527
+ onclick={() => {
528
+ const svg = get_svg_element()
529
+ if (svg) {
530
+ export_svg_as_png(svg, `chempot-${plot_elements.join(`-`)}.png`)
531
+ }
532
+ }}
533
+ aria-label="Download PNG"
534
+ >
535
+
536
+ </button>
537
+ </label>
538
+ </div>
539
+ <h4 id="export-data">Export Data</h4>
540
+ <div class="export-row">
541
+ <label>
542
+ JSON
543
+ <button type="button" onclick={export_json_file} aria-label="Download JSON">
544
+
545
+ </button>
546
+ <button
547
+ type="button"
548
+ onclick={copy_json}
549
+ aria-label="Copy JSON to clipboard"
550
+ >
551
+ {copy_status ? `✅` : `📋`}
552
+ </button>
553
+ </label>
554
+ </div>
555
+ </DraggablePane>
556
+ {/snippet}
557
+
558
+ {#snippet chempot_controls(_props: unknown)}
559
+ <h4 id="chempot">ChemPot</h4>
560
+ <label>
561
+ <span>Formal chempots:</span>
562
+ <input
563
+ type="checkbox"
564
+ checked={formal_chempots}
565
+ onchange={() => {
566
+ formal_chempots_override = !formal_chempots
567
+ }}
568
+ />
569
+ </label>
570
+ <label>
571
+ <span>Label stable:</span>
572
+ <input
573
+ type="checkbox"
574
+ checked={label_stable}
575
+ onchange={() => {
576
+ label_stable_override = !label_stable
577
+ }}
578
+ />
579
+ </label>
580
+ <label>
581
+ <span>Element padding (eV):</span>
582
+ <input
583
+ type="number"
584
+ min="0"
585
+ step="0.1"
586
+ value={element_padding}
587
+ oninput={(event) => {
588
+ element_padding_override = Number(event.currentTarget.value) || 0
589
+ }}
590
+ />
591
+ </label>
592
+ <label>
593
+ <span>Default min limit (eV):</span>
594
+ <input
595
+ type="number"
596
+ max="0"
597
+ step="1"
598
+ value={default_min_limit}
599
+ oninput={(event) => {
600
+ const raw = event.currentTarget.value
601
+ const parsed = parseFloat(raw)
602
+ default_min_limit_override = raw === `` || isNaN(parsed)
603
+ ? default_min_limit
604
+ : parsed
605
+ }}
606
+ />
607
+ </label>
608
+ <label>
609
+ <span>Color mode:</span>
610
+ <select
611
+ value={color_mode}
612
+ onchange={(event) => {
613
+ color_mode_override = event.currentTarget.value as typeof color_mode
614
+ }}
615
+ >
616
+ <option value="none">None</option>
617
+ <option value="energy">Energy/atom</option>
618
+ <option value="formation_energy">Formation energy</option>
619
+ <option value="arity">Element count</option>
620
+ <option value="entries">Entry count</option>
621
+ </select>
622
+ </label>
623
+ {#if color_mode !== `none` && color_mode !== `arity`}
624
+ <label>
625
+ <span>Color scale:</span>
626
+ <select
627
+ value={color_scale}
628
+ onchange={(event) => {
629
+ color_scale_override = event.currentTarget.value as D3InterpolateName
630
+ }}
631
+ >
632
+ <option value="interpolateViridis">Viridis</option>
633
+ <option value="interpolatePlasma">Plasma</option>
634
+ <option value="interpolateInferno">Inferno</option>
635
+ <option value="interpolateMagma">Magma</option>
636
+ <option value="interpolateCividis">Cividis</option>
637
+ <option value="interpolateTurbo">Turbo</option>
638
+ <option value="interpolateRdYlBu">RdYlBu</option>
639
+ <option value="interpolateSpectral">Spectral</option>
640
+ </select>
641
+ <span class="reverse-scale-toggle">
642
+ <span>Reverse:</span>
643
+ <input
644
+ type="checkbox"
645
+ checked={reverse_color_scale}
646
+ onchange={() => {
647
+ reverse_color_scale_override = !reverse_color_scale
648
+ }}
649
+ />
650
+ </span>
651
+ </label>
652
+ {/if}
653
+ <button type="button" onclick={reset_controls}>Reset defaults</button>
654
+ {/snippet}
655
+
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}
661
+ <div class="error-state" role="alert" aria-live="polite">
662
+ <p>Cannot compute chemical potential diagram.</p>
663
+ <p>Need at least 2 elements with elemental reference entries.</p>
664
+ </div>
665
+ {:else}
666
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
667
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
668
+ <div
669
+ class="chempot-diagram-2d"
670
+ bind:clientWidth={container_width}
671
+ role="application"
672
+ tabindex="0"
673
+ onkeydown={(event) => {
674
+ if (event.key === `Escape`) clear_hover_lock()
675
+ }}
676
+ onpointerdown={(event) => {
677
+ const target = event.target
678
+ if (!locked_hover_formula) return
679
+ const is_background_click = target === scatter_wrapper ||
680
+ (target instanceof SVGElement &&
681
+ target.closest(`g[data-series-id]`) === null)
682
+ if (is_background_click) {
683
+ clear_hover_lock()
684
+ }
685
+ }}
686
+ >
687
+ {@render export_toggle()}
688
+ <ScatterPlot
689
+ bind:wrapper={scatter_wrapper}
690
+ {series}
691
+ bind:x_axis
692
+ bind:y_axis
693
+ legend={null}
694
+ controls={{ show: true }}
695
+ controls_extra={chempot_controls}
696
+ user_content={domain_labels}
697
+ on_point_hover={handle_hover}
698
+ on_point_click={handle_click}
699
+ style="--scatter-width: 100%; --scatter-height: {render_height}px; --fullscreen-btn-offset: 68px"
700
+ />
701
+ {#if color_mode !== `none` && color_mode !== `arity` && color_range}
702
+ {@const color_bar_config = get_chempot_color_bar_config(
703
+ color_scale,
704
+ reverse_color_scale,
705
+ )}
706
+ <ColorBar
707
+ title={color_range.label}
708
+ range={[color_range.min, color_range.max]}
709
+ color_scale_fn={color_bar_config.color_scale_fn}
710
+ color_scale_domain={color_bar_config.color_scale_domain}
711
+ wrapper_style="position: absolute; bottom: 70px; left: 50px; width: 180px; z-index: 10;"
712
+ bar_style="height: 10px;"
713
+ title_style="margin-bottom: 3px;"
714
+ />
715
+ {/if}
716
+ {#if color_mode === `arity`}
717
+ <div class="arity-legend">
718
+ {#each [`Unary`, `Binary`, `Ternary`, `4+`] as label_text, color_idx (label_text)}
719
+ <span>
720
+ <span style:background={arity_colors[color_idx]}></span>
721
+ {label_text}
722
+ </span>
723
+ {/each}
724
+ </div>
725
+ {/if}
726
+ {#if render_local_tooltip && show_tooltip && hover_info?.view === `2d`}
727
+ <aside
728
+ bind:this={tooltip_el}
729
+ class="tooltip"
730
+ style:left="{tooltip_pos.x}px"
731
+ style:top="{tooltip_pos.y}px"
732
+ >
733
+ <strong>
734
+ {@html sanitize_html(get_electro_neg_formula(hover_info.formula, false, ``, `.3~s`))}
735
+ </strong>
736
+ {#if locked_hover_formula === hover_info.formula}
737
+ <div>Pinned · Press Esc to unlock</div>
738
+ {/if}
739
+ </aside>
740
+ {/if}
741
+ {#if show_temperature_slider && temperature !== undefined}
742
+ <TemperatureSlider
743
+ class="chempot-temp-slider"
744
+ {available_temperatures}
745
+ bind:temperature
746
+ />
747
+ {/if}
748
+ </div>
749
+ {/if}
750
+
751
+ <style>
752
+ .chempot-diagram-2d {
753
+ position: relative;
754
+ width: 100%;
755
+ }
756
+ .chempot-diagram-2d > :global(.pane-toggle) {
757
+ opacity: 0;
758
+ transition: opacity 0.2s, background-color 0.2s;
759
+ }
760
+ .chempot-diagram-2d:hover > :global(.pane-toggle),
761
+ .chempot-diagram-2d > :global(.pane-toggle:focus-visible),
762
+ .chempot-diagram-2d > :global(.pane-toggle[aria-expanded='true']) {
763
+ opacity: 1;
764
+ }
765
+ .chempot-diagram-2d :global(.draggable-pane label) {
766
+ display: flex;
767
+ align-items: center;
768
+ gap: 6pt;
769
+ margin: 4pt 0;
770
+ font-size: 0.95em;
771
+ }
772
+ .chempot-diagram-2d :global(.export-row) {
773
+ display: flex;
774
+ flex-wrap: wrap;
775
+ gap: 4pt 10pt;
776
+ margin: 0 0 4pt;
777
+ }
778
+ .chempot-diagram-2d :global(.export-row > label) {
779
+ margin: 0;
780
+ }
781
+ .chempot-diagram-2d :global(.chempot-temp-slider) {
782
+ top: var(--chempot-temp-slider-top, calc(1ex + 108px));
783
+ right: 4px;
784
+ z-index: 11;
785
+ }
786
+ .chempot-diagram-2d :global(.reverse-scale-toggle) {
787
+ display: flex;
788
+ align-items: center;
789
+ gap: 4pt;
790
+ margin-left: 4pt;
791
+ }
792
+ .computing-state {
793
+ display: flex;
794
+ align-items: center;
795
+ justify-content: center;
796
+ min-height: 200px;
797
+ }
798
+ .error-state {
799
+ display: flex;
800
+ flex-direction: column;
801
+ align-items: center;
802
+ justify-content: center;
803
+ height: 100%;
804
+ color: var(--text-color, #666);
805
+ }
806
+ .domain-label {
807
+ font-size: 12px;
808
+ fill: var(--text-color, currentColor);
809
+ opacity: 0.7;
810
+ pointer-events: none;
811
+ }
812
+ .tooltip {
813
+ position: absolute;
814
+ background: var(
815
+ --tooltip-bg,
816
+ light-dark(rgba(255, 255, 255, 0.95), rgba(0, 0, 0, 0.9))
817
+ );
818
+ color: var(--tooltip-text, var(--text-color, #fff));
819
+ padding: 4px 8px;
820
+ border-radius: 4px;
821
+ font-size: 12px;
822
+ pointer-events: none;
823
+ white-space: nowrap;
824
+ z-index: 10;
825
+ }
826
+ .arity-legend {
827
+ position: absolute;
828
+ bottom: 52px;
829
+ left: 24px;
830
+ display: flex;
831
+ gap: 10px;
832
+ font-size: 12px;
833
+ z-index: 10;
834
+ pointer-events: none;
835
+ }
836
+ .arity-legend > span {
837
+ display: flex;
838
+ align-items: center;
839
+ gap: 4px;
840
+ }
841
+ .arity-legend > span > span {
842
+ width: 10px;
843
+ height: 10px;
844
+ border-radius: 50%;
845
+ flex-shrink: 0;
846
+ }
847
+ </style>