matterviz 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/dist/FilePicker.svelte +40 -50
  2. package/dist/api/optimade.js +10 -7
  3. package/dist/app.css +15 -0
  4. package/dist/brillouin/BrillouinZone.svelte +18 -9
  5. package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
  6. package/dist/brillouin/compute.js +10 -16
  7. package/dist/brillouin/geometry.js +2 -2
  8. package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
  9. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
  10. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
  11. package/dist/chempot-diagram/async-compute.svelte.js +12 -13
  12. package/dist/chempot-diagram/color.d.ts +21 -1
  13. package/dist/chempot-diagram/color.js +70 -1
  14. package/dist/chempot-diagram/compute.d.ts +7 -0
  15. package/dist/chempot-diagram/compute.js +85 -26
  16. package/dist/chempot-diagram/export.js +1 -6
  17. package/dist/chempot-diagram/temperature.js +9 -10
  18. package/dist/composition/BarChart.svelte +7 -9
  19. package/dist/composition/Composition.svelte +3 -8
  20. package/dist/composition/Formula.svelte +2 -2
  21. package/dist/composition/FormulaFilter.svelte +27 -93
  22. package/dist/composition/format.js +6 -8
  23. package/dist/composition/parse.js +50 -105
  24. package/dist/constants.d.ts +0 -1
  25. package/dist/constants.js +6 -17
  26. package/dist/convex-hull/ConvexHull2D.svelte +0 -2
  27. package/dist/convex-hull/ConvexHull3D.svelte +19 -47
  28. package/dist/convex-hull/ConvexHull4D.svelte +65 -95
  29. package/dist/convex-hull/StructurePopup.svelte +1 -1
  30. package/dist/convex-hull/gas-thermodynamics.js +21 -53
  31. package/dist/convex-hull/helpers.js +1 -7
  32. package/dist/convex-hull/thermodynamics.js +19 -27
  33. package/dist/coordination/CoordinationBarPlot.svelte +12 -21
  34. package/dist/element/BohrAtom.svelte +0 -1
  35. package/dist/element/ElementTile.svelte +26 -44
  36. package/dist/element/data.d.ts +2 -0
  37. package/dist/element/data.js +5 -1
  38. package/dist/element/index.d.ts +1 -1
  39. package/dist/element/index.js +1 -1
  40. package/dist/fermi-surface/FermiSurface.svelte +22 -23
  41. package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
  42. package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
  43. package/dist/fermi-surface/compute.js +22 -55
  44. package/dist/fermi-surface/parse.js +38 -70
  45. package/dist/file-viewer/JsonBrowser.svelte +1043 -0
  46. package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
  47. package/dist/file-viewer/PlotPanel.svelte +350 -0
  48. package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
  49. package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
  50. package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
  51. package/dist/file-viewer/detect.d.ts +11 -0
  52. package/dist/file-viewer/detect.js +381 -0
  53. package/dist/file-viewer/eligibility.d.ts +4 -0
  54. package/dist/file-viewer/eligibility.js +53 -0
  55. package/dist/file-viewer/host-protocol.d.ts +50 -0
  56. package/dist/file-viewer/host-transfer.d.ts +25 -0
  57. package/dist/file-viewer/host-transfer.js +42 -0
  58. package/dist/file-viewer/index.d.ts +5 -0
  59. package/dist/file-viewer/index.js +5 -0
  60. package/dist/file-viewer/main.d.ts +44 -0
  61. package/dist/file-viewer/main.js +535 -0
  62. package/dist/file-viewer/parse.d.ts +9 -0
  63. package/dist/file-viewer/parse.js +170 -0
  64. package/dist/file-viewer/plot-utils.d.ts +24 -0
  65. package/dist/file-viewer/plot-utils.js +191 -0
  66. package/dist/file-viewer/types.d.ts +4 -0
  67. package/dist/file-viewer/types.js +5 -0
  68. package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
  69. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
  70. package/dist/index.d.ts +2 -2
  71. package/dist/index.js +1 -1
  72. package/dist/io/ExportPane.svelte +1 -1
  73. package/dist/io/decompress.d.ts +2 -0
  74. package/dist/io/decompress.js +3 -2
  75. package/dist/io/export.d.ts +7 -4
  76. package/dist/io/export.js +70 -18
  77. package/dist/io/file-drop.js +60 -35
  78. package/dist/io/url-drop.d.ts +2 -0
  79. package/dist/io/url-drop.js +28 -16
  80. package/dist/isosurface/Isosurface.svelte +687 -196
  81. package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
  82. package/dist/isosurface/IsosurfaceControls.svelte +564 -85
  83. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
  84. package/dist/isosurface/VolumeSlice.svelte +224 -0
  85. package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
  86. package/dist/isosurface/coloring.d.ts +20 -0
  87. package/dist/isosurface/coloring.js +118 -0
  88. package/dist/isosurface/geometry-worker-types.d.ts +38 -0
  89. package/dist/isosurface/geometry.worker.d.ts +1 -0
  90. package/dist/isosurface/geometry.worker.js +59 -0
  91. package/dist/isosurface/grid.d.ts +10 -0
  92. package/dist/isosurface/grid.js +31 -0
  93. package/dist/isosurface/index.d.ts +4 -0
  94. package/dist/isosurface/index.js +4 -0
  95. package/dist/isosurface/profile.d.ts +12 -0
  96. package/dist/isosurface/profile.js +19 -0
  97. package/dist/isosurface/sampling.d.ts +34 -0
  98. package/dist/isosurface/sampling.js +408 -0
  99. package/dist/isosurface/slice-rendering.d.ts +13 -0
  100. package/dist/isosurface/slice-rendering.js +79 -0
  101. package/dist/isosurface/slice.d.ts +22 -2
  102. package/dist/isosurface/slice.js +200 -116
  103. package/dist/isosurface/types.d.ts +24 -1
  104. package/dist/isosurface/types.js +120 -8
  105. package/dist/json-path.d.ts +4 -0
  106. package/dist/json-path.js +89 -0
  107. package/dist/labels.js +1 -1
  108. package/dist/layout/InfoCard.svelte +1 -3
  109. package/dist/layout/SettingsSection.svelte +52 -61
  110. package/dist/layout/SettingsSection.svelte.d.ts +6 -7
  111. package/dist/layout/ViewerChrome.svelte +7 -1
  112. package/dist/layout/json-tree/JsonNode.svelte +14 -48
  113. package/dist/layout/json-tree/JsonTree.svelte +48 -95
  114. package/dist/layout/json-tree/JsonValue.svelte +3 -18
  115. package/dist/layout/json-tree/utils.d.ts +0 -3
  116. package/dist/layout/json-tree/utils.js +50 -179
  117. package/dist/marching-cubes.d.ts +8 -1
  118. package/dist/marching-cubes.js +115 -48
  119. package/dist/math.d.ts +3 -2
  120. package/dist/math.js +29 -38
  121. package/dist/overlays/ContextMenu.svelte +15 -32
  122. package/dist/overlays/DraggablePane.svelte +39 -58
  123. package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
  124. package/dist/overlays/GlassChip.svelte +3 -1
  125. package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
  126. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
  127. package/dist/phase-diagram/build-diagram.js +1 -6
  128. package/dist/phase-diagram/svg-to-diagram.js +9 -14
  129. package/dist/plot/bar/BarPlot.svelte +38 -42
  130. package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
  131. package/dist/plot/box/BoxPlot.svelte +27 -23
  132. package/dist/plot/core/auto-place.d.ts +1 -1
  133. package/dist/plot/core/auto-place.js +1 -1
  134. package/dist/plot/core/data-transform.d.ts +1 -2
  135. package/dist/plot/core/data-transform.js +0 -10
  136. package/dist/plot/core/layout.d.ts +12 -5
  137. package/dist/plot/core/layout.js +37 -27
  138. package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
  139. package/dist/plot/core/pan-zoom.svelte.js +34 -17
  140. package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
  141. package/dist/plot/core/placed-tween.svelte.js +68 -21
  142. package/dist/plot/core/svg.js +0 -1
  143. package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
  144. package/dist/plot/core/utils/hierarchy-chart.js +12 -0
  145. package/dist/plot/core/utils/hierarchy-labels.js +3 -2
  146. package/dist/plot/histogram/Histogram.svelte +21 -26
  147. package/dist/plot/sankey/sankey.js +13 -9
  148. package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
  149. package/dist/plot/scatter/ScatterPlot.svelte +67 -52
  150. package/dist/plot/scatter/adaptive-density.d.ts +1 -1
  151. package/dist/plot/scatter/adaptive-density.js +36 -20
  152. package/dist/plot/sunburst/Sunburst.svelte +20 -20
  153. package/dist/plot/treemap/Treemap.svelte +175 -60
  154. package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
  155. package/dist/plot/treemap/index.d.ts +1 -0
  156. package/dist/plot/treemap/labels.d.ts +38 -0
  157. package/dist/plot/treemap/labels.js +80 -0
  158. package/dist/plot/treemap/treemap.d.ts +4 -2
  159. package/dist/plot/treemap/treemap.js +5 -2
  160. package/dist/rdf/RdfPlot.svelte +17 -35
  161. package/dist/rdf/calc-rdf.js +59 -44
  162. package/dist/rdf/index.d.ts +0 -1
  163. package/dist/sanitize.js +84 -26
  164. package/dist/scene/SceneCamera.svelte +1 -1
  165. package/dist/scene/SceneCamera.svelte.d.ts +1 -1
  166. package/dist/scene/index.d.ts +1 -2
  167. package/dist/scene/index.js +1 -1
  168. package/dist/scene/{props.js → props.svelte.js} +15 -1
  169. package/dist/settings.js +159 -482
  170. package/dist/spectral/Bands.svelte +151 -158
  171. package/dist/spectral/BrillouinBandsDos.svelte +4 -19
  172. package/dist/spectral/Dos.svelte +26 -24
  173. package/dist/spectral/helpers.js +40 -105
  174. package/dist/structure/AtomLegend.svelte +8 -13
  175. package/dist/structure/AtomLegend.svelte.d.ts +2 -3
  176. package/dist/structure/CellSelect.svelte +35 -10
  177. package/dist/structure/Structure.svelte +177 -99
  178. package/dist/structure/Structure.svelte.d.ts +1 -1
  179. package/dist/structure/StructureCarousel.svelte +10 -3
  180. package/dist/structure/StructureControls.svelte +8 -26
  181. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  182. package/dist/structure/StructureInfoPane.svelte +65 -76
  183. package/dist/structure/StructureScene.svelte +75 -109
  184. package/dist/structure/StructureScene.svelte.d.ts +3 -1
  185. package/dist/structure/StructureViewport.svelte +120 -55
  186. package/dist/structure/StructureViewport.svelte.d.ts +3 -1
  187. package/dist/structure/atom-properties.d.ts +1 -1
  188. package/dist/structure/atom-properties.js +4 -3
  189. package/dist/structure/bond-order-perception.js +3 -8
  190. package/dist/structure/bonding.d.ts +0 -1
  191. package/dist/structure/bonding.js +16 -23
  192. package/dist/structure/export.js +50 -74
  193. package/dist/structure/format-detect.js +5 -7
  194. package/dist/structure/index.js +6 -12
  195. package/dist/structure/parse.js +38 -100
  196. package/dist/structure/partial-occupancy.js +7 -4
  197. package/dist/structure/pbc.js +8 -10
  198. package/dist/structure/polyhedra.js +5 -4
  199. package/dist/structure/prop-groups.d.ts +85 -0
  200. package/dist/structure/prop-groups.js +18 -0
  201. package/dist/symmetry/SymmetryStats.svelte +0 -4
  202. package/dist/symmetry/WyckoffTable.svelte +5 -4
  203. package/dist/symmetry/cell-transform.js +5 -14
  204. package/dist/symmetry/index.js +6 -9
  205. package/dist/table/HeatmapTable.svelte +61 -103
  206. package/dist/table/ToggleMenu.svelte +166 -44
  207. package/dist/table/ToggleMenu.svelte.d.ts +1 -1
  208. package/dist/theme/embedded.d.ts +18 -0
  209. package/dist/theme/embedded.js +206 -0
  210. package/dist/theme/index.d.ts +4 -0
  211. package/dist/theme/index.js +1 -2
  212. package/dist/theme/themes.mjs +31 -60
  213. package/dist/tooltip/TooltipContent.svelte +1 -1
  214. package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
  215. package/dist/tooltip/index.d.ts +8 -1
  216. package/dist/trajectory/Trajectory.svelte +140 -120
  217. package/dist/trajectory/TrajectoryError.svelte +1 -66
  218. package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
  219. package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
  220. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
  221. package/dist/trajectory/extract.js +19 -29
  222. package/dist/trajectory/format-detect.d.ts +2 -0
  223. package/dist/trajectory/format-detect.js +7 -5
  224. package/dist/trajectory/frame-reader.js +3 -4
  225. package/dist/trajectory/helpers.js +16 -49
  226. package/dist/trajectory/index.js +2 -8
  227. package/dist/trajectory/parse/ase.js +1 -1
  228. package/dist/trajectory/parse/index.d.ts +4 -3
  229. package/dist/trajectory/parse/index.js +12 -17
  230. package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
  231. package/dist/trajectory/parse/vaspout-h5.js +1 -4
  232. package/dist/trajectory/parse/xyz.js +48 -2
  233. package/dist/trajectory/plotting.js +14 -37
  234. package/dist/xrd/XrdPlot.svelte +43 -68
  235. package/dist/xrd/calc-xrd.js +22 -58
  236. package/dist/xrd/parse.js +13 -22
  237. package/package.json +40 -8
  238. package/readme.md +6 -5
  239. package/dist/scene/types.d.ts +0 -26
  240. package/dist/tooltip/types.d.ts +0 -8
  241. package/dist/trajectory/constants.d.ts +0 -6
  242. package/dist/trajectory/constants.js +0 -11
  243. /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
  244. /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
