matterviz 0.3.5 → 0.3.7

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 (229) hide show
  1. package/dist/MillerIndexInput.svelte +5 -5
  2. package/dist/api/optimade.js +3 -3
  3. package/dist/brillouin/BrillouinZone.svelte +5 -2
  4. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  5. package/dist/brillouin/BrillouinZoneExportPane.svelte +1 -3
  6. package/dist/brillouin/BrillouinZoneInfoPane.svelte +1 -1
  7. package/dist/brillouin/BrillouinZoneScene.svelte +5 -5
  8. package/dist/brillouin/compute.js +21 -21
  9. package/dist/brillouin/index.d.ts +1 -1
  10. package/dist/brillouin/index.js +0 -1
  11. package/dist/brillouin/types.d.ts +8 -13
  12. package/dist/chempot-diagram/ChemPotDiagram.svelte +3 -3
  13. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +3 -4
  14. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +33 -34
  15. package/dist/chempot-diagram/compute.js +1 -7
  16. package/dist/chempot-diagram/temperature.d.ts +1 -1
  17. package/dist/chempot-diagram/temperature.js +1 -3
  18. package/dist/chempot-diagram/types.d.ts +4 -9
  19. package/dist/colors/index.js +5 -5
  20. package/dist/composition/Composition.svelte +2 -1
  21. package/dist/composition/Formula.svelte +7 -4
  22. package/dist/composition/FormulaFilter.svelte +1 -3
  23. package/dist/composition/format.js +4 -4
  24. package/dist/composition/parse.d.ts +2 -1
  25. package/dist/composition/parse.js +61 -46
  26. package/dist/convex-hull/ConvexHull2D.svelte +62 -51
  27. package/dist/convex-hull/ConvexHull3D.svelte +101 -90
  28. package/dist/convex-hull/ConvexHull4D.svelte +70 -58
  29. package/dist/convex-hull/ConvexHullControls.svelte +24 -35
  30. package/dist/convex-hull/ConvexHullInfoPane.svelte +8 -5
  31. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +2 -0
  32. package/dist/convex-hull/ConvexHullStats.svelte +9 -2
  33. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +2 -0
  34. package/dist/convex-hull/GasPressureControls.svelte +7 -7
  35. package/dist/convex-hull/StructurePopup.svelte +65 -30
  36. package/dist/convex-hull/StructurePopup.svelte.d.ts +6 -6
  37. package/dist/convex-hull/TemperatureSlider.svelte +8 -5
  38. package/dist/convex-hull/barycentric-coords.d.ts +2 -2
  39. package/dist/convex-hull/barycentric-coords.js +2 -2
  40. package/dist/convex-hull/gas-thermodynamics.js +2 -4
  41. package/dist/convex-hull/helpers.d.ts +13 -2
  42. package/dist/convex-hull/helpers.js +37 -16
  43. package/dist/convex-hull/index.d.ts +1 -0
  44. package/dist/convex-hull/index.js +1 -0
  45. package/dist/convex-hull/thermodynamics.d.ts +2 -1
  46. package/dist/convex-hull/thermodynamics.js +7 -7
  47. package/dist/convex-hull/types.d.ts +15 -15
  48. package/dist/effects.svelte.d.ts +12 -0
  49. package/dist/effects.svelte.js +37 -0
  50. package/dist/element/BohrAtom.svelte +4 -4
  51. package/dist/element/data.json.gz.d.ts +3 -1
  52. package/dist/element/index.d.ts +1 -1
  53. package/dist/element/index.js +0 -1
  54. package/dist/fermi-surface/FermiSurface.svelte +4 -4
  55. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  56. package/dist/fermi-surface/FermiSurfaceControls.svelte +15 -19
  57. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
  58. package/dist/fermi-surface/FermiSurfaceScene.svelte +8 -6
  59. package/dist/fermi-surface/compute.js +2 -2
  60. package/dist/fermi-surface/export.js +13 -26
  61. package/dist/fermi-surface/parse.js +8 -12
  62. package/dist/fermi-surface/types.d.ts +2 -5
  63. package/dist/heatmap-matrix/HeatmapMatrix.svelte +21 -3
  64. package/dist/heatmap-matrix/index.js +6 -6
  65. package/dist/io/decompress.d.ts +2 -1
  66. package/dist/io/decompress.js +1 -1
  67. package/dist/io/export.js +1 -1
  68. package/dist/io/index.d.ts +1 -1
  69. package/dist/io/index.js +0 -1
  70. package/dist/io/url-drop.js +7 -1
  71. package/dist/isosurface/IsosurfaceControls.svelte +11 -25
  72. package/dist/isosurface/slice.js +1 -1
  73. package/dist/isosurface/types.js +12 -12
  74. package/dist/labels.d.ts +1 -1
  75. package/dist/labels.js +14 -11
  76. package/dist/layout/InfoTag.svelte +6 -4
  77. package/dist/layout/PropertyFilter.svelte +4 -2
  78. package/dist/layout/json-tree/JsonTree.svelte +22 -14
  79. package/dist/layout/json-tree/JsonValue.svelte +2 -2
  80. package/dist/layout/json-tree/types.d.ts +3 -2
  81. package/dist/layout/json-tree/types.js +0 -1
  82. package/dist/layout/json-tree/utils.d.ts +4 -4
  83. package/dist/layout/json-tree/utils.js +12 -20
  84. package/dist/marching-cubes.js +13 -15
  85. package/dist/math.d.ts +11 -1
  86. package/dist/math.js +15 -6
  87. package/dist/overlays/DragControlTab.svelte +98 -0
  88. package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
  89. package/dist/overlays/DraggablePane.svelte +7 -84
  90. package/dist/overlays/index.d.ts +1 -0
  91. package/dist/overlays/index.js +1 -0
  92. package/dist/periodic-table/PeriodicTable.svelte +11 -11
  93. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +4 -2
  94. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
  95. package/dist/phase-diagram/PhaseDiagramControls.svelte +4 -9
  96. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
  97. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +2 -10
  98. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +2 -3
  99. package/dist/phase-diagram/TdbInfoPanel.svelte +3 -3
  100. package/dist/phase-diagram/build-diagram.js +11 -18
  101. package/dist/phase-diagram/diagram-input.d.ts +5 -9
  102. package/dist/phase-diagram/index.d.ts +2 -2
  103. package/dist/phase-diagram/index.js +0 -2
  104. package/dist/phase-diagram/parse.d.ts +2 -2
  105. package/dist/phase-diagram/parse.js +6 -10
  106. package/dist/phase-diagram/svg-to-diagram.js +15 -15
  107. package/dist/phase-diagram/types.d.ts +5 -11
  108. package/dist/phase-diagram/utils.d.ts +2 -2
  109. package/dist/phase-diagram/utils.js +9 -11
  110. package/dist/plot/BarPlot.svelte +162 -314
  111. package/dist/plot/BarPlot.svelte.d.ts +5 -4
  112. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  113. package/dist/plot/BinnedScatterPlot.svelte +1114 -0
  114. package/dist/plot/BinnedScatterPlot.svelte.d.ts +66 -0
  115. package/dist/plot/ColorBar.svelte +19 -17
  116. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  117. package/dist/plot/FillArea.svelte +2 -4
  118. package/dist/plot/FillArea.svelte.d.ts +1 -1
  119. package/dist/plot/Histogram.svelte +167 -281
  120. package/dist/plot/Histogram.svelte.d.ts +1 -1
  121. package/dist/plot/HistogramControls.svelte.d.ts +1 -1
  122. package/dist/plot/InteractiveAxisLabel.svelte +5 -3
  123. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  124. package/dist/plot/PlotAxis.svelte +169 -0
  125. package/dist/plot/PlotAxis.svelte.d.ts +24 -0
  126. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  127. package/dist/plot/ReferenceLine3D.svelte +53 -51
  128. package/dist/plot/ReferencePlane.svelte +39 -42
  129. package/dist/plot/ScatterPlot.svelte +300 -367
  130. package/dist/plot/ScatterPlot.svelte.d.ts +8 -5
  131. package/dist/plot/ScatterPlot3D.svelte +33 -6
  132. package/dist/plot/ScatterPlot3D.svelte.d.ts +3 -2
  133. package/dist/plot/ScatterPlot3DControls.svelte +9 -9
  134. package/dist/plot/ScatterPlotControls.svelte +3 -4
  135. package/dist/plot/ScatterPoint.svelte +18 -27
  136. package/dist/plot/ScatterPoint.svelte.d.ts +4 -3
  137. package/dist/plot/Surface3D.svelte +4 -7
  138. package/dist/plot/ZeroLines.svelte +2 -1
  139. package/dist/plot/ZeroLines.svelte.d.ts +2 -1
  140. package/dist/plot/ZoomRect.svelte +2 -2
  141. package/dist/plot/ZoomRect.svelte.d.ts +3 -3
  142. package/dist/plot/adaptive-density.d.ts +69 -0
  143. package/dist/plot/adaptive-density.js +191 -0
  144. package/dist/plot/auto-place.d.ts +43 -0
  145. package/dist/plot/auto-place.js +122 -0
  146. package/dist/plot/axis-utils.js +3 -5
  147. package/dist/plot/binned-scatter-types.d.ts +59 -0
  148. package/dist/plot/binned-scatter-types.js +1 -0
  149. package/dist/plot/data-cleaning.js +1 -1
  150. package/dist/plot/data-transform.js +1 -1
  151. package/dist/plot/fill-utils.d.ts +4 -9
  152. package/dist/plot/fill-utils.js +29 -44
  153. package/dist/plot/index.d.ts +4 -0
  154. package/dist/plot/index.js +2 -0
  155. package/dist/plot/interactions.d.ts +4 -4
  156. package/dist/plot/interactions.js +4 -3
  157. package/dist/plot/layout.d.ts +20 -2
  158. package/dist/plot/layout.js +59 -16
  159. package/dist/plot/reference-line.d.ts +1 -1
  160. package/dist/plot/reference-line.js +9 -11
  161. package/dist/plot/scales.d.ts +1 -1
  162. package/dist/plot/scales.js +20 -23
  163. package/dist/plot/types.d.ts +30 -58
  164. package/dist/plot/types.js +2 -6
  165. package/dist/plot/utils/label-placement.d.ts +24 -3
  166. package/dist/plot/utils/label-placement.js +82 -12
  167. package/dist/plot/utils/series-visibility.d.ts +8 -2
  168. package/dist/plot/utils/series-visibility.js +23 -5
  169. package/dist/rdf/RdfPlot.svelte +5 -5
  170. package/dist/rdf/calc-rdf.js +3 -3
  171. package/dist/sanitize.d.ts +2 -0
  172. package/dist/sanitize.js +2 -0
  173. package/dist/spectral/Bands.svelte +1 -1
  174. package/dist/spectral/BandsAndDos.svelte +22 -16
  175. package/dist/spectral/BrillouinBandsDos.svelte +20 -16
  176. package/dist/spectral/Dos.svelte +1 -1
  177. package/dist/spectral/helpers.d.ts +4 -2
  178. package/dist/spectral/helpers.js +44 -35
  179. package/dist/spectral/index.d.ts +1 -1
  180. package/dist/spectral/index.js +0 -1
  181. package/dist/structure/AtomLegend.svelte +23 -6
  182. package/dist/structure/AtomLegend.svelte.d.ts +1 -0
  183. package/dist/structure/CanvasTooltip.svelte +9 -9
  184. package/dist/structure/CanvasTooltip.svelte.d.ts +1 -1
  185. package/dist/structure/CellSelect.svelte +14 -16
  186. package/dist/structure/Structure.svelte +317 -68
  187. package/dist/structure/Structure.svelte.d.ts +4 -2
  188. package/dist/structure/StructureControls.svelte +20 -45
  189. package/dist/structure/StructureExportPane.svelte +2 -1
  190. package/dist/structure/StructureInfoPane.svelte +10 -8
  191. package/dist/structure/StructureScene.svelte +527 -177
  192. package/dist/structure/StructureScene.svelte.d.ts +5 -2
  193. package/dist/structure/atom-properties.js +4 -4
  194. package/dist/structure/bond-order-perception.js +115 -98
  195. package/dist/structure/bonding.d.ts +27 -1
  196. package/dist/structure/bonding.js +187 -16
  197. package/dist/structure/export.js +1 -1
  198. package/dist/structure/index.d.ts +3 -2
  199. package/dist/structure/index.js +0 -2
  200. package/dist/structure/parse.js +88 -59
  201. package/dist/symmetry/WyckoffTable.svelte +7 -0
  202. package/dist/symmetry/index.js +13 -14
  203. package/dist/table/HeatmapTable.svelte +45 -66
  204. package/dist/table/HeatmapTable.svelte.d.ts +1 -1
  205. package/dist/table/ToggleMenu.svelte +19 -10
  206. package/dist/theme/themes.mjs +12 -0
  207. package/dist/tooltip/index.d.ts +1 -1
  208. package/dist/tooltip/index.js +0 -1
  209. package/dist/trajectory/Trajectory.svelte +43 -15
  210. package/dist/trajectory/TrajectoryInfoPane.svelte +2 -2
  211. package/dist/trajectory/extract.js +1 -1
  212. package/dist/trajectory/frame-reader.js +4 -4
  213. package/dist/trajectory/helpers.d.ts +5 -4
  214. package/dist/trajectory/helpers.js +9 -17
  215. package/dist/trajectory/index.d.ts +2 -2
  216. package/dist/trajectory/index.js +2 -2
  217. package/dist/trajectory/parse/ase.js +4 -4
  218. package/dist/trajectory/parse/hdf5.js +1 -1
  219. package/dist/trajectory/parse/index.js +2 -3
  220. package/dist/trajectory/parse/lammps.js +1 -1
  221. package/dist/trajectory/parse/vasp.js +1 -1
  222. package/dist/trajectory/plotting.d.ts +1 -1
  223. package/dist/trajectory/plotting.js +38 -38
  224. package/dist/trajectory/types.d.ts +1 -1
  225. package/dist/utils.d.ts +1 -0
  226. package/dist/utils.js +9 -0
  227. package/dist/xrd/calc-xrd.js +3 -4
  228. package/dist/xrd/parse.js +1 -1
  229. package/package.json +42 -22
