matterviz 0.3.3 → 0.3.5

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 (126) hide show
  1. package/dist/FilePicker.svelte +1 -1
  2. package/dist/app.css +7 -0
  3. package/dist/brillouin/BrillouinZone.svelte +5 -2
  4. package/dist/brillouin/compute.js +8 -4
  5. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +6 -6
  6. package/dist/chempot-diagram/async-compute.svelte.js +6 -5
  7. package/dist/chempot-diagram/chempot-worker.js +2 -2
  8. package/dist/chempot-diagram/compute.js +16 -16
  9. package/dist/composition/FormulaFilter.svelte +3 -3
  10. package/dist/constants.js +2 -8
  11. package/dist/convex-hull/ConvexHull.svelte +2 -2
  12. package/dist/convex-hull/ConvexHull2D.svelte +11 -10
  13. package/dist/convex-hull/ConvexHull3D.svelte +16 -14
  14. package/dist/convex-hull/ConvexHull4D.svelte +26 -14
  15. package/dist/convex-hull/ConvexHullControls.svelte +1 -1
  16. package/dist/convex-hull/ConvexHullInfoPane.svelte +68 -61
  17. package/dist/convex-hull/ConvexHullStats.svelte +23 -6
  18. package/dist/convex-hull/GasPressureControls.svelte +3 -3
  19. package/dist/convex-hull/TemperatureSlider.svelte +1 -1
  20. package/dist/convex-hull/barycentric-coords.js +2 -2
  21. package/dist/convex-hull/helpers.js +45 -27
  22. package/dist/convex-hull/thermodynamics.js +2 -2
  23. package/dist/element/BohrAtom.svelte +25 -27
  24. package/dist/element/BohrAtom.svelte.d.ts +2 -2
  25. package/dist/element/data.d.ts +2 -3
  26. package/dist/element/data.js +1 -1
  27. package/dist/fermi-surface/FermiSurface.svelte +5 -2
  28. package/dist/fermi-surface/compute.js +3 -3
  29. package/dist/fermi-surface/parse.js +2 -2
  30. package/dist/fermi-surface/symmetry.js +1 -1
  31. package/dist/heatmap-matrix/HeatmapMatrix.svelte +8 -8
  32. package/dist/icons.d.ts +6 -6
  33. package/dist/icons.js +6 -6
  34. package/dist/io/decompress.js +12 -7
  35. package/dist/io/export.js +20 -16
  36. package/dist/io/is-binary.js +19 -4
  37. package/dist/isosurface/parse.js +8 -8
  38. package/dist/isosurface/types.js +9 -9
  39. package/dist/layout/InfoTag.svelte +1 -1
  40. package/dist/layout/json-tree/JsonNode.svelte +1 -0
  41. package/dist/layout/json-tree/utils.js +2 -1
  42. package/dist/marching-cubes.js +1 -1
  43. package/dist/math.js +1 -1
  44. package/dist/overlays/CopyButton.svelte +45 -0
  45. package/dist/overlays/CopyButton.svelte.d.ts +8 -0
  46. package/dist/overlays/InfoPaneCards.svelte +149 -0
  47. package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
  48. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +33 -35
  49. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +2 -2
  50. package/dist/phase-diagram/PhaseDiagramControls.svelte +27 -29
  51. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +2 -2
  52. package/dist/phase-diagram/parse.js +3 -3
  53. package/dist/phase-diagram/svg-to-diagram.js +10 -12
  54. package/dist/plot/BarPlot.svelte +24 -15
  55. package/dist/plot/BarPlot.svelte.d.ts +3 -2
  56. package/dist/plot/FillArea.svelte +2 -3
  57. package/dist/plot/FillArea.svelte.d.ts +3 -2
  58. package/dist/plot/Histogram.svelte +37 -19
  59. package/dist/plot/Line.svelte +2 -3
  60. package/dist/plot/Line.svelte.d.ts +2 -2
  61. package/dist/plot/PlotLegend.svelte +79 -8
  62. package/dist/plot/PlotLegend.svelte.d.ts +4 -0
  63. package/dist/plot/PortalSelect.svelte +5 -5
  64. package/dist/plot/ScatterPlot.svelte +47 -33
  65. package/dist/plot/ScatterPlot.svelte.d.ts +5 -4
  66. package/dist/plot/ScatterPlot3D.svelte +6 -3
  67. package/dist/plot/ScatterPoint.svelte +10 -4
  68. package/dist/plot/ScatterPoint.svelte.d.ts +4 -2
  69. package/dist/plot/SpacegroupBarPlot.svelte +5 -4
  70. package/dist/plot/data-cleaning.js +9 -9
  71. package/dist/plot/index.d.ts +0 -6
  72. package/dist/plot/scales.d.ts +3 -3
  73. package/dist/plot/scales.js +29 -29
  74. package/dist/plot/types.d.ts +5 -9
  75. package/dist/rdf/calc-rdf.js +1 -1
  76. package/dist/sanitize.js +22 -15
  77. package/dist/settings.d.ts +2 -0
  78. package/dist/settings.js +12 -3
  79. package/dist/spectral/Bands.svelte +6 -6
  80. package/dist/spectral/BandsAndDos.svelte +4 -4
  81. package/dist/spectral/BrillouinBandsDos.svelte +3 -3
  82. package/dist/spectral/Dos.svelte +2 -2
  83. package/dist/spectral/helpers.js +1 -1
  84. package/dist/structure/AtomLegend.svelte +4 -4
  85. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  86. package/dist/structure/Cylinder.svelte +7 -7
  87. package/dist/structure/Structure.svelte +169 -27
  88. package/dist/structure/Structure.svelte.d.ts +6 -2
  89. package/dist/structure/StructureControls.svelte +130 -16
  90. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  91. package/dist/structure/StructureInfoPane.svelte +519 -218
  92. package/dist/structure/StructureInfoPane.svelte.d.ts +2 -1
  93. package/dist/structure/StructureScene.svelte +399 -68
  94. package/dist/structure/StructureScene.svelte.d.ts +8 -4
  95. package/dist/structure/atom-properties.js +3 -1
  96. package/dist/structure/bond-order-perception.d.ts +13 -0
  97. package/dist/structure/bond-order-perception.js +367 -0
  98. package/dist/structure/bonding.d.ts +10 -1
  99. package/dist/structure/bonding.js +232 -11
  100. package/dist/structure/export.js +6 -4
  101. package/dist/structure/index.d.ts +19 -4
  102. package/dist/structure/index.js +3 -0
  103. package/dist/structure/label-placement.d.ts +14 -0
  104. package/dist/structure/label-placement.js +72 -0
  105. package/dist/structure/parse.d.ts +2 -1
  106. package/dist/structure/parse.js +25 -36
  107. package/dist/structure/supercell.js +35 -2
  108. package/dist/symmetry/SymmetryStats.svelte +1 -1
  109. package/dist/symmetry/cell-transform.js +15 -1
  110. package/dist/symmetry/index.js +3 -3
  111. package/dist/table/HeatmapTable.svelte +3 -3
  112. package/dist/table/ToggleMenu.svelte +1 -1
  113. package/dist/trajectory/Trajectory.svelte +2 -2
  114. package/dist/trajectory/TrajectoryInfoPane.svelte +14 -88
  115. package/dist/trajectory/extract.js +4 -4
  116. package/dist/trajectory/frame-reader.js +2 -2
  117. package/dist/trajectory/parse/ase.js +2 -6
  118. package/dist/trajectory/parse/hdf5.js +1 -3
  119. package/dist/trajectory/plotting.js +1 -1
  120. package/dist/utils.js +1 -1
  121. package/dist/xrd/calc-xrd.js +1 -1
  122. package/package.json +23 -38
  123. package/dist/structure/ferrox-wasm-types.d.ts +0 -46
  124. package/dist/structure/ferrox-wasm-types.js +0 -18
  125. package/dist/structure/ferrox-wasm.d.ts +0 -94
  126. package/dist/structure/ferrox-wasm.js +0 -249