@@ -1,12 +1,11 @@
1
1
  <script lang="ts">
2
2
  import type { PaneProps, PaneToggleProps } from '../overlays'
3
3
  import { get_electro_neg_formula } from '../composition'
4
- import { element_data, type ElementSymbol } from '../element'
4
+ import { element_by_symbol, type ElementSymbol } from '../element'
5
5
  import Icon from '../Icon.svelte'
6
6
  import { format_num } from '../labels'
7
7
  import type { InfoItem } from '../layout'
8
8
  import type { Vec2 } from '../math'
9
- import CopyButton from '../overlays/CopyButton.svelte'
10
9
  import DraggablePane from '../overlays/DraggablePane.svelte'
11
10
  import { sanitize_html } from '../sanitize'
12
11
  import { colors } from '../state.svelte'
@@ -93,11 +92,6 @@
93
92
  const copy_to_clipboard = (label: string, value: string, key: string): Promise<void> =>
94
93
  copy(`${label}: ${value}`, key)
95
94
 
96
- function copy_event(event: MouseEvent, label: string, value: string, key: string) {
97
- event.stopPropagation()
98
- copy_to_clipboard(label, value, key)
99
- }
100
-
101
95
  function copy_info_item(item: InfoItem) {
102
96
  copy_to_clipboard(item.label, String(item.value), item.key ?? item.label)
103
97
  }
