matterviz 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FilePicker.svelte +40 -50
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +15 -0
- package/dist/brillouin/BrillouinZone.svelte +18 -9
- package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
- package/dist/chempot-diagram/async-compute.svelte.js +12 -13
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/composition/BarChart.svelte +7 -9
- package/dist/composition/Composition.svelte +3 -8
- package/dist/composition/Formula.svelte +2 -2
- package/dist/composition/FormulaFilter.svelte +27 -93
- package/dist/composition/format.js +6 -8
- package/dist/composition/parse.js +50 -105
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +6 -17
- package/dist/convex-hull/ConvexHull2D.svelte +0 -2
- package/dist/convex-hull/ConvexHull3D.svelte +19 -47
- package/dist/convex-hull/ConvexHull4D.svelte +65 -95
- package/dist/convex-hull/StructurePopup.svelte +1 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.js +1 -7
- package/dist/convex-hull/thermodynamics.js +19 -27
- package/dist/coordination/CoordinationBarPlot.svelte +12 -21
- package/dist/element/BohrAtom.svelte +0 -1
- package/dist/element/ElementTile.svelte +26 -44
- package/dist/element/data.d.ts +2 -0
- package/dist/element/data.js +5 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +22 -23
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
- package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
- package/dist/fermi-surface/compute.js +22 -55
- package/dist/fermi-surface/parse.js +38 -70
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +1 -1
- package/dist/io/decompress.d.ts +2 -0
- package/dist/io/decompress.js +3 -2
- package/dist/io/export.d.ts +7 -4
- package/dist/io/export.js +70 -18
- package/dist/io/file-drop.js +60 -35
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +28 -16
- package/dist/isosurface/Isosurface.svelte +687 -196
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +564 -85
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.js +1 -1
- package/dist/layout/InfoCard.svelte +1 -3
- package/dist/layout/SettingsSection.svelte +52 -61
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/ViewerChrome.svelte +7 -1
- package/dist/layout/json-tree/JsonNode.svelte +14 -48
- package/dist/layout/json-tree/JsonTree.svelte +48 -95
- package/dist/layout/json-tree/JsonValue.svelte +3 -18
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -179
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +3 -2
- package/dist/math.js +29 -38
- package/dist/overlays/ContextMenu.svelte +15 -32
- package/dist/overlays/DraggablePane.svelte +39 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
- package/dist/overlays/GlassChip.svelte +3 -1
- package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
- package/dist/phase-diagram/build-diagram.js +1 -6
- package/dist/phase-diagram/svg-to-diagram.js +9 -14
- package/dist/plot/bar/BarPlot.svelte +38 -42
- package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
- package/dist/plot/box/BoxPlot.svelte +27 -23
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +1 -1
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +0 -10
- package/dist/plot/core/layout.d.ts +12 -5
- package/dist/plot/core/layout.js +37 -27
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
- package/dist/plot/core/utils/hierarchy-chart.js +12 -0
- package/dist/plot/core/utils/hierarchy-labels.js +3 -2
- package/dist/plot/histogram/Histogram.svelte +21 -26
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
- package/dist/plot/scatter/ScatterPlot.svelte +67 -52
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/sunburst/Sunburst.svelte +20 -20
- package/dist/plot/treemap/Treemap.svelte +175 -60
- package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
- package/dist/plot/treemap/index.d.ts +1 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +4 -2
- package/dist/plot/treemap/treemap.js +5 -2
- package/dist/rdf/RdfPlot.svelte +17 -35
- package/dist/rdf/calc-rdf.js +59 -44
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +84 -26
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.js +159 -482
- package/dist/spectral/Bands.svelte +151 -158
- package/dist/spectral/BrillouinBandsDos.svelte +4 -19
- package/dist/spectral/Dos.svelte +26 -24
- package/dist/spectral/helpers.js +40 -105
- package/dist/structure/AtomLegend.svelte +8 -13
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/CellSelect.svelte +35 -10
- package/dist/structure/Structure.svelte +177 -99
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureCarousel.svelte +10 -3
- package/dist/structure/StructureControls.svelte +8 -26
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +65 -76
- package/dist/structure/StructureScene.svelte +75 -109
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +120 -55
- package/dist/structure/StructureViewport.svelte.d.ts +3 -1
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +4 -3
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +0 -1
- package/dist/structure/bonding.js +16 -23
- package/dist/structure/export.js +50 -74
- package/dist/structure/format-detect.js +5 -7
- package/dist/structure/index.js +6 -12
- package/dist/structure/parse.js +38 -100
- package/dist/structure/partial-occupancy.js +7 -4
- package/dist/structure/pbc.js +8 -10
- package/dist/structure/polyhedra.js +5 -4
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/symmetry/SymmetryStats.svelte +0 -4
- package/dist/symmetry/WyckoffTable.svelte +5 -4
- package/dist/symmetry/cell-transform.js +5 -14
- package/dist/symmetry/index.js +6 -9
- package/dist/table/HeatmapTable.svelte +61 -103
- package/dist/table/ToggleMenu.svelte +166 -44
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +1 -2
- package/dist/theme/themes.mjs +31 -60
- package/dist/tooltip/TooltipContent.svelte +1 -1
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +140 -120
- package/dist/trajectory/TrajectoryError.svelte +1 -66
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +19 -29
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +7 -5
- package/dist/trajectory/frame-reader.js +3 -4
- package/dist/trajectory/helpers.js +16 -49
- package/dist/trajectory/index.js +2 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/index.d.ts +4 -3
- package/dist/trajectory/parse/index.js +12 -17
- package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
- package/dist/trajectory/parse/vaspout-h5.js +1 -4
- package/dist/trajectory/parse/xyz.js +48 -2
- package/dist/trajectory/plotting.js +14 -37
- package/dist/xrd/XrdPlot.svelte +43 -68
- package/dist/xrd/calc-xrd.js +22 -58
- package/dist/xrd/parse.js +13 -22
- package/package.json +40 -8
- package/readme.md +6 -5
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
package/dist/labels.js
CHANGED
|
@@ -141,7 +141,7 @@ export const format_num = (num, fmt) => {
|
|
|
141
141
|
return format(fmt)(num);
|
|
142
142
|
};
|
|
143
143
|
// Format a 3D vector as "(x, y, z)" with configurable precision
|
|
144
|
-
export const format_vec3 = (vec, fmt_spec = `.
|
|
144
|
+
export const format_vec3 = (vec, fmt_spec = `.3~`) => `(${format_num(vec[0], fmt_spec)}, ${format_num(vec[1], fmt_spec)}, ${format_num(vec[2], fmt_spec)})`;
|
|
145
145
|
const BYTE_UNITS = [`B`, `KiB`, `MiB`, `GiB`, `TiB`, `PiB`];
|
|
146
146
|
// Format file sizes using IEC binary units (1024 factor).
|
|
147
147
|
export const format_bytes = (bytes) => {
|
|
@@ -26,14 +26,12 @@
|
|
|
26
26
|
data = [],
|
|
27
27
|
title = ``,
|
|
28
28
|
fallback = ``,
|
|
29
|
-
fmt = `.2f`,
|
|
29
|
+
fmt: default_fmt = `.2f`,
|
|
30
30
|
as = `section`,
|
|
31
31
|
title_snippet,
|
|
32
32
|
fallback_snippet,
|
|
33
33
|
...rest
|
|
34
34
|
}: Props = $props()
|
|
35
|
-
|
|
36
|
-
let default_fmt = $derived(fmt) // rename fmt to default_fmt for internal use
|
|
37
35
|
</script>
|
|
38
36
|
|
|
39
37
|
<svelte:element this={as} {...rest} class={[`info-card`, rest.class]}>
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
import { untrack } from 'svelte'
|
|
5
5
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
6
6
|
|
|
7
|
+
type SettingsSectionContext = {
|
|
8
|
+
current_values: Record<string, unknown>
|
|
9
|
+
has_changes: boolean
|
|
10
|
+
reference_values: Record<string, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
let {
|
|
8
14
|
title,
|
|
9
15
|
current_values,
|
|
@@ -13,15 +19,7 @@
|
|
|
13
19
|
}: HTMLAttributes<HTMLElementTagNameMap[`section`]> & {
|
|
14
20
|
title: string
|
|
15
21
|
current_values: Record<string, unknown>
|
|
16
|
-
children: Snippet<
|
|
17
|
-
[
|
|
18
|
-
{
|
|
19
|
-
current_values: Record<string, unknown>
|
|
20
|
-
has_changes: boolean
|
|
21
|
-
reference_values: Record<string, unknown>
|
|
22
|
-
},
|
|
23
|
-
]
|
|
24
|
-
>
|
|
22
|
+
children: Snippet<[SettingsSectionContext]>
|
|
25
23
|
on_reset?: () => void
|
|
26
24
|
} = $props()
|
|
27
25
|
|
|
@@ -30,17 +28,10 @@
|
|
|
30
28
|
if (obj === null || typeof obj !== `object`) return obj
|
|
31
29
|
if (obj instanceof Date) return new Date(obj)
|
|
32
30
|
if (obj instanceof RegExp) return new RegExp(obj)
|
|
33
|
-
if (Array.isArray(obj))
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const copy: Record<string, unknown> = {}
|
|
40
|
-
for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
|
|
41
|
-
copy[key] = typeof value === `object` && value !== null ? deep_copy(value) : value
|
|
42
|
-
}
|
|
43
|
-
return copy
|
|
31
|
+
if (Array.isArray(obj)) return obj.map(deep_copy)
|
|
32
|
+
return Object.fromEntries(
|
|
33
|
+
Object.entries(obj).map(([key, value]) => [key, deep_copy(value)]),
|
|
34
|
+
)
|
|
44
35
|
}
|
|
45
36
|
|
|
46
37
|
// Capture initial values once at mount - must NOT be $derived or it tracks changes
|
|
@@ -49,45 +40,50 @@
|
|
|
49
40
|
// unique per-instance id so aria-labelledby stays valid with multiple sections on a page
|
|
50
41
|
const title_id = `settings-section-title-${crypto.randomUUID()}`
|
|
51
42
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
) {
|
|
74
|
-
return true
|
|
75
|
-
}
|
|
76
|
-
continue
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Handle undefined/null comparisons properly
|
|
80
|
-
if (reference_value === undefined && current_value === undefined) continue
|
|
81
|
-
if (reference_value === null && current_value === null) continue
|
|
82
|
-
|
|
83
|
-
// Basic comparison for primitives
|
|
84
|
-
if (current_value !== reference_value) {
|
|
85
|
-
return true
|
|
43
|
+
// Order-independent deep equality for setting values
|
|
44
|
+
const setting_equal = (left: unknown, right: unknown): boolean => {
|
|
45
|
+
if (Object.is(left, right)) return true
|
|
46
|
+
if (left == null || right == null) return false
|
|
47
|
+
if (typeof left !== `object` || typeof right !== `object`) return false
|
|
48
|
+
if (left instanceof Date || right instanceof Date) {
|
|
49
|
+
return (
|
|
50
|
+
left instanceof Date && right instanceof Date && left.getTime() === right.getTime()
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
if (left instanceof RegExp || right instanceof RegExp) {
|
|
54
|
+
return (
|
|
55
|
+
left instanceof RegExp &&
|
|
56
|
+
right instanceof RegExp &&
|
|
57
|
+
left.toString() === right.toString()
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
61
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
|
|
62
|
+
return false
|
|
86
63
|
}
|
|
64
|
+
return left.every((item, idx) => setting_equal(item, right[idx]))
|
|
87
65
|
}
|
|
88
|
-
|
|
89
|
-
|
|
66
|
+
const left_obj = left as Record<string, unknown>
|
|
67
|
+
const right_obj = right as Record<string, unknown>
|
|
68
|
+
const left_keys = Object.keys(left_obj)
|
|
69
|
+
if (left_keys.length !== Object.keys(right_obj).length) return false
|
|
70
|
+
return left_keys.every(
|
|
71
|
+
(key) => Object.hasOwn(right_obj, key) && setting_equal(left_obj[key], right_obj[key]),
|
|
72
|
+
)
|
|
73
|
+
}
|
|
90
74
|
|
|
75
|
+
// Key presence is independent of value: additions/removals count even when the
|
|
76
|
+
// value is undefined. Only compare values when both sides own the key.
|
|
77
|
+
let has_changes = $derived(
|
|
78
|
+
[...new Set([...Object.keys(reference_values), ...Object.keys(current_values)])].some(
|
|
79
|
+
(key) => {
|
|
80
|
+
const in_reference = Object.hasOwn(reference_values, key)
|
|
81
|
+
const in_current = Object.hasOwn(current_values, key)
|
|
82
|
+
if (in_reference !== in_current) return true
|
|
83
|
+
return !setting_equal(reference_values[key], current_values[key])
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
)
|
|
91
87
|
function handle_reset(event: MouseEvent) {
|
|
92
88
|
event.stopPropagation()
|
|
93
89
|
event.preventDefault()
|
|
@@ -144,9 +140,4 @@
|
|
|
144
140
|
opacity: 1;
|
|
145
141
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
146
142
|
}
|
|
147
|
-
.reset-button.standalone {
|
|
148
|
-
position: absolute;
|
|
149
|
-
top: -8pt;
|
|
150
|
-
right: -8pt;
|
|
151
|
-
}
|
|
152
143
|
</style>
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type SettingsSectionContext = {
|
|
4
|
+
current_values: Record<string, unknown>;
|
|
5
|
+
has_changes: boolean;
|
|
6
|
+
reference_values: Record<string, unknown>;
|
|
7
|
+
};
|
|
3
8
|
type $$ComponentProps = HTMLAttributes<HTMLElementTagNameMap[`section`]> & {
|
|
4
9
|
title: string;
|
|
5
10
|
current_values: Record<string, unknown>;
|
|
6
|
-
children: Snippet<[
|
|
7
|
-
{
|
|
8
|
-
current_values: Record<string, unknown>;
|
|
9
|
-
has_changes: boolean;
|
|
10
|
-
reference_values: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
]>;
|
|
11
|
+
children: Snippet<[SettingsSectionContext]>;
|
|
13
12
|
on_reset?: () => void;
|
|
14
13
|
};
|
|
15
14
|
declare const SettingsSection: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -70,6 +70,8 @@
|
|
|
70
70
|
right: var(--viewer-buttons-right, var(--ctrl-btn-right, 1ex));
|
|
71
71
|
gap: var(--viewer-buttons-gap, clamp(6pt, 1cqmin, 9pt));
|
|
72
72
|
z-index: var(--viewer-buttons-z-index, var(--z-index-overlay-controls, 100000000));
|
|
73
|
+
/* own compositing layer, or WKWebView paints the canvas over this (see app.css) */
|
|
74
|
+
will-change: transform;
|
|
73
75
|
opacity: 0;
|
|
74
76
|
pointer-events: none;
|
|
75
77
|
transition: opacity 0.2s ease;
|
|
@@ -99,7 +101,11 @@
|
|
|
99
101
|
font-size: var(--ctrl-btn-icon-size, clamp(0.7rem, 2cqmin, 0.85rem));
|
|
100
102
|
}
|
|
101
103
|
section.control-buttons :global(button:hover) {
|
|
102
|
-
background-color:
|
|
104
|
+
background-color: var(
|
|
105
|
+
--viewer-buttons-hover-bg,
|
|
106
|
+
color-mix(in srgb, currentColor 8%, transparent)
|
|
107
|
+
);
|
|
108
|
+
color: var(--viewer-buttons-hover-color, currentColor);
|
|
103
109
|
}
|
|
104
110
|
.filename {
|
|
105
111
|
font-family: monospace;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Icon from '../../Icon.svelte'
|
|
3
|
+
import { build_path } from '../../json-path'
|
|
3
4
|
import { getContext, onMount } from 'svelte'
|
|
4
5
|
// oxlint-disable-next-line import/no-self-import -- recursive Svelte component
|
|
5
6
|
import JsonNode from './JsonNode.svelte'
|
|
@@ -7,7 +8,6 @@
|
|
|
7
8
|
import type { JsonTreeContext } from './types'
|
|
8
9
|
import { JSON_TREE_CONTEXT_KEY } from './types'
|
|
9
10
|
import {
|
|
10
|
-
build_path,
|
|
11
11
|
estimate_byte_size,
|
|
12
12
|
format_byte_size,
|
|
13
13
|
format_preview,
|
|
@@ -40,13 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
// Determine value type
|
|
44
43
|
let value_type = $derived(get_value_type(value))
|
|
45
|
-
|
|
46
|
-
// Check if this node is expandable
|
|
47
44
|
let expandable = $derived(is_expandable(value))
|
|
48
|
-
|
|
49
|
-
// Get child count for preview
|
|
50
45
|
let child_count = $derived(expandable ? get_child_count(value) : 0)
|
|
51
46
|
|
|
52
47
|
// Determine if this node should be collapsed
|
|
@@ -77,14 +72,9 @@
|
|
|
77
72
|
})
|
|
78
73
|
|
|
79
74
|
// Note: Search highlighting is handled by CSS Highlight API in JsonTree.svelte
|
|
80
|
-
|
|
81
|
-
// Check if this node is focused
|
|
82
75
|
let is_focused = $derived(ctx?.focused_path === path)
|
|
83
|
-
|
|
84
|
-
// Check if this is the current search match being navigated
|
|
76
|
+
// Current search match being navigated
|
|
85
77
|
let is_current_match = $derived(ctx?.current_match_path === path)
|
|
86
|
-
|
|
87
|
-
// Check if this node is selected
|
|
88
78
|
let is_selected = $derived(ctx?.selected_paths.has(path) ?? false)
|
|
89
79
|
|
|
90
80
|
// Diff status for this node (null if no diff or unchanged)
|
|
@@ -103,58 +93,34 @@
|
|
|
103
93
|
}
|
|
104
94
|
}
|
|
105
95
|
|
|
106
|
-
// Toggle collapse recursively on double-click
|
|
96
|
+
// Toggle collapse recursively on double-click (collapsed expands all, and vice versa)
|
|
107
97
|
function toggle_collapse_recursive(event: MouseEvent) {
|
|
108
98
|
event.stopPropagation()
|
|
109
|
-
if (ctx && expandable)
|
|
110
|
-
// If collapsed, expand all; if expanded, collapse all
|
|
111
|
-
ctx.toggle_collapse_recursive(path, !is_collapsed)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Focus this node
|
|
116
|
-
function focus_node() {
|
|
117
|
-
ctx?.set_focused(path)
|
|
99
|
+
if (ctx && expandable) ctx.toggle_collapse_recursive(path, !is_collapsed)
|
|
118
100
|
}
|
|
119
101
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
102
|
+
// Children as { key, value } pairs based on value type (Map entries are wrapped so
|
|
103
|
+
// key and value render as an expandable pair; Set values get numeric keys)
|
|
104
|
+
let children = $derived.by((): { key: string | number; value: unknown }[] => {
|
|
124
105
|
if (value_type === `array`) {
|
|
125
106
|
return (value as unknown[]).map((val, idx) => ({ key: idx, value: val }))
|
|
126
107
|
}
|
|
127
|
-
|
|
128
108
|
if (value_type === `object`) {
|
|
129
|
-
|
|
130
|
-
if (ctx?.settings.sort_keys)
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
return keys.map((key) => ({
|
|
134
|
-
key,
|
|
135
|
-
value: (value as Record<string, unknown>)[key],
|
|
136
|
-
}))
|
|
109
|
+
const keys = Object.keys(value as Record<string, unknown>)
|
|
110
|
+
if (ctx?.settings.sort_keys) keys.sort()
|
|
111
|
+
return keys.map((key) => ({ key, value: (value as Record<string, unknown>)[key] }))
|
|
137
112
|
}
|
|
138
|
-
|
|
139
113
|
if (value_type === `map`) {
|
|
140
|
-
|
|
141
|
-
return Array.from(map.entries()).map(([key, val], idx) => ({
|
|
114
|
+
return Array.from(value as Map<unknown, unknown>, ([key, val], idx) => ({
|
|
142
115
|
key: idx,
|
|
143
116
|
value: { key, value: val },
|
|
144
117
|
}))
|
|
145
118
|
}
|
|
146
|
-
|
|
147
119
|
if (value_type === `set`) {
|
|
148
|
-
return Array.from(value as Set<unknown
|
|
149
|
-
key: idx,
|
|
150
|
-
value: val,
|
|
151
|
-
}))
|
|
120
|
+
return Array.from(value as Set<unknown>, (val, idx) => ({ key: idx, value: val }))
|
|
152
121
|
}
|
|
153
|
-
|
|
154
122
|
return []
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
let children = $derived(get_children())
|
|
123
|
+
})
|
|
158
124
|
|
|
159
125
|
// Ghost children: removed entries from diff (pre-computed in JsonTree for O(1) lookup)
|
|
160
126
|
let ghost_children = $derived.by(() => {
|
|
@@ -233,7 +199,7 @@
|
|
|
233
199
|
event.stopPropagation()
|
|
234
200
|
ctx?.toggle_select(path, event.shiftKey)
|
|
235
201
|
} else {
|
|
236
|
-
|
|
202
|
+
ctx?.set_focused(path)
|
|
237
203
|
}
|
|
238
204
|
}}
|
|
239
205
|
onauxclick={(event) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Icon from '../../Icon.svelte'
|
|
3
3
|
import { download } from '../../io/fetch'
|
|
4
|
+
import { parse_path } from '../../json-path'
|
|
4
5
|
import { make_change_detector } from '../../utils'
|
|
5
6
|
import { setContext, tick } from 'svelte'
|
|
6
7
|
import { highlight_matches, tooltip } from 'svelte-multiselect/attachments'
|
|
@@ -16,7 +17,6 @@
|
|
|
16
17
|
find_matching_paths,
|
|
17
18
|
format_preview,
|
|
18
19
|
get_ancestor_paths,
|
|
19
|
-
parse_path,
|
|
20
20
|
serialize_for_copy,
|
|
21
21
|
} from './utils'
|
|
22
22
|
|
|
@@ -147,31 +147,10 @@
|
|
|
147
147
|
current_match_index = -1
|
|
148
148
|
return
|
|
149
149
|
}
|
|
150
|
-
const paths_to_expand = new Set
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
let collapsed_changed = false
|
|
157
|
-
let force_expanded_changed = false
|
|
158
|
-
for (const path_to_expand of paths_to_expand) {
|
|
159
|
-
if (collapsed_paths.has(path_to_expand)) {
|
|
160
|
-
collapsed_paths.delete(path_to_expand)
|
|
161
|
-
collapsed_changed = true
|
|
162
|
-
}
|
|
163
|
-
// Also add to force_expanded to override auto-fold thresholds (depth/size)
|
|
164
|
-
if (!force_expanded.has(path_to_expand)) {
|
|
165
|
-
force_expanded.add(path_to_expand)
|
|
166
|
-
force_expanded_changed = true
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (collapsed_changed) {
|
|
170
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
171
|
-
}
|
|
172
|
-
if (force_expanded_changed) {
|
|
173
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
174
|
-
}
|
|
150
|
+
const paths_to_expand = new Set([...search_matches].flatMap(get_ancestor_paths))
|
|
151
|
+
// Also add to force_expanded to override auto-fold thresholds (depth/size)
|
|
152
|
+
// Only reassign (which rerenders every node) the sets that actually changed
|
|
153
|
+
set_collapsed(paths_to_expand, () => false)
|
|
175
154
|
// Wait for DOM to update before scrolling to match
|
|
176
155
|
await tick()
|
|
177
156
|
if (sorted_matches.length > 0) {
|
|
@@ -213,32 +192,34 @@
|
|
|
213
192
|
// Previous values map for change detection
|
|
214
193
|
const prev_values = new Map<string, unknown>()
|
|
215
194
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
195
|
+
// Move each path into collapsed or force_expanded (which overrides auto-fold
|
|
196
|
+
// thresholds), then reassign only the sets that changed (reassignment rerenders
|
|
197
|
+
// every node subscribed to the set's identity)
|
|
198
|
+
function set_collapsed(paths: Iterable<string>, collapse: (path: string) => boolean) {
|
|
199
|
+
let collapsed_changed = false
|
|
200
|
+
let force_expanded_changed = false
|
|
201
|
+
for (const path of paths) {
|
|
202
|
+
if (collapse(path)) {
|
|
203
|
+
force_expanded_changed = force_expanded.delete(path) || force_expanded_changed
|
|
204
|
+
collapsed_changed = !collapsed_paths.has(path) || collapsed_changed
|
|
205
|
+
collapsed_paths.add(path)
|
|
206
|
+
} else {
|
|
207
|
+
collapsed_changed = collapsed_paths.delete(path) || collapsed_changed
|
|
208
|
+
force_expanded_changed = !force_expanded.has(path) || force_expanded_changed
|
|
209
|
+
force_expanded.add(path)
|
|
210
|
+
}
|
|
224
211
|
}
|
|
225
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
226
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
212
|
+
if (collapsed_changed) collapsed_paths = new SvelteSet(collapsed_paths)
|
|
213
|
+
if (force_expanded_changed) force_expanded = new SvelteSet(force_expanded)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function toggle_collapse(path: string, is_currently_collapsed: boolean): void {
|
|
217
|
+
set_collapsed([path], () => !is_currently_collapsed)
|
|
227
218
|
}
|
|
228
219
|
|
|
229
220
|
// Toggle collapse recursively for all descendants
|
|
230
221
|
function toggle_collapse_recursive(path: string, collapse: boolean): void {
|
|
231
|
-
|
|
232
|
-
if (collapse) {
|
|
233
|
-
force_expanded.delete(desc)
|
|
234
|
-
collapsed_paths.add(desc)
|
|
235
|
-
} else {
|
|
236
|
-
collapsed_paths.delete(desc)
|
|
237
|
-
force_expanded.add(desc)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
241
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
222
|
+
set_collapsed(get_descendants(path), () => collapse)
|
|
242
223
|
}
|
|
243
224
|
|
|
244
225
|
// Get all descendant paths of a given path (including the path itself)
|
|
@@ -358,19 +339,9 @@
|
|
|
358
339
|
// Pre-compute ghost children map for O(1) lookup per node
|
|
359
340
|
let ghost_map = $derived(diff_map ? build_ghost_map(diff_map) : new Map())
|
|
360
341
|
|
|
361
|
-
// Collapse all descendants but keep the given node expanded
|
|
342
|
+
// Collapse all descendants but keep the given node expanded
|
|
362
343
|
function collapse_children_only(target_path: string): void {
|
|
363
|
-
|
|
364
|
-
if (desc === target_path) {
|
|
365
|
-
collapsed_paths.delete(desc)
|
|
366
|
-
force_expanded.add(desc)
|
|
367
|
-
} else {
|
|
368
|
-
force_expanded.delete(desc)
|
|
369
|
-
collapsed_paths.add(desc)
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
373
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
344
|
+
set_collapsed(get_descendants(target_path), (desc) => desc !== target_path)
|
|
374
345
|
}
|
|
375
346
|
|
|
376
347
|
// Context menu handlers
|
|
@@ -512,6 +483,13 @@
|
|
|
512
483
|
|
|
513
484
|
// Keyboard navigation at tree level
|
|
514
485
|
function handle_tree_keydown(event: KeyboardEvent) {
|
|
486
|
+
// F3 navigates search matches (search input handles its own F3/Enter)
|
|
487
|
+
if (event.key === `F3`) {
|
|
488
|
+
event.preventDefault()
|
|
489
|
+
if (event.shiftKey) go_to_prev_match()
|
|
490
|
+
else go_to_next_match()
|
|
491
|
+
return
|
|
492
|
+
}
|
|
515
493
|
// Escape closes context menu first, then clears selection
|
|
516
494
|
if (event.key === `Escape`) {
|
|
517
495
|
if (context_menu_state) {
|
|
@@ -600,15 +578,7 @@
|
|
|
600
578
|
role="tree"
|
|
601
579
|
aria-label="JSON tree viewer"
|
|
602
580
|
{...rest}
|
|
603
|
-
onkeydown={
|
|
604
|
-
handle_tree_keydown(event)
|
|
605
|
-
// F3 at tree level for match navigation
|
|
606
|
-
if (event.key === `F3`) {
|
|
607
|
-
event.preventDefault()
|
|
608
|
-
if (event.shiftKey) go_to_prev_match()
|
|
609
|
-
else go_to_next_match()
|
|
610
|
-
}
|
|
611
|
-
}}
|
|
581
|
+
onkeydown={handle_tree_keydown}
|
|
612
582
|
>
|
|
613
583
|
{#if show_header}
|
|
614
584
|
<header class="json-tree-header">
|
|
@@ -686,30 +656,16 @@
|
|
|
686
656
|
<button type="button" onclick={collapse_all} title="Collapse all" {@attach tooltip()}>
|
|
687
657
|
<Icon icon="Collapse" style="width: 14px; height: 14px" />
|
|
688
658
|
</button>
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
onclick={() => collapse_to_level(2)}
|
|
700
|
-
title="Collapse to level 2"
|
|
701
|
-
{@attach tooltip()}
|
|
702
|
-
>
|
|
703
|
-
2
|
|
704
|
-
</button>
|
|
705
|
-
<button
|
|
706
|
-
type="button"
|
|
707
|
-
onclick={() => collapse_to_level(3)}
|
|
708
|
-
title="Collapse to level 3"
|
|
709
|
-
{@attach tooltip()}
|
|
710
|
-
>
|
|
711
|
-
3
|
|
712
|
-
</button>
|
|
659
|
+
{#each [1, 2, 3] as level (level)}
|
|
660
|
+
<button
|
|
661
|
+
type="button"
|
|
662
|
+
onclick={() => collapse_to_level(level)}
|
|
663
|
+
title="Collapse to level {level}"
|
|
664
|
+
{@attach tooltip()}
|
|
665
|
+
>
|
|
666
|
+
{level}
|
|
667
|
+
</button>
|
|
668
|
+
{/each}
|
|
713
669
|
</div>
|
|
714
670
|
<div class="divider"></div>
|
|
715
671
|
<div class="controls">
|
|
@@ -910,9 +866,6 @@
|
|
|
910
866
|
border-radius: var(--jt-border-radius, 4px);
|
|
911
867
|
overflow: hidden;
|
|
912
868
|
}
|
|
913
|
-
/* --jt-header-bg, --jt-header-border, --jt-btn-bg, --jt-btn-hover-bg, --jt-btn-active-bg
|
|
914
|
-
intentionally removed in favor of transparent/opacity-based button styling.
|
|
915
|
-
Use --jt-hover-bg to customize the button hover background. */
|
|
916
869
|
.json-tree-header {
|
|
917
870
|
display: flex;
|
|
918
871
|
align-items: center;
|
|
@@ -28,8 +28,6 @@
|
|
|
28
28
|
|
|
29
29
|
// Expanded state for long strings
|
|
30
30
|
let is_expanded = $state(false)
|
|
31
|
-
|
|
32
|
-
// Extract max_string_length threshold for reuse
|
|
33
31
|
let max_len = $derived(ctx?.settings.max_string_length ?? 200)
|
|
34
32
|
let is_long_string = $derived(value_type === `string` && (value as string).length > max_len)
|
|
35
33
|
|
|
@@ -84,7 +82,6 @@
|
|
|
84
82
|
|
|
85
83
|
// Format display value - strings use custom truncation, others use format_preview
|
|
86
84
|
let display_value = $derived.by(() => {
|
|
87
|
-
if (value_type === `circular`) return `[Circular]`
|
|
88
85
|
if (value_type === `string`) {
|
|
89
86
|
const str = value as string
|
|
90
87
|
return is_long_string && !is_expanded ? `"${str.slice(0, max_len)}..."` : `"${str}"`
|
|
@@ -92,10 +89,6 @@
|
|
|
92
89
|
return format_preview(value)
|
|
93
90
|
})
|
|
94
91
|
|
|
95
|
-
// Check if string is truncated
|
|
96
|
-
let is_truncated = $derived(is_long_string && !is_expanded)
|
|
97
|
-
|
|
98
|
-
// Toggle string expansion
|
|
99
92
|
function toggle_expand(event: MouseEvent) {
|
|
100
93
|
event.stopPropagation()
|
|
101
94
|
is_expanded = !is_expanded
|
|
@@ -186,18 +179,14 @@
|
|
|
186
179
|
{:else}
|
|
187
180
|
{display_value}
|
|
188
181
|
{/if}
|
|
189
|
-
{#if
|
|
182
|
+
{#if is_long_string}
|
|
190
183
|
<button
|
|
191
184
|
type="button"
|
|
192
185
|
class="expand-btn"
|
|
193
186
|
onclick={toggle_expand}
|
|
194
|
-
title=
|
|
187
|
+
title={is_expanded ? `Collapse string` : `Show full string`}
|
|
195
188
|
>
|
|
196
|
-
|
|
197
|
-
</button>
|
|
198
|
-
{:else if is_long_string && is_expanded}
|
|
199
|
-
<button type="button" class="expand-btn" onclick={toggle_expand} title="Collapse string">
|
|
200
|
-
▲
|
|
189
|
+
{is_expanded ? `▲` : `...`}
|
|
201
190
|
</button>
|
|
202
191
|
{/if}
|
|
203
192
|
{#if ctx?.settings.show_data_types && value_type !== `null` && value_type !== `undefined`}
|
|
@@ -259,10 +248,6 @@
|
|
|
259
248
|
&.error {
|
|
260
249
|
color: var(--jt-error, light-dark(#a31515, #f48771));
|
|
261
250
|
}
|
|
262
|
-
&.circular {
|
|
263
|
-
color: var(--jt-circular, light-dark(#808080, #808080));
|
|
264
|
-
font-style: italic;
|
|
265
|
-
}
|
|
266
251
|
&.changed {
|
|
267
252
|
animation: value-change 1s ease-out;
|
|
268
253
|
}
|
|
@@ -4,15 +4,12 @@ export declare const is_expandable_type: (value_type: JsonValueType) => boolean;
|
|
|
4
4
|
export declare const is_primitive_type: (value_type: JsonValueType) => boolean;
|
|
5
5
|
export declare const is_expandable: (value: unknown) => boolean;
|
|
6
6
|
export declare function get_child_count(value: unknown): number;
|
|
7
|
-
export declare function format_path(segments: (string | number)[]): string;
|
|
8
|
-
export declare function build_path(parent_path: string, key: string | number): string;
|
|
9
7
|
export declare function serialize_for_copy(value: unknown): string;
|
|
10
8
|
export declare function format_preview(value: unknown, max_length?: number): string;
|
|
11
9
|
export declare function matches_search(path: string, key: string | number | null, value: unknown, query: string): boolean;
|
|
12
10
|
export declare function collect_all_paths(value: unknown, current_path?: string, max_depth?: number, current_depth?: number, seen?: WeakSet<object>): string[];
|
|
13
11
|
export declare function find_matching_paths(value: unknown, query: string, current_path?: string, current_key?: string | number | null, seen?: WeakSet<object>): Set<string>;
|
|
14
12
|
export declare function get_ancestor_paths(path: string): string[];
|
|
15
|
-
export declare function parse_path(path: string): (string | number)[];
|
|
16
13
|
export declare function values_equal(val_a: unknown, val_b: unknown): boolean;
|
|
17
14
|
export declare function parse_edited_value(text: string): unknown;
|
|
18
15
|
export declare function set_at_path(root: unknown, path_str: string, new_value: unknown, root_label?: string): unknown;
|