@@ -7,7 +7,7 @@
7
7
 
8
8
  // Format: compact "001" for single-digit, spaced "10 0 1" for multi-digit
9
9
  let hkl_text = $derived(
10
- value.every((v) => Math.abs(v) < 10) ? value.join(``) : value.join(` `),
10
+ value.every((component) => Math.abs(component) < 10) ? value.join(``) : value.join(` `),
11
11
  )
12
12
 
13
13
  // Parse hkl string: supports compact "001"/"-101" and spaced/comma "10, 0, 1"
@@ -16,7 +16,7 @@
16
16
  const spaced = input.trim().split(/[,\s]+/)
17
17
  if (spaced.length === 3) {
18
18
  const nums = spaced.map(Number)
19
- if (nums.every((n) => !isNaN(n))) return nums as Vec3
19
+ if (nums.every((num) => !isNaN(num))) return nums as Vec3
20
20
  }
21
21
  // Fall back to compact single-digit format: "001", "-101"
22
22
  const compact = input.replace(/\s+/g, ``)
@@ -25,8 +25,8 @@
25
25
  return null
26
26
  }
27
27
 
28
- function handle_input(event: Event) {
29
- const parsed = parse_hkl((event.target as HTMLInputElement).value)
28
+ function oninput(event: Event & { currentTarget: HTMLInputElement }) {
29
+ const parsed = parse_hkl(event.currentTarget.value)
30
30
  if (parsed) value = parsed
31
31
  }
32
32
  </script>
@@ -36,7 +36,7 @@
36
36
  <input
37
37
  type="text"
38
38
  value={hkl_text}
39
- oninput={handle_input}
39
+ {oninput}
40
40
  placeholder="001"
41
41
  maxlength="12"
42
42
  title="Miller indices (e.g. 001, -101, or 10 0 1)"
@@ -102,10 +102,10 @@ export const decode_structure_id = (encoded_id) => decodeURIComponent(encoded_id
102
102
  export function detect_provider_from_slug(slug, providers) {
103
103
  const decoded_slug = decode_structure_id(slug);
104
104
  const prefix = decoded_slug.split(`-`)[0].toLowerCase();
105
- return providers.find((p) => p.id === prefix)?.id ?? ``;
105
+ return providers.find((provider) => provider.id === prefix)?.id ?? ``;
106
106
  }
107
107
  export async function fetch_optimade_structure(structure_id, provider, providers) {
108
- const provider_config = providers.find((p) => p.id === provider);
108
+ const provider_config = providers.find((entry) => entry.id === provider);
109
109
  if (!provider_config)
110
110
  throw new Error(`Unknown provider: ${provider}`);
111
111
  const base_url = await resolve_provider_url(provider_config.attributes.base_url);
@@ -118,7 +118,7 @@ export async function fetch_optimade_structure(structure_id, provider, providers
118
118
  return Array.isArray(data.data) ? data.data[0] : data.data;
119
119
  }
120
120
  export async function fetch_suggested_structures(provider, providers, limit = 12) {
121
- const provider_config = providers.find((p) => p.id === provider);
121
+ const provider_config = providers.find((entry) => entry.id === provider);
122
122
  if (!provider_config)
123
123
  throw new Error(`Unknown provider: ${provider}`);
124
124
  try {
@@ -293,8 +293,11 @@
293
293
  })
294
294
 
295
295
  function onkeydown(event: KeyboardEvent) {
296
- const target = event.target as HTMLElement
297
- if (target.tagName === `INPUT` || target.tagName === `TEXTAREA`) return
296
+ const target = event.target
297
+ if (
298
+ target instanceof HTMLElement &&
299
+ (target.tagName === `INPUT` || target.tagName === `TEXTAREA`)
300
+ ) return
298
301
 
299
302
  if (event.key === `f` && fullscreen_toggle) toggle_fullscreen(wrapper)
300
303
  else if (event.key === `i`) info_pane_open = !info_pane_open
@@ -78,6 +78,6 @@ type $$ComponentProps = {
78
78
  on_fullscreen_change?: (data: BZHandlerData) => void;
79
79
  on_hover?: (data: BZHoverData | null) => void;
80
80
  } & HTMLAttributes<HTMLDivElement>;
81
- declare const BrillouinZone: import("svelte").Component<$$ComponentProps, {}, "structure" | "height" | "width" | "dragover" | "fullscreen" | "hovered" | "controls_open" | "loading" | "camera_projection" | "vector_scale" | "info_pane_open" | "wrapper" | "png_dpi" | "error_msg" | "bz_order" | "surface_color" | "surface_opacity" | "edge_color" | "edge_width" | "show_vectors" | "show_ibz" | "ibz_color" | "ibz_opacity" | "bz_data" | "ibz_data">;
81
+ declare const BrillouinZone: import("svelte").Component<$$ComponentProps, {}, "structure" | "height" | "width" | "dragover" | "loading" | "fullscreen" | "hovered" | "controls_open" | "camera_projection" | "vector_scale" | "info_pane_open" | "wrapper" | "png_dpi" | "error_msg" | "bz_order" | "surface_color" | "surface_opacity" | "edge_color" | "edge_width" | "show_vectors" | "show_ibz" | "ibz_color" | "ibz_opacity" | "bz_data" | "ibz_data">;
82
82
  type BrillouinZone = ReturnType<typeof BrillouinZone>;
83
83
  export default BrillouinZone;
@@ -102,9 +102,7 @@
102
102
  </label>
103
103
 
104
104
  <h4 id="export-as-data"
105
- {@attach tooltip({
106
- content: `Includes vertices, faces, edges, and reciprocal lattice vectors`,
107
- })}
105
+ {@attach tooltip({ content: `Includes vertices, faces, edges, and reciprocal lattice vectors` })}
108
106
  >
109
107
  Export as data
110
108
  </h4>
@@ -94,7 +94,7 @@
94
94
  // Reciprocal Lattice section
95
95
  const k_lattice_items: InfoItem[] = bz_data.k_lattice.map((vec, idx) => ({
96
96
  label: [`b₁`, `b₂`, `b₃`][idx],
97
- value: `(${vec.map((x) => format_num(x, `.3~f`)).join(`, `)})`,
97
+ value: `(${vec.map((coord) => format_num(coord, `.3~f`)).join(`, `)})`,
98
98
  key: `reciprocal-${[`b1`, `b2`, `b3`][idx]}`,
99
99
  }))
100
100
 
@@ -118,7 +118,7 @@
118
118
  })
119
119
 
120
120
  const computed_camera_position = $derived.by(() =>
121
- camera_position || ([10, 3, 8].map((x) => x * Math.max(1, bz_size)) as Vec3)
121
+ camera_position || ([10, 3, 8].map((coord) => coord * Math.max(1, bz_size)) as Vec3)
122
122
  )
123
123
 
124
124
  const gizmo_props = $derived({
@@ -189,7 +189,7 @@
189
189
  const e2: Vec3 = math.subtract(v2, v0)
190
190
  const normal_vec = math.cross_3d(e1, e2)
191
191
  const len = Math.hypot(...normal_vec)
192
- const norm = len > 1e-10 ? normal_vec.map((x) => x / len) : [0, 0, 0]
192
+ const norm = len > 1e-10 ? normal_vec.map((coord) => coord / len) : [0, 0, 0]
193
193
  normals.push(...norm, ...norm, ...norm)
194
194
  }
195
195
  }
@@ -388,8 +388,8 @@
388
388
  <!-- Reciprocal lattice vectors -->
389
389
  {#if show_vectors && bz_data.k_lattice}
390
390
  {#each bz_data.k_lattice as vec, idx (idx)}
391
- {@const scaled_vec = vec.map((x) => x * vector_scale) as Vec3}
392
- {@const label_position = scaled_vec.map((x) => x * 1.15) as Vec3}
391
+ {@const scaled_vec = vec.map((coord) => coord * vector_scale) as Vec3}
392
+ {@const label_position = scaled_vec.map((coord) => coord * 1.15) as Vec3}
393
393
  <Arrow
394
394
  position={[0, 0, 0]}
395
395
  vector={scaled_vec}
@@ -443,7 +443,7 @@
443
443
  {#if k_path_labels}
444
444
  {#each k_path_labels as { position, label }, idx (`${label}-${idx}`)}
445
445
  {#if label}
446
- <extras.HTML center position={position.map((x) => x * 1.1) as Vec3}>
446
+ <extras.HTML center position={position.map((coord) => coord * 1.1) as Vec3}>
447
447
  <span
448
448
  style="background: rgba(0, 0, 0, 0.3); padding: 0 3px; border-radius: 2px; color: white"
449
449
  >
@@ -8,9 +8,7 @@ const normalize = (vec) => {
8
8
  return mag < 1e-10 ? [0, 0, 0] : [vec[0] / mag, vec[1] / mag, vec[2] / mag];
9
9
  };
10
10
  // Check if rotation matrix is identity
11
- function is_identity_rotation(rot) {
12
- return rot.every((row, idx) => row.every((val, jdx) => Math.abs(val - (idx === jdx ? 1 : 0)) < TOL));
13
- }
11
+ const is_identity_rotation = (rot) => rot.every((row, idx) => row.every((val, jdx) => Math.abs(val - (idx === jdx ? 1 : 0)) < TOL));
14
12
  // Extract unique point group rotation matrices from space group operations.
15
13
  // Returns fractional-coordinate rotations (W matrices from spglib convention).
16
14
  // These must be converted to Cartesian k-space before use in clipping.
@@ -149,12 +147,12 @@ max_planes_by_order = { 1: 26, 2: 80, 3: 150 }) {
149
147
  const dist_sq = pt[0] ** 2 + pt[1] ** 2 + pt[2] ** 2;
150
148
  return { normal: normalize(pt), dist: Math.sqrt(dist_sq) / 2, dist_sq };
151
149
  })
152
- .filter((p) => p !== null)
150
+ .filter((plane) => plane !== null)
153
151
  .sort((a, b) => a.dist_sq - b.dist_sq)
154
152
  .slice(0, max_planes);
155
153
  // Pre-compute plane data for fast access
156
- const normals = planes.map((p) => p.normal);
157
- const distances = planes.map((p) => p.dist);
154
+ const normals = planes.map((plane) => plane.normal);
155
+ const distances = planes.map((plane) => plane.dist);
158
156
  const dedup = new VertexDeduplicator(TOL * 10);
159
157
  const vertices = [];
160
158
  // Test all three-plane intersections
@@ -203,7 +201,7 @@ export function compute_convex_hull(vertices, edge_sharp_angle_deg = 5) {
203
201
  if (vertices.length < 4) {
204
202
  throw new Error(`Need ≥4 vertices for convex hull, got ${vertices.length}`);
205
203
  }
206
- const geometry = new ConvexGeometry(vertices.map((cert) => new Vector3(...cert)));
204
+ const geometry = new ConvexGeometry(vertices.map((vertex) => new Vector3(...vertex)));
207
205
  const pos = geometry.getAttribute(`position`);
208
206
  const geometry_index = geometry.index;
209
207
  // Deduplicate vertices from Three.js geometry
@@ -211,9 +209,9 @@ export function compute_convex_hull(vertices, edge_sharp_angle_deg = 5) {
211
209
  const vert_map = new Map();
212
210
  for (let idx_vertex = 0; idx_vertex < pos.count; idx_vertex++) {
213
211
  const vert = [pos.getX(idx_vertex), pos.getY(idx_vertex), pos.getZ(idx_vertex)];
214
- const existing_idx = unique_verts.findIndex((u) => Math.abs(u[0] - vert[0]) < TOL &&
215
- Math.abs(u[1] - vert[1]) < TOL &&
216
- Math.abs(u[2] - vert[2]) < TOL);
212
+ const existing_idx = unique_verts.findIndex((unique_vert) => Math.abs(unique_vert[0] - vert[0]) < TOL &&
213
+ Math.abs(unique_vert[1] - vert[1]) < TOL &&
214
+ Math.abs(unique_vert[2] - vert[2]) < TOL);
217
215
  vert_map.set(idx_vertex, existing_idx === -1 ? unique_verts.push(vert) - 1 : existing_idx);
218
216
  }
219
217
  // Build faces with deduplicated vertex indices
@@ -227,23 +225,25 @@ export function compute_convex_hull(vertices, edge_sharp_angle_deg = 5) {
227
225
  geometry_index.getX(idx_face * 3 + 2),
228
226
  ]
229
227
  : [idx_face * 3, idx_face * 3 + 1, idx_face * 3 + 2];
230
- faces.push(tri.map((j) => {
231
- const mapped = vert_map.get(j);
228
+ faces.push(tri.map((vertex_idx) => {
229
+ const mapped = vert_map.get(vertex_idx);
232
230
  if (mapped === undefined)
233
- throw new Error(`Vertex ${j} not mapped`);
231
+ throw new Error(`Vertex ${vertex_idx} not mapped`);
234
232
  return mapped;
235
233
  }));
236
234
  }
237
235
  // Compute face normals and build edge-to-face adjacency
238
236
  const face_normals = faces.map((face) => {
239
- const [v0, v1, v2] = face.slice(0, 3).map((vi) => unique_verts[vi]);
237
+ const [v0, v1, v2] = face.slice(0, 3).map((vertex_idx) => unique_verts[vertex_idx]);
240
238
  return normalize(math.cross_3d(math.subtract(v1, v0), math.subtract(v2, v0)));
241
239
  });
242
240
  const edge_to_faces = new Map();
243
241
  faces.forEach((face, face_idx) => {
244
- face.forEach((v1, idx) => {
245
- const v2 = face[(idx + 1) % face.length];
246
- const key = v1 < v2 ? `${v1},${v2}` : `${v2},${v1}`;
242
+ face.forEach((from_vertex_idx, idx) => {
243
+ const to_vertex_idx = face[(idx + 1) % face.length];
244
+ const key = from_vertex_idx < to_vertex_idx
245
+ ? `${from_vertex_idx},${to_vertex_idx}`
246
+ : `${to_vertex_idx},${from_vertex_idx}`;
247
247
  const adj = edge_to_faces.get(key);
248
248
  if (adj)
249
249
  adj.push(face_idx);
@@ -322,14 +322,14 @@ export function compute_ibz_clipping_planes(point_group_ops) {
322
322
  return planes;
323
323
  }
324
324
  // Flip a clipping plane to the opposite half-space
325
- const flip_plane = (p) => ({
326
- normal: math.scale(p.normal, -1),
327
- dist: -p.dist,
325
+ const flip_plane = (plane) => ({
326
+ normal: math.scale(plane.normal, -1),
327
+ dist: -plane.dist,
328
328
  });
329
329
  // Clip polyhedron vertices by a half-space, adding intersection points where edges cross
330
330
  function clip_polyhedron_by_plane(vertices, faces, plane) {
331
331
  const { normal, dist } = plane;
332
- const signed_dists = vertices.map((v) => math.dot(v, normal) - dist);
332
+ const signed_dists = vertices.map((vertex) => math.dot(vertex, normal) - dist);
333
333
  // Keep vertices inside the half-space
334
334
  const result = vertices.filter((_, idx) => signed_dists[idx] <= TOL);
335
335
  // Build edge set from faces
@@ -5,4 +5,4 @@ export { default as BrillouinZoneInfoPane } from './BrillouinZoneInfoPane.svelte
5
5
  export { default as BrillouinZoneScene } from './BrillouinZoneScene.svelte';
6
6
  export { default as BrillouinZoneTooltip } from './BrillouinZoneTooltip.svelte';
7
7
  export * from './compute';
8
- export * from './types';
8
+ export type * from './types';
@@ -5,4 +5,3 @@ export { default as BrillouinZoneInfoPane } from './BrillouinZoneInfoPane.svelte
5
5
  export { default as BrillouinZoneScene } from './BrillouinZoneScene.svelte';
6
6
  export { default as BrillouinZoneTooltip } from './BrillouinZoneTooltip.svelte';
7
7
  export * from './compute';
8
- export * from './types';
@@ -1,13 +1,10 @@
1
- import type { Matrix3x3, Vec3 } from '../math';
1
+ import type { Matrix3x3, Point2D, Vec3 } from '../math';
2
2
  import type { Crystal } from '../structure';
3
3
  import type { TooltipConfig, TooltipProp } from '../tooltip';
4
4
  export type BZHoverData = {
5
5
  position_cartesian: Vec3;
6
6
  position_fractional: Vec3 | null;
7
- screen_position: {
8
- x: number;
9
- y: number;
10
- };
7
+ screen_position: Point2D;
11
8
  is_ibz: boolean;
12
9
  bz_order: number;
13
10
  bz_volume: number;
@@ -18,17 +15,16 @@ export type BZTooltipConfig = TooltipConfig<BZHoverData>;
18
15
  export type BZTooltipProp = TooltipProp<BZHoverData, [{
19
16
  hover_data: BZHoverData;
20
17
  }]>;
21
- export type IrreducibleBZData = {
18
+ type BZMeshData = {
22
19
  vertices: Vec3[];
23
20
  faces: number[][];
24
21
  edges: Vec3[][];
22
+ };
23
+ export type IrreducibleBZData = BZMeshData & {
25
24
  volume: number;
26
25
  };
27
- export type BrillouinZoneData = {
26
+ export type BrillouinZoneData = BZMeshData & {
28
27
  order: number;
29
- vertices: Vec3[];
30
- faces: number[][];
31
- edges: Vec3[][];
32
28
  k_lattice: Matrix3x3;
33
29
  volume: number;
34
30
  };
@@ -41,8 +37,7 @@ export type BrillouinZoneProps = {
41
37
  edge_width?: number;
42
38
  show_vectors?: boolean;
43
39
  };
44
- export type ConvexHullData = {
45
- vertices: Vec3[];
46
- faces: number[][];
40
+ export type ConvexHullData = Pick<BZMeshData, `vertices` | `faces`> & {
47
41
  edges: [number, number][];
48
42
  };
43
+ export {};
@@ -233,9 +233,9 @@ const is_hover_info_3d = (value: ChemPotHoverInfo | null): value is ChemPotHover
233
233
  <h5 id="neighbors">Neighbors ({hover_info.neighbors.length})</h5>
234
234
  <p>
235
235
  {
236
- hover_info.neighbors.map((f) => get_electro_neg_formula(f, true, ``, `.3~s`)).join(
237
- `, `,
238
- )
236
+ hover_info.neighbors.map((formula) =>
237
+ get_electro_neg_formula(formula, true, ``, `.3~s`)
238
+ ).join(`, `)
239
239
  }
240
240
  </p>
241
241
  {/if}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { type D3InterpolateName } from '../colors'
2
+ import type { D3InterpolateName } from '../colors'
3
3
  import { get_electro_neg_formula } from '../composition/format'
4
4
  import { extract_formula_elements } from '../composition/parse'
5
5
  import TemperatureSlider from '../convex-hull/TemperatureSlider.svelte'
@@ -433,9 +433,8 @@
433
433
  let copy_status = $state(false)
434
434
  let copy_timeout_id: ReturnType<typeof setTimeout> | null = null
435
435
 
436
- function get_svg_element(): SVGSVGElement | null {
437
- return scatter_wrapper?.querySelector<SVGSVGElement>(`svg`) ?? null
438
- }
436
+ const get_svg_element = (): SVGSVGElement | null =>
437
+ scatter_wrapper?.querySelector<SVGSVGElement>(`svg`) ?? null
439
438
 
440
439
  function get_json_string(): string {
441
440
  return JSON.stringify(
@@ -1,10 +1,7 @@
1
1
  <script lang="ts">
2
- import { type D3InterpolateName } from '../colors'
3
- import {
4
- get_electro_neg_formula,
5
- get_formula_label_segments,
6
- type FormulaLabelSegment,
7
- } from '../composition/format'
2
+ import type { D3InterpolateName } from '../colors'
3
+ import { get_electro_neg_formula, get_formula_label_segments } from '../composition/format'
4
+ import type { FormulaLabelSegment } from '../composition/format'
8
5
  import { extract_formula_elements } from '../composition/parse'
9
6
  import TemperatureSlider from '../convex-hull/TemperatureSlider.svelte'
10
7
  import type { PhaseData } from '../convex-hull/types'
@@ -74,6 +71,9 @@
74
71
  } from './types'
75
72
  import { CHEMPOT_DEFAULTS } from './types'
76
73
 
74
+ const edge_key = (key_a: string, key_b: string): string =>
75
+ key_a < key_b ? `${key_a}|${key_b}` : `${key_b}|${key_a}`
76
+
77
77
  let {
78
78
  entries = [],
79
79
  config = {},
@@ -153,9 +153,8 @@
153
153
  : CHEMPOT_DEFAULTS.formula_colors,
154
154
  )
155
155
 
156
- function formula_label_segments(formula: string): FormulaLabelSegment[] {
157
- return get_formula_label_segments(get_electro_neg_formula(formula, true, ``, `.3~s`))
158
- }
156
+ const formula_label_segments = (formula: string): FormulaLabelSegment[] =>
157
+ get_formula_label_segments(get_electro_neg_formula(formula, true, ``, `.3~s`))
159
158
 
160
159
  function normalize_projection_triplet(
161
160
  maybe_triplet: string[] | undefined,
@@ -421,7 +420,11 @@
421
420
  label_font_size: bbox_diagonal(padded),
422
421
  })
423
422
  }
424
- const fonts = scale_to_font_range(result.map((d) => d.label_font_size), 9, 15)
423
+ const fonts = scale_to_font_range(
424
+ result.map((render_domain) => render_domain.label_font_size),
425
+ 9,
426
+ 15,
427
+ )
425
428
  for (let idx = 0; idx < result.length; idx++) result[idx].label_font_size = fonts[idx]
426
429
  return result
427
430
  })
@@ -792,8 +795,8 @@
792
795
  // Compute edges in swizzled (Three.js) coords since ConvexGeometry works there
793
796
  const swizzled = domain.points_3d.map((point) => to_render_xyz(point))
794
797
  for (const [pa, pb] of get_domain_edges(swizzled)) {
795
- const ka = pa.map((v) => v.toFixed(4)).join(`,`)
796
- const kb = pb.map((v) => v.toFixed(4)).join(`,`)
798
+ const ka = pa.map((coord) => coord.toFixed(4)).join(`,`)
799
+ const kb = pb.map((coord) => coord.toFixed(4)).join(`,`)
797
800
  const key = ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`
798
801
  if (seen.has(key)) continue
799
802
  seen.add(key)
@@ -893,17 +896,17 @@
893
896
 
894
897
  // Domain vertex centroids in render coords (swizzled + axis stretch), matching hull_base_geometry.
895
898
  const centroids = render_domains
896
- .filter((d) => !d.is_draw_formula && d.points_3d.length > 0)
897
- .map((d) => {
899
+ .filter((domain) => !domain.is_draw_formula && domain.points_3d.length > 0)
900
+ .map((domain) => {
898
901
  let sx = 0, sy = 0, sz = 0
899
- for (const pt of d.points_3d) {
902
+ for (const pt of domain.points_3d) {
900
903
  const [x_val, y_val, z_val] = to_render_xyz(pt)
901
904
  sx += x_val
902
905
  sy += y_val
903
906
  sz += z_val
904
907
  }
905
- const n = d.points_3d.length
906
- return { formula: d.formula, cx: sx / n, cy: sy / n, cz: sz / n }
908
+ const n_points = domain.points_3d.length
909
+ return { formula: domain.formula, cx: sx / n_points, cy: sy / n_points, cz: sz / n_points }
907
910
  })
908
911
 
909
912
  // Assign each face to the nearest domain centroid
@@ -936,8 +939,6 @@
936
939
  `${round(pos.getX(vert_idx))},${round(pos.getY(vert_idx))},${
937
940
  round(pos.getZ(vert_idx))
938
941
  }`
939
- const ekey = (ka: string, kb: string): string =>
940
- ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`
941
942
  // Compute face normals
942
943
  const normals: Vec3[] = []
943
944
  for (let face_idx = 0; face_idx < n_faces; face_idx++) {
@@ -961,9 +962,9 @@
961
962
  const keys = [vkey(base), vkey(base + 1), vkey(base + 2)]
962
963
  for (
963
964
  const ek of [
964
- ekey(keys[0], keys[1]),
965
- ekey(keys[1], keys[2]),
966
- ekey(keys[0], keys[2]),
965
+ edge_key(keys[0], keys[1]),
966
+ edge_key(keys[1], keys[2]),
967
+ edge_key(keys[0], keys[2]),
967
968
  ]
968
969
  ) {
969
970
  const list = edge_faces.get(ek)
@@ -1132,9 +1133,8 @@
1132
1133
  })
1133
1134
 
1134
1135
  // Deduplicate 3D points within tolerance (reuses compute.ts dedup_points)
1135
- function dedup_3d(pts: number[][], tol: number = 1e-4): number[][] {
1136
- return dedup_points(pts, tol).unique
1137
- }
1136
+ const dedup_3d = (pts: number[][], tol: number = 1e-4): number[][] =>
1137
+ dedup_points(pts, tol).unique
1138
1138
 
1139
1139
  const controls_series = $derived<DataSeries3D[]>([
1140
1140
  {
@@ -1243,7 +1243,7 @@
1243
1243
  if (unique_points.length === 3) {
1244
1244
  const geom = new THREE.BufferGeometry()
1245
1245
  const vectors = unique_points.map((pt) => to_vec3(pt))
1246
- const verts = new Float32Array(vectors.flatMap((v) => [v.x, v.y, v.z]))
1246
+ const verts = new Float32Array(vectors.flatMap((vec) => [vec.x, vec.y, vec.z]))
1247
1247
  geom.setAttribute(`position`, new THREE.Float32BufferAttribute(verts, 3))
1248
1248
  geom.setIndex([0, 1, 2, 2, 1, 0]) // both winding orders for double-sided pick
1249
1249
  geom.computeVertexNormals()
@@ -1387,7 +1387,7 @@
1387
1387
 
1388
1388
  // Bounding box of all data points in DATA coordinates (before swizzle)
1389
1389
  const raw_data_bbox = $derived.by(() => {
1390
- const pts = render_domains.flatMap((d) => d.points_3d)
1390
+ const pts = render_domains.flatMap((domain) => domain.points_3d)
1391
1391
  if (pts.length === 0) return { mins: [0, 0, 0], maxs: [1, 1, 1] }
1392
1392
  const mins = [Infinity, Infinity, Infinity]
1393
1393
  const maxs = [-Infinity, -Infinity, -Infinity]
@@ -1486,11 +1486,10 @@
1486
1486
  // Place axis label just past the outer end of the axis (the end closer to 0).
1487
1487
  // In isometric 3D, the end near 0 projects outward at the front edge of the
1488
1488
  // bounding box, while the negative end projects inward toward the center.
1489
- function outer_end(range: [number, number]): number {
1490
- return Math.abs(range[0]) <= Math.abs(range[1]) ? range[0] : range[1]
1491
- }
1489
+ const outer_end = (range: [number, number]): number =>
1490
+ Math.abs(range[0]) <= Math.abs(range[1]) ? range[0] : range[1]
1492
1491
  // Direction from range center toward outer end (to extend the label beyond the grid)
1493
- function outer_dir(range: [number, number]): number {
1492
+ const outer_direction = (range: [number, number]): number => {
1494
1493
  const end = outer_end(range)
1495
1494
  const mid = (range[0] + range[1]) / 2
1496
1495
  return end >= mid ? 1 : -1
@@ -1524,7 +1523,7 @@
1524
1523
  line_geom = make_line_geom(ls, le)
1525
1524
  // Axis label past the outer end of the axis (near 0, projects outward)
1526
1525
  label_pos = swiz(
1527
- outer_end(r0) + outer_dir(r0) * axis_label_dist,
1526
+ outer_end(r0) + outer_direction(r0) * axis_label_dist,
1528
1527
  back[1] + out_x * tick_label_dist * 0.5,
1529
1528
  back[2] + out_y * tick_label_dist,
1530
1529
  )
@@ -1555,7 +1554,7 @@
1555
1554
  line_geom = make_line_geom(ls, le)
1556
1555
  label_pos = swiz(
1557
1556
  back[0],
1558
- outer_end(r1) + outer_dir(r1) * axis_label_dist,
1557
+ outer_end(r1) + outer_direction(r1) * axis_label_dist,
1559
1558
  back[2] + out_y * tick_label_dist,
1560
1559
  )
1561
1560
  for (const val of ticks) {
@@ -1582,7 +1581,7 @@
1582
1581
  label_pos = swiz(
1583
1582
  back[0],
1584
1583
  back[1] + out_x * tick_label_dist,
1585
- outer_end(r2) + outer_dir(r2) * axis_label_dist,
1584
+ outer_end(r2) + outer_direction(r2) * axis_label_dist,
1586
1585
  )
1587
1586
  for (const val of ticks) {
1588
1587
  tick_geoms.push(make_line_geom(
@@ -12,7 +12,6 @@ const get_reduced_formula = (composition) => {
12
12
  const amounts = Object.values(composition).filter((amt) => amt > 0);
13
13
  if (amounts.length === 0)
14
14
  return {};
15
- // For fractional amounts, find smallest multiplier (1–100) that yields near-integer ratios
16
15
  let scale = 1;
17
16
  if (!amounts.every((amt) => Number.isInteger(amt))) {
18
17
  scale = 0;
@@ -371,12 +370,7 @@ export function compute_domains(hyperplanes, border_hyperplanes, hyperplane_entr
371
370
  if (!advance_combo())
372
371
  break;
373
372
  }
374
- // Remove empty domains
375
- for (const key of Object.keys(domains)) {
376
- if (domains[key].length === 0)
377
- delete domains[key];
378
- }
379
- return domains;
373
+ return Object.fromEntries(Object.entries(domains).filter(([, domain]) => domain.length > 0));
380
374
  }
381
375
  // Apply element padding: replace coordinates close to default_min_limit with
382
376
  // actual_min - padding for cleaner visual bounds. Single pass over all points.
@@ -9,7 +9,7 @@ export interface TempFilterPayload {
9
9
  available_temperatures: number[];
10
10
  temp_filtered_entries: PhaseData[];
11
11
  }
12
- export declare function get_projection_source_entries(entries: PhaseData[], temp_filtered_entries: PhaseData[]): PhaseData[];
12
+ export declare const get_projection_source_entries: (entries: PhaseData[], temp_filtered_entries: PhaseData[]) => PhaseData[];
13
13
  export declare function get_temp_filter_payload(entries: PhaseData[], temperature: number | undefined, config: ChemPotDiagramConfig, props: TempFilterProps): TempFilterPayload;
14
14
  export declare function get_valid_temperature(temperature: number | undefined, has_temp_data: boolean, available_temperatures: number[]): number | undefined;
15
15
  export {};
@@ -1,8 +1,6 @@
1
1
  import { analyze_temperature_data, filter_entries_at_temperature, } from '../convex-hull/helpers';
2
2
  import { CHEMPOT_DEFAULTS } from './types';
3
- export function get_projection_source_entries(entries, temp_filtered_entries) {
4
- return temp_filtered_entries.length > 0 ? temp_filtered_entries : entries;
5
- }
3
+ export const get_projection_source_entries = (entries, temp_filtered_entries) => (temp_filtered_entries.length > 0 ? temp_filtered_entries : entries);
6
4
  const resolve_temp_filter_options = (config, props) => ({
7
5
  interpolate: config.interpolate_temperature ??
8
6
  props.interpolate_temperature ??
@@ -1,5 +1,6 @@
1
1
  import type { D3InterpolateName } from '../colors';
2
2
  import type { PhaseData } from '../convex-hull/types';
3
+ import type { Point2D } from '../math';
3
4
  export type ChemPotLimits = Partial<Record<string, [number, number]>>;
4
5
  export type ChemPotColorMode = `none` | `energy` | `formation_energy` | `arity` | `entries`;
5
6
  export type ChemPotProjectionMode = `single` | `grid`;
@@ -32,14 +33,12 @@ export interface ChemPotDiagramData {
32
33
  hyperplane_entries: PhaseData[];
33
34
  lims: [number, number][];
34
35
  }
35
- export interface ChemPotHoverPointer {
36
- x: number;
37
- y: number;
38
- }
39
36
  export interface ChemPotHoverInfoBase {
40
37
  formula: string;
41
38
  view: `2d` | `3d`;
42
- pointer?: ChemPotHoverPointer;
39
+ pointer?: Point2D;
40
+ n_points: number;
41
+ axis_ranges: AxisRangeData[];
43
42
  }
44
43
  export interface AxisRangeData {
45
44
  element: string;
@@ -48,16 +47,12 @@ export interface AxisRangeData {
48
47
  }
49
48
  export interface ChemPotHoverInfo2D extends ChemPotHoverInfoBase {
50
49
  view: `2d`;
51
- n_points: number;
52
- axis_ranges: AxisRangeData[];
53
50
  }
54
51
  export interface ChemPotHoverInfo3D extends ChemPotHoverInfoBase {
55
52
  view: `3d`;
56
53
  n_vertices: number;
57
54
  n_edges: number;
58
- n_points: number;
59
55
  ann_loc: number[];
60
- axis_ranges: AxisRangeData[];
61
56
  touches_limits: string[];
62
57
  is_elemental: boolean;
63
58
  is_draw_formula: boolean;
@@ -103,6 +103,7 @@ export function pick_contrast_color(options = {}) {
103
103
  export const contrast_color = (options = {}) => (node) => {
104
104
  node.style.color = pick_contrast_color({ ...options, bg_color: get_bg_color(node) });
105
105
  };
106
+ const is_valid_bg = (bg) => bg !== `` && bg !== `rgba(0, 0, 0, 0)` && bg !== `transparent`;
106
107
  // Detect and return the page background color from html/body elements or user preferences
107
108
  export function get_page_background(fallback_dark = `#1a1a1a`, fallback_light = `#ffffff`) {
108
109
  if (typeof window === `undefined`)
@@ -111,7 +112,6 @@ export function get_page_background(fallback_dark = `#1a1a1a`, fallback_light =
111
112
  const html_bg = getComputedStyle(document.documentElement).backgroundColor;
112
113
  const body_bg = getComputedStyle(document.body).backgroundColor;
113
114
  // Check if background is not transparent/unset
114
- const is_valid_bg = (bg) => bg && bg !== `rgba(0, 0, 0, 0)` && bg !== `transparent`;
115
115
  // Prefer body background as it's more likely to be styled by the theme
116
116
  if (is_valid_bg(body_bg))
117
117
  return body_bg;
@@ -186,10 +186,10 @@ export function add_alpha(color, alpha) {
186
186
  return color;
187
187
  // Extract RGB, ignoring any existing alpha channel
188
188
  const is_short = hex.length === 3 || hex.length === 4;
189
- const r = parseInt(is_short ? hex[0] + hex[0] : hex.slice(0, 2), 16);
190
- const g = parseInt(is_short ? hex[1] + hex[1] : hex.slice(2, 4), 16);
191
- const b = parseInt(is_short ? hex[2] + hex[2] : hex.slice(4, 6), 16);
192
- return `rgba(${r}, ${g}, ${b}, ${clamped_alpha})`;
189
+ const red = parseInt(is_short ? hex[0] + hex[0] : hex.slice(0, 2), 16);
190
+ const green = parseInt(is_short ? hex[1] + hex[1] : hex.slice(2, 4), 16);
191
+ const blue = parseInt(is_short ? hex[2] + hex[2] : hex.slice(4, 6), 16);
192
+ return `rgba(${red}, ${green}, ${blue}, ${clamped_alpha})`;
193
193
  }
194
194
  // Handle rgb() colors
195
195
  if (color.startsWith(`rgb(`)) {
@@ -135,7 +135,8 @@
135
135
  onkeydown={(event: KeyboardEvent) => {
136
136
  if ([`Enter`, ` `].includes(event.key)) {
137
137
  event.preventDefault()
138
- const target = event.currentTarget as Element
138
+ const target = event.currentTarget
139
+ if (!(target instanceof Element)) return
139
140
  const rect = target.getBoundingClientRect()
140
141
  context_menu.x = window.scrollX + rect.left + rect.width / 2
141
142
  context_menu.y = window.scrollY + rect.top + rect.height / 2