@@ -153,19 +147,22 @@
153
147
  sibling_cards[next_idx]?.focus()
154
148
  }
155
149
 
156
- const get_element_name = (element: string): string =>
157
- element_data?.find((element_record) => element_record.symbol === element)?.name || element
158
-
159
150
  const site_summary = (card: SiteCard): string =>
160
151
  [card.element_name, ...card.details.map(({ label, value }) => `${label}: ${value}`)].join(
161
152
  `; `,
162
153
  )
163
154
 
155
+ function compact_site_detail_label(detail: SiteDetail): string {
156
+ if (detail.key === `fractional`) return `Frac.`
157
+ if (detail.key === `cartesian`) return `Cart.`
158
+ return detail.label
159
+ }
160
+
164
161
  function format_site_property(prop_key: string, prop_value: unknown): SiteDetail | null {
165
162
  if (prop_value == null) return null
166
- const format_numeric_value = (value: unknown, format = `.3~f`): string | null => {
163
+ const format_numeric_value = (value: unknown): string | null => {
167
164
  const numeric_value = Number(value)
168
- return Number.isNaN(numeric_value) ? null : format_num(numeric_value, format)
165
+ return Number.isNaN(numeric_value) ? null : format_num(numeric_value, `.3~f`)
169
166
  }
170
167
  const format_value_list = (values: unknown[]): string =>
171
168
  `(${values.map((value) => format_numeric_value(value) ?? String(value)).join(`, `)})`
@@ -241,7 +238,7 @@
241
238
  },
242
239
  {
243
240
  label: `a, b, c`,
244
- value: `${format_num(a, `.4~f`)}, ${format_num(b, `.4~f`)}, ${format_num(c, `.4~f`)} Å`,
241
+ value: `${format_num(a, `.3~f`)}, ${format_num(b, `.3~f`)}, ${format_num(c, `.3~f`)} Å`,
245
242
  key: `cell-abc`,
246
243
  },
247
244
  {
@@ -319,7 +316,7 @@
319
316
  if (!structure || !site_cards_visible) return []
320
317
  return structure.sites.map((site: Site, idx: number) => {
321
318
  const element = site.species?.[0]?.element || `Unknown`
322
- const element_name = get_element_name(element)
319
+ const element_name = element_by_symbol.get(element as ElementSymbol)?.name ?? element
323
320
  const details: SiteDetail[] = []
324
321
  for (const [label, key, coords, unit] of [
325
322
  [`Fractional`, `fractional`, site.abc, ``],
@@ -329,7 +326,7 @@
329
326
  details.push({
330
327
  label,
331
328
  key,
332
- value: `(${coords.map((coord) => format_num(coord, `.4~f`)).join(`, `)})${unit}`,
329
+ value: `(${coords.map((coord) => format_num(coord, `.3~f`)).join(`, `)})${unit}`,
333
330
  })
334
331
  }
335
332
  if (site.properties) {
@@ -404,7 +401,12 @@
404
401
  }}
405
402
  open_icon="Cross"
406
403
  closed_icon="Info"
407
- pane_props={{ ...pane_props, class: `structure-info-pane ${pane_props?.class ?? ``}` }}
404
+ icon_style="transform: scale(1.15)"
405
+ pane_props={{
406
+ ...pane_props,
407
+ class: `structure-info-pane ${pane_props?.class ?? ``}`,
408
+ style: `--pane-padding: 4pt; --pane-gap: 2pt; ${pane_props?.style ?? ``}`,
409
+ }}
408
410
  {...rest}
409
411
  >
410
412
  <h4 id="structure-info" style="margin-top: 0">Structure Info</h4>
@@ -446,7 +448,7 @@
446
448
  {wyckoff_positions}
447
449
  on_hover={(site_indices) => (highlighted_sites = site_indices ?? [])}
448
450
  on_click={(site_indices) => (selected_sites = site_indices ?? [])}
449
- style="width: 100%; margin-top: 0.5em; font-size: 0.8em"
451
+ style="width: 100%; margin-top: 2pt; font-size: 0.8em"
450
452
  />
451
453
  {/if}
452
454
  </section>
@@ -531,42 +533,18 @@
531
533
  onclick={(event) => select_site(card.idx, event)}
532
534
  onkeydown={(event) => handle_site_keydown(event, card)}
533
535
  >
534
- <div class="site-card-header">
535
- <span class="site-title">
536
- <span class="site-color" aria-hidden="true"></span>
537
- <strong>{card.title}</strong>
538
- <span>{card.element_name}</span>
539
- </span>
540
- <CopyButton
541
- label="Copy {card.title}"
542
- title="Copy {card.title}"
543
- copied={copied.has(`site-${card.idx}-summary`)}
544
- onclick={(event) =>
545
- copy_event(
546
- event,
547
- card.title,
548
- site_summary(card),
549
- `site-${card.idx}-summary`,
550
- )}
551
- />
552
- </div>
536
+ <span class="site-title">
537
+ <span class="site-color" aria-hidden="true"></span>
538
+ <strong>{card.title}</strong>
539
+ <span>{card.element_name}</span>
540
+ </span>
553
541
  <div class="site-card-details">
554
542
  {#each card.details as detail (`site-${card.idx}-${detail.key}`)}
555
543
  <div class="site-detail">
556
- <span>{@html sanitize_html(detail.label)}</span>
544
+ <span title={detail.label}
545
+ >{@html sanitize_html(compact_site_detail_label(detail))}</span
546
+ >
557
547
  <span title={detail.tooltip}>{@html sanitize_html(detail.value)}</span>
558
- <CopyButton
559
- label="Copy {card.title} {detail.label}"
560
- title="Copy {detail.label}"
561
- copied={copied.has(`site-${card.idx}-${detail.key}`)}
562
- onclick={(event) =>
563
- copy_event(
564
- event,
565
- `${card.title} ${detail.label}`,
566
- detail.value,
567
- `site-${card.idx}-${detail.key}`,
568
- )}
569
- />
570
548
  </div>
571
549
  {/each}
572
550
  </div>
@@ -591,13 +569,20 @@
591
569
  </DraggablePane>
592
570
 
593
571
  <style>
594
- .info-row,
595
- .tips-item {
572
+ h4 {
573
+ margin: 2pt 0;
574
+ font-size: 0.95em;
575
+ }
576
+ hr {
577
+ margin: 2pt 0;
578
+ }
579
+ .info-row {
596
580
  display: flex;
597
581
  justify-content: space-between;
598
- gap: 6pt;
599
- padding: 1pt;
600
- line-height: 1.5;
582
+ gap: 4pt;
583
+ padding: 0 1pt;
584
+ font-size: 0.9em;
585
+ line-height: 1.3;
601
586
  }
602
587
  .info-row.clickable {
603
588
  cursor: pointer;
@@ -610,10 +595,7 @@
610
595
  display: flex;
611
596
  align-items: center;
612
597
  justify-content: space-between;
613
- gap: 6pt;
614
- h4 {
615
- margin: 0.5em 0;
616
- }
598
+ gap: 4pt;
617
599
  }
618
600
  .sites-toggle,
619
601
  .site-window-controls button {
@@ -630,8 +612,8 @@
630
612
  .site-filter {
631
613
  box-sizing: border-box;
632
614
  width: 100%;
633
- margin-bottom: 5pt;
634
- padding: 4pt 6pt;
615
+ margin-bottom: 3pt;
616
+ padding: 3pt 5pt;
635
617
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
636
618
  border-radius: var(--border-radius, 3pt);
637
619
  background: color-mix(in srgb, var(--pane-bg, Canvas) 88%, currentColor);
@@ -646,8 +628,8 @@
646
628
  display: flex;
647
629
  align-items: center;
648
630
  justify-content: space-between;
649
- gap: 5pt;
650
- margin-bottom: 5pt;
631
+ gap: 4pt;
632
+ margin-bottom: 3pt;
651
633
  font-size: 0.8em;
652
634
  button {
653
635
  padding: 2pt 5pt;
@@ -659,13 +641,12 @@
659
641
  }
660
642
  .site-cards {
661
643
  display: grid;
662
- gap: 5pt;
644
+ gap: 6pt;
663
645
  }
664
646
  .site-card {
665
647
  border-left: 3px solid var(--site-color, #888);
666
- border-radius: var(--border-radius, 3pt);
667
648
  background: color-mix(in srgb, currentColor 4%, transparent);
668
- padding: 5pt;
649
+ padding: 4pt 8pt;
669
650
  cursor: pointer;
670
651
  outline: none;
671
652
  &:is(:hover, :focus-visible, .highlighted) {
@@ -676,16 +657,12 @@
676
657
  background: color-mix(in srgb, var(--site-color, currentColor) 25%, transparent);
677
658
  }
678
659
  }
679
- .site-card-header,
680
660
  .site-title,
681
661
  .site-detail {
682
662
  display: flex;
683
663
  align-items: center;
684
664
  gap: 5pt;
685
665
  }
686
- .site-card-header {
687
- justify-content: space-between;
688
- }
689
666
  .site-title {
690
667
  min-width: 0;
691
668
  span:last-child {
@@ -700,13 +677,17 @@
700
677
  background: var(--site-color, #888);
701
678
  }
702
679
  .site-card-details {
703
- display: grid;
704
- gap: 2pt;
705
- margin-top: 3pt;
706
- font-size: 0.86em;
680
+ display: flex;
681
+ flex-wrap: wrap;
682
+ gap: 1pt 4pt;
683
+ margin-top: 2pt;
684
+ font-size: 0.73em;
685
+ line-height: 1.3;
707
686
  }
708
687
  .site-detail {
709
- justify-content: space-between;
688
+ gap: 2pt;
689
+ min-width: 0;
690
+ max-width: 100%;
710
691
  span:first-child {
711
692
  opacity: 0.75;
712
693
  }
@@ -735,8 +716,16 @@
735
716
  }
736
717
  }
737
718
  .tips-item {
738
- flex-direction: column;
739
- gap: 2pt;
719
+ display: grid;
720
+ grid-template-columns: max-content minmax(0, 1fr);
721
+ gap: 4pt;
722
+ padding: 1pt 0;
723
+ font-size: 0.8em;
724
+ line-height: 1.25;
725
+ span:first-child {
726
+ font-weight: 600;
727
+ text-align: right;
728
+ }
740
729
  span:last-child {
741
730
  opacity: 0.8;
742
731
  }
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import type { D3InterpolateName } from '../colors'
3
- import { get_d3_interpolator } from '../colors'
3
+ import { get_d3_interpolator, is_dark_mode, watch_dark_mode } from '../colors'
4
4
  import type { ElementSymbol } from '../element'
5
- import { element_data } from '../element'
5
+ import { element_by_symbol } from '../element'
6
6
  import Isosurface from '../isosurface/Isosurface.svelte'
7
7
  import type { IsosurfaceSettings, VolumetricData } from '../isosurface/types'
8
8
  import { DEFAULT_ISOSURFACE_SETTINGS } from '../isosurface/types'
@@ -211,13 +211,14 @@
211
211
  dragging_atoms = $bindable(false),
212
212
  volumetric_data = undefined,
213
213
  isosurface_settings = DEFAULT_ISOSURFACE_SETTINGS,
214
+ active_volume_idx = 0,
215
+ volume_scaling = [1, 1, 1],
214
216
  interactive = true,
215
217
  }: SceneControlProps & {
216
218
  structure?: AnyStructure
217
219
  base_structure?: AnyStructure // The original structure without image atoms, used for property color calculation
218
220
  atom_radius?: number // scale factor for atomic radii
219
- same_size_atoms?: boolean // whether to use the same radius for all atoms. if not, the radius will be
220
- // determined by the atomic radius of the element
221
+ same_size_atoms?: boolean // uniform radius for all atoms (else per-element atomic radii)
221
222
  camera_position?: [x: number, y: number, z: number] // initial camera position from which to render the scene
222
223
  camera_target?: Vec3 // external orbit-controls target for pan synchronization
223
224
  // When set (and camera_position is unset/zero), auto-place the camera along this
@@ -316,8 +317,12 @@
316
317
  add_element?: ElementSymbol // element to add when clicking in add-atom mode
317
318
  cursor?: string // cursor style for the 3D canvas
318
319
  dragging_atoms?: boolean // true while TransformControls drag is active (skips expensive recalculations)
319
- volumetric_data?: VolumetricData // Active volumetric data for isosurface rendering
320
+ // Loaded volumetric datasets for isosurface rendering (single volume accepted
321
+ // for backwards compatibility)
322
+ volumetric_data?: VolumetricData | VolumetricData[]
320
323
  isosurface_settings?: IsosurfaceSettings // Isosurface rendering settings
324
+ active_volume_idx?: number // Volume implicit single-isovalue settings apply to
325
+ volume_scaling?: Vec3 // Supercell tiling applied to isosurface geometry
321
326
  // When false, render the scene without hover/edit raycast helpers. Used by multi-side
322
327
  // view so inactive panes skip interaction-only work while the active pane stays editable.
323
328
  interactive?: boolean
@@ -328,6 +333,8 @@
328
333
  { step: 0.015, frequency: 5 },
329
334
  )
330
335
  let pulse_opacity = $derived(0.15 + 0.25 * pulse.unit)
336
+ let dark_mode = $state(is_dark_mode())
337
+ $effect(() => watch_dark_mode((dark) => (dark_mode = dark)))
331
338
 
332
339
  bind_renderer((threlte_scene, threlte_camera) => {
333
340
  scene = threlte_scene
@@ -431,6 +438,22 @@
431
438
  bond_context_target = null
432
439
  }
433
440
 
441
+ // Shared handlers for bond context-menu buttons: act on pointerdown (a click would
442
+ // arrive after orbit-controls' start handler already closed the menu) or Enter/Space
443
+ const menu_action_props = (action: () => void) => {
444
+ const run = (event: Event) => {
445
+ event.preventDefault()
446
+ event.stopPropagation()
447
+ action()
448
+ }
449
+ return {
450
+ onpointerdown: run,
451
+ onkeydown: (event: KeyboardEvent) => {
452
+ if (event.key === `Enter` || event.key === ` `) run(event)
453
+ },
454
+ }
455
+ }
456
+
434
457
  const canonical_bond_target = (bond: BondKeyTarget): BondKeyTarget =>
435
458
  canonicalize_bond_target(bond, structure?.sites)
436
459
 
@@ -500,20 +523,14 @@
500
523
  const BOND_ENDPOINT_HIT_FRACTION = 0.3
501
524
  const BOND_ENDPOINT_SITE_MATCH_TOLERANCE = 1e-6
502
525
  const EDITABLE_ATOM_HIT_RADIUS_SCALE = 1.15
503
- const skip_raycast = (): void => undefined
504
526
 
505
527
  function apply_bond_transform(mesh: Mesh, bond: BondPair): void {
506
528
  mesh.matrix.fromArray(bond.transform_matrix)
507
529
  mesh.matrixWorldNeedsUpdate = true
508
530
  }
509
531
 
510
- function apply_non_raycastable_bond_hit_transform(mesh: Mesh, bond: BondPair): void {
511
- apply_bond_transform(mesh, bond)
512
- disable_raycast(mesh)
513
- }
514
-
515
532
  function disable_raycast(mesh: Mesh): void {
516
- mesh.raycast = skip_raycast
533
+ mesh.raycast = () => undefined
517
534
  }
518
535
 
519
536
  function site_world_position(parent: Object3D, site: Site): Vector3 {
@@ -1464,7 +1481,7 @@
1464
1481
  : get_site_radius(site, site_idx)
1465
1482
  targets.push({ kind, site, site_idx, color, radius })
1466
1483
  }
1467
- add(`hover`, hovered_site, hovered_idx, `white`)
1484
+ add(`hover`, hovered_site, hovered_idx, dark_mode ? `white` : `#333`)
1468
1485
  for (const idx of selected_sites ?? []) {
1469
1486
  add(`selected`, structure?.sites?.[idx] ?? null, idx, selection_highlight_color)
1470
1487
  }
@@ -1689,12 +1706,6 @@
1689
1706
  })
1690
1707
  </script>
1691
1708
 
1692
- {#snippet bond_instanced_mesh_snippet(group: ComponentProps<typeof Bond>[`group`])}
1693
- {#key group.instances.length}
1694
- <Bond {group} />
1695
- {/key}
1696
- {/snippet}
1697
-
1698
1709
  {#snippet site_label_snippet(site_idx: number)}
1699
1710
  {@const site = structure!.sites[site_idx]}
1700
1711
  {#if site}
@@ -1800,42 +1811,27 @@
1800
1811
  />
1801
1812
  </T.Mesh>
1802
1813
 
1803
- {#if atom.render_start_cap}
1804
- <T.Mesh rotation={[0, atom.start_phi, 0]}>
1805
- <T.CircleGeometry
1806
- args={[
1807
- 0.5,
1808
- sphere_segments,
1809
- PARTIAL_OCCUPANCY_CAP_ARC.start_cap_arc_start,
1810
- PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
1811
- ]}
1812
- />
1813
- <T.MeshStandardMaterial
1814
- color={partial_color}
1815
- side={2}
1816
- opacity={ghost_opacity}
1817
- transparent={partial_edit_image}
1818
- />
1819
- </T.Mesh>
1820
- {/if}
1821
- {#if atom.render_end_cap}
1822
- <T.Mesh rotation={[0, atom.end_phi, 0]}>
1823
- <T.CircleGeometry
1824
- args={[
1825
- 0.5,
1826
- sphere_segments,
1827
- PARTIAL_OCCUPANCY_CAP_ARC.end_cap_arc_start,
1828
- PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
1829
- ]}
1830
- />
1831
- <T.MeshStandardMaterial
1832
- color={partial_color}
1833
- side={2}
1834
- opacity={ghost_opacity}
1835
- transparent={partial_edit_image}
1836
- />
1837
- </T.Mesh>
1838
- {/if}
1814
+ <!-- Flat caps closing the wedge at its start/end azimuthal angles -->
1815
+ {#each [[atom.render_start_cap, atom.start_phi, PARTIAL_OCCUPANCY_CAP_ARC.start_cap_arc_start], [atom.render_end_cap, atom.end_phi, PARTIAL_OCCUPANCY_CAP_ARC.end_cap_arc_start]] as const as [render_cap, phi, arc_start], cap_idx (cap_idx)}
1816
+ {#if render_cap}
1817
+ <T.Mesh rotation={[0, phi, 0]}>
1818
+ <T.CircleGeometry
1819
+ args={[
1820
+ 0.5,
1821
+ sphere_segments,
1822
+ arc_start,
1823
+ PARTIAL_OCCUPANCY_CAP_ARC.arc_length,
1824
+ ]}
1825
+ />
1826
+ <T.MeshStandardMaterial
1827
+ color={partial_color}
1828
+ side={2}
1829
+ opacity={ghost_opacity}
1830
+ transparent={partial_edit_image}
1831
+ />
1832
+ </T.Mesh>
1833
+ {/if}
1834
+ {/each}
1839
1835
  </T.Group>
1840
1836
  {/each}
1841
1837
 
@@ -1881,11 +1877,11 @@
1881
1877
  {/each}
1882
1878
 
1883
1879
  <!-- Instanced bond rendering with gradient colors -->
1884
- {#if instanced_bond_groups.length > 0}
1885
- {#each instanced_bond_groups as group (group.thickness + group.instances.length)}
1886
- {@render bond_instanced_mesh_snippet(group)}
1887
- {/each}
1888
- {/if}
1880
+ {#each instanced_bond_groups as group (group.thickness + group.instances.length)}
1881
+ {#key group.instances.length}
1882
+ <Bond {group} />
1883
+ {/key}
1884
+ {/each}
1889
1885
 
1890
1886
  <!-- Coordination polyhedra: all faces in one merged mesh, edges in one
1891
1887
  LineSegments (1-2 draw calls regardless of supercell size) -->
@@ -1954,7 +1950,10 @@
1954
1950
  {#if is_hovered}
1955
1951
  <T.Mesh
1956
1952
  matrixAutoUpdate={false}
1957
- oncreate={(ref) => apply_non_raycastable_bond_hit_transform(ref, bond)}
1953
+ oncreate={(ref) => {
1954
+ apply_bond_transform(ref, bond)
1955
+ disable_raycast(ref)
1956
+ }}
1958
1957
  >
1959
1958
  <T.CylinderGeometry args={[bond_hover_radius, bond_hover_radius, 1, 6]} />
1960
1959
  <T.MeshBasicMaterial
@@ -1996,52 +1995,15 @@
1996
1995
  {#each BOND_ORDER_OPTIONS as { order, label } (label)}
1997
1996
  <button
1998
1997
  type="button"
1999
- onpointerdown={(event: PointerEvent) => {
2000
- event.preventDefault()
2001
- event.stopPropagation()
2002
- set_context_bond_order(order)
2003
- }}
2004
- onkeydown={(event: KeyboardEvent) => {
2005
- if (event.key !== `Enter` && event.key !== ` `) return
2006
- event.preventDefault()
2007
- event.stopPropagation()
2008
- set_context_bond_order(order)
2009
- }}
1998
+ {...menu_action_props(() => set_context_bond_order(order))}
2010
1999
  >
2011
2000
  {label}
2012
2001
  </button>
2013
2002
  {/each}
2014
- <button
2015
- type="button"
2016
- class="remove"
2017
- onpointerdown={(event: PointerEvent) => {
2018
- event.preventDefault()
2019
- event.stopPropagation()
2020
- remove_context_bond()
2021
- }}
2022
- onkeydown={(event: KeyboardEvent) => {
2023
- if (event.key !== `Enter` && event.key !== ` `) return
2024
- event.preventDefault()
2025
- event.stopPropagation()
2026
- remove_context_bond()
2027
- }}
2028
- >
2003
+ <button type="button" class="remove" {...menu_action_props(remove_context_bond)}>
2029
2004
  Remove
2030
2005
  </button>
2031
- <button
2032
- type="button"
2033
- onpointerdown={(event: PointerEvent) => {
2034
- event.preventDefault()
2035
- event.stopPropagation()
2036
- close_bond_context_menu()
2037
- }}
2038
- onkeydown={(event: KeyboardEvent) => {
2039
- if (event.key !== `Enter` && event.key !== ` `) return
2040
- event.preventDefault()
2041
- event.stopPropagation()
2042
- close_bond_context_menu()
2043
- }}
2044
- >
2006
+ <button type="button" {...menu_action_props(close_bond_context_menu)}>
2045
2007
  Close
2046
2008
  </button>
2047
2009
  </div>
@@ -2122,7 +2084,7 @@
2122
2084
  <div class="elements">
2123
2085
  {#each tooltip_species as { element, occu, oxidation_state: oxi_state }, idx (`${element ?? ``}-${occu ?? ``}-${oxi_state ?? ``}-${idx}`)}
2124
2086
  {@const element_name =
2125
- element_data.find((elem) => elem.symbol === element)?.name ?? ``}
2087
+ element_by_symbol.get(element as ElementSymbol)?.name ?? ``}
2126
2088
  <span class="species">
2127
2089
  {#if occu !== 1}<span class="occupancy">{format_num(occu, `.3~f`)}</span>{/if}
2128
2090
  <strong>
@@ -2228,7 +2190,15 @@
2228
2190
 
2229
2191
  <!-- Isosurface rendering from volumetric data (CHGCAR, .cube files) -->
2230
2192
  {#if volumetric_data && isosurface_settings}
2231
- <Isosurface volume={volumetric_data} settings={isosurface_settings} />
2193
+ {@const volume_list = Array.isArray(volumetric_data)
2194
+ ? volumetric_data
2195
+ : [volumetric_data]}
2196
+ <Isosurface
2197
+ volumes={volume_list}
2198
+ settings={isosurface_settings}
2199
+ {active_volume_idx}
2200
+ tiling={volume_scaling}
2201
+ />
2232
2202
  {/if}
2233
2203
 
2234
2204
  <!-- Measurement overlays for measured sites -->
@@ -2241,17 +2211,13 @@
2241
2211
  {@const pos_i = site_i.xyz}
2242
2212
  {@const pos_j = site_j.xyz}
2243
2213
  <Cylinder from={pos_i} to={pos_j} thickness={0.12} color={measure_line_color} />
2244
- {@const midpoint = [
2245
- (pos_i[0] + pos_j[0]) / 2,
2246
- (pos_i[1] + pos_j[1]) / 2,
2247
- (pos_i[2] + pos_j[2]) / 2,
2248
- ] as Vec3}
2214
+ {@const mid_pos = midpoint(pos_i, pos_j)}
2249
2215
  {@const direct = math.euclidean_dist(pos_i, pos_j)}
2250
2216
  {@const pbc = lattice
2251
2217
  ? measure.distance_pbc(pos_i, pos_j, lattice.matrix, undefined, lattice.pbc)
2252
2218
  : direct}
2253
2219
  {@const differ = lattice ? Math.abs(pbc - direct) > 1e-6 : false}
2254
- <extras.HTML center position={midpoint}>
2220
+ <extras.HTML center position={mid_pos}>
2255
2221
  <span class="measure-label">
2256
2222
  {#if differ}
2257
2223
  PBC: {format_num(pbc, float_fmt)} Å<br /><small>
@@ -106,8 +106,10 @@ type $$ComponentProps = SceneControlProps & {
106
106
  add_element?: ElementSymbol;
107
107
  cursor?: string;
108
108
  dragging_atoms?: boolean;
109
- volumetric_data?: VolumetricData;
109
+ volumetric_data?: VolumetricData | VolumetricData[];
110
110
  isosurface_settings?: IsosurfaceSettings;
111
+ active_volume_idx?: number;
112
+ volume_scaling?: Vec3;
111
113
  interactive?: boolean;
112
114
  };
113
115
  declare const StructureScene: import("svelte").Component<$$ComponentProps, {}, "cursor" | "camera" | "site_label_offset" | "vector_configs" | "orbit_controls" | "hovered_idx" | "hovered_site" | "polyhedra_rendered_elements" | "camera_is_moving" | "selected_sites" | "measured_sites" | "added_bonds" | "removed_bonds" | "bond_order_overrides" | "bond_edit_mode" | "active_sites" | "scene" | "rotation_target_ref" | "initial_computed_zoom" | "hidden_elements" | "hidden_prop_vals" | "element_radius_overrides" | "site_radius_overrides" | "add_atom_mode" | "add_element" | "dragging_atoms">;