@@ -492,7 +492,7 @@
492
492
  offset={{ x: 12, y: -12 }}
493
493
  bg_color={hover_data.surface_color}
494
494
  fixed
495
- style="z-index: 100000001; backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)"
495
+ style="z-index: var(--z-index-overlay-nav, 100000001); backdrop-filter: blur(4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)"
496
496
  >
497
497
  <FermiSurfaceTooltip {hover_data} tooltip={tooltip_config} />
498
498
  </PlotTooltip>
@@ -555,7 +555,10 @@
555
555
  top: var(--fermi-buttons-top, var(--ctrl-btn-top, 1ex));
556
556
  right: var(--fermi-buttons-right, var(--ctrl-btn-right, 1ex));
557
557
  gap: clamp(6pt, 1cqmin, 9pt);
558
- z-index: var(--fermi-buttons-z-index, 100000000);
558
+ z-index: var(
559
+ --fermi-buttons-z-index,
560
+ var(--z-index-overlay-controls, 100000000)
561
+ );
559
562
  opacity: 0;
560
563
  pointer-events: none;
561
564
  transition: opacity 0.2s ease;
@@ -71,10 +71,10 @@ function upsample_grid(grid, factor) {
71
71
  for (let iz = 0; iz < new_nz; iz++)
72
72
  fz_arr[iz] = (iz / new_nz) * nz;
73
73
  // Preallocate output grid
74
- const new_grid = new Array(new_nx);
74
+ const new_grid = Array(new_nx);
75
75
  for (let ix = 0; ix < new_nx; ix++) {
76
76
  const fx = fx_arr[ix];
77
- const iy_arr = new Array(new_ny);
77
+ const iy_arr = Array(new_ny);
78
78
  for (let iy = 0; iy < new_ny; iy++) {
79
79
  const fy = fy_arr[iy];
80
80
  const iz_arr = new Float64Array(new_nz);
@@ -492,7 +492,7 @@ function slice_surface_with_plane(surface, plane_normal, plane_distance, in_plan
492
492
  const last = contour_points[contour_points.length - 1];
493
493
  const is_closed = math.euclidean_dist(first, last) < CLOSED_CONTOUR_TOLERANCE;
494
494
  // Project to 2D (inlined dot product for speed)
495
- const points_2d = new Array(contour_points.length);
495
+ const points_2d = Array(contour_points.length);
496
496
  for (let idx = 0; idx < contour_points.length; idx++) {
497
497
  const point = contour_points[idx];
498
498
  points_2d[idx] = [
@@ -97,10 +97,10 @@ function parse_bxsf(content) {
97
97
  throw new Error(`Band ${band_idx}: expected ${total_points} values, got ${energy_values.length}`);
98
98
  }
99
99
  // Reshape into 3D grid [kx][ky][kz] (preallocated for speed)
100
- const band_grid = new Array(nx);
100
+ const band_grid = Array(nx);
101
101
  let val_idx = 0;
102
102
  for (let ix = 0; ix < nx; ix++) {
103
- const iy_arr = new Array(ny);
103
+ const iy_arr = Array(ny);
104
104
  for (let iy = 0; iy < ny; iy++) {
105
105
  const iz_arr = energy_values.slice(val_idx, val_idx + nz);
106
106
  val_idx += nz;
@@ -29,7 +29,7 @@ function generate_oh_symmetry_matrices() {
29
29
  for (const perm of AXIS_PERMUTATIONS) {
30
30
  for (const sign of SIGN_COMBINATIONS) {
31
31
  // Column-major 4x4 matrix: element at (row, col) is at index col*4 + row
32
- const mat = new Array(16).fill(0);
32
+ const mat = Array(16).fill(0);
33
33
  mat[15] = 1; // w component
34
34
  // Build 3x3 rotation part: M[row][col] = sign[row] if perm[row] === col
35
35
  for (let col = 0; col < 3; col++) {
@@ -286,8 +286,8 @@
286
286
  }
287
287
  }
288
288
 
289
- const col_has_data = new Array(x_items.length).fill(false)
290
- const row_has_data = new Array(y_items.length).fill(false)
289
+ const col_has_data = Array(x_items.length).fill(false)
290
+ const row_has_data = Array(y_items.length).fill(false)
291
291
  for (let y_idx = 0; y_idx < y_items.length; y_idx++) {
292
292
  for (let x_idx = 0; x_idx < x_items.length; x_idx++) {
293
293
  if (get_value(x_idx, y_idx) !== null) {
@@ -437,7 +437,7 @@
437
437
  let n_x = $derived(x_items.length)
438
438
  let bg_flat = $derived.by(() => {
439
439
  const n_y = y_items.length
440
- const colors = new Array<string | null>(n_x * n_y)
440
+ const colors: (string | null)[] = Array(n_x * n_y)
441
441
  for (let y_idx = 0; y_idx < n_y; y_idx++) {
442
442
  const row_offset = y_idx * n_x
443
443
  for (let x_idx = 0; x_idx < n_x; x_idx++) {
@@ -1010,11 +1010,11 @@
1010
1010
  grid_offset_top = first_rendered_cell.offsetTop - vis_row * tile_stride_px
1011
1011
  }
1012
1012
 
1013
- function compute_summary(values: number[]): number | null {
1014
- if (!values.length) return null
1015
- if (summary_fn) return summary_fn(values)
1016
- const total = values.reduce((sum, value) => sum + value, 0)
1017
- return total / values.length
1013
+ function compute_summary(summary_values: number[]): number | null {
1014
+ if (!summary_values.length) return null
1015
+ if (summary_fn) return summary_fn(summary_values)
1016
+ const total = summary_values.reduce((sum, value) => sum + value, 0)
1017
+ return total / summary_values.length
1018
1018
  }
1019
1019
 
1020
1020
  function summarize_axis_values(
package/dist/icons.d.ts CHANGED
@@ -60,7 +60,7 @@ export declare const ICON_DATA: {
60
60
  readonly path: "M13 3a9 9 0 0 0-9 9H1l4 4l4-4H6c0-3.87 3.13-7 7-7s7 3.13 7 7s-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.95 8.95 0 0 0 13 21a9 9 0 0 0 0-18m-1 5v5l4.25 2.52l.77-1.28l-3.52-2.09V8z";
61
61
  };
62
62
  readonly Info: {
63
- readonly viewBox: "0 0 24 24";
63
+ readonly viewBox: "1.5 1.5 21 21";
64
64
  readonly path: "M11 9h2V7h-2m1 13c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-1 15h2v-6h-2z";
65
65
  };
66
66
  readonly Edit: {
@@ -244,12 +244,12 @@ export declare const ICON_DATA: {
244
244
  readonly path: "<g fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"20\" r=\"3\"/><circle cx=\"6\" cy=\"6\" r=\"3\"/><circle cx=\"18\" cy=\"6\" r=\"3\"/><path d=\"M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9\"/><path d=\"M12 12v5\"/></g>";
245
245
  };
246
246
  readonly Fullscreen: {
247
- readonly viewBox: "0 0 24 24";
248
- readonly path: "M5 5h5V3H3v7h2zm5 14H5v-5H3v7h7zm11-5h-2v5h-5v2h7zm-2-4h2V3h-7v2h5z";
247
+ readonly viewBox: "1.25 1.25 21.5 21.5";
248
+ readonly path: "M3 3h7v2.5H5.5V10H3zm0 11h2.5v4.5H10V21H3zm11 7v-2.5h4.5V14H21v7zm4.5-11V5.5H14V3h7v7z";
249
249
  };
250
250
  readonly ExitFullscreen: {
251
- readonly viewBox: "0 0 24 24";
252
- readonly path: "M10 4H8v4H4v2h6zM8 20h2v-6H4v2h4zm12-6h-6v6h2v-4h4zm0-6h-4V4h-2v6h6z";
251
+ readonly viewBox: "1.25 1.25 21.5 21.5";
252
+ readonly path: "M10 4H7.5v4.5H4V11h6zm-2.5 16H10v-6H4v2.5h3.5zM20 14h-6v6h2.5v-3.5H20zm0-5.5h-3.5V4H14v7h6z";
253
253
  };
254
254
  readonly Weight: {
255
255
  readonly viewBox: "0 0 24 24";
@@ -297,7 +297,7 @@ export declare const ICON_DATA: {
297
297
  };
298
298
  readonly Reset: {
299
299
  readonly viewBox: "0 0 15 15";
300
- readonly path: "M4.854 2.146a.5.5 0 0 1 0 .708L3.707 4H9a4.5 4.5 0 1 1 0 9H5a.5.5 0 0 1 0-1h4a3.5 3.5 0 1 0 0-7H3.707l1.147 1.146a.5.5 0 1 1-.708.708l-2-2a.5.5 0 0 1 0-.708l2-2a.5.5 0 0 1 .708 0";
300
+ readonly path: "M4.94 2.06a.62.62 0 0 1 0 .88L4.13 3.75H9a4.75 4.75 0 1 1 0 9.5H5a.62.62 0 0 1 0-1.24h4a3.51 3.51 0 1 0 0-7.02H4.13l.81.81a.62.62 0 1 1-.88.88l-2-2a.62.62 0 0 1 0-.88l2-2a.62.62 0 0 1 .88 0";
301
301
  };
302
302
  readonly Undo: {
303
303
  readonly viewBox: "0 0 24 24";
package/dist/icons.js CHANGED
@@ -61,7 +61,7 @@ export const ICON_DATA = {
61
61
  path: `M13 3a9 9 0 0 0-9 9H1l4 4l4-4H6c0-3.87 3.13-7 7-7s7 3.13 7 7s-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.95 8.95 0 0 0 13 21a9 9 0 0 0 0-18m-1 5v5l4.25 2.52l.77-1.28l-3.52-2.09V8z`,
62
62
  },
63
63
  Info: {
64
- viewBox: `0 0 24 24`,
64
+ viewBox: `1.5 1.5 21 21`,
65
65
  path: `M11 9h2V7h-2m1 13c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-1 15h2v-6h-2z`,
66
66
  },
67
67
  Edit: {
@@ -283,12 +283,12 @@ export const ICON_DATA = {
283
283
  },
284
284
  Fullscreen: {
285
285
  // from BoxIcons by Atisa
286
- viewBox: `0 0 24 24`,
287
- path: `M5 5h5V3H3v7h2zm5 14H5v-5H3v7h7zm11-5h-2v5h-5v2h7zm-2-4h2V3h-7v2h5z`,
286
+ viewBox: `1.25 1.25 21.5 21.5`,
287
+ path: `M3 3h7v2.5H5.5V10H3zm0 11h2.5v4.5H10V21H3zm11 7v-2.5h4.5V14H21v7zm4.5-11V5.5H14V3h7v7z`,
288
288
  },
289
289
  ExitFullscreen: {
290
- viewBox: `0 0 24 24`,
291
- path: `M10 4H8v4H4v2h6zM8 20h2v-6H4v2h4zm12-6h-6v6h2v-4h4zm0-6h-4V4h-2v6h6z`,
290
+ viewBox: `1.25 1.25 21.5 21.5`,
291
+ path: `M10 4H7.5v4.5H4V11h6zm-2.5 16H10v-6H4v2.5h3.5zM20 14h-6v6h2.5v-3.5H20zm0-5.5h-3.5V4H14v7h6z`,
292
292
  },
293
293
  Weight: {
294
294
  viewBox: `0 0 24 24`,
@@ -336,7 +336,7 @@ export const ICON_DATA = {
336
336
  },
337
337
  Reset: {
338
338
  viewBox: `0 0 15 15`,
339
- path: `M4.854 2.146a.5.5 0 0 1 0 .708L3.707 4H9a4.5 4.5 0 1 1 0 9H5a.5.5 0 0 1 0-1h4a3.5 3.5 0 1 0 0-7H3.707l1.147 1.146a.5.5 0 1 1-.708.708l-2-2a.5.5 0 0 1 0-.708l2-2a.5.5 0 0 1 .708 0`,
339
+ path: `M4.94 2.06a.62.62 0 0 1 0 .88L4.13 3.75H9a4.75 4.75 0 1 1 0 9.5H5a.62.62 0 0 1 0-1.24h4a3.51 3.51 0 1 0 0-7.02H4.13l.81.81a.62.62 0 1 1-.88.88l-2-2a.62.62 0 0 1 0-.88l2-2a.62.62 0 0 1 .88 0`,
340
340
  },
341
341
  Undo: {
342
342
  viewBox: `0 0 24 24`,
@@ -42,25 +42,30 @@ export function decompress_file(file) {
42
42
  const is_supported = Boolean(format && ![`zip`, `xz`, `bz2`].includes(format));
43
43
  return new Promise((resolve, reject) => {
44
44
  const reader = new FileReader();
45
- reader.onload = async (event) => {
45
+ reader.addEventListener(`load`, () => {
46
46
  try {
47
- const result = event.target?.result;
47
+ const result = reader.result;
48
48
  if (!result)
49
49
  throw new Error(`Failed to read file`);
50
50
  if (is_supported && format) {
51
- const content = await decompress_data(result, format);
52
- const filename = file.name.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
53
- resolve({ content, filename });
51
+ if (!(result instanceof ArrayBuffer))
52
+ throw new Error(`Expected binary file data`);
53
+ decompress_data(result, format).then((content) => {
54
+ const filename = file.name.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
55
+ resolve({ content, filename });
56
+ }, reject);
54
57
  }
55
58
  else {
59
+ if (typeof result !== `string`)
60
+ throw new Error(`Expected text file data`);
56
61
  resolve({ content: result, filename: file.name });
57
62
  }
58
63
  }
59
64
  catch (error) {
60
65
  reject(error);
61
66
  }
62
- };
63
- reader.onerror = () => reject(new Error(`Failed to read file ${file.name}`));
67
+ }, { once: true });
68
+ reader.addEventListener(`error`, () => reject(new Error(`Failed to read file ${file.name}`)));
64
69
  if (is_supported)
65
70
  reader.readAsArrayBuffer(file);
66
71
  else
package/dist/io/export.js CHANGED
@@ -11,6 +11,10 @@ function is_webgl_renderer_like(value) {
11
11
  typeof renderer_obj.getSize === `function` &&
12
12
  typeof renderer_obj.setSize === `function`);
13
13
  }
14
+ function get_canvas_renderer(canvas) {
15
+ const renderer_val = canvas[`__renderer`];
16
+ return is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
17
+ }
14
18
  // Capture a WebGL canvas as a PNG Blob at the given DPI.
15
19
  // Temporarily adjusts renderer pixel ratio for high-res capture, then restores.
16
20
  // Returns data directly (no browser download), suitable for programmatic capture
@@ -18,8 +22,7 @@ function is_webgl_renderer_like(value) {
18
22
  // DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
19
23
  export function canvas_to_png_blob(canvas, png_dpi = 150, scene = null, camera = null) {
20
24
  const resolution_multiplier = Math.min(png_dpi / 72, 10);
21
- const renderer_val = canvas.__renderer;
22
- const renderer = is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
25
+ const renderer = get_canvas_renderer(canvas);
23
26
  if (resolution_multiplier <= 1.1 || !renderer) {
24
27
  if (renderer && scene && camera)
25
28
  renderer.render(scene, camera);
@@ -157,7 +160,7 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
157
160
  const svg_data_url = URL.createObjectURL(svg_blob);
158
161
  return new Promise((resolve, reject) => {
159
162
  const img = new Image();
160
- img.onload = () => {
163
+ img.addEventListener(`load`, () => {
161
164
  try {
162
165
  ctx.clearRect(0, 0, pixel_width, pixel_height);
163
166
  ctx.drawImage(img, 0, 0, pixel_width, pixel_height);
@@ -174,11 +177,11 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
174
177
  finally {
175
178
  URL.revokeObjectURL(svg_data_url);
176
179
  }
177
- };
178
- img.onerror = () => {
180
+ });
181
+ img.addEventListener(`error`, () => {
179
182
  URL.revokeObjectURL(svg_data_url);
180
183
  reject(new Error(`Failed to load SVG for PNG export`));
181
- };
184
+ });
182
185
  img.src = svg_data_url;
183
186
  });
184
187
  }
@@ -205,8 +208,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
205
208
  typeof MediaRecorder === `undefined` ||
206
209
  !MediaRecorder.isTypeSupported(`video/webm;codecs=vp9`))
207
210
  throw new Error(`WebM video recording not supported in this browser`);
208
- const renderer_val = canvas.__renderer;
209
- const renderer = is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
211
+ const renderer = get_canvas_renderer(canvas);
210
212
  // Store original renderer settings if changing resolution
211
213
  let orig_pixel_ratio;
212
214
  let orig_size;
@@ -231,10 +233,10 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
231
233
  mimeType: `video/webm;codecs=vp9`,
232
234
  videoBitsPerSecond: bitrate,
233
235
  });
234
- recorder.ondataavailable = (event) => {
236
+ recorder.addEventListener(`dataavailable`, (event) => {
235
237
  if (event.data.size > 0)
236
238
  chunks.push(event.data);
237
- };
239
+ });
238
240
  const track = stream.getVideoTracks()[0];
239
241
  // Start recording
240
242
  recorder.start();
@@ -269,7 +271,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
269
271
  // Finalize recording
270
272
  return new Promise((resolve, reject) => {
271
273
  let is_resolved = false;
272
- recorder.onstop = () => {
274
+ recorder.addEventListener(`stop`, () => {
273
275
  if (is_resolved)
274
276
  return;
275
277
  is_resolved = true;
@@ -283,14 +285,16 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
283
285
  catch (error) {
284
286
  reject(error);
285
287
  }
286
- };
287
- recorder.onerror = (event) => {
288
+ });
289
+ recorder.addEventListener(`error`, (event) => {
288
290
  if (is_resolved)
289
291
  return;
290
292
  is_resolved = true;
291
- // Extract error details from MediaRecorderErrorEvent or ErrorEvent
292
- reject(new Error(`MediaRecorder error: ${event.error}`));
293
- };
293
+ const error_msg = event instanceof ErrorEvent && event.error instanceof Error
294
+ ? event.error.message
295
+ : event.type;
296
+ reject(new Error(`MediaRecorder error: ${error_msg}`));
297
+ });
294
298
  // Stop recording with safety timeout
295
299
  try {
296
300
  recorder.stop();
@@ -1,5 +1,20 @@
1
1
  // Simplified binary detection
2
- export const is_binary = (content) => content.includes(`\0`) ||
3
- (content.match(/[\u0000-\u0008\u000E-\u001F\u007F-\u00FF]/g) || []).length / content.length >
4
- 0.1 ||
5
- (content.match(/[\u0020-\u007E]/g) || []).length / content.length < 0.7;
2
+ export const is_binary = (content) => {
3
+ if (!content)
4
+ return false;
5
+ if (content.includes(`\0`))
6
+ return true;
7
+ let binary_char_count = 0;
8
+ let printable_ascii_count = 0;
9
+ for (let char_idx = 0; char_idx < content.length; char_idx += 1) {
10
+ const char_code = content.charCodeAt(char_idx);
11
+ if (char_code <= 8 ||
12
+ (char_code >= 14 && char_code <= 31) ||
13
+ (char_code >= 127 && char_code <= 255)) {
14
+ binary_char_count += 1;
15
+ }
16
+ if (char_code >= 32 && char_code <= 126)
17
+ printable_ascii_count += 1;
18
+ }
19
+ return (binary_char_count / content.length > 0.1 || printable_ascii_count / content.length < 0.7);
20
+ };
@@ -87,16 +87,16 @@ function read_lines(text, pos, count) {
87
87
  return { lines: result, next: pos };
88
88
  }
89
89
  function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }) {
90
- const grid = new Array(nx);
90
+ const grid = Array(nx);
91
91
  let [min_val, max_val, sum] = [Infinity, -Infinity, 0];
92
92
  const total = nx * ny * nz;
93
93
  const data_len = Math.min(data.length, total);
94
94
  if (data_len === 0) {
95
95
  // Empty data: return zeroed grid with neutral data_range
96
96
  for (let ix = 0; ix < nx; ix++) {
97
- const plane = new Array(ny);
97
+ const plane = Array(ny);
98
98
  for (let iy = 0; iy < ny; iy++)
99
- plane[iy] = new Array(nz).fill(0);
99
+ plane[iy] = Array(nz).fill(0);
100
100
  grid[ix] = plane;
101
101
  }
102
102
  return { grid, data_range: { min: 0, max: 0, abs_max: 0, mean: 0 } };
@@ -105,9 +105,9 @@ function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }
105
105
  // .cube convention: z varies fastest, then y, then x.
106
106
  const ny_nz = ny * nz;
107
107
  for (let ix = 0; ix < nx; ix++) {
108
- const plane = new Array(ny);
108
+ const plane = Array(ny);
109
109
  for (let iy = 0; iy < ny; iy++) {
110
- const row = new Array(nz).fill(0);
110
+ const row = Array(nz).fill(0);
111
111
  const base = ix * ny_nz + iy * nz;
112
112
  const row_end = Math.min(base + nz, data_len);
113
113
  for (let flat_idx = base; flat_idx < row_end; flat_idx++) {
@@ -127,9 +127,9 @@ function build_grid({ data, nx, ny, nz, divisor = 1, data_order = `z_fastest`, }
127
127
  else {
128
128
  // VASP CHGCAR/ELFCAR/LOCPOT convention: x varies fastest, then y, then z.
129
129
  for (let ix = 0; ix < nx; ix++) {
130
- const plane = new Array(ny);
130
+ const plane = Array(ny);
131
131
  for (let iy = 0; iy < ny; iy++)
132
- plane[iy] = new Array(nz).fill(0);
132
+ plane[iy] = Array(nz).fill(0);
133
133
  grid[ix] = plane;
134
134
  }
135
135
  let [flat_idx, data_exhausted] = [0, false];
@@ -504,7 +504,7 @@ export function parse_cube(content, options = {}) {
504
504
  function atomic_number_to_symbol(atomic_number) {
505
505
  // ELEM_SYMBOLS is 0-indexed (H at index 0), atomic numbers are 1-indexed
506
506
  const idx = atomic_number - 1;
507
- return (idx >= 0 && idx < ELEM_SYMBOLS.length ? ELEM_SYMBOLS[idx] : `H`);
507
+ return idx >= 0 && idx < ELEM_SYMBOLS.length ? ELEM_SYMBOLS[idx] : `H`;
508
508
  }
509
509
  // Auto-detect and parse volumetric file format based on filename and content
510
510
  export function parse_volumetric_file(content, filename) {
@@ -50,12 +50,12 @@ export function pad_periodic_grid(grid, dims, pad_fraction) {
50
50
  const out_ny = ny + 2 * py;
51
51
  const out_nz = nz + 2 * pz;
52
52
  const wrap = (val, size) => ((val % size) + size) % size;
53
- const out = new Array(out_nx);
53
+ const out = Array(out_nx);
54
54
  for (let ix = 0; ix < out_nx; ix++) {
55
- const plane = new Array(out_ny);
55
+ const plane = Array(out_ny);
56
56
  const src_x = wrap(ix - px, nx);
57
57
  for (let iy = 0; iy < out_ny; iy++) {
58
- const row = new Array(out_nz);
58
+ const row = Array(out_nz);
59
59
  const src_y = wrap(iy - py, ny);
60
60
  for (let iz = 0; iz < out_nz; iz++) {
61
61
  row[iz] = grid[src_x][src_y][wrap(iz - pz, nz)];
@@ -110,12 +110,12 @@ export function downsample_grid(grid, dims, max_points = MAX_GRID_POINTS) {
110
110
  const x_ranges = partition(new_nx, nx);
111
111
  const y_ranges = partition(new_ny, ny);
112
112
  const z_ranges = partition(new_nz, nz);
113
- const out = new Array(new_nx);
113
+ const out = Array(new_nx);
114
114
  for (let ix = 0; ix < new_nx; ix++) {
115
- const plane = new Array(new_ny);
115
+ const plane = Array(new_ny);
116
116
  const [sx_start, sx_end] = x_ranges[ix];
117
117
  for (let iy = 0; iy < new_ny; iy++) {
118
- const row = new Array(new_nz);
118
+ const row = Array(new_nz);
119
119
  const [sy_start, sy_end] = y_ranges[iy];
120
120
  for (let iz = 0; iz < new_nz; iz++) {
121
121
  let sum = 0;
@@ -203,12 +203,12 @@ export function tile_volumetric_data(volume, scaling) {
203
203
  const new_nx = nx * sx;
204
204
  const new_ny = ny * sy;
205
205
  const new_nz = nz * sz;
206
- const new_grid = new Array(new_nx);
206
+ const new_grid = Array(new_nx);
207
207
  for (let ix = 0; ix < new_nx; ix++) {
208
- const plane = new Array(new_ny);
208
+ const plane = Array(new_ny);
209
209
  const src_x = ix % nx;
210
210
  for (let iy = 0; iy < new_ny; iy++) {
211
- const row = new Array(new_nz);
211
+ const row = Array(new_nz);
212
212
  const src_y = iy % ny;
213
213
  const src_row = src_grid[src_x][src_y];
214
214
  for (let iz = 0; iz < new_nz; iz++) {
@@ -2,7 +2,7 @@
2
2
  import Icon from '../Icon.svelte'
3
3
  import { sanitize_html } from '../sanitize'
4
4
  import type { Snippet } from 'svelte'
5
- import { tooltip } from 'svelte-multiselect'
5
+ import { tooltip } from 'svelte-multiselect/attachments'
6
6
  import type { HTMLAttributes } from 'svelte/elements'
7
7
  import type { InfoTagSize, InfoTagVariant } from './index'
8
8
 
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import Icon from '../../Icon.svelte'
3
3
  import { getContext, onMount } from 'svelte'
4
+ // oxlint-disable-next-line import/no-self-import -- recursive Svelte component
4
5
  import JsonNode from './JsonNode.svelte'
5
6
  import JsonValue from './JsonValue.svelte'
6
7
  import type { JsonTreeContext } from './types'
@@ -273,7 +273,8 @@ export function find_matching_paths(value, query, current_path = ``, current_key
273
273
  for_each_child(value, type, (child_value, key, map_key) => {
274
274
  const child_path = build_path(current_path, key);
275
275
  // Also check if Map key matches
276
- if (map_key !== undefined && String(map_key).toLowerCase().includes(lower_query)) {
276
+ if (map_key !== undefined &&
277
+ serialize_for_copy(map_key).toLowerCase().includes(lower_query)) {
277
278
  matches.add(child_path);
278
279
  }
279
280
  for (const match of find_matching_paths(child_value, query, child_path, key, seen)) {
@@ -432,7 +432,7 @@ export function marching_cubes(grid, iso_value, k_lattice, options = {}) {
432
432
  const max_y = periodic ? ny : ny - 1;
433
433
  const max_z = periodic ? nz : nz - 1;
434
434
  // Preallocate cube_values array (reuse across iterations)
435
- const cube_values = new Array(8);
435
+ const cube_values = Array(8);
436
436
  for (let ix = 0; ix < max_x; ix++) {
437
437
  const ix_row = grid[ix];
438
438
  const ix1_row = grid[(ix + 1) % nx];
package/dist/math.js CHANGED
@@ -222,7 +222,7 @@ export function dot(vec1, vec2) {
222
222
  if (mat1_cols !== mat2.length) {
223
223
  throw new Error(`First matrix columns must equal second matrix rows`);
224
224
  }
225
- return mat1.map((_, ii) => Array.from({ length: mat2_cols }, (_, jj) => mat1[ii].reduce((sum, _val, kk) => sum + mat1[ii][kk] * mat2[kk][jj], 0)));
225
+ return mat1.map((_row, ii) => Array.from({ length: mat2_cols }, (_col, jj) => mat1[ii].reduce((sum, _val, kk) => sum + mat1[ii][kk] * mat2[kk][jj], 0)));
226
226
  }
227
227
  throw new Error(`Unsupported input types for dot product`);
228
228
  }
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import Icon from '../Icon.svelte'
3
+ import type { HTMLButtonAttributes } from 'svelte/elements'
4
+
5
+ let { copied = false, label, title = label, onclick, ...rest }: Omit<
6
+ HTMLButtonAttributes,
7
+ `type` | `aria-label`
8
+ > & { copied?: boolean; label: string } = $props()
9
+ </script>
10
+
11
+ <button
12
+ {...rest}
13
+ type="button"
14
+ class="copy-button {rest.class ?? ``}"
15
+ aria-label={label}
16
+ {title}
17
+ {onclick}
18
+ >
19
+ <Icon icon={copied ? `Check` : `Copy`} />
20
+ </button>
21
+
22
+ <style>
23
+ .copy-button {
24
+ display: inline-grid;
25
+ place-items: center;
26
+ flex: 0 0 auto;
27
+ width: 1.6em;
28
+ height: 1.6em;
29
+ border: 0;
30
+ border-radius: var(--border-radius, 3pt);
31
+ background: color-mix(in srgb, currentColor 8%, transparent);
32
+ color: inherit;
33
+ cursor: pointer;
34
+ opacity: 0.75;
35
+ padding: 0;
36
+ &:is(:hover, :focus-visible) {
37
+ opacity: 1;
38
+ background: color-mix(in srgb, currentColor 14%, transparent);
39
+ }
40
+ :global(svg) {
41
+ width: 0.9em;
42
+ height: 0.9em;
43
+ }
44
+ }
45
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { HTMLButtonAttributes } from 'svelte/elements';
2
+ type $$ComponentProps = Omit<HTMLButtonAttributes, `type` | `aria-label`> & {
3
+ copied?: boolean;
4
+ label: string;
5
+ };
6
+ declare const CopyButton: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type CopyButton = ReturnType<typeof CopyButton>;
8
+ export default CopyButton;