matterviz 0.4.1 → 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/EmptyState.svelte +10 -4
- package/dist/FilePicker.svelte +48 -60
- package/dist/Icon.svelte +7 -4
- package/dist/MillerIndexInput.svelte +2 -2
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +68 -0
- package/dist/brillouin/BrillouinZone.svelte +90 -91
- package/dist/brillouin/BrillouinZone.svelte.d.ts +5 -7
- package/dist/brillouin/BrillouinZoneControls.svelte +3 -6
- package/dist/brillouin/BrillouinZoneExportPane.svelte +18 -14
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +6 -10
- package/dist/brillouin/BrillouinZoneScene.svelte +46 -68
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +48 -50
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +61 -169
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +559 -755
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +1 -1
- package/dist/chempot-diagram/async-compute.svelte.js +13 -14
- 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/controls-state.svelte.js +1 -1
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/colors/index.d.ts +0 -4
- package/dist/colors/index.js +19 -20
- package/dist/composition/BarChart.svelte +30 -26
- package/dist/composition/BubbleChart.svelte +17 -17
- package/dist/composition/Composition.svelte +10 -19
- package/dist/composition/Formula.svelte +20 -27
- package/dist/composition/FormulaFilter.svelte +115 -184
- package/dist/composition/PieChart.svelte +55 -41
- package/dist/composition/format.js +7 -9
- package/dist/composition/index.d.ts +2 -0
- package/dist/composition/index.js +14 -0
- package/dist/composition/parse.d.ts +2 -2
- package/dist/composition/parse.js +61 -115
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +17 -26
- package/dist/convex-hull/ConvexHull.svelte +12 -15
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +93 -94
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +127 -186
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +131 -180
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullChrome.svelte +36 -40
- package/dist/convex-hull/ConvexHullControls.svelte +46 -48
- package/dist/convex-hull/ConvexHullStats.svelte +193 -195
- package/dist/convex-hull/ConvexHullTooltip.svelte +5 -12
- package/dist/convex-hull/GasPressureControls.svelte +5 -12
- package/dist/convex-hull/StructurePopup.svelte +18 -22
- package/dist/convex-hull/StructurePopup.svelte.d.ts +2 -0
- package/dist/convex-hull/TemperatureSlider.svelte +4 -10
- package/dist/convex-hull/canvas-interactions.svelte.js +2 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.d.ts +8 -10
- package/dist/convex-hull/helpers.js +37 -43
- package/dist/convex-hull/hull-state.svelte.d.ts +1 -1
- package/dist/convex-hull/thermodynamics.js +37 -44
- package/dist/coordination/CoordinationBarPlot.svelte +49 -53
- package/dist/coordination/CoordinationBarPlot.svelte.d.ts +1 -1
- package/dist/coordination/calc-coordination.d.ts +1 -1
- package/dist/coordination/calc-coordination.js +28 -25
- package/dist/element/BohrAtom.svelte +5 -9
- package/dist/element/ElementHeading.svelte +4 -1
- package/dist/element/ElementPhoto.svelte +11 -9
- package/dist/element/ElementStats.svelte +12 -10
- package/dist/element/ElementTile.svelte +44 -67
- package/dist/element/Nucleus.svelte +4 -2
- package/dist/element/data.d.ts +3 -2
- package/dist/element/data.js +5 -1
- package/dist/element/helpers.d.ts +4 -0
- package/dist/element/helpers.js +18 -0
- package/dist/element/index.d.ts +2 -5
- package/dist/element/index.js +4 -19
- package/dist/feedback/DragOverlay.svelte +5 -5
- package/dist/feedback/DragOverlay.svelte.d.ts +2 -2
- package/dist/feedback/StatusMessage.svelte +7 -4
- package/dist/fermi-surface/FermiSlice.svelte +21 -18
- package/dist/fermi-surface/FermiSurface.svelte +35 -44
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +7 -13
- package/dist/fermi-surface/FermiSurfaceControls.svelte +6 -20
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +64 -136
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +9 -6
- package/dist/fermi-surface/compute.js +25 -58
- package/dist/fermi-surface/parse.js +54 -88
- 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 +153 -219
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +2 -2
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +9 -7
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/index.d.ts +5 -0
- package/dist/icons.d.ts +4 -0
- package/dist/icons.js +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +11 -7
- package/dist/io/decompress.d.ts +4 -1
- package/dist/io/decompress.js +24 -36
- package/dist/io/export.d.ts +11 -4
- package/dist/io/export.js +75 -19
- package/dist/io/fetch.d.ts +0 -3
- package/dist/io/fetch.js +0 -11
- package/dist/io/file-drop.js +60 -35
- package/dist/io/index.d.ts +0 -1
- package/dist/io/index.js +2 -9
- package/dist/io/is-binary.d.ts +9 -0
- package/dist/io/is-binary.js +45 -1
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +40 -40
- package/dist/isosurface/Isosurface.svelte +700 -215
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +590 -116
- 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/parse-vaspwave.d.ts +3 -0
- package/dist/isosurface/parse-vaspwave.js +138 -0
- package/dist/isosurface/parse.js +6 -6
- 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.d.ts +6 -2
- package/dist/labels.js +44 -14
- package/dist/layout/FullscreenButton.svelte +2 -1
- package/dist/layout/FullscreenToggle.svelte +10 -4
- package/dist/layout/InfoCard.svelte +8 -21
- package/dist/layout/InfoTag.svelte +1 -3
- package/dist/layout/NumberRangeInput.svelte +46 -0
- package/dist/layout/NumberRangeInput.svelte.d.ts +12 -0
- package/dist/layout/PropertyFilter.svelte +2 -6
- package/dist/layout/SettingsSection.svelte +57 -62
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/SubpageGrid.svelte +3 -1
- package/dist/layout/ViewerChrome.svelte +16 -11
- package/dist/layout/ViewerChrome.svelte.d.ts +2 -1
- package/dist/layout/fullscreen.js +3 -5
- package/dist/layout/fullscreen.svelte.js +1 -2
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.js +1 -0
- package/dist/layout/json-tree/JsonNode.svelte +22 -59
- package/dist/layout/json-tree/JsonTree.svelte +101 -172
- package/dist/layout/json-tree/JsonValue.svelte +9 -32
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -188
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +5 -2
- package/dist/math.js +48 -38
- package/dist/overlays/ContextMenu.svelte +19 -34
- package/dist/overlays/CopyButton.svelte +11 -5
- package/dist/overlays/DragControlTab.svelte +6 -1
- package/dist/overlays/DraggablePane.svelte +110 -74
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -1
- package/dist/overlays/GlassChip.svelte +32 -0
- package/dist/overlays/GlassChip.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +11 -23
- package/dist/overlays/index.d.ts +7 -0
- package/dist/overlays/index.js +26 -0
- package/dist/overlays/portal.d.ts +2 -0
- package/dist/overlays/portal.js +14 -0
- package/dist/periodic-table/PeriodicTable.svelte +113 -145
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +5 -3
- package/dist/periodic-table/PeriodicTableControls.svelte +37 -115
- package/dist/periodic-table/TableInset.svelte +9 -4
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +68 -105
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -8
- package/dist/phase-diagram/PhaseDiagramControls.svelte +70 -94
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +2 -1
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +12 -12
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +54 -58
- package/dist/phase-diagram/TdbInfoPanel.svelte +21 -15
- package/dist/phase-diagram/build-diagram.js +4 -9
- package/dist/phase-diagram/diagram-input.d.ts +1 -2
- package/dist/phase-diagram/parse.js +15 -15
- package/dist/phase-diagram/svg-to-diagram.js +28 -27
- package/dist/phase-diagram/utils.js +2 -3
- package/dist/plot/bar/BarPlot.svelte +461 -426
- package/dist/plot/bar/BarPlot.svelte.d.ts +2 -0
- package/dist/plot/bar/BarPlotControls.svelte +1 -3
- package/dist/plot/bar/BarPlotControls.svelte.d.ts +4 -6
- package/dist/plot/bar/SpacegroupBarPlot.svelte +46 -77
- package/dist/plot/bar/data.d.ts +5 -4
- package/dist/plot/bar/data.js +2 -1
- package/dist/plot/box/BoxPlot.svelte +322 -226
- package/dist/plot/box/BoxPlot.svelte.d.ts +2 -0
- package/dist/plot/box/Violin.svelte.d.ts +1 -0
- package/dist/plot/box/box-plot.js +1 -1
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +2 -2
- package/dist/plot/core/axis-utils.d.ts +12 -7
- package/dist/plot/core/axis-utils.js +48 -48
- package/dist/plot/core/components/AxisLabel.svelte +82 -20
- package/dist/plot/core/components/AxisLabel.svelte.d.ts +1 -0
- package/dist/plot/core/components/ColorBar.svelte +101 -106
- package/dist/plot/core/components/ColorScaleSelect.svelte +1 -1
- package/dist/plot/core/components/FillArea.svelte +10 -17
- package/dist/plot/core/components/HierarchyControls.svelte +249 -0
- package/dist/plot/{sunburst/SunburstControls.svelte.d.ts → core/components/HierarchyControls.svelte.d.ts} +12 -8
- package/dist/plot/core/components/InteractiveAxisLabel.svelte +1 -1
- package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/core/components/Line.svelte +18 -9
- package/dist/plot/core/components/Line.svelte.d.ts +2 -0
- package/dist/plot/core/components/PlotAxis.svelte +40 -17
- package/dist/plot/core/components/PlotControls.svelte +65 -41
- package/dist/plot/core/components/PlotLegend.svelte +31 -35
- package/dist/plot/core/components/PlotMarginals.svelte +616 -0
- package/dist/plot/core/components/PlotMarginals.svelte.d.ts +15 -0
- package/dist/plot/core/components/PlotTooltip.svelte +1 -1
- package/dist/plot/core/components/PortalSelect.svelte +4 -8
- package/dist/plot/core/components/ReferenceLine.svelte +27 -24
- package/dist/plot/core/components/ReferenceLine3D.svelte +54 -57
- package/dist/plot/core/components/ReferencePlane.svelte +7 -8
- package/dist/plot/core/components/ZeroLines.svelte +4 -8
- package/dist/plot/core/components/ZoomRect.svelte +1 -2
- package/dist/plot/core/components/index.d.ts +1 -0
- package/dist/plot/core/components/index.js +1 -0
- package/dist/plot/core/data-cleaning-signal.d.ts +46 -0
- package/dist/plot/core/data-cleaning-signal.js +467 -0
- package/dist/plot/core/data-cleaning.d.ts +2 -42
- package/dist/plot/core/data-cleaning.js +27 -474
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +9 -15
- package/dist/plot/core/fill-utils.d.ts +3 -1
- package/dist/plot/core/fill-utils.js +28 -6
- package/dist/plot/core/index.d.ts +1 -0
- package/dist/plot/core/index.js +1 -0
- package/dist/plot/core/interactions.js +0 -2
- package/dist/plot/core/layout.d.ts +15 -6
- package/dist/plot/core/layout.js +57 -38
- package/dist/plot/core/marginals.d.ts +170 -0
- package/dist/plot/core/marginals.js +436 -0
- 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/reference-line.js +1 -1
- package/dist/plot/core/scales.d.ts +23 -0
- package/dist/plot/core/scales.js +39 -13
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/types/fills.d.ts +101 -0
- package/dist/plot/core/types/fills.js +15 -0
- package/dist/plot/core/types/plot-3d.d.ts +87 -0
- package/dist/plot/core/types/plot-3d.js +4 -0
- package/dist/plot/core/types/reference-lines.d.ts +140 -0
- package/dist/plot/core/types/reference-lines.js +9 -0
- package/dist/plot/core/types.d.ts +29 -324
- package/dist/plot/core/types.js +23 -20
- package/dist/plot/core/utils/hierarchy-chart.d.ts +52 -0
- package/dist/plot/core/utils/hierarchy-chart.js +191 -0
- package/dist/plot/core/utils/hierarchy-labels.d.ts +20 -0
- package/dist/plot/core/utils/hierarchy-labels.js +100 -0
- package/dist/plot/core/utils/label-placement.js +2 -2
- package/dist/plot/histogram/Histogram.svelte +314 -340
- package/dist/plot/histogram/Histogram.svelte.d.ts +7 -7
- package/dist/plot/histogram/HistogramControls.svelte +30 -28
- package/dist/plot/histogram/histogram.d.ts +31 -0
- package/dist/plot/histogram/histogram.js +70 -0
- package/dist/plot/histogram/index.d.ts +1 -0
- package/dist/plot/histogram/index.js +1 -0
- package/dist/plot/index.d.ts +1 -0
- package/dist/plot/index.js +1 -0
- package/dist/plot/sankey/Sankey.svelte +78 -69
- package/dist/plot/sankey/Sankey.svelte.d.ts +2 -2
- package/dist/plot/sankey/SankeyControls.svelte +22 -16
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +311 -141
- package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +7 -0
- package/dist/plot/scatter/ElementScatter.svelte +4 -1
- package/dist/plot/scatter/ScatterPlot.svelte +517 -410
- package/dist/plot/scatter/ScatterPlot.svelte.d.ts +2 -0
- package/dist/plot/scatter/ScatterPlotControls.svelte +18 -19
- package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/scatter/ScatterPoint.svelte +16 -9
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/scatter/binned-scatter-types.d.ts +3 -2
- package/dist/plot/scatter/binned-scatter-types.js +6 -1
- package/dist/plot/scatter/scatter-data.d.ts +1 -1
- package/dist/plot/scatter/scatter-data.js +8 -7
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte +25 -34
- package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +8 -5
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +113 -116
- package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/scatter-3d/Surface3D.svelte +4 -13
- package/dist/plot/sunburst/Sunburst.svelte +269 -347
- package/dist/plot/sunburst/Sunburst.svelte.d.ts +2 -2
- package/dist/plot/sunburst/index.d.ts +1 -1
- package/dist/plot/sunburst/index.js +2 -1
- package/dist/plot/sunburst/render.d.ts +2 -2
- package/dist/plot/sunburst/render.js +47 -22
- package/dist/plot/sunburst/sunburst.d.ts +5 -1
- package/dist/plot/sunburst/sunburst.js +10 -3
- package/dist/plot/treemap/Treemap.svelte +1022 -0
- package/dist/plot/treemap/Treemap.svelte.d.ts +90 -0
- package/dist/plot/treemap/index.d.ts +4 -0
- package/dist/plot/treemap/index.js +4 -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 +28 -0
- package/dist/plot/treemap/treemap.js +102 -0
- package/dist/rdf/RdfPlot.svelte +37 -59
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/calc-rdf.js +63 -52
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +85 -27
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/bind-renderer.svelte.js +14 -6
- 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.d.ts +19 -9
- package/dist/settings.js +229 -525
- package/dist/spectral/Bands.svelte +207 -221
- package/dist/spectral/BandsAndDos.svelte +11 -9
- package/dist/spectral/BrillouinBandsDos.svelte +19 -42
- package/dist/spectral/Dos.svelte +92 -93
- package/dist/spectral/helpers.d.ts +3 -3
- package/dist/spectral/helpers.js +65 -140
- package/dist/state.svelte.js +5 -18
- package/dist/structure/Arrow.svelte +5 -18
- package/dist/structure/ArrowInstances.svelte +160 -0
- package/dist/structure/ArrowInstances.svelte.d.ts +16 -0
- package/dist/structure/AtomLegend.svelte +120 -85
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/Bond.svelte +31 -23
- package/dist/structure/CanvasTooltip.svelte +15 -3
- package/dist/structure/CellSelect.svelte +45 -35
- package/dist/structure/Cylinder.svelte +24 -40
- package/dist/structure/InstancedAtoms.svelte +114 -0
- package/dist/structure/InstancedAtoms.svelte.d.ts +15 -0
- package/dist/structure/Lattice.svelte +85 -149
- package/dist/structure/SiteLabels.svelte +122 -0
- package/dist/structure/SiteLabels.svelte.d.ts +18 -0
- package/dist/structure/Structure.svelte +636 -504
- package/dist/structure/Structure.svelte.d.ts +4 -2
- package/dist/structure/StructureCarousel.svelte +637 -0
- package/dist/structure/StructureCarousel.svelte.d.ts +19 -0
- package/dist/structure/StructureControls.svelte +165 -425
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureExportPane.svelte +17 -21
- package/dist/structure/StructureInfoPane.svelte +128 -133
- package/dist/structure/StructureInfoPane.svelte.d.ts +1 -1
- package/dist/structure/StructureScene.svelte +535 -578
- package/dist/structure/StructureScene.svelte.d.ts +6 -2
- package/dist/structure/StructureViewport.svelte +451 -0
- package/dist/structure/StructureViewport.svelte.d.ts +65 -0
- package/dist/structure/atom-label-placement.d.ts +16 -12
- package/dist/structure/atom-label-placement.js +70 -37
- package/dist/structure/atom-properties.d.ts +2 -0
- package/dist/structure/atom-properties.js +105 -38
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +9 -3
- package/dist/structure/bonding.js +95 -46
- package/dist/structure/edit-history.d.ts +6 -0
- package/dist/structure/edit-history.js +27 -0
- package/dist/structure/export.d.ts +1 -0
- package/dist/structure/export.js +89 -105
- package/dist/structure/format-detect.d.ts +1 -0
- package/dist/structure/format-detect.js +20 -0
- package/dist/structure/geometry.d.ts +9 -0
- package/dist/structure/geometry.js +27 -0
- package/dist/structure/index.d.ts +16 -1
- package/dist/structure/index.js +15 -10
- package/dist/structure/measure.d.ts +1 -1
- package/dist/structure/measure.js +6 -12
- package/dist/structure/parse.d.ts +2 -1
- package/dist/structure/parse.js +272 -313
- package/dist/structure/partial-occupancy.js +10 -5
- package/dist/structure/pbc.js +85 -107
- package/dist/structure/polyhedra.d.ts +0 -1
- package/dist/structure/polyhedra.js +7 -21
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/structure/supercell.d.ts +1 -2
- package/dist/structure/supercell.js +7 -13
- package/dist/symmetry/SymmetryElements.svelte +11 -15
- package/dist/symmetry/SymmetryStats.svelte +55 -71
- package/dist/symmetry/WyckoffTable.svelte +10 -28
- package/dist/symmetry/cell-transform.js +6 -14
- package/dist/symmetry/index.d.ts +1 -0
- package/dist/symmetry/index.js +13 -10
- package/dist/symmetry/spacegroups.js +3 -2
- package/dist/table/HeatmapTable.svelte +1142 -447
- package/dist/table/HeatmapTable.svelte.d.ts +12 -2
- package/dist/table/ToggleMenu.svelte +168 -53
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/table/index.d.ts +21 -6
- package/dist/table/index.js +34 -21
- package/dist/theme/ThemeControl.svelte +16 -8
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +5 -5
- package/dist/theme/index.js +5 -12
- package/dist/theme/themes.mjs +44 -66
- package/dist/tooltip/KCoords.svelte +4 -1
- package/dist/tooltip/TooltipContent.svelte +3 -6
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +613 -386
- package/dist/trajectory/Trajectory.svelte.d.ts +1 -1
- package/dist/trajectory/TrajectoryError.svelte +12 -74
- package/dist/trajectory/TrajectoryExportPane.svelte +14 -32
- package/dist/trajectory/TrajectoryInfoPane.svelte +127 -164
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +61 -49
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +17 -13
- package/dist/trajectory/frame-reader.d.ts +2 -0
- package/dist/trajectory/frame-reader.js +35 -32
- package/dist/trajectory/helpers.d.ts +2 -0
- package/dist/trajectory/helpers.js +34 -56
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +5 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/h5-utils.d.ts +13 -0
- package/dist/trajectory/parse/h5-utils.js +109 -0
- package/dist/trajectory/parse/hdf5.d.ts +1 -1
- package/dist/trajectory/parse/hdf5.js +116 -106
- package/dist/trajectory/parse/index.d.ts +6 -6
- package/dist/trajectory/parse/index.js +25 -33
- package/dist/trajectory/parse/lammps.js +8 -12
- package/dist/trajectory/parse/vasp.js +10 -7
- package/dist/trajectory/parse/vaspout-electronic.d.ts +13 -0
- package/dist/trajectory/parse/vaspout-electronic.js +207 -0
- package/dist/trajectory/parse/vaspout-h5.d.ts +4 -0
- package/dist/trajectory/parse/vaspout-h5.js +281 -0
- package/dist/trajectory/parse/xyz.js +59 -9
- package/dist/trajectory/plotting.d.ts +8 -9
- package/dist/trajectory/plotting.js +113 -64
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +7 -0
- package/dist/xrd/XrdPlot.svelte +79 -118
- package/dist/xrd/broadening.js +2 -1
- package/dist/xrd/calc-xrd.js +24 -62
- package/dist/xrd/parse.d.ts +0 -1
- package/dist/xrd/parse.js +69 -95
- package/package.json +94 -34
- package/readme.md +6 -5
- package/dist/api/mp.d.ts +0 -6
- package/dist/api/mp.js +0 -22
- package/dist/plot/sunburst/SunburstControls.svelte +0 -200
- 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,1043 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Split-pane browser for navigating and visualizing structured JSON data.
|
|
3
|
+
// Left sidebar shows a JsonTree for navigating the file's JSON structure.
|
|
4
|
+
// Right canvas renders one or more visualization panels in a split layout.
|
|
5
|
+
// Users can click tree nodes to render in the main panel, or drag nodes
|
|
6
|
+
// to specific edges to create horizontal/vertical splits.
|
|
7
|
+
import BrillouinZone from '../brillouin/BrillouinZone.svelte'
|
|
8
|
+
import ConvexHull from '../convex-hull/ConvexHull.svelte'
|
|
9
|
+
import type { PhaseData } from '../convex-hull/types'
|
|
10
|
+
import FermiSurface from '../fermi-surface/FermiSurface.svelte'
|
|
11
|
+
import { is_fermi_surface_data } from '../fermi-surface/types'
|
|
12
|
+
import type { VolumetricData } from '../isosurface/types'
|
|
13
|
+
import JsonTree from '../layout/json-tree/JsonTree.svelte'
|
|
14
|
+
import IsobaricBinaryPhaseDiagram from '../phase-diagram/IsobaricBinaryPhaseDiagram.svelte'
|
|
15
|
+
import type { PhaseDiagramData } from '../phase-diagram/types'
|
|
16
|
+
import { merge, type DefaultSettings } from '../settings'
|
|
17
|
+
import Bands from '../spectral/Bands.svelte'
|
|
18
|
+
import BandsAndDos from '../spectral/BandsAndDos.svelte'
|
|
19
|
+
import Dos from '../spectral/Dos.svelte'
|
|
20
|
+
import type { BaseBandStructure, DosInput } from '../spectral/types'
|
|
21
|
+
import type { AnyStructure, LatticeType } from '../structure'
|
|
22
|
+
import {
|
|
23
|
+
is_optimade_raw,
|
|
24
|
+
normalize_fractional_coords,
|
|
25
|
+
parse_optimade_from_raw,
|
|
26
|
+
} from '../structure/parse'
|
|
27
|
+
import { build_structure_props_from_settings as structure_props } from '../structure/prop-groups'
|
|
28
|
+
import Structure from '../structure/Structure.svelte'
|
|
29
|
+
import type { XrdPattern } from '../xrd'
|
|
30
|
+
import XrdPlot from '../xrd/XrdPlot.svelte'
|
|
31
|
+
import { mount, unmount } from 'svelte'
|
|
32
|
+
import {
|
|
33
|
+
detect_view_type,
|
|
34
|
+
resolve_path,
|
|
35
|
+
scan_renderable_paths,
|
|
36
|
+
TYPE_COLORS,
|
|
37
|
+
TYPE_LABELS,
|
|
38
|
+
} from './detect'
|
|
39
|
+
import type { RenderableType } from './detect'
|
|
40
|
+
import PlotPanel from './PlotPanel.svelte'
|
|
41
|
+
|
|
42
|
+
let {
|
|
43
|
+
value,
|
|
44
|
+
defaults,
|
|
45
|
+
filename,
|
|
46
|
+
}: {
|
|
47
|
+
value: unknown
|
|
48
|
+
defaults?: DefaultSettings
|
|
49
|
+
filename?: string
|
|
50
|
+
} = $props()
|
|
51
|
+
|
|
52
|
+
// === Panel state ===
|
|
53
|
+
// Each panel holds a mounted component. Panels are arranged in a flat list
|
|
54
|
+
// with split info (direction) between consecutive panels.
|
|
55
|
+
// Panel sizing is stored as a parallel array of flex weights.
|
|
56
|
+
interface PanelInfo {
|
|
57
|
+
id: string
|
|
58
|
+
data_path: string
|
|
59
|
+
detected_type: RenderableType
|
|
60
|
+
val: unknown
|
|
61
|
+
component: ReturnType<typeof mount> | null
|
|
62
|
+
element: HTMLElement | null
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type SplitDirection = `horizontal` | `vertical`
|
|
66
|
+
|
|
67
|
+
let panels = $state<PanelInfo[]>([])
|
|
68
|
+
let panel_sizes = $state<number[]>([]) // flex weight per panel (parallel to panels[])
|
|
69
|
+
let split_directions = $state<SplitDirection[]>([]) // direction between panels[i] and panels[i+1]
|
|
70
|
+
|
|
71
|
+
// Debounce timer for rapid clicks (cleaned up when component is destroyed)
|
|
72
|
+
let select_timer: ReturnType<typeof setTimeout> | undefined
|
|
73
|
+
let destroyed = false
|
|
74
|
+
$effect(() => () => {
|
|
75
|
+
destroyed = true
|
|
76
|
+
if (select_timer) clearTimeout(select_timer)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Unmount all imperatively mounted panels when this component is destroyed
|
|
80
|
+
// (prevents leaked WebGL contexts, Three.js renderers, etc.)
|
|
81
|
+
$effect(() => () => {
|
|
82
|
+
for (let idx = 0; idx < panels.length; idx++) {
|
|
83
|
+
unmount_panel(idx)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Scan for renderable paths asynchronously to avoid blocking the UI on large JSON files.
|
|
88
|
+
// Uses requestIdleCallback to yield to the main thread during tree rendering.
|
|
89
|
+
let renderable_paths = $state(new Map<string, { type: RenderableType; label: string }>())
|
|
90
|
+
let auto_rendered = false
|
|
91
|
+
$effect(() => {
|
|
92
|
+
const current_value = value
|
|
93
|
+
const idle_handle = requestIdleCallback(() => {
|
|
94
|
+
renderable_paths = scan_renderable_paths(current_value)
|
|
95
|
+
// Auto-render if the root value itself is a single renderable type
|
|
96
|
+
// (avoids forcing the user to click for single-type JSON files)
|
|
97
|
+
if (!auto_rendered && renderable_paths.has(``)) {
|
|
98
|
+
auto_rendered = true
|
|
99
|
+
const info = renderable_paths.get(``)
|
|
100
|
+
if (info) replace_or_add_panel(``, info.type, current_value)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
return () => cancelIdleCallback(idle_handle)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// === Draggable sidebar divider ===
|
|
107
|
+
let sidebar_width = $state(320)
|
|
108
|
+
let is_sidebar_dragging = $state(false)
|
|
109
|
+
|
|
110
|
+
// Generic drag cleanup helper -- in a webview iframe the cursor can leave the
|
|
111
|
+
// document entirely, so we listen for mouseup, blur, and pointerleave to ensure
|
|
112
|
+
// the drag always terminates.
|
|
113
|
+
function start_drag(on_move: (event: MouseEvent) => void, on_done: () => void): void {
|
|
114
|
+
let cleaned_up = false
|
|
115
|
+
function cleanup(): void {
|
|
116
|
+
if (cleaned_up) return
|
|
117
|
+
cleaned_up = true
|
|
118
|
+
on_done()
|
|
119
|
+
globalThis.removeEventListener(`mousemove`, on_move)
|
|
120
|
+
globalThis.removeEventListener(`mouseup`, cleanup)
|
|
121
|
+
globalThis.removeEventListener(`blur`, cleanup)
|
|
122
|
+
document.documentElement.removeEventListener(`mouseleave`, cleanup)
|
|
123
|
+
}
|
|
124
|
+
globalThis.addEventListener(`mousemove`, on_move)
|
|
125
|
+
globalThis.addEventListener(`mouseup`, cleanup)
|
|
126
|
+
globalThis.addEventListener(`blur`, cleanup)
|
|
127
|
+
document.documentElement.addEventListener(`mouseleave`, cleanup)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function on_sidebar_divider_mousedown(event: MouseEvent): void {
|
|
131
|
+
event.preventDefault()
|
|
132
|
+
is_sidebar_dragging = true
|
|
133
|
+
const start_x = event.clientX
|
|
134
|
+
const start_width = sidebar_width
|
|
135
|
+
start_drag(
|
|
136
|
+
(move_event) => {
|
|
137
|
+
const delta = move_event.clientX - start_x
|
|
138
|
+
sidebar_width = Math.max(150, Math.min(start_width + delta, window.innerWidth * 0.6))
|
|
139
|
+
},
|
|
140
|
+
() => {
|
|
141
|
+
is_sidebar_dragging = false
|
|
142
|
+
},
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// === Drag-and-drop from tree ===
|
|
147
|
+
let drop_zone = $state<`top` | `bottom` | `left` | `right` | `center` | null>(null)
|
|
148
|
+
let drop_target_panel_idx = $state<number>(-1)
|
|
149
|
+
let canvas_element: HTMLElement | undefined = $state()
|
|
150
|
+
|
|
151
|
+
let sidebar_element: HTMLElement | undefined = $state()
|
|
152
|
+
|
|
153
|
+
// Strip internal suffix used to register multiple renderable types at the same path
|
|
154
|
+
function strip_type_suffix(path: string): string {
|
|
155
|
+
const idx = path.indexOf(`\u0000`)
|
|
156
|
+
return idx !== -1 ? path.slice(0, idx) : path
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Convert a data path (relative to JSON root) to the tree path used by JsonTree
|
|
160
|
+
function data_to_tree_path(data_path: string): string {
|
|
161
|
+
const clean = strip_type_suffix(data_path)
|
|
162
|
+
return filename ? (clean ? `${filename}.${clean}` : filename) : clean
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Build a map of tree paths that are renderable (for fast draggable lookup).
|
|
166
|
+
// Skip synthetic suffix paths (\x00...) since those have their own badge drag handlers.
|
|
167
|
+
let renderable_tree_paths = $derived(
|
|
168
|
+
new Map(
|
|
169
|
+
[...renderable_paths]
|
|
170
|
+
.filter(([data_path]) => !data_path.includes(`\u0000`))
|
|
171
|
+
.map(([data_path, info]) => [data_to_tree_path(data_path), { data_path, ...info }]),
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
// Mark renderable tree nodes as draggable via attribute (no per-node listeners)
|
|
176
|
+
function mark_draggable_nodes(): void {
|
|
177
|
+
if (!sidebar_element) return
|
|
178
|
+
for (const node of sidebar_element.querySelectorAll(`[data-path]`)) {
|
|
179
|
+
const el = node as HTMLElement
|
|
180
|
+
const tree_path = el.getAttribute(`data-path`) ?? ``
|
|
181
|
+
el.draggable = renderable_tree_paths.has(tree_path)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Single delegated dragstart handler on sidebar (no per-node listeners needed)
|
|
186
|
+
$effect(() => {
|
|
187
|
+
if (!sidebar_element) return
|
|
188
|
+
function on_dragstart(event: DragEvent): void {
|
|
189
|
+
if (!event.dataTransfer) {
|
|
190
|
+
event.preventDefault()
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
const origin = event.target
|
|
194
|
+
if (!(origin instanceof HTMLElement)) return
|
|
195
|
+
// If dragging a badge directly, use its own data attributes
|
|
196
|
+
const badge = origin.closest(`.renderable-badge`) as HTMLElement | null
|
|
197
|
+
if (badge) {
|
|
198
|
+
const data_path = badge.dataset.renderable_path ?? ``
|
|
199
|
+
const detected_type = badge.dataset.renderable_type ?? ``
|
|
200
|
+
event.dataTransfer.setData(`text/plain`, JSON.stringify({ data_path, detected_type }))
|
|
201
|
+
event.dataTransfer.effectAllowed = `copy`
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
// Otherwise dragging a tree node -- look up from tree path
|
|
205
|
+
const target = origin.closest(`[data-path]`) as HTMLElement | null
|
|
206
|
+
if (!target) return
|
|
207
|
+
const tree_path = target.getAttribute(`data-path`) ?? ``
|
|
208
|
+
const info = renderable_tree_paths.get(tree_path)
|
|
209
|
+
if (!info) {
|
|
210
|
+
event.preventDefault()
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
event.dataTransfer.setData(
|
|
214
|
+
`text/plain`,
|
|
215
|
+
JSON.stringify({ data_path: info.data_path, detected_type: info.type }),
|
|
216
|
+
)
|
|
217
|
+
event.dataTransfer.effectAllowed = `copy`
|
|
218
|
+
}
|
|
219
|
+
sidebar_element.addEventListener(`dragstart`, on_dragstart)
|
|
220
|
+
return () => sidebar_element?.removeEventListener(`dragstart`, on_dragstart)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// === Badge injection ===
|
|
224
|
+
function apply_badges(): void {
|
|
225
|
+
if (!sidebar_element || renderable_paths.size === 0) return
|
|
226
|
+
for (const existing of sidebar_element.querySelectorAll(`.renderable-badge`)) {
|
|
227
|
+
existing.remove()
|
|
228
|
+
}
|
|
229
|
+
for (const [data_path, info] of renderable_paths) {
|
|
230
|
+
const node = sidebar_element.querySelector(
|
|
231
|
+
`[data-path="${CSS.escape(data_to_tree_path(data_path))}"]`,
|
|
232
|
+
)
|
|
233
|
+
if (!node) continue
|
|
234
|
+
const colon_el = node.querySelector(`.colon`)
|
|
235
|
+
const insert_after = colon_el ?? node.querySelector(`.node-key`) ?? node
|
|
236
|
+
const badge = document.createElement(`span`)
|
|
237
|
+
badge.className = `renderable-badge`
|
|
238
|
+
badge.textContent = info.label
|
|
239
|
+
badge.title = `Drag to canvas or click to render`
|
|
240
|
+
badge.dataset.renderable_path = data_path
|
|
241
|
+
badge.dataset.renderable_type = info.type
|
|
242
|
+
badge.style.background = TYPE_COLORS[info.type]
|
|
243
|
+
badge.draggable = true
|
|
244
|
+
insert_after.after(badge)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Delegated click handler for badges (avoids per-badge listeners that leak on re-render)
|
|
249
|
+
// Uses capture phase to intercept before tree node fold/select handlers
|
|
250
|
+
$effect(() => {
|
|
251
|
+
if (!sidebar_element) return
|
|
252
|
+
function on_badge_click(event: MouseEvent): void {
|
|
253
|
+
const origin = event.target
|
|
254
|
+
if (!(origin instanceof HTMLElement)) return
|
|
255
|
+
const badge = origin.closest(`.renderable-badge`) as HTMLElement | null
|
|
256
|
+
if (!badge) return
|
|
257
|
+
event.stopPropagation()
|
|
258
|
+
event.preventDefault()
|
|
259
|
+
const raw_path = badge.dataset.renderable_path ?? ``
|
|
260
|
+
const data_path = strip_type_suffix(raw_path)
|
|
261
|
+
const detected_type = badge.dataset.renderable_type
|
|
262
|
+
if (!detected_type || !(detected_type in TYPE_LABELS)) return
|
|
263
|
+
const val = resolve_path(value, data_path)
|
|
264
|
+
if (val !== undefined)
|
|
265
|
+
replace_or_add_panel(data_path, detected_type as RenderableType, val)
|
|
266
|
+
}
|
|
267
|
+
sidebar_element.addEventListener(`click`, on_badge_click, true)
|
|
268
|
+
return () => sidebar_element?.removeEventListener(`click`, on_badge_click, true)
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
// Escape key closes all panels, returning to the overview
|
|
272
|
+
$effect(() => {
|
|
273
|
+
if (panels.length === 0) return
|
|
274
|
+
function on_keydown(event: KeyboardEvent): void {
|
|
275
|
+
if (event.key !== `Escape`) return
|
|
276
|
+
const target = event.target
|
|
277
|
+
if (
|
|
278
|
+
target instanceof HTMLElement &&
|
|
279
|
+
[`INPUT`, `SELECT`, `TEXTAREA`].includes(target.tagName)
|
|
280
|
+
)
|
|
281
|
+
return
|
|
282
|
+
close_all_panels()
|
|
283
|
+
}
|
|
284
|
+
globalThis.addEventListener(`keydown`, on_keydown)
|
|
285
|
+
return () => globalThis.removeEventListener(`keydown`, on_keydown)
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
// Re-apply badges + draggable attributes when tree DOM changes.
|
|
289
|
+
// Guard with a flag so our own badge DOM mutations don't re-trigger the observer,
|
|
290
|
+
// and coalesce rapid mutations into a single rAF.
|
|
291
|
+
let applying_badges = false
|
|
292
|
+
$effect(() => {
|
|
293
|
+
if (!sidebar_element) return
|
|
294
|
+
let pending_raf: number | null = null
|
|
295
|
+
function schedule_refresh(): void {
|
|
296
|
+
if (pending_raf) return
|
|
297
|
+
pending_raf = requestAnimationFrame(() => {
|
|
298
|
+
pending_raf = null
|
|
299
|
+
if (applying_badges) return
|
|
300
|
+
applying_badges = true
|
|
301
|
+
try {
|
|
302
|
+
apply_badges()
|
|
303
|
+
mark_draggable_nodes()
|
|
304
|
+
} finally {
|
|
305
|
+
applying_badges = false
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
schedule_refresh()
|
|
310
|
+
const observer = new MutationObserver(schedule_refresh)
|
|
311
|
+
observer.observe(sidebar_element, { childList: true, subtree: true })
|
|
312
|
+
return () => {
|
|
313
|
+
observer.disconnect()
|
|
314
|
+
if (pending_raf) cancelAnimationFrame(pending_raf)
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// === Panel management ===
|
|
319
|
+
|
|
320
|
+
const make_panel_id = (): string => `panel_${crypto.randomUUID()}`
|
|
321
|
+
|
|
322
|
+
// Click replaces the single/first panel; drag adds a split
|
|
323
|
+
function replace_or_add_panel(
|
|
324
|
+
data_path: string,
|
|
325
|
+
detected_type: RenderableType,
|
|
326
|
+
val: unknown,
|
|
327
|
+
): void {
|
|
328
|
+
if (panels.length === 0) {
|
|
329
|
+
panels = [
|
|
330
|
+
{ id: make_panel_id(), data_path, detected_type, val, component: null, element: null },
|
|
331
|
+
]
|
|
332
|
+
panel_sizes = [1]
|
|
333
|
+
} else {
|
|
334
|
+
unmount_panel(0)
|
|
335
|
+
panels[0] = {
|
|
336
|
+
id: make_panel_id(),
|
|
337
|
+
data_path,
|
|
338
|
+
detected_type,
|
|
339
|
+
val,
|
|
340
|
+
component: null,
|
|
341
|
+
element: null,
|
|
342
|
+
}
|
|
343
|
+
panels = [...panels] // trigger reactivity
|
|
344
|
+
}
|
|
345
|
+
requestAnimationFrame(() => mount_all_panels())
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function add_panel_with_split(
|
|
349
|
+
data_path: string,
|
|
350
|
+
detected_type: RenderableType,
|
|
351
|
+
val: unknown,
|
|
352
|
+
target_idx: number,
|
|
353
|
+
zone: `top` | `bottom` | `left` | `right`,
|
|
354
|
+
): void {
|
|
355
|
+
const new_panel: PanelInfo = {
|
|
356
|
+
id: make_panel_id(),
|
|
357
|
+
data_path,
|
|
358
|
+
detected_type,
|
|
359
|
+
val,
|
|
360
|
+
component: null,
|
|
361
|
+
element: null,
|
|
362
|
+
}
|
|
363
|
+
const direction: SplitDirection =
|
|
364
|
+
zone === `top` || zone === `bottom` ? `vertical` : `horizontal`
|
|
365
|
+
const insert_before = zone === `top` || zone === `left`
|
|
366
|
+
|
|
367
|
+
if (panels.length === 0) {
|
|
368
|
+
panels = [new_panel]
|
|
369
|
+
panel_sizes = [1]
|
|
370
|
+
} else {
|
|
371
|
+
const new_panels = [...panels]
|
|
372
|
+
const new_sizes = [...panel_sizes]
|
|
373
|
+
const new_dirs = [...split_directions]
|
|
374
|
+
const insert_idx = insert_before ? target_idx : target_idx + 1
|
|
375
|
+
// Split the target panel's size in half: new panel gets half, target keeps half
|
|
376
|
+
const target_size = new_sizes[target_idx] ?? 1
|
|
377
|
+
new_sizes[target_idx] = target_size / 2
|
|
378
|
+
new_panels.splice(insert_idx, 0, new_panel)
|
|
379
|
+
new_sizes.splice(insert_idx, 0, target_size / 2)
|
|
380
|
+
// Add direction between the two panels
|
|
381
|
+
new_dirs.splice(target_idx, 0, direction)
|
|
382
|
+
panels = new_panels
|
|
383
|
+
panel_sizes = new_sizes
|
|
384
|
+
split_directions = new_dirs
|
|
385
|
+
}
|
|
386
|
+
requestAnimationFrame(() => mount_all_panels())
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function close_panel(idx: number): void {
|
|
390
|
+
unmount_panel(idx)
|
|
391
|
+
const new_panels = [...panels]
|
|
392
|
+
const new_sizes = [...panel_sizes]
|
|
393
|
+
const new_dirs = [...split_directions]
|
|
394
|
+
// Give the closed panel's size to its neighbor
|
|
395
|
+
const closed_size = new_sizes[idx] ?? 0
|
|
396
|
+
const neighbor_idx = idx > 0 ? idx - 1 : idx + 1
|
|
397
|
+
if (neighbor_idx < new_sizes.length) new_sizes[neighbor_idx] += closed_size
|
|
398
|
+
new_panels.splice(idx, 1)
|
|
399
|
+
new_sizes.splice(idx, 1)
|
|
400
|
+
// Remove the adjacent split direction
|
|
401
|
+
if (new_dirs.length > 0) {
|
|
402
|
+
const dir_idx = Math.min(idx, new_dirs.length - 1)
|
|
403
|
+
new_dirs.splice(dir_idx, 1)
|
|
404
|
+
}
|
|
405
|
+
panels = new_panels
|
|
406
|
+
panel_sizes = new_sizes
|
|
407
|
+
split_directions = new_dirs
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function close_all_panels(): void {
|
|
411
|
+
for (let idx = panels.length - 1; idx >= 0; idx--) unmount_panel(idx)
|
|
412
|
+
panels = []
|
|
413
|
+
panel_sizes = []
|
|
414
|
+
split_directions = []
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function unmount_panel(idx: number): void {
|
|
418
|
+
const panel = panels[idx]
|
|
419
|
+
if (panel?.component) {
|
|
420
|
+
try {
|
|
421
|
+
unmount(panel.component)
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.error(`JsonBrowser: unmount failed:`, error)
|
|
424
|
+
}
|
|
425
|
+
panel.component = null
|
|
426
|
+
}
|
|
427
|
+
if (panel?.element) panel.element.innerHTML = ``
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// === Component mounting ===
|
|
431
|
+
|
|
432
|
+
function prepare_structure(val: unknown): unknown {
|
|
433
|
+
if (is_optimade_raw(val)) {
|
|
434
|
+
const result = parse_optimade_from_raw(val)
|
|
435
|
+
if (result) return result
|
|
436
|
+
}
|
|
437
|
+
const record = val as Record<string, unknown>
|
|
438
|
+
if (Array.isArray(record.sites) && record.lattice) {
|
|
439
|
+
return normalize_fractional_coords(
|
|
440
|
+
val as Parameters<typeof normalize_fractional_coords>[0],
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
return val
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Merge defaults once (reused across all panel mounts)
|
|
447
|
+
const merged_defaults = $derived(merge(defaults))
|
|
448
|
+
const common_props = { fullscreen_toggle: false, style: `height:100%` }
|
|
449
|
+
|
|
450
|
+
function mount_into(
|
|
451
|
+
target: HTMLElement,
|
|
452
|
+
val: unknown,
|
|
453
|
+
detected_type: RenderableType,
|
|
454
|
+
panel_id?: string,
|
|
455
|
+
): ReturnType<typeof mount> | null {
|
|
456
|
+
const onclose =
|
|
457
|
+
panel_id !== undefined
|
|
458
|
+
? () => {
|
|
459
|
+
const idx = panels.findIndex((panel) => panel.id === panel_id)
|
|
460
|
+
if (idx === -1) return
|
|
461
|
+
if (panels.length > 1) close_panel(idx)
|
|
462
|
+
else close_all_panels()
|
|
463
|
+
}
|
|
464
|
+
: undefined
|
|
465
|
+
target.innerHTML = ``
|
|
466
|
+
// Force layout so Three.js gets real dimensions
|
|
467
|
+
void target.offsetHeight
|
|
468
|
+
|
|
469
|
+
const structure_mount_props = {
|
|
470
|
+
allow_file_drop: false,
|
|
471
|
+
enable_tips: false,
|
|
472
|
+
...structure_props(merged_defaults),
|
|
473
|
+
...common_props,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
if (detected_type === `structure`) {
|
|
478
|
+
return mount(Structure, {
|
|
479
|
+
target,
|
|
480
|
+
props: {
|
|
481
|
+
structure: prepare_structure(val) as AnyStructure,
|
|
482
|
+
...structure_mount_props,
|
|
483
|
+
},
|
|
484
|
+
})
|
|
485
|
+
} else if (detected_type === `fermi_surface` || detected_type === `band_grid`) {
|
|
486
|
+
const props: Record<string, unknown> = { allow_file_drop: false, ...common_props }
|
|
487
|
+
if (is_fermi_surface_data(val as Parameters<typeof is_fermi_surface_data>[0])) {
|
|
488
|
+
props.fermi_data = val
|
|
489
|
+
} else props.band_data = val
|
|
490
|
+
return mount(FermiSurface, { target, props })
|
|
491
|
+
} else if (detected_type === `convex_hull`) {
|
|
492
|
+
return mount(ConvexHull, {
|
|
493
|
+
target,
|
|
494
|
+
props: { entries: val as PhaseData[], ...common_props },
|
|
495
|
+
})
|
|
496
|
+
} else if (detected_type === `phase_diagram`) {
|
|
497
|
+
return mount(IsobaricBinaryPhaseDiagram, {
|
|
498
|
+
target,
|
|
499
|
+
props: { data: val as PhaseDiagramData, ...common_props },
|
|
500
|
+
})
|
|
501
|
+
} else if (detected_type === `volumetric`) {
|
|
502
|
+
const vol_data = val as { lattice: LatticeType }
|
|
503
|
+
return mount(Structure, {
|
|
504
|
+
target,
|
|
505
|
+
props: {
|
|
506
|
+
structure: { sites: [], lattice: vol_data.lattice } as AnyStructure,
|
|
507
|
+
volumetric_data: [val as VolumetricData],
|
|
508
|
+
...structure_mount_props,
|
|
509
|
+
},
|
|
510
|
+
})
|
|
511
|
+
} else if (detected_type === `bands_and_dos`) {
|
|
512
|
+
const data = val as Record<string, unknown>
|
|
513
|
+
// Support both { band_structure, dos } wrapper and combined-fields format
|
|
514
|
+
const band_data = (data.band_structure ?? val) as BaseBandStructure
|
|
515
|
+
const dos_data = (data.dos ?? val) as DosInput
|
|
516
|
+
return mount(BandsAndDos, {
|
|
517
|
+
target,
|
|
518
|
+
props: { band_structs: band_data, doses: dos_data, ...common_props },
|
|
519
|
+
})
|
|
520
|
+
} else if (detected_type === `band_structure`) {
|
|
521
|
+
return mount(Bands, {
|
|
522
|
+
target,
|
|
523
|
+
props: {
|
|
524
|
+
band_structs: val as BaseBandStructure,
|
|
525
|
+
...common_props,
|
|
526
|
+
padding: { b: 60 },
|
|
527
|
+
},
|
|
528
|
+
})
|
|
529
|
+
} else if (detected_type === `dos`) {
|
|
530
|
+
return mount(Dos, {
|
|
531
|
+
target,
|
|
532
|
+
props: { doses: val as DosInput, ...common_props, padding: { b: 60 } },
|
|
533
|
+
})
|
|
534
|
+
} else if (detected_type === `brillouin_zone`) {
|
|
535
|
+
const bz_val = val as Record<string, unknown>
|
|
536
|
+
const bz_props: Record<string, unknown> = { allow_file_drop: false, ...common_props }
|
|
537
|
+
if (bz_val.structure) bz_props.structure = prepare_structure(bz_val.structure)
|
|
538
|
+
// Pass pre-computed BZ data (vertices, faces, edges) if present
|
|
539
|
+
if (bz_val.vertices && bz_val.faces) bz_props.bz_data = bz_val
|
|
540
|
+
return mount(BrillouinZone, { target, props: bz_props })
|
|
541
|
+
} else if (detected_type === `xrd`) {
|
|
542
|
+
return mount(XrdPlot, {
|
|
543
|
+
target,
|
|
544
|
+
props: { patterns: val as XrdPattern, allow_file_drop: false, ...common_props },
|
|
545
|
+
})
|
|
546
|
+
} else if (detected_type === `table`) {
|
|
547
|
+
return mount(PlotPanel, {
|
|
548
|
+
target,
|
|
549
|
+
props: { data: val, initial_type: `table`, onclose, ...common_props },
|
|
550
|
+
})
|
|
551
|
+
} else if (detected_type === `plot`) {
|
|
552
|
+
return mount(PlotPanel, { target, props: { data: val, onclose, ...common_props } })
|
|
553
|
+
}
|
|
554
|
+
} catch (error) {
|
|
555
|
+
console.error(`JsonBrowser: mount failed for ${detected_type}:`, error)
|
|
556
|
+
}
|
|
557
|
+
return null
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function mount_all_panels(): void {
|
|
561
|
+
for (const panel of panels) {
|
|
562
|
+
if (panel.component) continue // already mounted
|
|
563
|
+
const el = document.querySelector<HTMLElement>(`#${panel.id}`)
|
|
564
|
+
if (!el) continue
|
|
565
|
+
panel.element = el
|
|
566
|
+
panel.component = mount_into(el, panel.val, panel.detected_type, panel.id)
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// === Drop zone detection ===
|
|
571
|
+
|
|
572
|
+
function get_drop_zone(
|
|
573
|
+
event: DragEvent,
|
|
574
|
+
rect: DOMRect,
|
|
575
|
+
): `top` | `bottom` | `left` | `right` | `center` {
|
|
576
|
+
const rel_x = (event.clientX - rect.left) / rect.width
|
|
577
|
+
const rel_y = (event.clientY - rect.top) / rect.height
|
|
578
|
+
const edge_threshold = 0.25
|
|
579
|
+
if (rel_y < edge_threshold) return `top`
|
|
580
|
+
if (rel_y > 1 - edge_threshold) return `bottom`
|
|
581
|
+
if (rel_x < edge_threshold) return `left`
|
|
582
|
+
if (rel_x > 1 - edge_threshold) return `right`
|
|
583
|
+
return `center`
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function on_canvas_dragover(event: DragEvent): void {
|
|
587
|
+
event.preventDefault()
|
|
588
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = `copy`
|
|
589
|
+
if (!canvas_element) return
|
|
590
|
+
// Default to center/first panel; override if cursor is inside a specific panel
|
|
591
|
+
drop_zone = `center`
|
|
592
|
+
drop_target_panel_idx = 0
|
|
593
|
+
const panel_els = canvas_element.querySelectorAll(`.viz-panel`)
|
|
594
|
+
for (let idx = 0; idx < panel_els.length; idx++) {
|
|
595
|
+
const rect = panel_els[idx].getBoundingClientRect()
|
|
596
|
+
if (
|
|
597
|
+
event.clientX >= rect.left &&
|
|
598
|
+
event.clientX <= rect.right &&
|
|
599
|
+
event.clientY >= rect.top &&
|
|
600
|
+
event.clientY <= rect.bottom
|
|
601
|
+
) {
|
|
602
|
+
drop_zone = get_drop_zone(event, rect)
|
|
603
|
+
drop_target_panel_idx = idx
|
|
604
|
+
break
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// Prevent mixed-axis splits until nested layouts are supported
|
|
608
|
+
if (split_directions.length > 0) {
|
|
609
|
+
const layout = split_directions[0]
|
|
610
|
+
if (layout === `vertical` && (drop_zone === `left` || drop_zone === `right`)) {
|
|
611
|
+
drop_zone = `center`
|
|
612
|
+
} else if (layout === `horizontal` && (drop_zone === `top` || drop_zone === `bottom`)) {
|
|
613
|
+
drop_zone = `center`
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function on_canvas_dragleave(event: DragEvent): void {
|
|
619
|
+
// Only clear if leaving the canvas entirely
|
|
620
|
+
if (canvas_element && !canvas_element.contains(event.relatedTarget as Node)) {
|
|
621
|
+
drop_zone = null
|
|
622
|
+
drop_target_panel_idx = -1
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function on_canvas_drop(event: DragEvent): void {
|
|
627
|
+
event.preventDefault()
|
|
628
|
+
const raw = event.dataTransfer?.getData(`text/plain`)
|
|
629
|
+
// Capture the already-computed drop state before clearing it
|
|
630
|
+
const zone = drop_zone
|
|
631
|
+
const target_idx = Math.max(0, drop_target_panel_idx)
|
|
632
|
+
drop_zone = null
|
|
633
|
+
drop_target_panel_idx = -1
|
|
634
|
+
if (!raw) return
|
|
635
|
+
try {
|
|
636
|
+
const parsed = JSON.parse(raw) as { data_path: string; detected_type: RenderableType }
|
|
637
|
+
const data_path = strip_type_suffix(parsed.data_path)
|
|
638
|
+
const { detected_type } = parsed
|
|
639
|
+
if (!detected_type || !(detected_type in TYPE_LABELS)) return
|
|
640
|
+
const val = resolve_path(value, data_path)
|
|
641
|
+
if (val === undefined) return
|
|
642
|
+
if (panels.length === 0 || zone === `center` || !zone) {
|
|
643
|
+
replace_or_add_panel(data_path, detected_type, val)
|
|
644
|
+
} else {
|
|
645
|
+
add_panel_with_split(data_path, detected_type, val, target_idx, zone)
|
|
646
|
+
}
|
|
647
|
+
} catch (error) {
|
|
648
|
+
console.error(`JsonBrowser: drop failed:`, error)
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// === Panel split divider dragging ===
|
|
653
|
+
let split_dragging_idx = $state(-1)
|
|
654
|
+
|
|
655
|
+
function on_split_divider_mousedown(event: MouseEvent, split_idx: number): void {
|
|
656
|
+
event.preventDefault()
|
|
657
|
+
split_dragging_idx = split_idx
|
|
658
|
+
const direction = split_directions[split_idx]
|
|
659
|
+
if (!direction) return
|
|
660
|
+
const panel_container = canvas_element?.querySelector(
|
|
661
|
+
`.panel-container`,
|
|
662
|
+
) as HTMLElement | null
|
|
663
|
+
if (!panel_container) return
|
|
664
|
+
const container_rect = panel_container.getBoundingClientRect()
|
|
665
|
+
|
|
666
|
+
const is_vertical = direction === `vertical`
|
|
667
|
+
const start_pos = is_vertical ? event.clientY : event.clientX
|
|
668
|
+
const container_size = is_vertical ? container_rect.height : container_rect.width
|
|
669
|
+
// Total flex of the two adjacent panels
|
|
670
|
+
const left_idx = split_idx
|
|
671
|
+
const right_idx = split_idx + 1
|
|
672
|
+
const total_flex = (panel_sizes[left_idx] ?? 1) + (panel_sizes[right_idx] ?? 1)
|
|
673
|
+
const start_left_size = panel_sizes[left_idx] ?? total_flex / 2
|
|
674
|
+
|
|
675
|
+
start_drag(
|
|
676
|
+
(move_event) => {
|
|
677
|
+
const current_pos = is_vertical ? move_event.clientY : move_event.clientX
|
|
678
|
+
const delta_fraction = (current_pos - start_pos) / container_size
|
|
679
|
+
const delta_flex = delta_fraction * total_flex
|
|
680
|
+
const new_left = Math.max(
|
|
681
|
+
total_flex * 0.1,
|
|
682
|
+
Math.min(total_flex * 0.9, start_left_size + delta_flex),
|
|
683
|
+
)
|
|
684
|
+
panel_sizes[left_idx] = new_left
|
|
685
|
+
panel_sizes[right_idx] = total_flex - new_left
|
|
686
|
+
},
|
|
687
|
+
() => {
|
|
688
|
+
split_dragging_idx = -1
|
|
689
|
+
},
|
|
690
|
+
)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// === Helpers ===
|
|
694
|
+
|
|
695
|
+
function strip_root_prefix(path: string): string {
|
|
696
|
+
if (!filename || !path.startsWith(filename)) return path
|
|
697
|
+
const stripped = path.slice(filename.length)
|
|
698
|
+
return stripped.startsWith(`.`) ? stripped.slice(1) : stripped
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function handle_select(path: string, val: unknown): void {
|
|
702
|
+
const data_path = strip_root_prefix(path)
|
|
703
|
+
if (select_timer) clearTimeout(select_timer)
|
|
704
|
+
select_timer = setTimeout(() => {
|
|
705
|
+
if (destroyed) return // component was destroyed while timer was pending
|
|
706
|
+
const detected = detect_view_type(val)
|
|
707
|
+
if (detected) replace_or_add_panel(data_path, detected, val)
|
|
708
|
+
}, 150)
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// The first split direction determines the flex layout direction
|
|
712
|
+
let layout_direction = $derived(
|
|
713
|
+
split_directions.length > 0 ? split_directions[0] : `vertical`,
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
const type_color = (key: string) => TYPE_COLORS[key as RenderableType] ?? `#888`
|
|
717
|
+
</script>
|
|
718
|
+
|
|
719
|
+
{#snippet type_list(header: string, extra_style?: string)}
|
|
720
|
+
<div class="type-list" style={extra_style ?? ``}>
|
|
721
|
+
<p class="type-list-header">{header}</p>
|
|
722
|
+
{#each Object.entries(TYPE_LABELS) as [type_key, label] (type_key)}
|
|
723
|
+
<span class="type-tag" style="border-color: {type_color(type_key)}44;">
|
|
724
|
+
<span class="chip-dot" style="background: {type_color(type_key)};"></span>
|
|
725
|
+
{label}
|
|
726
|
+
</span>
|
|
727
|
+
{/each}
|
|
728
|
+
</div>
|
|
729
|
+
{/snippet}
|
|
730
|
+
|
|
731
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
732
|
+
<div class="json-browser" class:dragging={is_sidebar_dragging || split_dragging_idx >= 0}>
|
|
733
|
+
<aside class="sidebar" bind:this={sidebar_element} style="width: {sidebar_width}px">
|
|
734
|
+
<JsonTree
|
|
735
|
+
{value}
|
|
736
|
+
root_label={filename}
|
|
737
|
+
default_fold_level={1}
|
|
738
|
+
onselect={handle_select}
|
|
739
|
+
show_header
|
|
740
|
+
/>
|
|
741
|
+
</aside>
|
|
742
|
+
|
|
743
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
744
|
+
<div class="divider sidebar-divider" onmousedown={on_sidebar_divider_mousedown}></div>
|
|
745
|
+
|
|
746
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
747
|
+
<div
|
|
748
|
+
class="canvas"
|
|
749
|
+
bind:this={canvas_element}
|
|
750
|
+
ondragover={on_canvas_dragover}
|
|
751
|
+
ondragleave={on_canvas_dragleave}
|
|
752
|
+
ondrop={on_canvas_drop}
|
|
753
|
+
>
|
|
754
|
+
{#if panels.length === 0}
|
|
755
|
+
<!-- Placeholder when no panels -->
|
|
756
|
+
<div class="placeholder">
|
|
757
|
+
{#if renderable_paths.size === 0}
|
|
758
|
+
<p class="placeholder-title">No renderable data found</p>
|
|
759
|
+
<p class="placeholder-sub">
|
|
760
|
+
This JSON file doesn't contain recognized visualization data.
|
|
761
|
+
</p>
|
|
762
|
+
{@render type_list(`Recognized types:`)}
|
|
763
|
+
{:else}
|
|
764
|
+
<p class="placeholder-title">Click or drag a data node to visualize it</p>
|
|
765
|
+
<p class="placeholder-sub">
|
|
766
|
+
Found {renderable_paths.size} renderable item{renderable_paths.size === 1
|
|
767
|
+
? ``
|
|
768
|
+
: `s`}. Click to render, or drag to an edge to create a split view.
|
|
769
|
+
</p>
|
|
770
|
+
<div
|
|
771
|
+
style="margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;"
|
|
772
|
+
>
|
|
773
|
+
{#each [...renderable_paths] as [data_path, info] (data_path)}
|
|
774
|
+
<button
|
|
775
|
+
type="button"
|
|
776
|
+
class="renderable-chip"
|
|
777
|
+
style="background: {type_color(info.type)}22; border: 1px solid {type_color(
|
|
778
|
+
info.type,
|
|
779
|
+
)}66;"
|
|
780
|
+
onclick={() => {
|
|
781
|
+
const clean_path = strip_type_suffix(data_path)
|
|
782
|
+
const val = resolve_path(value, clean_path)
|
|
783
|
+
if (val !== undefined) {
|
|
784
|
+
replace_or_add_panel(clean_path, info.type, val)
|
|
785
|
+
}
|
|
786
|
+
}}
|
|
787
|
+
>
|
|
788
|
+
<span class="chip-dot" style="background: {type_color(info.type)};"></span>
|
|
789
|
+
{info.label}: <code>{strip_type_suffix(data_path) || `root`}</code>
|
|
790
|
+
</button>
|
|
791
|
+
{/each}
|
|
792
|
+
</div>
|
|
793
|
+
{@render type_list(`All recognized types:`, `margin-top: 20px`)}
|
|
794
|
+
{/if}
|
|
795
|
+
</div>
|
|
796
|
+
{:else}
|
|
797
|
+
<!-- Panel layout -->
|
|
798
|
+
<div
|
|
799
|
+
class="panel-container"
|
|
800
|
+
class:vertical={layout_direction === `vertical`}
|
|
801
|
+
class:horizontal={layout_direction === `horizontal`}
|
|
802
|
+
>
|
|
803
|
+
{#each panels as panel, idx (panel.id)}
|
|
804
|
+
{#if idx > 0 && split_directions[idx - 1]}
|
|
805
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
806
|
+
<div
|
|
807
|
+
class="split-divider"
|
|
808
|
+
class:vertical={split_directions[idx - 1] === `vertical`}
|
|
809
|
+
class:horizontal={split_directions[idx - 1] === `horizontal`}
|
|
810
|
+
class:active={split_dragging_idx === idx - 1}
|
|
811
|
+
onmousedown={(event) => on_split_divider_mousedown(event, idx - 1)}
|
|
812
|
+
></div>
|
|
813
|
+
{/if}
|
|
814
|
+
<div class="viz-panel" style="flex: {panel_sizes[idx] ?? 1}">
|
|
815
|
+
<div class="panel-mount" id={panel.id}></div>
|
|
816
|
+
<!-- Panel label -->
|
|
817
|
+
<div class="panel-label" style="background: {TYPE_COLORS[panel.detected_type]}cc;">
|
|
818
|
+
{TYPE_LABELS[panel.detected_type]}: {strip_type_suffix(panel.data_path)}
|
|
819
|
+
</div>
|
|
820
|
+
</div>
|
|
821
|
+
{/each}
|
|
822
|
+
</div>
|
|
823
|
+
{/if}
|
|
824
|
+
|
|
825
|
+
<!-- Drop zone overlay -->
|
|
826
|
+
{#if drop_zone && drop_zone !== `center`}
|
|
827
|
+
<div class="drop-indicator {drop_zone}"></div>
|
|
828
|
+
{/if}
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
|
|
832
|
+
<style>
|
|
833
|
+
.json-browser {
|
|
834
|
+
display: flex;
|
|
835
|
+
width: 100%;
|
|
836
|
+
height: 100%;
|
|
837
|
+
overflow: hidden;
|
|
838
|
+
background: var(--vscode-editor-background, #1e1e1e);
|
|
839
|
+
color: var(--vscode-editor-foreground, #d4d4d4);
|
|
840
|
+
}
|
|
841
|
+
.json-browser.dragging {
|
|
842
|
+
user-select: none;
|
|
843
|
+
}
|
|
844
|
+
.sidebar {
|
|
845
|
+
flex-shrink: 0;
|
|
846
|
+
min-width: 150px;
|
|
847
|
+
max-width: 60%;
|
|
848
|
+
overflow: auto;
|
|
849
|
+
padding: 4px;
|
|
850
|
+
}
|
|
851
|
+
.divider {
|
|
852
|
+
width: 5px;
|
|
853
|
+
flex-shrink: 0;
|
|
854
|
+
background: var(--vscode-panel-border, rgba(255, 255, 255, 0.1));
|
|
855
|
+
cursor: col-resize;
|
|
856
|
+
transition: background 0.15s;
|
|
857
|
+
}
|
|
858
|
+
.divider:hover,
|
|
859
|
+
.json-browser.dragging .sidebar-divider {
|
|
860
|
+
background: var(--vscode-focusBorder, #007fd4);
|
|
861
|
+
}
|
|
862
|
+
.canvas {
|
|
863
|
+
flex: 1;
|
|
864
|
+
height: 100%;
|
|
865
|
+
position: relative;
|
|
866
|
+
overflow: hidden;
|
|
867
|
+
min-width: 200px;
|
|
868
|
+
}
|
|
869
|
+
/* === Panel layout === */
|
|
870
|
+
.panel-container {
|
|
871
|
+
display: flex;
|
|
872
|
+
width: 100%;
|
|
873
|
+
height: 100%;
|
|
874
|
+
}
|
|
875
|
+
.panel-container.vertical {
|
|
876
|
+
flex-direction: column;
|
|
877
|
+
}
|
|
878
|
+
.panel-container.horizontal {
|
|
879
|
+
flex-direction: row;
|
|
880
|
+
}
|
|
881
|
+
.viz-panel {
|
|
882
|
+
position: relative;
|
|
883
|
+
overflow: hidden;
|
|
884
|
+
min-width: 100px;
|
|
885
|
+
min-height: 80px;
|
|
886
|
+
}
|
|
887
|
+
.panel-mount {
|
|
888
|
+
width: 100%;
|
|
889
|
+
height: 100%;
|
|
890
|
+
}
|
|
891
|
+
.panel-label {
|
|
892
|
+
position: absolute;
|
|
893
|
+
bottom: 4px;
|
|
894
|
+
left: 4px;
|
|
895
|
+
z-index: 10;
|
|
896
|
+
font-size: 10px;
|
|
897
|
+
padding: 2px 6px;
|
|
898
|
+
border-radius: 3px;
|
|
899
|
+
color: white;
|
|
900
|
+
opacity: 0.7;
|
|
901
|
+
pointer-events: none;
|
|
902
|
+
white-space: nowrap;
|
|
903
|
+
max-width: calc(100% - 16px);
|
|
904
|
+
overflow: hidden;
|
|
905
|
+
text-overflow: ellipsis;
|
|
906
|
+
}
|
|
907
|
+
/* === Split dividers === */
|
|
908
|
+
.split-divider {
|
|
909
|
+
flex-shrink: 0;
|
|
910
|
+
background: var(--vscode-panel-border, rgba(255, 255, 255, 0.15));
|
|
911
|
+
transition: background 0.15s;
|
|
912
|
+
z-index: 5;
|
|
913
|
+
}
|
|
914
|
+
.split-divider.vertical {
|
|
915
|
+
height: 5px;
|
|
916
|
+
cursor: row-resize;
|
|
917
|
+
}
|
|
918
|
+
.split-divider.horizontal {
|
|
919
|
+
width: 5px;
|
|
920
|
+
cursor: col-resize;
|
|
921
|
+
}
|
|
922
|
+
.split-divider:hover,
|
|
923
|
+
.split-divider.active {
|
|
924
|
+
background: var(--vscode-focusBorder, #007fd4);
|
|
925
|
+
}
|
|
926
|
+
/* === Drop zone indicators === */
|
|
927
|
+
.drop-indicator {
|
|
928
|
+
position: absolute;
|
|
929
|
+
background: rgba(0, 127, 212, 0.25);
|
|
930
|
+
border: 2px solid var(--vscode-focusBorder, #007fd4);
|
|
931
|
+
z-index: 20;
|
|
932
|
+
pointer-events: none;
|
|
933
|
+
transition: all 0.1s;
|
|
934
|
+
}
|
|
935
|
+
.drop-indicator.top {
|
|
936
|
+
top: 0;
|
|
937
|
+
left: 0;
|
|
938
|
+
right: 0;
|
|
939
|
+
height: 40%;
|
|
940
|
+
}
|
|
941
|
+
.drop-indicator.bottom {
|
|
942
|
+
bottom: 0;
|
|
943
|
+
left: 0;
|
|
944
|
+
right: 0;
|
|
945
|
+
height: 40%;
|
|
946
|
+
}
|
|
947
|
+
.drop-indicator.left {
|
|
948
|
+
top: 0;
|
|
949
|
+
left: 0;
|
|
950
|
+
bottom: 0;
|
|
951
|
+
width: 40%;
|
|
952
|
+
}
|
|
953
|
+
.drop-indicator.right {
|
|
954
|
+
top: 0;
|
|
955
|
+
right: 0;
|
|
956
|
+
bottom: 0;
|
|
957
|
+
width: 40%;
|
|
958
|
+
}
|
|
959
|
+
/* === Placeholder === */
|
|
960
|
+
.placeholder {
|
|
961
|
+
display: flex;
|
|
962
|
+
flex-direction: column;
|
|
963
|
+
align-items: center;
|
|
964
|
+
justify-content: center;
|
|
965
|
+
height: 100%;
|
|
966
|
+
opacity: 0.8;
|
|
967
|
+
padding: 2rem;
|
|
968
|
+
text-align: center;
|
|
969
|
+
}
|
|
970
|
+
.placeholder-title {
|
|
971
|
+
font-size: 16px;
|
|
972
|
+
margin-bottom: 8px;
|
|
973
|
+
font-weight: 500;
|
|
974
|
+
}
|
|
975
|
+
.placeholder-sub {
|
|
976
|
+
font-size: 13px;
|
|
977
|
+
opacity: 0.65;
|
|
978
|
+
max-width: 400px;
|
|
979
|
+
line-height: 1.5;
|
|
980
|
+
}
|
|
981
|
+
.type-list {
|
|
982
|
+
margin-top: 16px;
|
|
983
|
+
display: flex;
|
|
984
|
+
flex-wrap: wrap;
|
|
985
|
+
gap: 6px;
|
|
986
|
+
justify-content: center;
|
|
987
|
+
align-items: center;
|
|
988
|
+
}
|
|
989
|
+
.type-list-header {
|
|
990
|
+
width: 100%;
|
|
991
|
+
font-size: 11px;
|
|
992
|
+
text-transform: uppercase;
|
|
993
|
+
letter-spacing: 0.5px;
|
|
994
|
+
opacity: 0.5;
|
|
995
|
+
margin-bottom: 4px;
|
|
996
|
+
}
|
|
997
|
+
.type-tag {
|
|
998
|
+
display: inline-flex;
|
|
999
|
+
align-items: center;
|
|
1000
|
+
gap: 5px;
|
|
1001
|
+
padding: 3px 8px;
|
|
1002
|
+
border-radius: 10px;
|
|
1003
|
+
font-size: 11px;
|
|
1004
|
+
border: 1px solid;
|
|
1005
|
+
opacity: 0.7;
|
|
1006
|
+
}
|
|
1007
|
+
.renderable-chip {
|
|
1008
|
+
display: inline-flex;
|
|
1009
|
+
align-items: center;
|
|
1010
|
+
gap: 6px;
|
|
1011
|
+
padding: 4px 10px;
|
|
1012
|
+
border-radius: 12px;
|
|
1013
|
+
font-size: 12px;
|
|
1014
|
+
cursor: pointer;
|
|
1015
|
+
color: inherit;
|
|
1016
|
+
transition: opacity 0.15s;
|
|
1017
|
+
}
|
|
1018
|
+
.renderable-chip:hover {
|
|
1019
|
+
opacity: 0.8;
|
|
1020
|
+
}
|
|
1021
|
+
.renderable-chip code {
|
|
1022
|
+
font-size: 11px;
|
|
1023
|
+
opacity: 0.8;
|
|
1024
|
+
}
|
|
1025
|
+
.chip-dot {
|
|
1026
|
+
width: 8px;
|
|
1027
|
+
height: 8px;
|
|
1028
|
+
border-radius: 50%;
|
|
1029
|
+
flex-shrink: 0;
|
|
1030
|
+
}
|
|
1031
|
+
/* Badges are injected imperatively — use :global so Svelte doesn't strip the rule */
|
|
1032
|
+
:global(.renderable-badge) {
|
|
1033
|
+
font-size: 9px;
|
|
1034
|
+
padding: 1px 4px;
|
|
1035
|
+
margin-left: 4px;
|
|
1036
|
+
border-radius: 3px;
|
|
1037
|
+
color: white;
|
|
1038
|
+
font-weight: 500;
|
|
1039
|
+
white-space: nowrap;
|
|
1040
|
+
cursor: grab;
|
|
1041
|
+
flex-shrink: 0;
|
|
1042
|
+
}
|
|
1043
|
+
</style>
|