matterviz 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FilePicker.svelte +40 -50
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +15 -0
- package/dist/brillouin/BrillouinZone.svelte +18 -9
- package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
- package/dist/chempot-diagram/async-compute.svelte.js +12 -13
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/composition/BarChart.svelte +7 -9
- package/dist/composition/Composition.svelte +3 -8
- package/dist/composition/Formula.svelte +2 -2
- package/dist/composition/FormulaFilter.svelte +27 -93
- package/dist/composition/format.js +6 -8
- package/dist/composition/parse.js +50 -105
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +6 -17
- package/dist/convex-hull/ConvexHull2D.svelte +0 -2
- package/dist/convex-hull/ConvexHull3D.svelte +19 -47
- package/dist/convex-hull/ConvexHull4D.svelte +65 -95
- package/dist/convex-hull/StructurePopup.svelte +1 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.js +1 -7
- package/dist/convex-hull/thermodynamics.js +19 -27
- package/dist/coordination/CoordinationBarPlot.svelte +12 -21
- package/dist/element/BohrAtom.svelte +0 -1
- package/dist/element/ElementTile.svelte +26 -44
- package/dist/element/data.d.ts +2 -0
- package/dist/element/data.js +5 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +22 -23
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
- package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
- package/dist/fermi-surface/compute.js +22 -55
- package/dist/fermi-surface/parse.js +38 -70
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +1 -1
- package/dist/io/decompress.d.ts +2 -0
- package/dist/io/decompress.js +3 -2
- package/dist/io/export.d.ts +7 -4
- package/dist/io/export.js +70 -18
- package/dist/io/file-drop.js +60 -35
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +28 -16
- package/dist/isosurface/Isosurface.svelte +687 -196
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +564 -85
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.js +1 -1
- package/dist/layout/InfoCard.svelte +1 -3
- package/dist/layout/SettingsSection.svelte +52 -61
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/ViewerChrome.svelte +7 -1
- package/dist/layout/json-tree/JsonNode.svelte +14 -48
- package/dist/layout/json-tree/JsonTree.svelte +48 -95
- package/dist/layout/json-tree/JsonValue.svelte +3 -18
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -179
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +3 -2
- package/dist/math.js +29 -38
- package/dist/overlays/ContextMenu.svelte +15 -32
- package/dist/overlays/DraggablePane.svelte +39 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
- package/dist/overlays/GlassChip.svelte +3 -1
- package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
- package/dist/phase-diagram/build-diagram.js +1 -6
- package/dist/phase-diagram/svg-to-diagram.js +9 -14
- package/dist/plot/bar/BarPlot.svelte +38 -42
- package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
- package/dist/plot/box/BoxPlot.svelte +27 -23
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +1 -1
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +0 -10
- package/dist/plot/core/layout.d.ts +12 -5
- package/dist/plot/core/layout.js +37 -27
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
- package/dist/plot/core/utils/hierarchy-chart.js +12 -0
- package/dist/plot/core/utils/hierarchy-labels.js +3 -2
- package/dist/plot/histogram/Histogram.svelte +21 -26
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
- package/dist/plot/scatter/ScatterPlot.svelte +67 -52
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/sunburst/Sunburst.svelte +20 -20
- package/dist/plot/treemap/Treemap.svelte +175 -60
- package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
- package/dist/plot/treemap/index.d.ts +1 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +4 -2
- package/dist/plot/treemap/treemap.js +5 -2
- package/dist/rdf/RdfPlot.svelte +17 -35
- package/dist/rdf/calc-rdf.js +59 -44
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +84 -26
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.js +159 -482
- package/dist/spectral/Bands.svelte +151 -158
- package/dist/spectral/BrillouinBandsDos.svelte +4 -19
- package/dist/spectral/Dos.svelte +26 -24
- package/dist/spectral/helpers.js +40 -105
- package/dist/structure/AtomLegend.svelte +8 -13
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/CellSelect.svelte +35 -10
- package/dist/structure/Structure.svelte +177 -99
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureCarousel.svelte +10 -3
- package/dist/structure/StructureControls.svelte +8 -26
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +65 -76
- package/dist/structure/StructureScene.svelte +75 -109
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +120 -55
- package/dist/structure/StructureViewport.svelte.d.ts +3 -1
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +4 -3
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +0 -1
- package/dist/structure/bonding.js +16 -23
- package/dist/structure/export.js +50 -74
- package/dist/structure/format-detect.js +5 -7
- package/dist/structure/index.js +6 -12
- package/dist/structure/parse.js +38 -100
- package/dist/structure/partial-occupancy.js +7 -4
- package/dist/structure/pbc.js +8 -10
- package/dist/structure/polyhedra.js +5 -4
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/symmetry/SymmetryStats.svelte +0 -4
- package/dist/symmetry/WyckoffTable.svelte +5 -4
- package/dist/symmetry/cell-transform.js +5 -14
- package/dist/symmetry/index.js +6 -9
- package/dist/table/HeatmapTable.svelte +61 -103
- package/dist/table/ToggleMenu.svelte +166 -44
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +1 -2
- package/dist/theme/themes.mjs +31 -60
- package/dist/tooltip/TooltipContent.svelte +1 -1
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +140 -120
- package/dist/trajectory/TrajectoryError.svelte +1 -66
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +19 -29
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +7 -5
- package/dist/trajectory/frame-reader.js +3 -4
- package/dist/trajectory/helpers.js +16 -49
- package/dist/trajectory/index.js +2 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/index.d.ts +4 -3
- package/dist/trajectory/parse/index.js +12 -17
- package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
- package/dist/trajectory/parse/vaspout-h5.js +1 -4
- package/dist/trajectory/parse/xyz.js +48 -2
- package/dist/trajectory/plotting.js +14 -37
- package/dist/xrd/XrdPlot.svelte +43 -68
- package/dist/xrd/calc-xrd.js +22 -58
- package/dist/xrd/parse.js +13 -22
- package/package.json +40 -8
- package/readme.md +6 -5
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -16,8 +16,8 @@ export { default as Icon } from './Icon.svelte';
|
|
|
16
16
|
export * from './icons';
|
|
17
17
|
export * from './io';
|
|
18
18
|
export { default as MillerIndexInput } from './MillerIndexInput.svelte';
|
|
19
|
-
export { auto_isosurface_settings, DEFAULT_ISOSURFACE_SETTINGS, generate_layers, grid_data_range, Isosurface as VolumetricIsosurface, IsosurfaceControls, LAYER_COLORS, parse_chgcar, parse_cube, parse_volumetric_file, sample_hkl_slice, tile_volumetric_data, trilinear_interpolate, } from './isosurface';
|
|
20
|
-
export type { DataRange, IsosurfaceLayer, IsosurfaceSettings, SliceResult, VolumetricData, VolumetricFileData, } from './isosurface';
|
|
19
|
+
export { auto_color_config, auto_isosurface_settings, auto_volume_layer, compare_volume_grids, compute_scalar_range, create_volume_sampler, DEFAULT_ISO_COLORMAP, DEFAULT_ISOSURFACE_SETTINGS, extract_volume_range, generate_layers, grid_data_range, is_signed_range, ISO_COLORMAPS, Isosurface as VolumetricIsosurface, IsosurfaceControls, label_file_volumes, lattices_match, LAYER_COLORS, materialize_layers, merge_imported_volumes, parse_chgcar, parse_cube, parse_volumetric_file, remove_volume, resolve_contour_thresholds, resolve_slice_color_range, resolve_volume_display_range, sample_hkl_slice, sample_plane_slice, sample_volume_at_positions, sanitize_display_range, scalars_to_vertex_colors, slice_to_rgba, tile_volumetric_data, trilinear_interpolate, VolumeSlice, } from './isosurface';
|
|
20
|
+
export type { CartesianPlane, DataRange, DisplayRange, GridCompatibility, IsoColormap, IsosurfaceLayer, IsosurfaceSettings, OutOfBoundsPolicy, PlaneSliceOptions, SliceResult, VolumeSliceMode, VolumeMergeResult, VolumeDisplayRangeOptions, VolumeSamplerOptions, VolumetricData, VolumetricFileData, } from './isosurface';
|
|
21
21
|
export * from './labels';
|
|
22
22
|
export * from './layout';
|
|
23
23
|
export * from './math';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export * from './icons';
|
|
|
17
17
|
export * from './io';
|
|
18
18
|
export { default as MillerIndexInput } from './MillerIndexInput.svelte';
|
|
19
19
|
// Explicit exports to avoid name clash with fermi-surface's Isosurface type
|
|
20
|
-
export { auto_isosurface_settings, DEFAULT_ISOSURFACE_SETTINGS, generate_layers, grid_data_range, Isosurface as VolumetricIsosurface, IsosurfaceControls, LAYER_COLORS, parse_chgcar, parse_cube, parse_volumetric_file, sample_hkl_slice, tile_volumetric_data, trilinear_interpolate, } from './isosurface';
|
|
20
|
+
export { auto_color_config, auto_isosurface_settings, auto_volume_layer, compare_volume_grids, compute_scalar_range, create_volume_sampler, DEFAULT_ISO_COLORMAP, DEFAULT_ISOSURFACE_SETTINGS, extract_volume_range, generate_layers, grid_data_range, is_signed_range, ISO_COLORMAPS, Isosurface as VolumetricIsosurface, IsosurfaceControls, label_file_volumes, lattices_match, LAYER_COLORS, materialize_layers, merge_imported_volumes, parse_chgcar, parse_cube, parse_volumetric_file, remove_volume, resolve_contour_thresholds, resolve_slice_color_range, resolve_volume_display_range, sample_hkl_slice, sample_plane_slice, sample_volume_at_positions, sanitize_display_range, scalars_to_vertex_colors, slice_to_rgba, tile_volumetric_data, trilinear_interpolate, VolumeSlice, } from './isosurface';
|
|
21
21
|
export * from './labels';
|
|
22
22
|
export * from './layout';
|
|
23
23
|
export * from './math';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { ExportItem, ExportSection } from './types'
|
|
4
4
|
import DraggablePane from '../overlays/DraggablePane.svelte'
|
|
5
5
|
import { sanitize_html } from '../sanitize'
|
|
6
|
-
import {
|
|
6
|
+
import { onDestroy, type Snippet } from 'svelte'
|
|
7
7
|
import { tooltip } from 'svelte-multiselect/attachments'
|
|
8
8
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
9
9
|
|
package/dist/io/decompress.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { COMPRESSION_FORMATS } from '../constants';
|
|
|
3
3
|
export declare function strip_compression_extensions(filename: string): string;
|
|
4
4
|
export type CompressionFormat = keyof typeof COMPRESSION_FORMATS;
|
|
5
5
|
export type CompressionExtension = (typeof COMPRESSION_EXTENSIONS)[number];
|
|
6
|
+
export type BrowserCompressionFormat = Exclude<CompressionFormat, `zip` | `xz` | `bz2`>;
|
|
7
|
+
export declare const is_browser_decompressible_format: (format: CompressionFormat | null) => format is BrowserCompressionFormat;
|
|
6
8
|
export declare function detect_compression_format(filename: string): CompressionFormat | null;
|
|
7
9
|
export declare function decompress_data(data: ArrayBuffer | ReadableStream<Uint8Array> | null, format: CompressionFormat): Promise<string>;
|
|
8
10
|
export declare function decompress_data_binary(data: ArrayBuffer | ReadableStream<Uint8Array> | null, format: CompressionFormat): Promise<ArrayBuffer>;
|
package/dist/io/decompress.js
CHANGED
|
@@ -8,6 +8,7 @@ export function strip_compression_extensions(filename) {
|
|
|
8
8
|
}
|
|
9
9
|
return base_name;
|
|
10
10
|
}
|
|
11
|
+
export const is_browser_decompressible_format = (format) => format !== null && format !== `zip` && format !== `xz` && format !== `bz2`;
|
|
11
12
|
export function detect_compression_format(filename) {
|
|
12
13
|
const lower = filename.toLowerCase();
|
|
13
14
|
for (const [format, extensions] of Object.entries(COMPRESSION_FORMATS)) {
|
|
@@ -24,7 +25,7 @@ export async function decompress_data(data, format) {
|
|
|
24
25
|
// Decompress data and return as ArrayBuffer (for binary files like .brml.gz)
|
|
25
26
|
export async function decompress_data_binary(data, format) {
|
|
26
27
|
try {
|
|
27
|
-
if (format
|
|
28
|
+
if (!is_browser_decompressible_format(format)) {
|
|
28
29
|
throw new Error(`${format.toUpperCase()} decompression is not supported in the browser. ` +
|
|
29
30
|
`Please extract the ${format.toUpperCase()} file first.`);
|
|
30
31
|
}
|
|
@@ -51,7 +52,7 @@ const to_content = (filename, buffer) => is_binary_payload(filename, buffer) ? b
|
|
|
51
52
|
export async function decompress_file(file) {
|
|
52
53
|
const format = detect_compression_format(file.name);
|
|
53
54
|
// zip/xz/bz2 are handled by their own code paths; null = not compressed
|
|
54
|
-
const decompressible = format
|
|
55
|
+
const decompressible = is_browser_decompressible_format(format) ? format : null;
|
|
55
56
|
const buffer = await file.arrayBuffer();
|
|
56
57
|
if (decompressible) {
|
|
57
58
|
const filename = file.name.replace(COMPRESSION_EXTENSIONS_REGEX, ``);
|
package/dist/io/export.d.ts
CHANGED
|
@@ -8,10 +8,13 @@ export declare const scene_registry: WeakMap<HTMLCanvasElement, {
|
|
|
8
8
|
export declare const dpi_to_scale: (png_dpi: number) => number;
|
|
9
9
|
export declare function canvas_to_png_blob(canvas: HTMLCanvasElement, png_dpi?: number, scene?: Scene | null, camera?: Camera | null): Promise<Blob>;
|
|
10
10
|
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;
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export declare function
|
|
11
|
+
export interface SvgExportOptions {
|
|
12
|
+
viewbox_padding?: number | `stroke`;
|
|
13
|
+
}
|
|
14
|
+
export declare function svg_to_svg_string(svg_element: SVGElement, inline_styles?: readonly string[], options?: SvgExportOptions): string;
|
|
15
|
+
export declare function export_svg_as_svg(svg_element: SVGElement | null, filename: string, inline_styles?: readonly string[], options?: SvgExportOptions): void;
|
|
16
|
+
export declare function svg_to_png_blob(svg_element: SVGElement, png_dpi?: number, inline_styles?: readonly string[], options?: SvgExportOptions): Promise<Blob>;
|
|
17
|
+
export declare function export_svg_as_png(svg_element: SVGElement | null, filename: string, png_dpi?: number, inline_styles?: readonly string[], options?: SvgExportOptions): void;
|
|
15
18
|
export declare function observe_canvas_presence(wrapper: HTMLElement | undefined, set: (has_canvas: boolean) => void): (() => void) | undefined;
|
|
16
19
|
export declare const estimate_video_bitrate: (pixel_count: number, fps: number) => number;
|
|
17
20
|
export declare function get_ffmpeg_conversion_command(input_filename: string): string;
|
package/dist/io/export.js
CHANGED
|
@@ -94,6 +94,54 @@ function set_svg_font_family(svg) {
|
|
|
94
94
|
// Also set as attribute for extra robustness
|
|
95
95
|
svg.setAttribute(`font-family`, `sans-serif`);
|
|
96
96
|
}
|
|
97
|
+
function svg_viewbox(svg, padding = 0) {
|
|
98
|
+
const parts = svg
|
|
99
|
+
.getAttribute(`viewBox`)
|
|
100
|
+
?.trim()
|
|
101
|
+
.split(/[\s,]+/)
|
|
102
|
+
.map(Number);
|
|
103
|
+
if (parts?.length !== 4 || !parts.every(Number.isFinite))
|
|
104
|
+
return null;
|
|
105
|
+
const [x, y, width, height] = parts;
|
|
106
|
+
if (width <= 0 || height <= 0)
|
|
107
|
+
return null;
|
|
108
|
+
const padded = [
|
|
109
|
+
x - padding,
|
|
110
|
+
y - padding,
|
|
111
|
+
width + 2 * padding,
|
|
112
|
+
height + 2 * padding,
|
|
113
|
+
];
|
|
114
|
+
return padded.every(Number.isFinite) && padded[2] > 0 && padded[3] > 0 ? padded : null;
|
|
115
|
+
}
|
|
116
|
+
function resolve_viewbox_padding(svg, options) {
|
|
117
|
+
if (options.viewbox_padding !== `stroke`) {
|
|
118
|
+
return Number.isFinite(options.viewbox_padding)
|
|
119
|
+
? Math.max(0, options.viewbox_padding ?? 0)
|
|
120
|
+
: 0;
|
|
121
|
+
}
|
|
122
|
+
let max_stroke_width = 0;
|
|
123
|
+
for (const element of [svg, ...svg.querySelectorAll(`*`)]) {
|
|
124
|
+
const computed = getComputedStyle(element);
|
|
125
|
+
const inline_style = element.style;
|
|
126
|
+
const stroke = [
|
|
127
|
+
inline_style.stroke,
|
|
128
|
+
computed.getPropertyValue(`stroke`),
|
|
129
|
+
element.getAttribute(`stroke`),
|
|
130
|
+
].find(Boolean) ?? ``;
|
|
131
|
+
if (!stroke || stroke === `none` || stroke === `transparent`)
|
|
132
|
+
continue;
|
|
133
|
+
// oxlint-disable-next-line unicorn/prefer-number-coercion -- CSS lengths include units
|
|
134
|
+
const stroke_width = Number.parseFloat([
|
|
135
|
+
inline_style.strokeWidth,
|
|
136
|
+
computed.getPropertyValue(`stroke-width`),
|
|
137
|
+
element.getAttribute(`stroke-width`),
|
|
138
|
+
].find(Boolean) ?? ``);
|
|
139
|
+
if (Number.isFinite(stroke_width)) {
|
|
140
|
+
max_stroke_width = Math.max(max_stroke_width, stroke_width);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return max_stroke_width / 2;
|
|
144
|
+
}
|
|
97
145
|
// Copy the given computed-style props from each live SVG element to its clone counterpart;
|
|
98
146
|
// identical structure lets querySelectorAll(`*`) walk both in lockstep. Writes clone-only.
|
|
99
147
|
function inline_computed_styles(live, clone, properties) {
|
|
@@ -110,10 +158,17 @@ function inline_computed_styles(live, clone, properties) {
|
|
|
110
158
|
}
|
|
111
159
|
// Clone, inline the given computed-style props, ensure font-family + xmlns, then serialize
|
|
112
160
|
// to a standalone SVG string. Never mutates the live element.
|
|
113
|
-
function serialize_svg_for_export(svg_element, inline_styles = []) {
|
|
161
|
+
function serialize_svg_for_export(svg_element, inline_styles = [], viewbox_padding = 0, strip_dimensions = false) {
|
|
114
162
|
const clone = svg_element.cloneNode(true);
|
|
115
163
|
if (inline_styles.length)
|
|
116
164
|
inline_computed_styles(svg_element, clone, inline_styles);
|
|
165
|
+
const padded_viewbox = viewbox_padding > 0 ? svg_viewbox(clone, viewbox_padding) : null;
|
|
166
|
+
if (padded_viewbox)
|
|
167
|
+
clone.setAttribute(`viewBox`, padded_viewbox.join(` `));
|
|
168
|
+
if (strip_dimensions) {
|
|
169
|
+
clone.removeAttribute(`width`);
|
|
170
|
+
clone.removeAttribute(`height`);
|
|
171
|
+
}
|
|
117
172
|
set_svg_font_family(clone);
|
|
118
173
|
if (!clone.hasAttribute(`xmlns`)) {
|
|
119
174
|
clone.setAttribute(`xmlns`, `http://www.w3.org/2000/svg`);
|
|
@@ -125,18 +180,19 @@ function serialize_svg_for_export(svg_element, inline_styles = []) {
|
|
|
125
180
|
export function svg_to_svg_string(svg_element,
|
|
126
181
|
// CSS props to inline from computed styles as presentation attributes; a standalone SVG
|
|
127
182
|
// drops page stylesheets (e.g. Svelte component styles), so class-based styling is lost.
|
|
128
|
-
inline_styles = []) {
|
|
129
|
-
const
|
|
183
|
+
inline_styles = [], options = {}) {
|
|
184
|
+
const viewbox_padding = resolve_viewbox_padding(svg_element, options);
|
|
185
|
+
const svg_string = serialize_svg_for_export(svg_element, inline_styles, viewbox_padding);
|
|
130
186
|
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}`;
|
|
131
187
|
}
|
|
132
188
|
// Export SVG element as SVG file (triggers browser download)
|
|
133
|
-
export function export_svg_as_svg(svg_element, filename, inline_styles = []) {
|
|
189
|
+
export function export_svg_as_svg(svg_element, filename, inline_styles = [], options = {}) {
|
|
134
190
|
if (!svg_element) {
|
|
135
191
|
console.warn(`SVG element not found for export`);
|
|
136
192
|
return;
|
|
137
193
|
}
|
|
138
194
|
try {
|
|
139
|
-
const svg_content = svg_to_svg_string(svg_element, inline_styles);
|
|
195
|
+
const svg_content = svg_to_svg_string(svg_element, inline_styles, options);
|
|
140
196
|
download(svg_content, filename, `image/svg+xml;charset=utf-8`);
|
|
141
197
|
}
|
|
142
198
|
catch (error) {
|
|
@@ -148,18 +204,14 @@ export function export_svg_as_svg(svg_element, filename, inline_styles = []) {
|
|
|
148
204
|
// Image element, and returns the resulting PNG Blob. Rejects if viewBox is
|
|
149
205
|
// missing or dimensions are invalid (zero width/height).
|
|
150
206
|
// DPI is converted to a resolution multiplier relative to 72 DPI baseline, capped at 10x.
|
|
151
|
-
export function svg_to_png_blob(svg_element, png_dpi = 150, inline_styles = []) {
|
|
152
|
-
|
|
153
|
-
if (!viewBox)
|
|
207
|
+
export function svg_to_png_blob(svg_element, png_dpi = 150, inline_styles = [], options = {}) {
|
|
208
|
+
if (!svg_element.getAttribute(`viewBox`)?.trim())
|
|
154
209
|
return Promise.reject(new Error(`SVG viewBox not found for PNG export`));
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
const [, , width, height] = parts;
|
|
160
|
-
if (!(width > 0) || !(height > 0)) {
|
|
210
|
+
const padding = resolve_viewbox_padding(svg_element, options);
|
|
211
|
+
const padded_viewbox = svg_viewbox(svg_element, padding);
|
|
212
|
+
if (!padded_viewbox)
|
|
161
213
|
return Promise.reject(new Error(`Invalid SVG dimensions for PNG export`));
|
|
162
|
-
|
|
214
|
+
const [, , width, height] = padded_viewbox;
|
|
163
215
|
if (!Number.isFinite(png_dpi) || png_dpi <= 0) {
|
|
164
216
|
return Promise.reject(new Error(`Invalid PNG DPI for export`));
|
|
165
217
|
}
|
|
@@ -173,7 +225,7 @@ export function svg_to_png_blob(svg_element, png_dpi = 150, inline_styles = [])
|
|
|
173
225
|
return Promise.reject(new Error(`Canvas 2D context not available`));
|
|
174
226
|
canvas.width = pixel_width;
|
|
175
227
|
canvas.height = pixel_height;
|
|
176
|
-
const serialized = serialize_svg_for_export(svg_element, inline_styles);
|
|
228
|
+
const serialized = serialize_svg_for_export(svg_element, inline_styles, padding, padding > 0);
|
|
177
229
|
const svg_blob = new Blob([serialized], { type: `image/svg+xml;charset=utf-8` });
|
|
178
230
|
const svg_data_url = URL.createObjectURL(svg_blob);
|
|
179
231
|
return new Promise((resolve, reject) => {
|
|
@@ -204,12 +256,12 @@ export function svg_to_png_blob(svg_element, png_dpi = 150, inline_styles = [])
|
|
|
204
256
|
});
|
|
205
257
|
}
|
|
206
258
|
// Export SVG element as PNG (triggers browser download)
|
|
207
|
-
export function export_svg_as_png(svg_element, filename, png_dpi = 150, inline_styles = []) {
|
|
259
|
+
export function export_svg_as_png(svg_element, filename, png_dpi = 150, inline_styles = [], options = {}) {
|
|
208
260
|
if (!svg_element) {
|
|
209
261
|
console.warn(`SVG element not found for PNG export`);
|
|
210
262
|
return;
|
|
211
263
|
}
|
|
212
|
-
svg_to_png_blob(svg_element, png_dpi, inline_styles)
|
|
264
|
+
svg_to_png_blob(svg_element, png_dpi, inline_styles, options)
|
|
213
265
|
.then((blob) => download(blob, filename, `image/png`))
|
|
214
266
|
.catch((error) => console.error(`Error exporting PNG:`, error));
|
|
215
267
|
}
|
package/dist/io/file-drop.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Shared file-drop handler composable for drag-and-drop file loading.
|
|
2
2
|
import { decompress_file } from './decompress';
|
|
3
|
-
import {
|
|
3
|
+
import { dropped_file_url, load_from_url } from './url-drop';
|
|
4
4
|
import { to_error } from '../utils';
|
|
5
5
|
// Drag-over visual-state handlers for file-drop zones; spread onto the drop target
|
|
6
6
|
// alongside `ondrop` from create_file_drop_handler
|
|
@@ -16,40 +16,65 @@ export const drag_over_handlers = (opts) => ({
|
|
|
16
16
|
ondragleave: () => opts.set_dragover(false),
|
|
17
17
|
});
|
|
18
18
|
// Handles URL drops (from FilePicker), direct file drops with decompression,
|
|
19
|
-
// loading state, and error reporting.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
19
|
+
// loading state, and error reporting. Multiple dropped files are processed
|
|
20
|
+
// sequentially in drop order so e.g. several cube files can be imported at once.
|
|
21
|
+
// Overlapping drops are queued: a batch starting while a previous one is still
|
|
22
|
+
// processing would interleave on_drop state mutations (e.g. torn volume lists).
|
|
23
|
+
export const create_file_drop_handler = (opts) => {
|
|
24
|
+
async function process_batch(url, files) {
|
|
25
|
+
opts.set_loading?.(true);
|
|
26
|
+
try {
|
|
27
|
+
// One failing item must not abort the rest of the batch
|
|
28
|
+
const failures = [];
|
|
29
|
+
if (url) {
|
|
30
|
+
try {
|
|
31
|
+
await load_from_url(url, opts.on_drop);
|
|
32
|
+
}
|
|
33
|
+
catch (exc) {
|
|
34
|
+
// URL failed; if plain files were also dropped, still process them
|
|
35
|
+
// and fold the URL failure into the aggregate report
|
|
36
|
+
if (files.length === 0) {
|
|
37
|
+
opts.on_error?.(`Failed to load from URL: ${to_error(exc).message}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
failures.push(`URL ${url}: ${to_error(exc).message}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (files.length === 0)
|
|
44
|
+
return;
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
try {
|
|
47
|
+
const { content, filename } = await decompress_file(file);
|
|
48
|
+
if (content)
|
|
49
|
+
await opts.on_drop(content, filename);
|
|
50
|
+
else
|
|
51
|
+
failures.push(`${file.name}: file is empty`);
|
|
52
|
+
}
|
|
53
|
+
catch (exc) {
|
|
54
|
+
failures.push(`${file.name}: ${to_error(exc).message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (failures.length > 0) {
|
|
58
|
+
opts.on_error?.(`Failed to load ${failures.length} file${failures.length > 1 ? `s` : ``} — ${failures.join(`; `)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (exc) {
|
|
62
|
+
opts.on_error?.(`Failed to load file: ${to_error(exc).message}`);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
opts.set_loading?.(false);
|
|
39
66
|
}
|
|
40
|
-
drop_filename = file.name;
|
|
41
|
-
const { content, filename } = await decompress_file(file);
|
|
42
|
-
if (content)
|
|
43
|
-
await opts.on_drop(content, filename);
|
|
44
|
-
}
|
|
45
|
-
catch (exc) {
|
|
46
|
-
const detail = to_error(exc).message;
|
|
47
|
-
const msg = drop_filename
|
|
48
|
-
? `Failed to load file ${drop_filename}: ${detail}`
|
|
49
|
-
: `Failed to load file: ${detail}`;
|
|
50
|
-
opts.on_error?.(msg);
|
|
51
|
-
}
|
|
52
|
-
finally {
|
|
53
|
-
opts.set_loading?.(false);
|
|
54
67
|
}
|
|
68
|
+
let queue = Promise.resolve();
|
|
69
|
+
return (event) => {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
if (!opts.allow())
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
// DataTransfer contents are only readable during drop-event dispatch, so
|
|
74
|
+
// capture them before deferring to the queue
|
|
75
|
+
const url = dropped_file_url(event);
|
|
76
|
+
const files = Array.from(event.dataTransfer?.files ?? []);
|
|
77
|
+
queue = queue.then(() => process_batch(url, files)).catch(() => undefined);
|
|
78
|
+
return queue;
|
|
79
|
+
};
|
|
55
80
|
};
|
package/dist/io/url-drop.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
export declare const basename_from_url: (url: string) => string;
|
|
2
|
+
export declare function dropped_file_url(drag_event: DragEvent): string | undefined;
|
|
1
3
|
export declare function handle_url_drop(drag_event: DragEvent, callback: (content: string | ArrayBuffer, filename: string) => Promise<void> | void): Promise<boolean>;
|
|
2
4
|
export declare function load_from_url(url: string, callback: (content: string | ArrayBuffer, filename: string) => Promise<void> | void): Promise<void>;
|
package/dist/io/url-drop.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { load_binary_traj } from '../trajectory/parse';
|
|
2
2
|
import { decompress_data_binary } from './decompress';
|
|
3
3
|
import { BINARY_EXTENSIONS, ext_of, has_binary_inner_ext, has_binary_magic, has_gzip_magic, has_hdf5_magic, is_known_text_file, strip_gz_ext, } from './is-binary';
|
|
4
|
+
// Strip query/hash; last path segment (same basename load_from_url uses).
|
|
5
|
+
// Trailing-slash URLs yield an empty segment — fall back to the original URL.
|
|
6
|
+
export const basename_from_url = (url) => {
|
|
7
|
+
const basename = url.split(/[?#]/)[0].split(`/`).pop();
|
|
8
|
+
if (!basename)
|
|
9
|
+
return url;
|
|
10
|
+
return basename;
|
|
11
|
+
};
|
|
4
12
|
// Extract filename from Content-Disposition header, falling back to url_basename.
|
|
5
13
|
function extract_filename(headers, fallback) {
|
|
6
14
|
if (!headers)
|
|
@@ -38,27 +46,35 @@ async function decompress_gz_payload(buffer, filename) {
|
|
|
38
46
|
: new TextDecoder().decode(decompressed);
|
|
39
47
|
return [content, strip_gz_ext(filename)];
|
|
40
48
|
}
|
|
41
|
-
//
|
|
42
|
-
|
|
49
|
+
// Extract the URL of a FilePicker-style drop payload. Synchronous because
|
|
50
|
+
// DataTransfer.getData is only readable during drop-event dispatch — callers
|
|
51
|
+
// that defer processing (e.g. drop queues) must capture the URL up front.
|
|
52
|
+
export function dropped_file_url(drag_event) {
|
|
43
53
|
const json_data = drag_event.dataTransfer?.getData(`application/json`);
|
|
44
54
|
if (!json_data)
|
|
45
|
-
return
|
|
46
|
-
let file_info;
|
|
55
|
+
return undefined;
|
|
47
56
|
try {
|
|
48
|
-
|
|
57
|
+
// Runtime-check instead of trusting the FileInfo cast: drop payloads are
|
|
58
|
+
// external input and a truthy non-string url must not reach fetch()
|
|
59
|
+
const { url } = JSON.parse(json_data);
|
|
60
|
+
return typeof url === `string` && url ? url : undefined;
|
|
49
61
|
}
|
|
50
62
|
catch {
|
|
51
|
-
return
|
|
63
|
+
return undefined;
|
|
52
64
|
}
|
|
53
|
-
|
|
65
|
+
}
|
|
66
|
+
// Handle URL-based file drop data by fetching content lazily
|
|
67
|
+
export async function handle_url_drop(drag_event, callback) {
|
|
68
|
+
const url = dropped_file_url(drag_event);
|
|
69
|
+
if (!url)
|
|
54
70
|
return false;
|
|
55
|
-
await load_from_url(
|
|
71
|
+
await load_from_url(url, callback);
|
|
56
72
|
return true;
|
|
57
73
|
}
|
|
58
74
|
export async function load_from_url(url, callback) {
|
|
59
75
|
// Strip query string/hash before basename/extension detection so pre-signed
|
|
60
76
|
// URLs like traj.h5?X-Amz-Expires=300 still hit the right format path
|
|
61
|
-
const url_basename = url
|
|
77
|
+
const url_basename = basename_from_url(url);
|
|
62
78
|
const ext = ext_of(url_basename);
|
|
63
79
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
64
80
|
// Force binary mode for known binary files to handle GitHub Pages content-type issues
|
|
@@ -99,9 +115,7 @@ export async function load_from_url(url, callback) {
|
|
|
99
115
|
}
|
|
100
116
|
// Skip Range requests for known text formats to avoid production server issues
|
|
101
117
|
// Include VASP files that don't have extensions (POSCAR, XDATCAR, CONTCAR)
|
|
102
|
-
|
|
103
|
-
let sniffed_callback_args;
|
|
104
|
-
if (!is_known_text) {
|
|
118
|
+
if (!is_known_text_file(url_basename)) {
|
|
105
119
|
// Only the Range sniff is guarded (failure → plain text fetch). Once magic bytes
|
|
106
120
|
// commit to a binary format, download/decompress errors must propagate instead of
|
|
107
121
|
// falling through to a text fetch that would parse the binary bytes as garbage.
|
|
@@ -129,12 +143,10 @@ export async function load_from_url(url, callback) {
|
|
|
129
143
|
const filename = extract_filename(resp.headers, url_basename);
|
|
130
144
|
const buffer = await resp.arrayBuffer();
|
|
131
145
|
// Gunzip sniffed gzip — downstream parsers can't handle raw gzip bytes
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
const args = sniffed === `gzip` ? await decompress_gz_payload(buffer, filename) : [buffer, filename];
|
|
147
|
+
return callback(...args);
|
|
134
148
|
}
|
|
135
149
|
}
|
|
136
|
-
if (sniffed_callback_args)
|
|
137
|
-
return callback(...sniffed_callback_args);
|
|
138
150
|
const resp = await fetch(url);
|
|
139
151
|
if (!resp.ok)
|
|
140
152
|
throw new Error(`Fetch failed: ${resp.status}`);
|