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
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
// VS Code's webview postMessage API takes a single argument (no targetOrigin),
|
|
2
|
+
// so unicorn's require-post-message-target-origin is a false positive here.
|
|
3
|
+
// oxlint-disable eslint-plugin-unicorn/require-post-message-target-origin
|
|
4
|
+
// Import MatterViz parsing functions and components
|
|
5
|
+
// oxlint-disable-next-line eslint-plugin-import/no-unassigned-import -- side-effect only
|
|
6
|
+
import '../app.css';
|
|
7
|
+
import ConvexHull from '../convex-hull/ConvexHull.svelte';
|
|
8
|
+
import FermiSurface from '../fermi-surface/FermiSurface.svelte';
|
|
9
|
+
import { is_fermi_surface_data } from '../fermi-surface/types';
|
|
10
|
+
import IsobaricBinaryPhaseDiagram from '../phase-diagram/IsobaricBinaryPhaseDiagram.svelte';
|
|
11
|
+
import { merge } from '../settings';
|
|
12
|
+
import Bands from '../spectral/Bands.svelte';
|
|
13
|
+
import BandsAndDos from '../spectral/BandsAndDos.svelte';
|
|
14
|
+
import Dos from '../spectral/Dos.svelte';
|
|
15
|
+
import Structure from '../structure/Structure.svelte';
|
|
16
|
+
import { ensure_moyo_wasm_ready } from '../symmetry';
|
|
17
|
+
import { apply_theme_to_dom, is_valid_theme_name } from '../theme/index';
|
|
18
|
+
// oxlint-disable-next-line eslint-plugin-import/no-unassigned-import -- side-effect only
|
|
19
|
+
import '../theme/themes.mjs';
|
|
20
|
+
import { is_indexable_trajectory_filename } from '../trajectory/format-detect';
|
|
21
|
+
import Trajectory from '../trajectory/Trajectory.svelte';
|
|
22
|
+
import { build_structure_props_from_settings as structure_props } from '../structure/prop-groups';
|
|
23
|
+
import { mount, unmount } from 'svelte';
|
|
24
|
+
import TrajectoryWithDos from './TrajectoryWithDos.svelte';
|
|
25
|
+
import { parse_large_file_marker } from './host-transfer';
|
|
26
|
+
import JsonBrowser from './JsonBrowser.svelte';
|
|
27
|
+
import { parse_file_content } from './parse';
|
|
28
|
+
import { escape_html, to_error } from '../utils';
|
|
29
|
+
// Shared postMessage request/response plumbing for talking to the extension
|
|
30
|
+
// host: tags the request with a UUID, forwards responses carrying that id to
|
|
31
|
+
// on_response (which returns true once it settled the promise), and rejects
|
|
32
|
+
// on timeout. Always removes the listener + timer once settled.
|
|
33
|
+
function post_request(api, message, timeout_ms, timeout_error, on_response) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const request_id = crypto.randomUUID();
|
|
36
|
+
const timer = setTimeout(() => {
|
|
37
|
+
globalThis.removeEventListener(`message`, handler);
|
|
38
|
+
reject(new Error(timeout_error));
|
|
39
|
+
}, timeout_ms);
|
|
40
|
+
const handler = (event) => {
|
|
41
|
+
if (event.data?.request_id !== request_id)
|
|
42
|
+
return;
|
|
43
|
+
if (on_response(event.data, resolve, reject)) {
|
|
44
|
+
globalThis.removeEventListener(`message`, handler);
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
globalThis.addEventListener(`message`, handler);
|
|
49
|
+
api.postMessage({ ...message, request_id });
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// VS Code Frame Loader - streams frames via extension communication
|
|
53
|
+
export class VSCodeFrameLoader {
|
|
54
|
+
file_path;
|
|
55
|
+
vscode_api;
|
|
56
|
+
constructor(file_path, vscode_api) {
|
|
57
|
+
this.file_path = file_path;
|
|
58
|
+
this.vscode_api = vscode_api;
|
|
59
|
+
}
|
|
60
|
+
// Only implement the method we actually use
|
|
61
|
+
async load_frame(_data, frame_index, timeout = 10) {
|
|
62
|
+
const message = {
|
|
63
|
+
command: `request_frame`,
|
|
64
|
+
file_path: this.file_path,
|
|
65
|
+
frame_index,
|
|
66
|
+
};
|
|
67
|
+
return post_request(this.vscode_api, message, timeout * 1000, `Frame ${frame_index} timeout after ${timeout}s`, (data, resolve, reject) => {
|
|
68
|
+
if (data.command !== `frame_response`)
|
|
69
|
+
return false;
|
|
70
|
+
if (data.error)
|
|
71
|
+
reject(new Error(data.error));
|
|
72
|
+
else
|
|
73
|
+
resolve(data.frame);
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Required by the FrameLoader interface but never called for host-streamed trajectories
|
|
78
|
+
async get_total_frames() {
|
|
79
|
+
throw new Error(`Not implemented`);
|
|
80
|
+
}
|
|
81
|
+
async build_frame_index() {
|
|
82
|
+
throw new Error(`Not implemented`);
|
|
83
|
+
}
|
|
84
|
+
async extract_plot_metadata() {
|
|
85
|
+
throw new Error(`Not implemented`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Store VSCode API instance to avoid multiple acquisitions
|
|
89
|
+
let vscode_api = null;
|
|
90
|
+
let current_app = null;
|
|
91
|
+
let file_change_listener_registered = false;
|
|
92
|
+
let file_change_generation = 0;
|
|
93
|
+
let file_change_queue = Promise.resolve();
|
|
94
|
+
let viewer_disposed = false;
|
|
95
|
+
let viewer_lifecycle_generation = 0;
|
|
96
|
+
const global_window = globalThis;
|
|
97
|
+
const is_current_file_change = (generation) => !viewer_disposed && generation === file_change_generation;
|
|
98
|
+
const is_current_lifecycle = (generation) => !viewer_disposed && generation === viewer_lifecycle_generation;
|
|
99
|
+
// Initialize VSCode API at module level
|
|
100
|
+
try {
|
|
101
|
+
vscode_api = globalThis.acquireVsCodeApi?.() ?? null;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.warn(`VSCode API already acquired or not available:`, error);
|
|
105
|
+
vscode_api = null;
|
|
106
|
+
}
|
|
107
|
+
// Set up VSCode-specific download override for file exports
|
|
108
|
+
export const setup_vscode_download = () => {
|
|
109
|
+
if (!vscode_api)
|
|
110
|
+
return;
|
|
111
|
+
global_window.download = (data, filename) => {
|
|
112
|
+
if (!filename?.trim()) {
|
|
113
|
+
console.error(`Invalid filename provided to download`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const send_message = (content, is_binary) => {
|
|
117
|
+
vscode_api?.postMessage({
|
|
118
|
+
command: `saveAs`,
|
|
119
|
+
content,
|
|
120
|
+
filename,
|
|
121
|
+
is_binary,
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
try {
|
|
125
|
+
if (typeof data === `string`) {
|
|
126
|
+
send_message(data, false);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const reader = new FileReader();
|
|
130
|
+
reader.addEventListener(`load`, () => send_message(reader.result, true));
|
|
131
|
+
reader.addEventListener(`error`, () => {
|
|
132
|
+
console.error(`Failed to read binary data for download`);
|
|
133
|
+
vscode_api?.postMessage({
|
|
134
|
+
command: `error`,
|
|
135
|
+
text: `Failed to read binary data for download`,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
reader.readAsDataURL(data);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.error(`VSCode download failed:`, error);
|
|
143
|
+
vscode_api?.postMessage({
|
|
144
|
+
command: `error`,
|
|
145
|
+
text: `Download failed: ${error}`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
// Unmount the existing component before replacement to prevent memory leaks.
|
|
151
|
+
async function unmount_current_app() {
|
|
152
|
+
const app = current_app;
|
|
153
|
+
current_app = null;
|
|
154
|
+
if (app)
|
|
155
|
+
await unmount(app);
|
|
156
|
+
}
|
|
157
|
+
// Handle file change events from extension
|
|
158
|
+
const handle_file_change = async (message, generation) => {
|
|
159
|
+
if (!is_current_file_change(generation))
|
|
160
|
+
return;
|
|
161
|
+
if (message.command === `fileDeleted`) {
|
|
162
|
+
// File was deleted - show error message
|
|
163
|
+
await unmount_current_app();
|
|
164
|
+
if (!is_current_file_change(generation))
|
|
165
|
+
return;
|
|
166
|
+
const container = document.querySelector(`#matterviz-app`);
|
|
167
|
+
if (container) {
|
|
168
|
+
container.innerHTML = `
|
|
169
|
+
<div style="padding: 2rem; text-align: center; color: var(--vscode-errorForeground);">
|
|
170
|
+
<h2>File Deleted</h2>
|
|
171
|
+
<p>The file "${escape_html(message.file_path)}" has been deleted.</p>
|
|
172
|
+
</div>
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
if (message.theme && is_valid_theme_name(message.theme)) {
|
|
179
|
+
apply_theme_to_dom(message.theme);
|
|
180
|
+
}
|
|
181
|
+
const result = await parse_file_data(message.data);
|
|
182
|
+
if (!is_current_file_change(generation))
|
|
183
|
+
return;
|
|
184
|
+
const container = document.querySelector(`#matterviz-app`);
|
|
185
|
+
if (container) {
|
|
186
|
+
await unmount_current_app();
|
|
187
|
+
if (!is_current_file_change(generation))
|
|
188
|
+
return;
|
|
189
|
+
current_app = create_display(container, result);
|
|
190
|
+
}
|
|
191
|
+
vscode_api?.postMessage({ command: `info`, text: `File reloaded successfully` });
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (!is_current_file_change(generation))
|
|
195
|
+
return;
|
|
196
|
+
console.error(`Failed to reload file:`, error);
|
|
197
|
+
vscode_api?.postMessage({
|
|
198
|
+
command: `error`,
|
|
199
|
+
text: `Failed to reload file: ${error}`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const process_file_change = (message) => {
|
|
204
|
+
if (viewer_disposed)
|
|
205
|
+
return;
|
|
206
|
+
const generation = ++file_change_generation;
|
|
207
|
+
file_change_queue = file_change_queue
|
|
208
|
+
.then(() => handle_file_change(message, generation))
|
|
209
|
+
.catch((error) => {
|
|
210
|
+
if (!is_current_file_change(generation))
|
|
211
|
+
return;
|
|
212
|
+
console.error(`Failed to process file change:`, error);
|
|
213
|
+
vscode_api?.postMessage({
|
|
214
|
+
command: `error`,
|
|
215
|
+
text: `Failed to process file change: ${error}`,
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
// Request host-side parsing for a file too large to copy into the webview.
|
|
220
|
+
function request_large_file_content(file_path, timeout = 120_000) {
|
|
221
|
+
if (!vscode_api)
|
|
222
|
+
throw new Error(`VS Code API not available`);
|
|
223
|
+
const message = { command: `request_large_file`, file_path };
|
|
224
|
+
return post_request(vscode_api, message, timeout, `Large file timeout`, (data, resolve, reject) => {
|
|
225
|
+
if (data.command === `large_file_progress`) {
|
|
226
|
+
// TODO maybe forward file load progress to UI
|
|
227
|
+
console.info(`Progress: ${data.stage} - ${data.progress}%`);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (data.command !== `large_file_response`)
|
|
231
|
+
return false;
|
|
232
|
+
if (data.error)
|
|
233
|
+
reject(new Error(data.error));
|
|
234
|
+
else if (data.parsed_trajectory && typeof data.parsed_trajectory === `object`) {
|
|
235
|
+
resolve(data.parsed_trajectory);
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
reject(new TypeError(`Malformed large-file response`));
|
|
239
|
+
return true;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async function parse_file_data({ content, filename, is_base64, }) {
|
|
243
|
+
const marker = parse_large_file_marker(content);
|
|
244
|
+
if (!marker)
|
|
245
|
+
return parse_file_content(content, filename, is_base64);
|
|
246
|
+
if (!is_indexable_trajectory_filename(filename)) {
|
|
247
|
+
throw new Error(`Large-file loading is only supported for indexed trajectories: ${filename}`);
|
|
248
|
+
}
|
|
249
|
+
console.info(`Handling large file: ${filename} (${Math.round(marker.file_size / 1024 / 1024)}MB)`);
|
|
250
|
+
const trajectory = await request_large_file_content(marker.file_path);
|
|
251
|
+
return {
|
|
252
|
+
type: `trajectory`,
|
|
253
|
+
data: trajectory,
|
|
254
|
+
filename,
|
|
255
|
+
streaming_info: { file_path: marker.file_path },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// Create error display in container
|
|
259
|
+
const create_error_display = (container, error, filename) => {
|
|
260
|
+
container.innerHTML = `
|
|
261
|
+
<div style="padding: 20px; text-align: center; color: var(--vscode-errorForeground, #f85149);
|
|
262
|
+
background: var(--vscode-editor-background, #1e1e1e); height: 100%;
|
|
263
|
+
display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
|
264
|
+
<div style="font-size: 48px; margin-bottom: 20px;">❌</div>
|
|
265
|
+
<h2 style="margin: 0 0 15px 0;">Failed to Parse File</h2>
|
|
266
|
+
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 8px; max-width: 600px;">
|
|
267
|
+
<p style="margin: 0 0 10px 0;"><strong>File:</strong> ${escape_html(filename)}</p>
|
|
268
|
+
<p style="margin: 0 0 10px 0;"><strong>Error:</strong> ${escape_html(error.message)}</p>
|
|
269
|
+
<p style="margin: 0; font-size: 14px; opacity: 0.8;">
|
|
270
|
+
Supported formats: XYZ, CIF, JSON, POSCAR, trajectory files (.traj, .h5, .extxyz), etc.
|
|
271
|
+
</p>
|
|
272
|
+
</div>
|
|
273
|
+
</div>`;
|
|
274
|
+
};
|
|
275
|
+
// Mount Svelte component and create display
|
|
276
|
+
export const create_display = (container, result, display_options) => {
|
|
277
|
+
const { filename } = result;
|
|
278
|
+
Object.assign(container.style, {
|
|
279
|
+
width: `100%`,
|
|
280
|
+
height: `100%`,
|
|
281
|
+
position: `absolute`,
|
|
282
|
+
top: `0`,
|
|
283
|
+
left: `0`,
|
|
284
|
+
right: `0`,
|
|
285
|
+
bottom: `0`,
|
|
286
|
+
background: `var(--vscode-editor-background, #1e1e1e)`,
|
|
287
|
+
color: `var(--vscode-editor-foreground, #d4d4d4)`,
|
|
288
|
+
overflow: `hidden`,
|
|
289
|
+
});
|
|
290
|
+
container.innerHTML = ``;
|
|
291
|
+
// Get defaults and create props
|
|
292
|
+
const defaults = merge(globalThis.matterviz_data?.defaults);
|
|
293
|
+
const common_props = {
|
|
294
|
+
allow_file_drop: false,
|
|
295
|
+
style: `height: 100%; border-radius: 0`,
|
|
296
|
+
enable_tips: false,
|
|
297
|
+
fullscreen_toggle: false,
|
|
298
|
+
};
|
|
299
|
+
let app;
|
|
300
|
+
let log_message;
|
|
301
|
+
if (result.type === `trajectory`) {
|
|
302
|
+
// Prepare trajectory data for VS Code streaming if supported
|
|
303
|
+
let final_trajectory = result.data;
|
|
304
|
+
if (vscode_api && result.streaming_info?.file_path) {
|
|
305
|
+
final_trajectory = {
|
|
306
|
+
...final_trajectory,
|
|
307
|
+
is_indexed: true,
|
|
308
|
+
frames: final_trajectory.frames || [],
|
|
309
|
+
frame_loader: new VSCodeFrameLoader(result.streaming_info.file_path, vscode_api),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const { initial_step_idx, on_step_change } = display_options ?? {};
|
|
313
|
+
const trajectory_mount_props = {
|
|
314
|
+
trajectory: final_trajectory,
|
|
315
|
+
...trajectory_props(defaults),
|
|
316
|
+
...common_props,
|
|
317
|
+
...(initial_step_idx !== undefined && { current_step_idx: initial_step_idx }),
|
|
318
|
+
...(on_step_change && {
|
|
319
|
+
on_step_change: (data) => on_step_change(data.step_idx ?? 0, data.frame_count ?? 0),
|
|
320
|
+
}),
|
|
321
|
+
};
|
|
322
|
+
// vaspout.h5 files carrying results/electron_dos get a DOS panel below the trajectory
|
|
323
|
+
const traj_electronic = final_trajectory.metadata?.electronic;
|
|
324
|
+
if (traj_electronic?.dos) {
|
|
325
|
+
app = mount(TrajectoryWithDos, {
|
|
326
|
+
target: container,
|
|
327
|
+
props: { dos: traj_electronic.dos, trajectory_props: trajectory_mount_props },
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
app = mount(Trajectory, { target: container, props: trajectory_mount_props });
|
|
332
|
+
}
|
|
333
|
+
log_message = `Trajectory rendered: ${filename} (${final_trajectory.frames?.length ?? 0} initial frames, ${final_trajectory.total_frames ?? `unknown`} total)`;
|
|
334
|
+
}
|
|
335
|
+
else if (result.type === `vaspout_electronic`) {
|
|
336
|
+
const { dos, bands } = result.data;
|
|
337
|
+
const spectral_props = { style: `height: 100%`, class: `vaspout-electronic` };
|
|
338
|
+
if (bands && dos) {
|
|
339
|
+
app = mount(BandsAndDos, {
|
|
340
|
+
target: container,
|
|
341
|
+
props: {
|
|
342
|
+
band_structs: bands,
|
|
343
|
+
doses: dos,
|
|
344
|
+
bands_props: { band_type: `electronic` },
|
|
345
|
+
...spectral_props,
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
else if (bands) {
|
|
350
|
+
app = mount(Bands, {
|
|
351
|
+
target: container,
|
|
352
|
+
props: { band_structs: bands, band_type: `electronic`, ...spectral_props },
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
app = mount(Dos, {
|
|
357
|
+
target: container,
|
|
358
|
+
props: { doses: dos, ...spectral_props },
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const parts = [bands ? `bands` : null, dos ? `DOS` : null].filter(Boolean).join(` + `);
|
|
362
|
+
log_message = `Electronic structure rendered: ${filename} (${parts})`;
|
|
363
|
+
}
|
|
364
|
+
else if (result.type === `fermi_surface`) {
|
|
365
|
+
const props = { ...common_props };
|
|
366
|
+
if (is_fermi_surface_data(result.data)) {
|
|
367
|
+
props.fermi_data = result.data;
|
|
368
|
+
}
|
|
369
|
+
else
|
|
370
|
+
props.band_data = result.data;
|
|
371
|
+
app = mount(FermiSurface, { target: container, props });
|
|
372
|
+
log_message = `Fermi surface rendered: ${filename}`;
|
|
373
|
+
}
|
|
374
|
+
else if (result.type === `isosurface`) {
|
|
375
|
+
// VolumetricFileData has structure + volumes; render via Structure with volumetric_data
|
|
376
|
+
const vol_file = result.data;
|
|
377
|
+
app = mount(Structure, {
|
|
378
|
+
target: container,
|
|
379
|
+
props: {
|
|
380
|
+
structure: vol_file.structure,
|
|
381
|
+
volumetric_data: vol_file.volumes,
|
|
382
|
+
...structure_props(defaults),
|
|
383
|
+
...common_props,
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
log_message = `Volumetric data rendered: ${filename}`;
|
|
387
|
+
}
|
|
388
|
+
else if (result.type === `convex_hull`) {
|
|
389
|
+
const entries = result.data;
|
|
390
|
+
app = mount(ConvexHull, { target: container, props: { entries, ...common_props } });
|
|
391
|
+
log_message = `Convex hull rendered: ${filename} (${entries.length} entries)`;
|
|
392
|
+
}
|
|
393
|
+
else if (result.type === `phase_diagram`) {
|
|
394
|
+
app = mount(IsobaricBinaryPhaseDiagram, {
|
|
395
|
+
target: container,
|
|
396
|
+
props: { data: result.data, ...common_props },
|
|
397
|
+
});
|
|
398
|
+
log_message = `Phase diagram rendered: ${filename}`;
|
|
399
|
+
}
|
|
400
|
+
else if (result.type === `json_browser`) {
|
|
401
|
+
app = mount(JsonBrowser, {
|
|
402
|
+
target: container,
|
|
403
|
+
props: { value: result.data, defaults, filename },
|
|
404
|
+
});
|
|
405
|
+
log_message = `JSON browser opened: ${filename}`;
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
// Default: structure
|
|
409
|
+
const structure = result.data;
|
|
410
|
+
app = mount(Structure, {
|
|
411
|
+
target: container,
|
|
412
|
+
props: { structure, ...structure_props(defaults), ...common_props },
|
|
413
|
+
});
|
|
414
|
+
log_message = `Structure rendered: ${filename} (${structure.sites?.length ?? 0} sites)`;
|
|
415
|
+
}
|
|
416
|
+
vscode_api?.postMessage({ command: `info`, text: log_message });
|
|
417
|
+
return app;
|
|
418
|
+
};
|
|
419
|
+
// Map defaults to trajectory component props
|
|
420
|
+
const trajectory_props = (defaults) => {
|
|
421
|
+
const { trajectory, plot, scatter, histogram } = defaults;
|
|
422
|
+
return {
|
|
423
|
+
...trajectory,
|
|
424
|
+
structure_props: structure_props(defaults),
|
|
425
|
+
loading_options: {
|
|
426
|
+
bin_file_threshold: trajectory.bin_file_threshold,
|
|
427
|
+
text_file_threshold: trajectory.text_file_threshold,
|
|
428
|
+
use_indexing: trajectory.use_indexing,
|
|
429
|
+
chunk_size: trajectory.chunk_size,
|
|
430
|
+
max_frames_in_memory: trajectory.max_frames_in_memory,
|
|
431
|
+
enable_performance_monitoring: trajectory.enable_performance_monitoring,
|
|
432
|
+
prefetch_frames: trajectory.prefetch_frames,
|
|
433
|
+
cache_parsed_data: trajectory.cache_parsed_data,
|
|
434
|
+
},
|
|
435
|
+
scatter_props: {
|
|
436
|
+
markers: scatter.symbol_type,
|
|
437
|
+
line_width: scatter.line.width,
|
|
438
|
+
point_size: scatter.point.size,
|
|
439
|
+
show_legend: scatter.show_legend,
|
|
440
|
+
enable_zoom: plot.enable_zoom,
|
|
441
|
+
zoom_factor: plot.zoom_factor,
|
|
442
|
+
auto_fit_range: plot.auto_fit_range,
|
|
443
|
+
show_grid: plot.grid_lines,
|
|
444
|
+
show_axis_labels: plot.axis_labels,
|
|
445
|
+
animation_duration: plot.animation_duration,
|
|
446
|
+
legend: { show: scatter.show_legend },
|
|
447
|
+
},
|
|
448
|
+
histogram_props: {
|
|
449
|
+
mode: histogram.mode,
|
|
450
|
+
show_legend: histogram.show_legend,
|
|
451
|
+
bin_count: histogram.bin_count,
|
|
452
|
+
enable_zoom: plot.enable_zoom,
|
|
453
|
+
zoom_factor: plot.zoom_factor,
|
|
454
|
+
auto_fit_range: plot.auto_fit_range,
|
|
455
|
+
show_grid: plot.grid_lines,
|
|
456
|
+
show_axis_labels: plot.axis_labels,
|
|
457
|
+
animation_duration: plot.animation_duration,
|
|
458
|
+
legend: { show: histogram.show_legend },
|
|
459
|
+
},
|
|
460
|
+
spinner_props: { show_progress: trajectory.show_parsing_progress },
|
|
461
|
+
ELEM_PROPERTY_LABELS: {},
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
// Initialize the MatterViz application from data passed by the extension
|
|
465
|
+
async function initialize(lifecycle_generation) {
|
|
466
|
+
const file_data = globalThis.matterviz_data?.data;
|
|
467
|
+
const theme = globalThis.matterviz_data?.theme;
|
|
468
|
+
const moyo_wasm_url = globalThis.matterviz_data?.moyo_wasm_url;
|
|
469
|
+
if (!file_data?.content || !file_data.filename) {
|
|
470
|
+
throw new Error(`No data provided to MatterViz app`);
|
|
471
|
+
}
|
|
472
|
+
// Initialize WASM early with URL from extension (for symmetry analysis)
|
|
473
|
+
if (moyo_wasm_url)
|
|
474
|
+
await ensure_moyo_wasm_ready(moyo_wasm_url);
|
|
475
|
+
if (!is_current_lifecycle(lifecycle_generation))
|
|
476
|
+
return null;
|
|
477
|
+
setup_vscode_download();
|
|
478
|
+
if (theme)
|
|
479
|
+
apply_theme_to_dom(theme);
|
|
480
|
+
const container = document.querySelector(`#matterviz-app`);
|
|
481
|
+
if (!container)
|
|
482
|
+
throw new Error(`Target container not found in DOM`);
|
|
483
|
+
const result = await parse_file_data(file_data);
|
|
484
|
+
if (!is_current_lifecycle(lifecycle_generation))
|
|
485
|
+
return null;
|
|
486
|
+
const app = create_display(container, result);
|
|
487
|
+
// Store the app instance for file watching
|
|
488
|
+
current_app = app;
|
|
489
|
+
// Listen for file change messages from extension
|
|
490
|
+
if (vscode_api && !file_change_listener_registered) {
|
|
491
|
+
globalThis.addEventListener(`message`, (event) => {
|
|
492
|
+
if ([`fileUpdated`, `fileDeleted`].includes(event.data.command)) {
|
|
493
|
+
process_file_change(event.data);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
file_change_listener_registered = true;
|
|
497
|
+
}
|
|
498
|
+
return app;
|
|
499
|
+
}
|
|
500
|
+
// Cleanup function to properly dispose of components
|
|
501
|
+
async function cleanup_matterviz() {
|
|
502
|
+
viewer_disposed = true;
|
|
503
|
+
file_change_generation++;
|
|
504
|
+
viewer_lifecycle_generation++;
|
|
505
|
+
file_change_queue = Promise.resolve();
|
|
506
|
+
await unmount_current_app();
|
|
507
|
+
}
|
|
508
|
+
// Export initialization and cleanup functions to global scope
|
|
509
|
+
global_window.initializeMatterViz = async () => {
|
|
510
|
+
if (!globalThis.matterviz_data) {
|
|
511
|
+
console.warn(`No matterviz_data found on window`);
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
viewer_disposed = false;
|
|
515
|
+
const lifecycle_generation = ++viewer_lifecycle_generation;
|
|
516
|
+
try {
|
|
517
|
+
// initialize() already records the app in current_app
|
|
518
|
+
return await initialize(lifecycle_generation);
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
if (!is_current_lifecycle(lifecycle_generation))
|
|
522
|
+
return null;
|
|
523
|
+
const err = to_error(error);
|
|
524
|
+
const container = document.querySelector(`#matterviz-app`);
|
|
525
|
+
if (container) {
|
|
526
|
+
create_error_display(container, err, globalThis.matterviz_data?.data?.filename || `Unknown file`);
|
|
527
|
+
}
|
|
528
|
+
vscode_api?.postMessage({
|
|
529
|
+
command: `error`,
|
|
530
|
+
text: `Error rendering ${globalThis.matterviz_data?.data?.filename || `Unknown file`}: ${err.message}`,
|
|
531
|
+
});
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
global_window.cleanupMatterViz = cleanup_matterviz;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ViewType } from './types';
|
|
2
|
+
export type { ViewType } from './types';
|
|
3
|
+
export interface ParseResult {
|
|
4
|
+
type: ViewType;
|
|
5
|
+
data: unknown;
|
|
6
|
+
filename: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function base64_to_array_buffer(base64: string): ArrayBuffer;
|
|
9
|
+
export declare const parse_file_content: (content: string, filename: string, is_base64?: boolean) => Promise<ParseResult>;
|