matterviz 0.3.7 → 0.4.0
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.
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +1 -1
- package/dist/api/optimade.js +32 -26
- package/dist/app.css +0 -3
- package/dist/brillouin/BrillouinZone.svelte +8 -3
- package/dist/brillouin/BrillouinZone.svelte.d.ts +2 -1
- package/dist/brillouin/BrillouinZoneScene.svelte +52 -6
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -0
- package/dist/brillouin/BrillouinZoneTooltip.svelte +16 -25
- package/dist/brillouin/compute.js +10 -14
- package/dist/chempot-diagram/ChemPotDiagram.svelte +14 -13
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +12 -15
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +8 -10
- package/dist/chempot-diagram/async-compute.svelte.js +3 -1
- package/dist/chempot-diagram/chempot-worker.js +2 -1
- package/dist/chempot-diagram/compute.d.ts +1 -1
- package/dist/chempot-diagram/compute.js +17 -19
- package/dist/colors/index.js +6 -5
- package/dist/composition/FormulaFilter.svelte +12 -6
- package/dist/composition/PieChart.svelte +6 -5
- package/dist/composition/chem-sys.d.ts +8 -0
- package/dist/composition/chem-sys.js +85 -0
- package/dist/composition/format.js +4 -2
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -0
- package/dist/composition/parse.js +25 -13
- package/dist/convex-hull/ConvexHull2D.svelte +12 -10
- package/dist/convex-hull/ConvexHull3D.svelte +5 -5
- package/dist/convex-hull/ConvexHull4D.svelte +5 -9
- package/dist/convex-hull/ConvexHullStats.svelte +12 -12
- package/dist/convex-hull/GasPressureControls.svelte +4 -4
- package/dist/convex-hull/TemperatureSlider.svelte +2 -2
- package/dist/convex-hull/demo-temperature.d.ts +1 -1
- package/dist/convex-hull/demo-temperature.js +20 -22
- package/dist/convex-hull/gas-thermodynamics.d.ts +2 -2
- package/dist/convex-hull/gas-thermodynamics.js +22 -30
- package/dist/convex-hull/helpers.d.ts +3 -0
- package/dist/convex-hull/helpers.js +17 -9
- package/dist/convex-hull/index.d.ts +1 -1
- package/dist/convex-hull/thermodynamics.js +83 -78
- package/dist/convex-hull/types.d.ts +1 -1
- package/dist/coordination/CoordinationBarPlot.svelte +23 -23
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSlice.svelte +13 -5
- package/dist/fermi-surface/FermiSurface.svelte +11 -5
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +3 -0
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +8 -34
- package/dist/fermi-surface/compute.js +59 -59
- package/dist/fermi-surface/export.js +3 -2
- package/dist/fermi-surface/parse.js +7 -4
- package/dist/fermi-surface/types.d.ts +1 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +23 -21
- package/dist/heatmap-matrix/index.js +1 -1
- package/dist/io/decompress.js +4 -2
- package/dist/io/export.d.ts +4 -4
- package/dist/io/export.js +47 -25
- package/dist/io/fetch.js +5 -1
- package/dist/io/file-drop.d.ts +1 -1
- package/dist/io/file-drop.js +35 -36
- package/dist/io/url-drop.js +64 -33
- package/dist/isosurface/parse.js +6 -7
- package/dist/isosurface/slice.js +5 -4
- package/dist/isosurface/types.js +1 -1
- package/dist/keyboard.d.ts +3 -0
- package/dist/keyboard.js +23 -0
- package/dist/labels.d.ts +1 -1
- package/dist/labels.js +8 -7
- package/dist/layout/PropertyFilter.svelte +3 -2
- package/dist/layout/SettingsSection.svelte +1 -1
- package/dist/layout/json-tree/JsonNode.svelte +1 -1
- package/dist/layout/json-tree/JsonTree.svelte +2 -2
- package/dist/layout/json-tree/utils.js +5 -4
- package/dist/marching-cubes.js +8 -13
- package/dist/math.d.ts +5 -1
- package/dist/math.js +24 -9
- package/dist/overlays/DraggablePane.svelte +4 -4
- package/dist/periodic-table/PeriodicTable.svelte +20 -9
- package/dist/periodic-table/PropertySelect.svelte +1 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +9 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +1 -1
- package/dist/phase-diagram/build-diagram.js +2 -2
- package/dist/phase-diagram/parse.js +6 -5
- package/dist/phase-diagram/types.d.ts +1 -1
- package/dist/phase-diagram/utils.d.ts +3 -3
- package/dist/phase-diagram/utils.js +8 -12
- package/dist/plot/{BarPlot.svelte → bar/BarPlot.svelte} +229 -587
- package/dist/plot/{BarPlot.svelte.d.ts → bar/BarPlot.svelte.d.ts} +5 -5
- package/dist/plot/{BarPlotControls.svelte → bar/BarPlotControls.svelte} +6 -5
- package/dist/plot/{BarPlotControls.svelte.d.ts → bar/BarPlotControls.svelte.d.ts} +3 -3
- package/dist/plot/{SpacegroupBarPlot.svelte → bar/SpacegroupBarPlot.svelte} +6 -6
- package/dist/plot/{SpacegroupBarPlot.svelte.d.ts → bar/SpacegroupBarPlot.svelte.d.ts} +1 -1
- package/dist/plot/bar/data.d.ts +40 -0
- package/dist/plot/bar/data.js +154 -0
- package/dist/plot/bar/geometry.d.ts +39 -0
- package/dist/plot/bar/geometry.js +60 -0
- package/dist/plot/bar/index.d.ts +3 -0
- package/dist/plot/bar/index.js +3 -0
- package/dist/plot/box/BoxPlot.svelte +1462 -0
- package/dist/plot/box/BoxPlot.svelte.d.ts +94 -0
- package/dist/plot/box/BoxPlotControls.svelte +109 -0
- package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
- package/dist/plot/box/Violin.svelte +14 -0
- package/dist/plot/box/Violin.svelte.d.ts +70 -0
- package/dist/plot/box/box-plot.d.ts +55 -0
- package/dist/plot/box/box-plot.js +126 -0
- package/dist/plot/box/index.d.ts +5 -0
- package/dist/plot/box/index.js +5 -0
- package/dist/plot/box/kde.d.ts +16 -0
- package/dist/plot/box/kde.js +160 -0
- package/dist/plot/box/quantile.d.ts +3 -0
- package/dist/plot/box/quantile.js +53 -0
- package/dist/plot/{auto-place.js → core/auto-place.js} +2 -2
- package/dist/plot/core/axis-utils.d.ts +46 -0
- package/dist/plot/core/axis-utils.js +110 -0
- package/dist/plot/{AxisLabel.svelte → core/components/AxisLabel.svelte} +2 -2
- package/dist/plot/{AxisLabel.svelte.d.ts → core/components/AxisLabel.svelte.d.ts} +1 -1
- package/dist/plot/{ColorBar.svelte → core/components/ColorBar.svelte} +36 -33
- package/dist/plot/{ColorBar.svelte.d.ts → core/components/ColorBar.svelte.d.ts} +2 -2
- package/dist/plot/{ColorScaleSelect.svelte → core/components/ColorScaleSelect.svelte} +4 -3
- package/dist/plot/{ColorScaleSelect.svelte.d.ts → core/components/ColorScaleSelect.svelte.d.ts} +2 -2
- package/dist/plot/core/components/ControlPane.svelte +46 -0
- package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
- package/dist/plot/{FillArea.svelte → core/components/FillArea.svelte} +17 -6
- package/dist/plot/{FillArea.svelte.d.ts → core/components/FillArea.svelte.d.ts} +1 -1
- package/dist/plot/{InteractiveAxisLabel.svelte → core/components/InteractiveAxisLabel.svelte} +3 -3
- package/dist/plot/{InteractiveAxisLabel.svelte.d.ts → core/components/InteractiveAxisLabel.svelte.d.ts} +2 -2
- package/dist/plot/{Line.svelte → core/components/Line.svelte} +30 -13
- package/dist/plot/{PlotAxis.svelte → core/components/PlotAxis.svelte} +7 -5
- package/dist/plot/{PlotAxis.svelte.d.ts → core/components/PlotAxis.svelte.d.ts} +3 -2
- package/dist/plot/{PlotControls.svelte → core/components/PlotControls.svelte} +17 -29
- package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
- package/dist/plot/{PlotLegend.svelte → core/components/PlotLegend.svelte} +21 -10
- package/dist/plot/{PlotLegend.svelte.d.ts → core/components/PlotLegend.svelte.d.ts} +3 -2
- package/dist/plot/{PlotTooltip.svelte → core/components/PlotTooltip.svelte} +17 -1
- package/dist/plot/{PlotTooltip.svelte.d.ts → core/components/PlotTooltip.svelte.d.ts} +8 -0
- package/dist/plot/{PortalSelect.svelte → core/components/PortalSelect.svelte} +11 -7
- package/dist/plot/{ReferenceLine.svelte → core/components/ReferenceLine.svelte} +3 -3
- package/dist/plot/{ReferenceLine.svelte.d.ts → core/components/ReferenceLine.svelte.d.ts} +1 -1
- package/dist/plot/{ReferenceLine3D.svelte → core/components/ReferenceLine3D.svelte} +4 -4
- package/dist/plot/{ReferenceLine3D.svelte.d.ts → core/components/ReferenceLine3D.svelte.d.ts} +2 -2
- package/dist/plot/{ReferencePlane.svelte → core/components/ReferencePlane.svelte} +7 -7
- package/dist/plot/{ReferencePlane.svelte.d.ts → core/components/ReferencePlane.svelte.d.ts} +2 -2
- package/dist/plot/{ZeroLines.svelte → core/components/ZeroLines.svelte} +3 -3
- package/dist/plot/{ZeroLines.svelte.d.ts → core/components/ZeroLines.svelte.d.ts} +3 -3
- package/dist/plot/{ZoomRect.svelte → core/components/ZoomRect.svelte} +1 -1
- package/dist/plot/{ZoomRect.svelte.d.ts → core/components/ZoomRect.svelte.d.ts} +1 -1
- package/dist/plot/core/components/index.d.ts +17 -0
- package/dist/plot/core/components/index.js +17 -0
- package/dist/plot/{data-cleaning.d.ts → core/data-cleaning.d.ts} +71 -1
- package/dist/plot/{data-cleaning.js → core/data-cleaning.js} +3 -5
- package/dist/plot/{data-transform.d.ts → core/data-transform.d.ts} +2 -2
- package/dist/plot/{data-transform.js → core/data-transform.js} +3 -3
- package/dist/plot/core/fill-utils.d.ts +33 -0
- package/dist/plot/core/fill-utils.js +388 -0
- package/dist/plot/{hover-lock.svelte.js → core/hover-lock.svelte.js} +5 -6
- package/dist/plot/core/index.d.ts +10 -0
- package/dist/plot/core/index.js +11 -0
- package/dist/plot/core/interactions.d.ts +35 -0
- package/dist/plot/core/interactions.js +195 -0
- package/dist/plot/{layout.d.ts → core/layout.d.ts} +1 -0
- package/dist/plot/{layout.js → core/layout.js} +16 -8
- package/dist/plot/{reference-line.d.ts → core/reference-line.d.ts} +1 -1
- package/dist/plot/{reference-line.js → core/reference-line.js} +23 -36
- package/dist/plot/{scales.d.ts → core/scales.d.ts} +2 -2
- package/dist/plot/{scales.js → core/scales.js} +84 -85
- package/dist/plot/core/svg.d.ts +2 -0
- package/dist/plot/core/svg.js +41 -0
- package/dist/plot/{types.d.ts → core/types.d.ts} +19 -79
- package/dist/plot/{types.js → core/types.js} +1 -1
- package/dist/plot/{utils → core/utils}/label-placement.d.ts +2 -2
- package/dist/plot/core/utils/series-visibility.d.ts +26 -0
- package/dist/plot/{utils → core/utils}/series-visibility.js +29 -2
- package/dist/plot/core/utils.d.ts +11 -0
- package/dist/plot/core/utils.js +27 -0
- package/dist/plot/{Histogram.svelte → histogram/Histogram.svelte} +154 -294
- package/dist/plot/{Histogram.svelte.d.ts → histogram/Histogram.svelte.d.ts} +2 -2
- package/dist/plot/{HistogramControls.svelte → histogram/HistogramControls.svelte} +6 -6
- package/dist/plot/{HistogramControls.svelte.d.ts → histogram/HistogramControls.svelte.d.ts} +4 -4
- package/dist/plot/histogram/index.d.ts +2 -0
- package/dist/plot/histogram/index.js +2 -0
- package/dist/plot/index.d.ts +8 -41
- package/dist/plot/index.js +10 -39
- package/dist/plot/sankey/Sankey.svelte +700 -0
- package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
- package/dist/plot/sankey/SankeyControls.svelte +98 -0
- package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
- package/dist/plot/sankey/index.d.ts +4 -0
- package/dist/plot/sankey/index.js +3 -0
- package/dist/plot/sankey/sankey-types.d.ts +42 -0
- package/dist/plot/sankey/sankey-types.js +4 -0
- package/dist/plot/sankey/sankey.d.ts +52 -0
- package/dist/plot/sankey/sankey.js +187 -0
- package/dist/plot/{BinnedScatterPlot.svelte → scatter/BinnedScatterPlot.svelte} +61 -59
- package/dist/plot/{BinnedScatterPlot.svelte.d.ts → scatter/BinnedScatterPlot.svelte.d.ts} +4 -4
- package/dist/plot/{ElementScatter.svelte → scatter/ElementScatter.svelte} +6 -6
- package/dist/plot/{ElementScatter.svelte.d.ts → scatter/ElementScatter.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPlot.svelte → scatter/ScatterPlot.svelte} +221 -642
- package/dist/plot/{ScatterPlot.svelte.d.ts → scatter/ScatterPlot.svelte.d.ts} +7 -7
- package/dist/plot/{ScatterPlotControls.svelte → scatter/ScatterPlotControls.svelte} +6 -5
- package/dist/plot/{ScatterPlotControls.svelte.d.ts → scatter/ScatterPlotControls.svelte.d.ts} +1 -1
- package/dist/plot/{ScatterPoint.svelte → scatter/ScatterPoint.svelte} +7 -7
- package/dist/plot/{ScatterPoint.svelte.d.ts → scatter/ScatterPoint.svelte.d.ts} +3 -3
- package/dist/plot/{adaptive-density.d.ts → scatter/adaptive-density.d.ts} +14 -4
- package/dist/plot/{adaptive-density.js → scatter/adaptive-density.js} +46 -20
- package/dist/plot/{binned-scatter-types.d.ts → scatter/binned-scatter-types.d.ts} +3 -3
- package/dist/plot/scatter/index.d.ts +7 -0
- package/dist/plot/scatter/index.js +5 -0
- package/dist/plot/scatter/scatter-data.d.ts +19 -0
- package/dist/plot/scatter/scatter-data.js +212 -0
- package/dist/plot/{ScatterPlot3D.svelte → scatter-3d/ScatterPlot3D.svelte} +12 -10
- package/dist/plot/{ScatterPlot3D.svelte.d.ts → scatter-3d/ScatterPlot3D.svelte.d.ts} +7 -7
- package/dist/plot/{ScatterPlot3DControls.svelte → scatter-3d/ScatterPlot3DControls.svelte} +5 -4
- package/dist/plot/{ScatterPlot3DControls.svelte.d.ts → scatter-3d/ScatterPlot3DControls.svelte.d.ts} +2 -2
- package/dist/plot/{ScatterPlot3DScene.svelte → scatter-3d/ScatterPlot3DScene.svelte} +11 -11
- package/dist/plot/{ScatterPlot3DScene.svelte.d.ts → scatter-3d/ScatterPlot3DScene.svelte.d.ts} +3 -3
- package/dist/plot/{Surface3D.svelte → scatter-3d/Surface3D.svelte} +1 -1
- package/dist/plot/{Surface3D.svelte.d.ts → scatter-3d/Surface3D.svelte.d.ts} +1 -1
- package/dist/plot/scatter-3d/index.d.ts +4 -0
- package/dist/plot/scatter-3d/index.js +4 -0
- package/dist/plot/sunburst/Sunburst.svelte +1045 -0
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -0
- package/dist/plot/sunburst/SunburstControls.svelte +200 -0
- package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
- package/dist/plot/sunburst/index.d.ts +4 -0
- package/dist/plot/sunburst/index.js +4 -0
- package/dist/plot/sunburst/render.d.ts +34 -0
- package/dist/plot/sunburst/render.js +122 -0
- package/dist/plot/sunburst/sunburst.d.ts +62 -0
- package/dist/plot/sunburst/sunburst.js +266 -0
- package/dist/rdf/RdfPlot.svelte +2 -1
- package/dist/rdf/calc-rdf.js +11 -24
- package/dist/sanitize.js +1 -1
- package/dist/settings.d.ts +65 -1
- package/dist/settings.js +262 -0
- package/dist/spectral/Bands.svelte +39 -29
- package/dist/spectral/Bands.svelte.d.ts +3 -4
- package/dist/spectral/BandsAndDos.svelte +1 -1
- package/dist/spectral/BrillouinBandsDos.svelte +39 -27
- package/dist/spectral/Dos.svelte +10 -19
- package/dist/spectral/Dos.svelte.d.ts +2 -2
- package/dist/spectral/helpers.d.ts +3 -1
- package/dist/spectral/helpers.js +95 -29
- package/dist/structure/AtomLegend.svelte +8 -9
- package/dist/structure/CellSelect.svelte +1 -2
- package/dist/structure/Cylinder.svelte +12 -8
- package/dist/structure/Cylinder.svelte.d.ts +4 -1
- package/dist/structure/Structure.svelte +78 -72
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +5 -6
- package/dist/structure/StructureScene.svelte +11 -10
- package/dist/structure/atom-properties.js +6 -6
- package/dist/structure/bond-order-perception.js +1 -1
- package/dist/structure/bonding.d.ts +1 -0
- package/dist/structure/bonding.js +43 -15
- package/dist/structure/export.js +27 -23
- package/dist/structure/index.d.ts +2 -4
- package/dist/structure/index.js +1 -3
- package/dist/structure/label-placement.js +4 -4
- package/dist/structure/measure.d.ts +3 -2
- package/dist/structure/measure.js +6 -5
- package/dist/structure/parse.js +121 -103
- package/dist/structure/pbc.js +4 -0
- package/dist/symmetry/SymmetryStats.svelte +2 -2
- package/dist/symmetry/index.d.ts +1 -1
- package/dist/symmetry/index.js +22 -24
- package/dist/symmetry/spacegroups.d.ts +7 -0
- package/dist/symmetry/spacegroups.js +48 -13
- package/dist/table/HeatmapTable.svelte +63 -11
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/index.d.ts +1 -3
- package/dist/table/index.js +1 -1
- package/dist/theme/index.js +8 -8
- package/dist/tooltip/KCoords.svelte +45 -0
- package/dist/tooltip/KCoords.svelte.d.ts +8 -0
- package/dist/tooltip/index.d.ts +1 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/trajectory/Trajectory.svelte +66 -40
- package/dist/trajectory/Trajectory.svelte.d.ts +2 -1
- package/dist/trajectory/TrajectoryExportPane.svelte +2 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +2 -1
- package/dist/trajectory/format-detect.d.ts +1 -0
- package/dist/trajectory/format-detect.js +25 -11
- package/dist/trajectory/frame-reader.js +17 -50
- package/dist/trajectory/helpers.js +1 -1
- package/dist/trajectory/index.js +1 -1
- package/dist/trajectory/parse/hdf5.js +1 -1
- package/dist/trajectory/parse/index.js +14 -6
- package/dist/trajectory/parse/vasp.js +36 -17
- package/dist/trajectory/parse/xyz.d.ts +24 -0
- package/dist/trajectory/parse/xyz.js +102 -89
- package/dist/trajectory/plotting.d.ts +1 -1
- package/dist/trajectory/plotting.js +15 -15
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +6 -4
- package/dist/xrd/XrdPlot.svelte +2 -1
- package/dist/xrd/calc-xrd.js +15 -12
- package/dist/xrd/parse.js +2 -2
- package/package.json +22 -18
- package/dist/plot/PlotControls.svelte.d.ts +0 -4
- package/dist/plot/axis-utils.d.ts +0 -19
- package/dist/plot/axis-utils.js +0 -78
- package/dist/plot/defaults.d.ts +0 -19
- package/dist/plot/defaults.js +0 -9
- package/dist/plot/fill-utils.d.ts +0 -46
- package/dist/plot/fill-utils.js +0 -322
- package/dist/plot/interactions.d.ts +0 -12
- package/dist/plot/interactions.js +0 -101
- package/dist/plot/svg.d.ts +0 -1
- package/dist/plot/svg.js +0 -11
- package/dist/plot/utils/series-visibility.d.ts +0 -15
- package/dist/plot/utils.d.ts +0 -1
- package/dist/plot/utils.js +0 -14
- /package/dist/plot/{auto-place.d.ts → core/auto-place.d.ts} +0 -0
- /package/dist/plot/{Line.svelte.d.ts → core/components/Line.svelte.d.ts} +0 -0
- /package/dist/plot/{PortalSelect.svelte.d.ts → core/components/PortalSelect.svelte.d.ts} +0 -0
- /package/dist/plot/{hover-lock.svelte.d.ts → core/hover-lock.svelte.d.ts} +0 -0
- /package/dist/plot/{utils → core/utils}/label-placement.js +0 -0
- /package/dist/plot/{binned-scatter-types.js → scatter/binned-scatter-types.js} +0 -0
package/dist/io/decompress.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { COMPRESSION_EXTENSIONS_REGEX, COMPRESSION_FORMATS } from '../constants';
|
|
2
|
+
import { to_error } from '../utils';
|
|
2
3
|
export function detect_compression_format(filename) {
|
|
3
4
|
const lower = filename.toLowerCase();
|
|
4
5
|
for (const [format, extensions] of Object.entries(COMPRESSION_FORMATS)) {
|
|
@@ -45,7 +46,8 @@ export function decompress_file(file) {
|
|
|
45
46
|
reader.addEventListener(`load`, () => {
|
|
46
47
|
try {
|
|
47
48
|
const result = reader.result;
|
|
48
|
-
|
|
49
|
+
// strict null check: empty files legitimately read as '' (falsy)
|
|
50
|
+
if (result === null)
|
|
49
51
|
throw new Error(`Failed to read file`);
|
|
50
52
|
if (is_supported && format) {
|
|
51
53
|
if (!(result instanceof ArrayBuffer))
|
|
@@ -62,7 +64,7 @@ export function decompress_file(file) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
catch (error) {
|
|
65
|
-
reject(error);
|
|
67
|
+
reject(to_error(error));
|
|
66
68
|
}
|
|
67
69
|
}, { once: true });
|
|
68
70
|
reader.addEventListener(`error`, () => reject(new Error(`Failed to read file ${file.name}`)));
|
package/dist/io/export.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import type { AnyStructure } from '../structure';
|
|
|
2
2
|
import { type Camera, type Scene } from 'three';
|
|
3
3
|
export declare function canvas_to_png_blob(canvas: HTMLCanvasElement, png_dpi?: number, scene?: Scene | null, camera?: Camera | null): Promise<Blob>;
|
|
4
4
|
export declare function export_canvas_as_png(canvas: HTMLCanvasElement | null, structure_or_filename: AnyStructure | string | undefined, png_dpi?: number, scene?: Scene | null, camera?: Camera | null): void;
|
|
5
|
-
export declare function svg_to_svg_string(svg_element: SVGElement): string;
|
|
6
|
-
export declare function export_svg_as_svg(svg_element: SVGElement | null, filename: string): void;
|
|
7
|
-
export declare function svg_to_png_blob(svg_element: SVGElement, png_dpi?: number): Promise<Blob>;
|
|
8
|
-
export declare function export_svg_as_png(svg_element: SVGElement | null, filename: string, png_dpi?: number): void;
|
|
5
|
+
export declare function svg_to_svg_string(svg_element: SVGElement, inline_styles?: readonly string[]): string;
|
|
6
|
+
export declare function export_svg_as_svg(svg_element: SVGElement | null, filename: string, inline_styles?: readonly string[]): void;
|
|
7
|
+
export declare function svg_to_png_blob(svg_element: SVGElement, png_dpi?: number, inline_styles?: readonly string[]): Promise<Blob>;
|
|
8
|
+
export declare function export_svg_as_png(svg_element: SVGElement | null, filename: string, png_dpi?: number, inline_styles?: readonly string[]): void;
|
|
9
9
|
export declare function get_ffmpeg_conversion_command(input_filename: string): string;
|
|
10
10
|
export declare function export_trajectory_video(canvas: HTMLCanvasElement | null, filename: string, options?: {
|
|
11
11
|
fps?: number;
|
package/dist/io/export.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { download } from './fetch';
|
|
2
2
|
import { create_structure_filename } from '../structure/export';
|
|
3
|
+
import { to_error } from '../utils';
|
|
3
4
|
import { Vector2 } from 'three';
|
|
4
5
|
function is_webgl_renderer_like(value) {
|
|
5
6
|
if (typeof value !== `object` || !value)
|
|
@@ -12,7 +13,8 @@ function is_webgl_renderer_like(value) {
|
|
|
12
13
|
typeof renderer_obj.setSize === `function`);
|
|
13
14
|
}
|
|
14
15
|
function get_canvas_renderer(canvas) {
|
|
15
|
-
|
|
16
|
+
// oxlint-disable-next-line no-underscore-dangle -- three.js stores its renderer here
|
|
17
|
+
const renderer_val = canvas.__renderer;
|
|
16
18
|
return is_webgl_renderer_like(renderer_val) ? renderer_val : undefined;
|
|
17
19
|
}
|
|
18
20
|
// Capture a WebGL canvas as a PNG Blob at the given DPI.
|
|
@@ -36,7 +38,7 @@ export function canvas_to_png_blob(canvas, png_dpi = 150, scene = null, camera =
|
|
|
36
38
|
}, `image/png`);
|
|
37
39
|
}
|
|
38
40
|
catch (error) {
|
|
39
|
-
reject(error);
|
|
41
|
+
reject(to_error(error));
|
|
40
42
|
}
|
|
41
43
|
});
|
|
42
44
|
}
|
|
@@ -63,7 +65,7 @@ export function canvas_to_png_blob(canvas, png_dpi = 150, scene = null, camera =
|
|
|
63
65
|
}
|
|
64
66
|
catch (error) {
|
|
65
67
|
restore();
|
|
66
|
-
reject(error);
|
|
68
|
+
reject(to_error(error));
|
|
67
69
|
}
|
|
68
70
|
});
|
|
69
71
|
}
|
|
@@ -90,34 +92,56 @@ export function export_canvas_as_png(canvas, structure_or_filename, png_dpi = 15
|
|
|
90
92
|
}
|
|
91
93
|
// Helper to ensure font-family is set on SVG root
|
|
92
94
|
function set_svg_font_family(svg) {
|
|
93
|
-
const style = svg.getAttribute(`style`)
|
|
95
|
+
const style = svg.getAttribute(`style`) ?? ``;
|
|
94
96
|
if (!style.includes(`font-family`)) {
|
|
95
97
|
svg.setAttribute(`style`, `${style}${style ? `;` : ``}font-family:sans-serif;`);
|
|
96
98
|
}
|
|
97
99
|
// Also set as attribute for extra robustness
|
|
98
100
|
svg.setAttribute(`font-family`, `sans-serif`);
|
|
99
101
|
}
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
// Copy the given computed-style props from each live SVG element to its clone counterpart;
|
|
103
|
+
// identical structure lets querySelectorAll(`*`) walk both in lockstep. Writes clone-only.
|
|
104
|
+
function inline_computed_styles(live, clone, properties) {
|
|
105
|
+
const live_els = [live, ...live.querySelectorAll(`*`)];
|
|
106
|
+
const clone_els = [clone, ...clone.querySelectorAll(`*`)];
|
|
107
|
+
for (const [idx, live_el] of live_els.entries()) {
|
|
108
|
+
const computed = getComputedStyle(live_el);
|
|
109
|
+
for (const prop of properties) {
|
|
110
|
+
const val = computed.getPropertyValue(prop);
|
|
111
|
+
if (val)
|
|
112
|
+
clone_els[idx].setAttribute(prop, val);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Clone, inline the given computed-style props, ensure font-family + xmlns, then serialize
|
|
117
|
+
// to a standalone SVG string. Never mutates the live element.
|
|
118
|
+
function serialize_svg_for_export(svg_element, inline_styles = []) {
|
|
119
|
+
const clone = svg_element.cloneNode(true);
|
|
120
|
+
if (inline_styles.length)
|
|
121
|
+
inline_computed_styles(svg_element, clone, inline_styles);
|
|
122
|
+
set_svg_font_family(clone);
|
|
123
|
+
if (!clone.hasAttribute(`xmlns`)) {
|
|
124
|
+
clone.setAttribute(`xmlns`, `http://www.w3.org/2000/svg`);
|
|
109
125
|
}
|
|
110
|
-
|
|
126
|
+
return new XMLSerializer().serializeToString(clone);
|
|
127
|
+
}
|
|
128
|
+
// Wrap serialize_svg_for_export's output as a full SVG document string (XML declaration +
|
|
129
|
+
// DOCTYPE + SVG), suitable for saving to file.
|
|
130
|
+
export function svg_to_svg_string(svg_element,
|
|
131
|
+
// CSS props to inline from computed styles as presentation attributes; a standalone SVG
|
|
132
|
+
// drops page stylesheets (e.g. Svelte component styles), so class-based styling is lost.
|
|
133
|
+
inline_styles = []) {
|
|
134
|
+
const svg_string = serialize_svg_for_export(svg_element, inline_styles);
|
|
111
135
|
return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n${svg_string}`;
|
|
112
136
|
}
|
|
113
137
|
// Export SVG element as SVG file (triggers browser download)
|
|
114
|
-
export function export_svg_as_svg(svg_element, filename) {
|
|
138
|
+
export function export_svg_as_svg(svg_element, filename, inline_styles = []) {
|
|
115
139
|
if (!svg_element) {
|
|
116
140
|
console.warn(`SVG element not found for export`);
|
|
117
141
|
return;
|
|
118
142
|
}
|
|
119
143
|
try {
|
|
120
|
-
const svg_content = svg_to_svg_string(svg_element);
|
|
144
|
+
const svg_content = svg_to_svg_string(svg_element, inline_styles);
|
|
121
145
|
download(svg_content, filename, `image/svg+xml;charset=utf-8`);
|
|
122
146
|
}
|
|
123
147
|
catch (error) {
|
|
@@ -129,7 +153,7 @@ export function export_svg_as_svg(svg_element, filename) {
|
|
|
129
153
|
// Image element, and returns the resulting PNG Blob. Rejects if viewBox is
|
|
130
154
|
// missing or dimensions are invalid (zero width/height).
|
|
131
155
|
// DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
|
|
132
|
-
export function svg_to_png_blob(svg_element, png_dpi = 150) {
|
|
156
|
+
export function svg_to_png_blob(svg_element, png_dpi = 150, inline_styles = []) {
|
|
133
157
|
const viewBox = svg_element.getAttribute(`viewBox`)?.trim();
|
|
134
158
|
if (!viewBox)
|
|
135
159
|
return Promise.reject(new Error(`SVG viewBox not found for PNG export`));
|
|
@@ -153,9 +177,7 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
|
|
|
153
177
|
return Promise.reject(new Error(`Canvas 2D context not available`));
|
|
154
178
|
canvas.width = pixel_width;
|
|
155
179
|
canvas.height = pixel_height;
|
|
156
|
-
const
|
|
157
|
-
set_svg_font_family(cloned_svg);
|
|
158
|
-
const serialized = new XMLSerializer().serializeToString(cloned_svg);
|
|
180
|
+
const serialized = serialize_svg_for_export(svg_element, inline_styles);
|
|
159
181
|
const svg_blob = new Blob([serialized], { type: `image/svg+xml;charset=utf-8` });
|
|
160
182
|
const svg_data_url = URL.createObjectURL(svg_blob);
|
|
161
183
|
return new Promise((resolve, reject) => {
|
|
@@ -172,7 +194,7 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
|
|
|
172
194
|
}, `image/png`, 1);
|
|
173
195
|
}
|
|
174
196
|
catch (error) {
|
|
175
|
-
reject(error);
|
|
197
|
+
reject(to_error(error));
|
|
176
198
|
}
|
|
177
199
|
finally {
|
|
178
200
|
URL.revokeObjectURL(svg_data_url);
|
|
@@ -186,12 +208,12 @@ export function svg_to_png_blob(svg_element, png_dpi = 150) {
|
|
|
186
208
|
});
|
|
187
209
|
}
|
|
188
210
|
// Export SVG element as PNG (triggers browser download)
|
|
189
|
-
export function export_svg_as_png(svg_element, filename, png_dpi = 150) {
|
|
211
|
+
export function export_svg_as_png(svg_element, filename, png_dpi = 150, inline_styles = []) {
|
|
190
212
|
if (!svg_element) {
|
|
191
213
|
console.warn(`SVG element not found for PNG export`);
|
|
192
214
|
return;
|
|
193
215
|
}
|
|
194
|
-
svg_to_png_blob(svg_element, png_dpi)
|
|
216
|
+
svg_to_png_blob(svg_element, png_dpi, inline_styles)
|
|
195
217
|
.then((blob) => download(blob, filename, `image/png`))
|
|
196
218
|
.catch((error) => console.error(`Error exporting PNG:`, error));
|
|
197
219
|
}
|
|
@@ -283,7 +305,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
283
305
|
resolve();
|
|
284
306
|
}
|
|
285
307
|
catch (error) {
|
|
286
|
-
reject(error);
|
|
308
|
+
reject(to_error(error));
|
|
287
309
|
}
|
|
288
310
|
});
|
|
289
311
|
recorder.addEventListener(`error`, (event) => {
|
|
@@ -309,7 +331,7 @@ export async function export_trajectory_video(canvas, filename, options = {}) {
|
|
|
309
331
|
catch (error) {
|
|
310
332
|
if (!is_resolved) {
|
|
311
333
|
is_resolved = true;
|
|
312
|
-
reject(error);
|
|
334
|
+
reject(to_error(error));
|
|
313
335
|
}
|
|
314
336
|
}
|
|
315
337
|
});
|
package/dist/io/fetch.js
CHANGED
|
@@ -22,7 +22,11 @@ function default_download(data, filename, type) {
|
|
|
22
22
|
link.style.display = `none`;
|
|
23
23
|
link.href = url;
|
|
24
24
|
link.download = filename;
|
|
25
|
-
document
|
|
25
|
+
// keep the synthetic download click from bubbling to document-level handlers
|
|
26
|
+
// (e.g. DraggablePane's click-outside) that would dismiss an open pane the export
|
|
27
|
+
// button lives in
|
|
28
|
+
link.addEventListener(`click`, (evt) => evt.stopPropagation());
|
|
29
|
+
document.body.append(link);
|
|
26
30
|
link.click();
|
|
27
31
|
link.remove();
|
|
28
32
|
URL.revokeObjectURL(url);
|
package/dist/io/file-drop.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export interface FileDropOptions {
|
|
|
4
4
|
on_error?: (msg: string) => void;
|
|
5
5
|
set_loading?: (loading: boolean) => void;
|
|
6
6
|
}
|
|
7
|
-
export declare
|
|
7
|
+
export declare const create_file_drop_handler: (opts: FileDropOptions) => ((event: DragEvent) => Promise<void>);
|
package/dist/io/file-drop.js
CHANGED
|
@@ -1,43 +1,42 @@
|
|
|
1
1
|
// Shared file-drop handler composable for drag-and-drop file loading.
|
|
2
2
|
import { decompress_file } from './decompress';
|
|
3
3
|
import { handle_url_drop } from './url-drop';
|
|
4
|
+
import { to_error } from '../utils';
|
|
4
5
|
// Handles URL drops (from FilePicker), direct file drops with decompression,
|
|
5
6
|
// loading state, and error reporting.
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export const create_file_drop_handler = (opts) => async (event) => {
|
|
8
|
+
event.preventDefault();
|
|
9
|
+
if (!opts.allow())
|
|
10
|
+
return;
|
|
11
|
+
opts.set_loading?.(true);
|
|
12
|
+
let drop_filename = ``;
|
|
13
|
+
try {
|
|
14
|
+
let url_error;
|
|
15
|
+
const handled = await handle_url_drop(event, opts.on_drop).catch((exc) => {
|
|
16
|
+
url_error = to_error(exc).message;
|
|
17
|
+
return false;
|
|
18
|
+
});
|
|
19
|
+
if (handled)
|
|
20
|
+
return;
|
|
21
|
+
const file = event.dataTransfer?.files[0];
|
|
22
|
+
if (!file) {
|
|
23
|
+
if (url_error)
|
|
24
|
+
opts.on_error?.(`Failed to load from URL: ${url_error}`);
|
|
10
25
|
return;
|
|
11
|
-
opts.set_loading?.(true);
|
|
12
|
-
let drop_filename = ``;
|
|
13
|
-
try {
|
|
14
|
-
let url_error;
|
|
15
|
-
const handled = await handle_url_drop(event, opts.on_drop).catch((exc) => {
|
|
16
|
-
url_error = exc instanceof Error ? exc.message : String(exc);
|
|
17
|
-
return false;
|
|
18
|
-
});
|
|
19
|
-
if (handled)
|
|
20
|
-
return;
|
|
21
|
-
const file = event.dataTransfer?.files[0];
|
|
22
|
-
if (!file) {
|
|
23
|
-
if (url_error)
|
|
24
|
-
opts.on_error?.(`Failed to load from URL: ${url_error}`);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
drop_filename = file.name;
|
|
28
|
-
const { content, filename } = await decompress_file(file);
|
|
29
|
-
if (content)
|
|
30
|
-
await opts.on_drop(content, filename);
|
|
31
|
-
}
|
|
32
|
-
catch (exc) {
|
|
33
|
-
const detail = exc instanceof Error ? exc.message : String(exc);
|
|
34
|
-
const msg = drop_filename
|
|
35
|
-
? `Failed to load file ${drop_filename}: ${detail}`
|
|
36
|
-
: `Failed to load file: ${detail}`;
|
|
37
|
-
opts.on_error?.(msg);
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
opts.set_loading?.(false);
|
|
41
26
|
}
|
|
42
|
-
|
|
43
|
-
}
|
|
27
|
+
drop_filename = file.name;
|
|
28
|
+
const { content, filename } = await decompress_file(file);
|
|
29
|
+
if (content)
|
|
30
|
+
await opts.on_drop(content, filename);
|
|
31
|
+
}
|
|
32
|
+
catch (exc) {
|
|
33
|
+
const detail = to_error(exc).message;
|
|
34
|
+
const msg = drop_filename
|
|
35
|
+
? `Failed to load file ${drop_filename}: ${detail}`
|
|
36
|
+
: `Failed to load file: ${detail}`;
|
|
37
|
+
opts.on_error?.(msg);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
opts.set_loading?.(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
package/dist/io/url-drop.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { load_binary_traj } from '../trajectory/parse';
|
|
2
|
-
|
|
2
|
+
import { decompress_data_binary } from './decompress';
|
|
3
|
+
const BINARY_EXTENSIONS = new Set(`h5 hdf5 traj npz pkl dat gz gzip zip bz2 xz brml raw`.split(` `));
|
|
3
4
|
const TEXT_EXTENSIONS = new Set(`xyz extxyz json cif poscar yaml yml txt md py js ts css html xml`.split(` `));
|
|
4
5
|
const VASP_BASENAME_RE = /^(poscar|xdatcar|contcar)$/i;
|
|
6
|
+
const GZ_EXT_RE = /\.(gz|gzip)$/i;
|
|
5
7
|
// Extract filename from Content-Disposition header, falling back to url_basename.
|
|
6
8
|
function extract_filename(headers, fallback) {
|
|
7
9
|
if (!headers)
|
|
@@ -9,9 +11,13 @@ function extract_filename(headers, fallback) {
|
|
|
9
11
|
const content_disposition_str = headers.get(`content-disposition`);
|
|
10
12
|
if (!content_disposition_str)
|
|
11
13
|
return fallback;
|
|
12
|
-
const star_match = /filename\*=(
|
|
14
|
+
const star_match = /filename\*=([^;]+)/i.exec(content_disposition_str);
|
|
13
15
|
if (star_match?.[1]) {
|
|
14
|
-
|
|
16
|
+
let raw = star_match[1].trim().replaceAll(/^"|"$/g, ``);
|
|
17
|
+
// Strip any RFC 5987 charset'language' prefix; bare values pass through unchanged
|
|
18
|
+
const ext_value_match = /^[\w!#$%&+^`{}~-]+'[\w-]*'(.*)$/.exec(raw);
|
|
19
|
+
if (ext_value_match)
|
|
20
|
+
raw = ext_value_match[1];
|
|
15
21
|
try {
|
|
16
22
|
return decodeURIComponent(raw);
|
|
17
23
|
}
|
|
@@ -20,7 +26,24 @@ function extract_filename(headers, fallback) {
|
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
28
|
const plain_match = /filename\s*=\s*"?([^";]+)"?/i.exec(content_disposition_str);
|
|
23
|
-
|
|
29
|
+
// truthiness check (not ??) so whitespace-only `filename=` values fall back too
|
|
30
|
+
const name = plain_match?.[1]?.trim();
|
|
31
|
+
if (!name)
|
|
32
|
+
return fallback;
|
|
33
|
+
return name;
|
|
34
|
+
}
|
|
35
|
+
const ext_of = (name) => name.split(`.`).pop()?.toLowerCase() ?? ``;
|
|
36
|
+
// Whether the file inside a .gz/.gzip wrapper is a known binary format that a
|
|
37
|
+
// lossy text decode would corrupt (bytes >= 0x80 → U+FFFD)
|
|
38
|
+
const has_binary_inner_ext = (filename) => BINARY_EXTENSIONS.has(ext_of(filename.replace(GZ_EXT_RE, ``)));
|
|
39
|
+
// Gunzip a fetched payload → [content, filename] with .gz/.gzip stripped; content
|
|
40
|
+
// stays an ArrayBuffer for binary inner formats, decoded string otherwise
|
|
41
|
+
async function decompress_gz_payload(buffer, filename) {
|
|
42
|
+
const decompressed = await decompress_data_binary(buffer, `gzip`);
|
|
43
|
+
const content = has_binary_inner_ext(filename)
|
|
44
|
+
? decompressed
|
|
45
|
+
: new TextDecoder().decode(decompressed);
|
|
46
|
+
return [content, filename.replace(GZ_EXT_RE, ``)];
|
|
24
47
|
}
|
|
25
48
|
// Handle URL-based file drop data by fetching content lazily
|
|
26
49
|
export async function handle_url_drop(drag_event, callback) {
|
|
@@ -40,8 +63,10 @@ export async function handle_url_drop(drag_event, callback) {
|
|
|
40
63
|
return true;
|
|
41
64
|
}
|
|
42
65
|
export async function load_from_url(url, callback) {
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
// Strip query string/hash before basename/extension detection so pre-signed
|
|
67
|
+
// URLs like traj.h5?X-Amz-Expires=300 still hit the right format path
|
|
68
|
+
const url_basename = url.split(/[?#]/)[0].split(`/`).pop() ?? url;
|
|
69
|
+
const ext = ext_of(url_basename);
|
|
45
70
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
46
71
|
// Force binary mode for known binary files to handle GitHub Pages content-type issues
|
|
47
72
|
const resp = await fetch(url);
|
|
@@ -51,17 +76,11 @@ export async function load_from_url(url, callback) {
|
|
|
51
76
|
// Handle gzipped files with proper content-encoding detection
|
|
52
77
|
if (ext === `gz` || ext === `gzip`) {
|
|
53
78
|
if (resp.headers.get(`content-encoding`) === `gzip`) {
|
|
54
|
-
// Browser
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else {
|
|
58
|
-
// Need to decompress manually
|
|
59
|
-
const { decompress_data } = await import(`./decompress`);
|
|
60
|
-
const buffer = await resp.arrayBuffer();
|
|
61
|
-
const content = await decompress_data(buffer, `gzip`);
|
|
62
|
-
// Remove .gz extension when manually decompressing
|
|
63
|
-
return callback(content, filename.replace(/\.gz$/, ``));
|
|
79
|
+
// Browser already decompressed the stored .gz (GitHub Pages-style serving), so
|
|
80
|
+
// the body is the inner file — keep binary inner formats (.h5.gz, ...) binary
|
|
81
|
+
return callback(await (has_binary_inner_ext(filename) ? resp.arrayBuffer() : resp.text()), filename);
|
|
64
82
|
}
|
|
83
|
+
return callback(...(await decompress_gz_payload(await resp.arrayBuffer(), filename)));
|
|
65
84
|
}
|
|
66
85
|
// For H5 files, always load as binary regardless of signature
|
|
67
86
|
// to handle files that have .h5/.hdf5 extensions but may not have the proper HDF5 signature
|
|
@@ -82,40 +101,52 @@ export async function load_from_url(url, callback) {
|
|
|
82
101
|
const buffer = await load_binary_traj(resp, `.traj`);
|
|
83
102
|
return callback(buffer, filename);
|
|
84
103
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
104
|
+
// Content-Encoding is transparent transport compression: fetch already
|
|
105
|
+
// decompressed the body, so binary formats (npz, pkl, brml, ...) must still
|
|
106
|
+
// be read as ArrayBuffer — .text() would corrupt them via lossy UTF-8 decode
|
|
88
107
|
return callback(await resp.arrayBuffer(), filename);
|
|
89
108
|
}
|
|
90
109
|
// Skip Range requests for known text formats to avoid production server issues
|
|
91
110
|
// Include VASP files that don't have extensions (POSCAR, XDATCAR, CONTCAR)
|
|
92
111
|
const is_known_text = TEXT_EXTENSIONS.has(ext) || VASP_BASENAME_RE.test(url_basename);
|
|
93
|
-
let
|
|
112
|
+
let sniffed_callback_args;
|
|
94
113
|
if (!is_known_text) {
|
|
114
|
+
// Only the Range sniff is guarded (failure → plain text fetch). Once magic bytes
|
|
115
|
+
// commit to a binary format, download/decompress errors must propagate instead of
|
|
116
|
+
// falling through to a text fetch that would parse the binary bytes as garbage.
|
|
117
|
+
let sniffed = null;
|
|
95
118
|
try {
|
|
96
|
-
// Check for magic bytes only for unknown formats
|
|
119
|
+
// Check for magic bytes only for unknown formats (covers extensionless URLs
|
|
120
|
+
// like blob: object URLs whose basenames are UUIDs)
|
|
97
121
|
const head = await fetch(url, { headers: { Range: `bytes=0-15` } });
|
|
98
122
|
if (head.ok) {
|
|
99
123
|
const buf = new Uint8Array(await head.arrayBuffer());
|
|
100
124
|
const is_gzip = buf[0] === 0x1f && buf[1] === 0x8b;
|
|
101
125
|
const is_hdf5 = buf[0] === 0x89 && buf[1] === 0x48 && buf[2] === 0x44 && buf[3] === 0x46;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
extract_filename(resp.headers, url_basename),
|
|
109
|
-
];
|
|
110
|
-
}
|
|
126
|
+
// ASE .traj files start with the Ulm signature "- of Ulm"
|
|
127
|
+
const is_ase_traj = [0x2d, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x6c, 0x6d].every((byte, idx) => buf[idx] === byte);
|
|
128
|
+
if (is_gzip)
|
|
129
|
+
sniffed = `gzip`;
|
|
130
|
+
else if (is_hdf5 || is_ase_traj)
|
|
131
|
+
sniffed = `binary`;
|
|
111
132
|
}
|
|
112
133
|
}
|
|
113
134
|
catch {
|
|
114
|
-
// Fall through to text fetch if HEAD request fails
|
|
135
|
+
// Fall through to text fetch if the Range HEAD request fails
|
|
136
|
+
}
|
|
137
|
+
if (sniffed) {
|
|
138
|
+
const resp = await fetch(url);
|
|
139
|
+
if (!resp.ok)
|
|
140
|
+
throw new Error(`Fetch failed: ${resp.status}`);
|
|
141
|
+
const filename = extract_filename(resp.headers, url_basename);
|
|
142
|
+
const buffer = await resp.arrayBuffer();
|
|
143
|
+
// Gunzip sniffed gzip — downstream parsers can't handle raw gzip bytes
|
|
144
|
+
sniffed_callback_args =
|
|
145
|
+
sniffed === `gzip` ? await decompress_gz_payload(buffer, filename) : [buffer, filename];
|
|
115
146
|
}
|
|
116
147
|
}
|
|
117
|
-
if (
|
|
118
|
-
return callback(...
|
|
148
|
+
if (sniffed_callback_args)
|
|
149
|
+
return callback(...sniffed_callback_args);
|
|
119
150
|
const resp = await fetch(url);
|
|
120
151
|
if (!resp.ok)
|
|
121
152
|
throw new Error(`Fetch failed: ${resp.status}`);
|
package/dist/isosurface/parse.js
CHANGED
|
@@ -43,8 +43,8 @@ function parse_float_block(text, pos, max_count, data, data_offset = 0) {
|
|
|
43
43
|
const start = pos;
|
|
44
44
|
while (pos < len && text.charCodeAt(pos) > 32)
|
|
45
45
|
pos++;
|
|
46
|
-
// Parse number
|
|
47
|
-
const num =
|
|
46
|
+
// Parse number (handles scientific notation)
|
|
47
|
+
const num = Number(text.slice(start, pos));
|
|
48
48
|
if (!Number.isNaN(num)) {
|
|
49
49
|
data[idx++] = num;
|
|
50
50
|
}
|
|
@@ -68,7 +68,7 @@ function read_line(text, pos) {
|
|
|
68
68
|
let end = pos;
|
|
69
69
|
while (end < text.length && text.charCodeAt(end) !== 10 && text.charCodeAt(end) !== 13)
|
|
70
70
|
end++;
|
|
71
|
-
const line = text.
|
|
71
|
+
const line = text.slice(pos, end);
|
|
72
72
|
let next = end;
|
|
73
73
|
if (next < text.length && text.charCodeAt(next) === 13)
|
|
74
74
|
next++; // skip \r
|
|
@@ -200,7 +200,7 @@ export function parse_chgcar(content) {
|
|
|
200
200
|
}
|
|
201
201
|
// Detect VASP 5+ format (has element symbols before counts)
|
|
202
202
|
const first_token = cur.line.trim().split(/\s+/)[0];
|
|
203
|
-
const has_element_symbols = isNaN(parseInt(first_token));
|
|
203
|
+
const has_element_symbols = isNaN(parseInt(first_token, 10));
|
|
204
204
|
if (has_element_symbols) {
|
|
205
205
|
element_symbols = cur.line.trim().split(/\s+/);
|
|
206
206
|
pos = cur.next;
|
|
@@ -210,14 +210,13 @@ export function parse_chgcar(content) {
|
|
|
210
210
|
return null;
|
|
211
211
|
}
|
|
212
212
|
atom_counts = cur.line.trim().split(/\s+/).map(Number);
|
|
213
|
-
pos = cur.next;
|
|
214
213
|
}
|
|
215
214
|
else {
|
|
216
215
|
atom_counts = cur.line.trim().split(/\s+/).map(Number);
|
|
217
216
|
const fallback_elements = [`H`, `He`, `Li`, `Be`, `B`, `C`, `N`, `O`, `F`, `Ne`];
|
|
218
217
|
element_symbols = atom_counts.map((_count, idx) => fallback_elements[idx % fallback_elements.length]);
|
|
219
|
-
pos = cur.next;
|
|
220
218
|
}
|
|
219
|
+
pos = cur.next;
|
|
221
220
|
if (pos >= content.length) {
|
|
222
221
|
console.error(`CHGCAR: file ends before coordinate mode line`);
|
|
223
222
|
return null;
|
|
@@ -519,7 +518,7 @@ export function parse_volumetric_file(content, filename) {
|
|
|
519
518
|
// Content-based detection (only parse first few lines, not the whole file)
|
|
520
519
|
// Find enough lines for detection without splitting the entire string
|
|
521
520
|
const detection_end = find_line_offset(content, 10);
|
|
522
|
-
const detection_text = content.
|
|
521
|
+
const detection_text = content.slice(0, detection_end);
|
|
523
522
|
const lines = detection_text.split(/\r?\n/);
|
|
524
523
|
// .cube detection: line 3 has 4 numbers (n_atoms + origin), line 4 has 4 numbers (grid dim + voxel)
|
|
525
524
|
if (lines.length > 4) {
|
package/dist/isosurface/slice.js
CHANGED
|
@@ -32,9 +32,10 @@ export function trilinear_interpolate(grid, fx, fy, fz, periodic) {
|
|
|
32
32
|
const x1 = periodic ? (x0 + 1) % nx : Math.min(x0 + 1, nx - 1);
|
|
33
33
|
const y1 = periodic ? (y0 + 1) % ny : Math.min(y0 + 1, ny - 1);
|
|
34
34
|
const z1 = periodic ? (z0 + 1) % nz : Math.min(z0 + 1, nz - 1);
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
35
|
+
// deltas from clamped lower index (non-periodic x0 clamps to nx-2 so floor(gx) may != x0)
|
|
36
|
+
const xd = periodic ? gx - Math.floor(gx) : gx - x0;
|
|
37
|
+
const yd = periodic ? gy - Math.floor(gy) : gy - y0;
|
|
38
|
+
const zd = periodic ? gz - Math.floor(gz) : gz - z0;
|
|
38
39
|
// 8-point interpolation
|
|
39
40
|
const c000 = grid[x0][y0][z0];
|
|
40
41
|
const c001 = grid[x0][y0][z1];
|
|
@@ -71,7 +72,7 @@ export function sample_hkl_slice(volume, miller_indices, distance, n_points) {
|
|
|
71
72
|
];
|
|
72
73
|
if (Math.hypot(...plane_normal) < 1e-12)
|
|
73
74
|
return null; // degenerate normal
|
|
74
|
-
const unit_normal = math.
|
|
75
|
+
const unit_normal = math.normalize_vec(plane_normal);
|
|
75
76
|
// In-plane basis vectors
|
|
76
77
|
const [u_vec, v_vec] = math.compute_in_plane_basis(unit_normal);
|
|
77
78
|
const cart_to_frac = math.create_cart_to_frac(lattice);
|
package/dist/isosurface/types.js
CHANGED
|
@@ -13,7 +13,7 @@ export const LAYER_COLORS = [
|
|
|
13
13
|
// Compute min/max/abs_max/mean of a 3D grid.
|
|
14
14
|
// Prefer using the precomputed `data_range` field on VolumetricData when available.
|
|
15
15
|
export function grid_data_range(grid) {
|
|
16
|
-
if (
|
|
16
|
+
if (grid.length === 0 || !grid[0]?.length || !grid[0][0]?.length) {
|
|
17
17
|
return { min: 0, max: 0, abs_max: 0, mean: 0 };
|
|
18
18
|
}
|
|
19
19
|
let [min_val, max_val] = [Infinity, -Infinity];
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type KeydownHandler = (event: KeyboardEvent) => boolean;
|
|
2
|
+
export declare const handle_and_prevent: (handle: KeydownHandler) => (event: KeyboardEvent) => void;
|
|
3
|
+
export declare const forward_window_keydown: (is_hovered: () => boolean, handle: KeydownHandler) => (event: KeyboardEvent) => void;
|
package/dist/keyboard.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Shared helpers for wiring viewer keyboard shortcuts consistently across
|
|
2
|
+
// components (Structure, Trajectory, ...). A handler returns `true` when it
|
|
3
|
+
// handled the event, so the browser default is suppressed in exactly one place
|
|
4
|
+
// instead of scattered `preventDefault()` calls.
|
|
5
|
+
// Wrap a handler for an element-level `onkeydown` binding: run it and suppress
|
|
6
|
+
// the browser default (page scroll, find, ...) when it reports it handled the key.
|
|
7
|
+
export const handle_and_prevent = (handle) => (event) => {
|
|
8
|
+
if (handle(event))
|
|
9
|
+
event.preventDefault();
|
|
10
|
+
};
|
|
11
|
+
// Wrap a handler for a `<svelte:window onkeydown>` binding: forward keydowns to a
|
|
12
|
+
// viewer only while it's hovered and focus is on <body>. This lets shortcuts work
|
|
13
|
+
// without first clicking the viewer, while keeping multiple viewers on one page
|
|
14
|
+
// from all responding and never hijacking keys from a focused input/pane.
|
|
15
|
+
// Suppresses the browser default when the handler reports it handled the key.
|
|
16
|
+
export const forward_window_keydown = (is_hovered, handle) => {
|
|
17
|
+
const run = handle_and_prevent(handle);
|
|
18
|
+
return (event) => {
|
|
19
|
+
const active = document.activeElement;
|
|
20
|
+
if (is_hovered() && (!active || active === document.body))
|
|
21
|
+
run(event);
|
|
22
|
+
};
|
|
23
|
+
};
|
package/dist/labels.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const ELEM_PROPERTY_LABELS: Partial<Record<keyof ChemicalElement,
|
|
|
11
11
|
export declare const ELEM_HEATMAP_KEYS: (keyof ChemicalElement)[];
|
|
12
12
|
export declare const ELEM_HEATMAP_LABELS: Partial<Record<string, keyof ChemicalElement>>;
|
|
13
13
|
export declare const DEFAULT_FMT: [string, string];
|
|
14
|
-
export declare const FRACTION_GLYPHS:
|
|
14
|
+
export declare const FRACTION_GLYPHS: readonly (readonly [number, string])[];
|
|
15
15
|
export declare const format_num: (num: number, fmt?: string | number) => string;
|
|
16
16
|
export declare const format_vec3: (vec: Readonly<Vec3>, fmt_spec?: string) => string;
|
|
17
17
|
export declare const format_bytes: (bytes?: number) => string;
|
package/dist/labels.js
CHANGED
|
@@ -5,7 +5,7 @@ const is_d3_symbol_name = (name) => Object.hasOwn(d3_symbols, `symbol${name}`);
|
|
|
5
5
|
function name_for_symbol(sym) {
|
|
6
6
|
for (const [key, symbol] of Object.entries(d3_symbols)) {
|
|
7
7
|
if (symbol === sym && /^symbol[A-Z]/.test(key)) {
|
|
8
|
-
const name = key.
|
|
8
|
+
const name = key.slice(6);
|
|
9
9
|
if (is_d3_symbol_name(name))
|
|
10
10
|
return name;
|
|
11
11
|
}
|
|
@@ -34,7 +34,7 @@ export function format_value(value, formatter) {
|
|
|
34
34
|
if (Number.isNaN(value))
|
|
35
35
|
return `NaN`;
|
|
36
36
|
// Format and normalize unicode minus
|
|
37
|
-
const formatted = format(formatter)(value).
|
|
37
|
+
const formatted = format(formatter)(value).replaceAll('−', `-`);
|
|
38
38
|
// Handle percentage formatting - remove trailing zeros
|
|
39
39
|
if (formatter.includes(`%`)) {
|
|
40
40
|
return formatted.includes(`.`)
|
|
@@ -157,9 +157,10 @@ export function parse_si_float(value) {
|
|
|
157
157
|
if (typeof value !== `string`)
|
|
158
158
|
return value;
|
|
159
159
|
// Remove whitespace and commas
|
|
160
|
-
const cleaned = value.trim().
|
|
161
|
-
//
|
|
162
|
-
|
|
160
|
+
const cleaned = value.trim().replaceAll(/(\d),(\d)/g, `$1$2`);
|
|
161
|
+
// SI-formatted number (e.g. "1.23k", "789µ"). Suffixes are case-sensitive (m=milli vs
|
|
162
|
+
// M=mega), so no `i` flag: mismatched-case suffixes return the string as-is.
|
|
163
|
+
const match = /^([-+]?\d*\.?\d+)\s*([yzafpnµmkMGTPEZY])?$/.exec(cleaned);
|
|
163
164
|
if (match) {
|
|
164
165
|
const [, num_part, suffix] = match;
|
|
165
166
|
let multiplier = 1;
|
|
@@ -167,7 +168,7 @@ export function parse_si_float(value) {
|
|
|
167
168
|
const suffixes = `yzafpnµm kMGTPEZY`;
|
|
168
169
|
const index = suffixes.indexOf(suffix);
|
|
169
170
|
if (index !== -1) {
|
|
170
|
-
multiplier =
|
|
171
|
+
multiplier = 1000 ** (index - 8);
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
return parseFloat(num_part) * multiplier;
|
|
@@ -232,7 +233,7 @@ export const SUBSCRIPT_MAP = {
|
|
|
232
233
|
'9': `₉`,
|
|
233
234
|
};
|
|
234
235
|
// replaces all signs and digits with their unicode superscript equivalent
|
|
235
|
-
export const superscript_digits = (input) => input.
|
|
236
|
+
export const superscript_digits = (input) => input.replaceAll(/[\d+-]/g, (match) => is_superscript_key(match) ? SUPERSCRIPT_MAP[match] : match);
|
|
236
237
|
// Trajectory property configuration: clean labels and units as structured data
|
|
237
238
|
export const trajectory_property_config = {
|
|
238
239
|
// Energy properties
|