matterviz 0.3.2 → 0.3.4
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 -2
- package/dist/FilePicker.svelte +123 -82
- package/dist/Icon.svelte +18 -12
- package/dist/MillerIndexInput.svelte +27 -21
- package/dist/api/optimade.js +6 -6
- package/dist/app.css +216 -207
- package/dist/brillouin/BrillouinZone.svelte +292 -149
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
- package/dist/brillouin/BrillouinZoneExportPane.svelte +69 -42
- package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
- package/dist/brillouin/BrillouinZoneScene.svelte +275 -163
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneTooltip.svelte +17 -7
- package/dist/brillouin/compute.js +11 -6
- package/dist/chempot-diagram/ChemPotDiagram.svelte +162 -27
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +451 -281
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2148 -1642
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -5
- package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
- package/dist/chempot-diagram/async-compute.svelte.js +77 -0
- package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
- package/dist/chempot-diagram/chempot-worker.js +11 -0
- package/dist/chempot-diagram/color.js +1 -2
- package/dist/chempot-diagram/compute.d.ts +10 -0
- package/dist/chempot-diagram/compute.js +250 -88
- package/dist/chempot-diagram/index.d.ts +2 -1
- package/dist/chempot-diagram/index.js +2 -1
- package/dist/chempot-diagram/temperature.js +8 -9
- package/dist/chempot-diagram/types.d.ts +3 -0
- package/dist/chempot-diagram/types.js +1 -0
- package/dist/colors/index.d.ts +1 -1
- package/dist/colors/index.js +5 -3
- package/dist/composition/BarChart.svelte +128 -55
- package/dist/composition/BubbleChart.svelte +102 -49
- package/dist/composition/Composition.svelte +100 -79
- package/dist/composition/Formula.svelte +108 -62
- package/dist/composition/FormulaFilter.svelte +665 -537
- package/dist/composition/PieChart.svelte +183 -108
- package/dist/composition/format.d.ts +5 -0
- package/dist/composition/format.js +20 -3
- package/dist/composition/parse.js +14 -9
- package/dist/convex-hull/ConvexHull.svelte +93 -40
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +549 -360
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +1296 -827
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +1004 -688
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +115 -28
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
- package/dist/convex-hull/ConvexHullStats.svelte +425 -328
- package/dist/convex-hull/ConvexHullTooltip.svelte +40 -16
- package/dist/convex-hull/GasPressureControls.svelte +104 -61
- package/dist/convex-hull/StructurePopup.svelte +25 -4
- package/dist/convex-hull/TemperatureSlider.svelte +45 -25
- package/dist/convex-hull/barycentric-coords.js +13 -7
- package/dist/convex-hull/demo-temperature.js +8 -4
- package/dist/convex-hull/gas-thermodynamics.js +17 -12
- package/dist/convex-hull/helpers.d.ts +9 -0
- package/dist/convex-hull/helpers.js +77 -34
- package/dist/convex-hull/thermodynamics.js +61 -56
- package/dist/convex-hull/types.d.ts +9 -14
- package/dist/convex-hull/types.js +0 -17
- package/dist/coordination/CoordinationBarPlot.svelte +227 -154
- package/dist/element/BohrAtom.svelte +55 -12
- package/dist/element/ElementHeading.svelte +7 -2
- package/dist/element/ElementPhoto.svelte +15 -9
- package/dist/element/ElementStats.svelte +10 -4
- package/dist/element/ElementTile.svelte +137 -73
- package/dist/element/Nucleus.svelte +39 -11
- package/dist/element/data.js +1 -1
- package/dist/feedback/ClickFeedback.svelte +16 -5
- package/dist/feedback/DragOverlay.svelte +10 -2
- package/dist/feedback/Spinner.svelte +4 -2
- package/dist/feedback/StatusMessage.svelte +8 -2
- package/dist/fermi-surface/FermiSlice.svelte +118 -88
- package/dist/fermi-surface/FermiSurface.svelte +328 -187
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +535 -342
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
- package/dist/fermi-surface/compute.js +16 -20
- package/dist/fermi-surface/parse.js +24 -14
- package/dist/fermi-surface/symmetry.js +2 -7
- package/dist/fermi-surface/types.d.ts +3 -5
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1019 -765
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +1 -1
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +76 -22
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +2 -3
- package/dist/icons.js +47 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/io/decompress.js +1 -1
- package/dist/io/export.d.ts +3 -0
- package/dist/io/export.js +129 -143
- package/dist/io/is-binary.js +2 -3
- package/dist/io/url-drop.js +1 -2
- package/dist/isosurface/Isosurface.svelte +202 -148
- package/dist/isosurface/IsosurfaceControls.svelte +46 -28
- package/dist/isosurface/parse.js +34 -29
- package/dist/isosurface/slice.js +5 -10
- package/dist/isosurface/types.d.ts +2 -1
- package/dist/isosurface/types.js +61 -12
- package/dist/labels.js +11 -8
- package/dist/layout/FullscreenToggle.svelte +11 -2
- package/dist/layout/InfoCard.svelte +38 -6
- package/dist/layout/InfoTag.svelte +63 -32
- package/dist/layout/PropertyFilter.svelte +82 -37
- package/dist/layout/SettingsSection.svelte +85 -55
- package/dist/layout/SubpageGrid.svelte +10 -2
- package/dist/layout/json-tree/JsonNode.svelte +183 -138
- package/dist/layout/json-tree/JsonTree.svelte +499 -413
- package/dist/layout/json-tree/JsonValue.svelte +127 -99
- package/dist/layout/json-tree/utils.js +4 -2
- package/dist/marching-cubes.js +25 -2
- package/dist/math.d.ts +13 -17
- package/dist/math.js +133 -67
- package/dist/overlays/ContextMenu.svelte +65 -40
- package/dist/overlays/DraggablePane.svelte +211 -139
- package/dist/periodic-table/PeriodicTable.svelte +278 -145
- package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
- package/dist/periodic-table/PropertySelect.svelte +25 -7
- package/dist/periodic-table/TableInset.svelte +8 -3
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +446 -309
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramControls.svelte +102 -43
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +63 -40
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +71 -28
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +1 -1
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +158 -101
- package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
- package/dist/phase-diagram/build-diagram.js +9 -9
- package/dist/phase-diagram/colors.js +1 -3
- package/dist/phase-diagram/parse.js +10 -9
- package/dist/phase-diagram/svg-to-diagram.js +53 -49
- package/dist/phase-diagram/utils.d.ts +1 -0
- package/dist/phase-diagram/utils.js +80 -25
- package/dist/plot/AxisLabel.svelte +28 -3
- package/dist/plot/BarPlot.svelte +1182 -734
- package/dist/plot/BarPlot.svelte.d.ts +2 -2
- package/dist/plot/BarPlotControls.svelte +31 -5
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +479 -329
- package/dist/plot/ColorScaleSelect.svelte +27 -6
- package/dist/plot/ElementScatter.svelte +36 -15
- package/dist/plot/FillArea.svelte +152 -95
- package/dist/plot/Histogram.svelte +934 -571
- package/dist/plot/Histogram.svelte.d.ts +1 -1
- package/dist/plot/HistogramControls.svelte +53 -9
- package/dist/plot/HistogramControls.svelte.d.ts +1 -1
- package/dist/plot/InteractiveAxisLabel.svelte +34 -11
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
- package/dist/plot/Line.svelte +63 -28
- package/dist/plot/PlotControls.svelte +157 -114
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +174 -91
- package/dist/plot/PlotTooltip.svelte +45 -6
- package/dist/plot/PortalSelect.svelte +175 -147
- package/dist/plot/ReferenceLine.svelte +76 -22
- package/dist/plot/ReferenceLine3D.svelte +132 -107
- package/dist/plot/ReferencePlane.svelte +146 -121
- package/dist/plot/ScatterPlot.svelte +1681 -1091
- package/dist/plot/ScatterPlot.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3D.svelte +256 -131
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +113 -63
- package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
- package/dist/plot/ScatterPlot3DScene.svelte +608 -403
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +65 -25
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +98 -26
- package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
- package/dist/plot/SpacegroupBarPlot.svelte +142 -85
- package/dist/plot/Surface3D.svelte +159 -108
- package/dist/plot/ZeroLines.svelte +55 -3
- package/dist/plot/ZoomRect.svelte +4 -2
- package/dist/plot/axis-utils.js +1 -3
- package/dist/plot/data-cleaning.js +12 -28
- package/dist/plot/data-transform.js +2 -1
- package/dist/plot/fill-utils.js +2 -0
- package/dist/plot/layout.d.ts +4 -1
- package/dist/plot/layout.js +33 -14
- package/dist/plot/reference-line.d.ts +2 -2
- package/dist/plot/reference-line.js +7 -5
- package/dist/plot/scales.js +24 -36
- package/dist/plot/types.d.ts +11 -23
- package/dist/plot/types.js +6 -11
- package/dist/plot/utils/label-placement.d.ts +32 -15
- package/dist/plot/utils/label-placement.js +227 -66
- package/dist/plot/utils/series-visibility.js +2 -3
- package/dist/rdf/RdfPlot.svelte +143 -91
- package/dist/rdf/calc-rdf.js +4 -5
- package/dist/sanitize.d.ts +4 -0
- package/dist/sanitize.js +107 -0
- package/dist/settings.d.ts +18 -6
- package/dist/settings.js +46 -16
- package/dist/spectral/Bands.svelte +632 -453
- package/dist/spectral/BandsAndDos.svelte +90 -49
- package/dist/spectral/BrillouinBandsDos.svelte +151 -93
- package/dist/spectral/Dos.svelte +389 -258
- package/dist/spectral/helpers.js +55 -43
- package/dist/state.svelte.d.ts +1 -1
- package/dist/state.svelte.js +3 -2
- package/dist/structure/Arrow.svelte +59 -20
- package/dist/structure/AtomLegend.svelte +215 -134
- package/dist/structure/Bond.svelte +73 -47
- package/dist/structure/CanvasTooltip.svelte +10 -2
- package/dist/structure/CellSelect.svelte +72 -45
- package/dist/structure/Cylinder.svelte +33 -17
- package/dist/structure/Lattice.svelte +88 -33
- package/dist/structure/Structure.svelte +1063 -797
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureControls.svelte +349 -118
- package/dist/structure/StructureExportPane.svelte +124 -89
- package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +304 -237
- package/dist/structure/StructureScene.svelte +879 -443
- package/dist/structure/StructureScene.svelte.d.ts +15 -7
- package/dist/structure/atom-properties.js +8 -8
- package/dist/structure/bonding.js +6 -7
- package/dist/structure/export.js +14 -29
- package/dist/structure/ferrox-wasm.js +1 -1
- package/dist/structure/index.d.ts +13 -3
- package/dist/structure/index.js +83 -23
- package/dist/structure/measure.d.ts +2 -2
- package/dist/structure/measure.js +4 -44
- package/dist/structure/parse.js +113 -141
- package/dist/structure/partial-occupancy.js +7 -10
- package/dist/structure/pbc.d.ts +1 -0
- package/dist/structure/pbc.js +16 -6
- package/dist/structure/supercell.d.ts +2 -2
- package/dist/structure/supercell.js +12 -22
- package/dist/structure/validation.js +1 -2
- package/dist/symmetry/SymmetryStats.svelte +84 -41
- package/dist/symmetry/WyckoffTable.svelte +26 -6
- package/dist/symmetry/cell-transform.js +5 -3
- package/dist/symmetry/index.js +8 -7
- package/dist/symmetry/spacegroups.js +148 -148
- package/dist/table/HeatmapTable.svelte +790 -554
- package/dist/table/HeatmapTable.svelte.d.ts +1 -1
- package/dist/table/ToggleMenu.svelte +125 -92
- package/dist/table/index.js +2 -4
- package/dist/theme/ThemeControl.svelte +21 -12
- package/dist/time.js +4 -1
- package/dist/tooltip/TooltipContent.svelte +33 -8
- package/dist/trajectory/Trajectory.svelte +758 -558
- package/dist/trajectory/TrajectoryError.svelte +14 -3
- package/dist/trajectory/TrajectoryExportPane.svelte +137 -83
- package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
- package/dist/trajectory/extract.js +10 -26
- package/dist/trajectory/format-detect.js +5 -5
- package/dist/trajectory/frame-reader.d.ts +1 -1
- package/dist/trajectory/frame-reader.js +5 -12
- package/dist/trajectory/helpers.d.ts +0 -1
- package/dist/trajectory/helpers.js +2 -17
- package/dist/trajectory/index.js +14 -12
- package/dist/trajectory/parse/ase.js +5 -4
- package/dist/trajectory/parse/hdf5.js +26 -18
- package/dist/trajectory/parse/index.js +13 -18
- package/dist/trajectory/parse/lammps.js +17 -7
- package/dist/trajectory/parse/vasp.js +5 -2
- package/dist/trajectory/parse/xyz.js +8 -7
- package/dist/trajectory/plotting.js +13 -8
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -0
- package/dist/xrd/XrdPlot.svelte +337 -247
- package/dist/xrd/broadening.js +14 -9
- package/dist/xrd/calc-xrd.js +12 -18
- package/dist/xrd/parse.d.ts +1 -1
- package/dist/xrd/parse.js +17 -17
- package/package.json +99 -103
- package/readme.md +1 -1
- /package/dist/theme/{themes.js → themes.mjs} +0 -0
|
@@ -1,396 +1,475 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '../../Icon.svelte'
|
|
3
|
+
import { download } from '../../io/fetch'
|
|
4
|
+
import { setContext, tick } from 'svelte'
|
|
5
|
+
import { highlight_matches, tooltip } from 'svelte-multiselect/attachments'
|
|
6
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
7
|
+
import { SvelteSet } from 'svelte/reactivity'
|
|
8
|
+
import JsonNode from './JsonNode.svelte'
|
|
9
|
+
import type {
|
|
10
|
+
CopyEventPosition,
|
|
11
|
+
DiffEntry,
|
|
12
|
+
JsonTreeContext,
|
|
13
|
+
JsonTreeProps,
|
|
14
|
+
} from './types'
|
|
15
|
+
import { JSON_TREE_CONTEXT_KEY } from './types'
|
|
16
|
+
import {
|
|
17
|
+
build_ghost_map,
|
|
18
|
+
collect_all_paths,
|
|
19
|
+
compute_diff,
|
|
20
|
+
find_matching_paths,
|
|
21
|
+
format_preview,
|
|
22
|
+
get_ancestor_paths,
|
|
23
|
+
parse_path,
|
|
24
|
+
serialize_for_copy,
|
|
25
|
+
} from './utils'
|
|
26
|
+
|
|
27
|
+
// Constant set for arrow key detection (avoid allocating on every keydown)
|
|
28
|
+
const ARROW_KEYS = new Set([`ArrowDown`, `ArrowUp`, `ArrowLeft`, `ArrowRight`])
|
|
29
|
+
// Shared empty set for when there's no search query (avoid allocation on every derivation)
|
|
30
|
+
const EMPTY_MATCHES = new SvelteSet<string>()
|
|
31
|
+
|
|
32
|
+
let {
|
|
33
|
+
value,
|
|
34
|
+
root_label,
|
|
35
|
+
default_fold_level = 2,
|
|
36
|
+
auto_fold_arrays = 10,
|
|
37
|
+
auto_fold_objects = 20,
|
|
38
|
+
collapsed_paths = $bindable(new SvelteSet<string>()),
|
|
39
|
+
show_header = true,
|
|
40
|
+
show_data_types = $bindable(false),
|
|
41
|
+
show_array_indices = $bindable(true),
|
|
42
|
+
sort_keys = false,
|
|
43
|
+
max_string_length = 200,
|
|
44
|
+
highlight_changes = true,
|
|
45
|
+
onselect,
|
|
46
|
+
oncopy,
|
|
47
|
+
download_filename,
|
|
48
|
+
compare_value,
|
|
49
|
+
editable = false,
|
|
50
|
+
onchange,
|
|
51
|
+
...rest
|
|
52
|
+
}: JsonTreeProps & Omit<HTMLAttributes<HTMLDivElement>, `onselect` | `onchange`> =
|
|
53
|
+
$props()
|
|
54
|
+
|
|
55
|
+
// Internal state
|
|
56
|
+
let search_query = $state(``)
|
|
57
|
+
let search_input_value = $state(``)
|
|
58
|
+
let focused_path = $state<string | null>(null)
|
|
59
|
+
// Use Set for O(1) lookup/add/delete instead of O(n) array operations
|
|
60
|
+
let registered_paths_set = $state(new Set<string>())
|
|
61
|
+
let registered_paths_list = $state<string[]>([]) // ordered list for keyboard nav
|
|
62
|
+
let copy_feedback_path = $state<string | null>(null)
|
|
63
|
+
let copy_feedback_error = $state(false)
|
|
64
|
+
let copy_feedback_timeout: ReturnType<typeof setTimeout> | undefined
|
|
65
|
+
// Track paths explicitly expanded (overrides auto-fold thresholds)
|
|
66
|
+
let force_expanded = $state(new SvelteSet<string>())
|
|
67
|
+
// Current match index for navigation (0-based, -1 means no selection)
|
|
68
|
+
let current_match_index = $state(-1)
|
|
69
|
+
// Reference to the content container for scrolling
|
|
70
|
+
let content_element: HTMLDivElement | undefined = $state()
|
|
71
|
+
// Reference to the search input for focus management
|
|
72
|
+
let search_input_element: HTMLInputElement | undefined = $state()
|
|
73
|
+
// Context menu state (null when closed)
|
|
74
|
+
let context_menu_state = $state<
|
|
75
|
+
{
|
|
76
|
+
x: number
|
|
77
|
+
y: number
|
|
78
|
+
path: string
|
|
79
|
+
value: unknown
|
|
80
|
+
expandable: boolean
|
|
81
|
+
is_collapsed: boolean
|
|
82
|
+
} | null
|
|
83
|
+
>(null)
|
|
84
|
+
// Pinned paths for quick reference
|
|
85
|
+
let pinned_paths = $state(new SvelteSet<string>())
|
|
86
|
+
// Selection state for bulk operations
|
|
87
|
+
let selected_paths = $state(new SvelteSet<string>())
|
|
88
|
+
let last_selected_path = $state<string | null>(null)
|
|
89
|
+
// Copy feedback positioning (null = use default corner position)
|
|
90
|
+
let copy_feedback_pos = $state<{ x: number; y: number } | null>(null)
|
|
91
|
+
|
|
92
|
+
// Clear stale path-based state when value changes
|
|
93
|
+
let prev_value_ref: unknown
|
|
94
|
+
$effect.pre(() => {
|
|
44
95
|
if (prev_value_ref !== undefined && value !== prev_value_ref) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
96
|
+
force_expanded.clear()
|
|
97
|
+
pinned_paths = new SvelteSet()
|
|
98
|
+
selected_paths = new SvelteSet()
|
|
99
|
+
last_selected_path = null
|
|
49
100
|
}
|
|
50
|
-
prev_value_ref = value
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
101
|
+
prev_value_ref = value
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Debounce search input
|
|
105
|
+
let search_debounce_timeout: ReturnType<typeof setTimeout> | undefined
|
|
106
|
+
|
|
107
|
+
function handle_search_input(event: Event) {
|
|
108
|
+
const input = event.target as HTMLInputElement
|
|
109
|
+
search_input_value = input.value
|
|
110
|
+
|
|
111
|
+
if (search_debounce_timeout) clearTimeout(search_debounce_timeout)
|
|
59
112
|
search_debounce_timeout = setTimeout(() => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}, 150)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
113
|
+
search_query = search_input_value
|
|
114
|
+
// queueMicrotask lets derived search_matches update before expand_to_matches runs
|
|
115
|
+
queueMicrotask(() => expand_to_matches())
|
|
116
|
+
}, 150)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Root path used everywhere - avoids repeating `root_label ?? ''`
|
|
120
|
+
let root_path = $derived(root_label ?? ``)
|
|
121
|
+
|
|
122
|
+
// Compute search matches
|
|
123
|
+
let search_matches = $derived.by(() => {
|
|
124
|
+
if (!search_query) return EMPTY_MATCHES
|
|
125
|
+
return new SvelteSet(find_matching_paths(value, search_query, root_path))
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Sorted matches list for navigation (maintains DOM order via registered_paths_list)
|
|
129
|
+
let sorted_matches = $derived.by(() => {
|
|
130
|
+
if (search_matches.size === 0) return []
|
|
77
131
|
// Filter registered paths to only include matches, preserving DOM order
|
|
78
|
-
return registered_paths_list.filter((path) => search_matches.has(path))
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return sorted_matches[current_match_index] ?? null
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
|
|
132
|
+
return registered_paths_list.filter((path) => search_matches.has(path))
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Current match path (the one being navigated to)
|
|
136
|
+
let current_match_path = $derived.by(() => {
|
|
137
|
+
if (sorted_matches.length === 0 || current_match_index < 0) return null
|
|
138
|
+
return sorted_matches[current_match_index] ?? null
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Auto-expand ancestors of search matches when search query changes
|
|
142
|
+
// This is called manually from the search input handler to avoid reactivity issues
|
|
143
|
+
async function expand_to_matches(): Promise<void> {
|
|
89
144
|
if (search_matches.size === 0) {
|
|
90
|
-
|
|
91
|
-
|
|
145
|
+
current_match_index = -1
|
|
146
|
+
return
|
|
92
147
|
}
|
|
93
|
-
|
|
94
|
-
const paths_to_expand = new Set();
|
|
148
|
+
const paths_to_expand = new Set<string>()
|
|
95
149
|
for (const match of search_matches) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
150
|
+
for (const ancestor of get_ancestor_paths(match)) {
|
|
151
|
+
paths_to_expand.add(ancestor)
|
|
152
|
+
}
|
|
99
153
|
}
|
|
100
|
-
let collapsed_changed = false
|
|
101
|
-
let force_expanded_changed = false
|
|
154
|
+
let collapsed_changed = false
|
|
155
|
+
let force_expanded_changed = false
|
|
102
156
|
for (const path_to_expand of paths_to_expand) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
157
|
+
if (collapsed_paths.has(path_to_expand)) {
|
|
158
|
+
collapsed_paths.delete(path_to_expand)
|
|
159
|
+
collapsed_changed = true
|
|
160
|
+
}
|
|
161
|
+
// Also add to force_expanded to override auto-fold thresholds (depth/size)
|
|
162
|
+
if (!force_expanded.has(path_to_expand)) {
|
|
163
|
+
force_expanded.add(path_to_expand)
|
|
164
|
+
force_expanded_changed = true
|
|
165
|
+
}
|
|
112
166
|
}
|
|
113
167
|
if (collapsed_changed) {
|
|
114
|
-
|
|
168
|
+
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
115
169
|
}
|
|
116
170
|
if (force_expanded_changed) {
|
|
117
|
-
|
|
171
|
+
force_expanded = new SvelteSet(force_expanded)
|
|
118
172
|
}
|
|
119
173
|
// Wait for DOM to update before scrolling to match
|
|
120
|
-
await tick()
|
|
174
|
+
await tick()
|
|
121
175
|
if (sorted_matches.length > 0) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
176
|
+
// Reset to first match if no selection or index is out of bounds
|
|
177
|
+
if (current_match_index < 0 || current_match_index >= sorted_matches.length) {
|
|
178
|
+
current_match_index = 0
|
|
179
|
+
}
|
|
180
|
+
scroll_to_current_match()
|
|
127
181
|
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Scroll the current match into view
|
|
185
|
+
function scroll_to_current_match(): void {
|
|
186
|
+
if (!current_match_path || !content_element) return
|
|
133
187
|
// Find the element with the matching path using data attribute
|
|
134
|
-
const match_element = content_element.querySelector(
|
|
188
|
+
const match_element = content_element.querySelector(
|
|
189
|
+
`[data-path="${CSS.escape(current_match_path)}"]`,
|
|
190
|
+
)
|
|
135
191
|
if (match_element) {
|
|
136
|
-
|
|
192
|
+
match_element.scrollIntoView({ behavior: `smooth`, block: `center` })
|
|
137
193
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
current_match_index = (current_match_index + 1) % sorted_matches.length
|
|
144
|
-
scroll_to_current_match()
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Navigate to next match
|
|
197
|
+
function go_to_next_match(): void {
|
|
198
|
+
if (sorted_matches.length === 0) return
|
|
199
|
+
current_match_index = (current_match_index + 1) % sorted_matches.length
|
|
200
|
+
scroll_to_current_match()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Navigate to previous match
|
|
204
|
+
function go_to_prev_match(): void {
|
|
205
|
+
if (sorted_matches.length === 0) return
|
|
150
206
|
current_match_index = (current_match_index - 1 + sorted_matches.length) %
|
|
151
|
-
|
|
152
|
-
scroll_to_current_match()
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
207
|
+
sorted_matches.length
|
|
208
|
+
scroll_to_current_match()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Previous values map for change detection
|
|
212
|
+
const previous_values = new Map<string, unknown>()
|
|
213
|
+
|
|
214
|
+
// Toggle collapse - tracks force_expanded to override auto-fold thresholds
|
|
215
|
+
function toggle_collapse(path: string, is_currently_collapsed: boolean): void {
|
|
158
216
|
if (is_currently_collapsed) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
collapsed_paths.add(path);
|
|
217
|
+
collapsed_paths.delete(path)
|
|
218
|
+
force_expanded.add(path)
|
|
219
|
+
} else {
|
|
220
|
+
force_expanded.delete(path)
|
|
221
|
+
collapsed_paths.add(path)
|
|
165
222
|
}
|
|
166
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
167
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
223
|
+
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
224
|
+
force_expanded = new SvelteSet(force_expanded)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Toggle collapse recursively for all descendants
|
|
228
|
+
function toggle_collapse_recursive(path: string, collapse: boolean): void {
|
|
171
229
|
for (const desc of get_descendants(path)) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
230
|
+
if (collapse) {
|
|
231
|
+
force_expanded.delete(desc)
|
|
232
|
+
collapsed_paths.add(desc)
|
|
233
|
+
} else {
|
|
234
|
+
collapsed_paths.delete(desc)
|
|
235
|
+
force_expanded.add(desc)
|
|
236
|
+
}
|
|
180
237
|
}
|
|
181
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
182
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
238
|
+
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
239
|
+
force_expanded = new SvelteSet(force_expanded)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Get all descendant paths of a given path (including the path itself)
|
|
243
|
+
function get_descendants(target_path: string): string[] {
|
|
244
|
+
const all_paths = collect_all_paths(value, root_path)
|
|
245
|
+
const descendants = target_path === `` ? all_paths : all_paths.filter(
|
|
246
|
+
(entry) =>
|
|
247
|
+
entry === target_path || entry.startsWith(target_path + `.`) ||
|
|
248
|
+
entry.startsWith(target_path + `[`),
|
|
249
|
+
)
|
|
189
250
|
return descendants.includes(target_path)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
251
|
+
? descendants
|
|
252
|
+
: [target_path, ...descendants]
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function expand_all(): void {
|
|
256
|
+
force_expanded = new SvelteSet(collect_all_paths(value, root_path))
|
|
257
|
+
collapsed_paths = new SvelteSet()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function collapse_all(): void {
|
|
261
|
+
force_expanded = new SvelteSet()
|
|
262
|
+
collapsed_paths = new SvelteSet(collect_all_paths(value, root_path))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function collapse_to_level(level: number): void {
|
|
266
|
+
const all_paths = collect_all_paths(value, root_path)
|
|
267
|
+
const new_collapsed = new SvelteSet<string>()
|
|
268
|
+
const new_expanded = new SvelteSet<string>()
|
|
269
|
+
|
|
205
270
|
for (const path of all_paths) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
271
|
+
const segments = parse_path(path)
|
|
272
|
+
const depth = root_label && segments[0] === root_label
|
|
273
|
+
? segments.length - 1
|
|
274
|
+
: segments.length
|
|
275
|
+
;(depth >= level ? new_collapsed : new_expanded).add(path)
|
|
211
276
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
277
|
+
|
|
278
|
+
collapsed_paths = new_collapsed
|
|
279
|
+
force_expanded = new_expanded
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function set_focused(path: string | null): void {
|
|
283
|
+
focused_path = path
|
|
284
|
+
if (path) onselect?.(path, get_value_at_path(path))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Shared clipboard copy with feedback (event used for inline positioning)
|
|
288
|
+
async function copy_to_clipboard(
|
|
289
|
+
path: string,
|
|
290
|
+
text: string,
|
|
291
|
+
event?: CopyEventPosition,
|
|
292
|
+
): Promise<void> {
|
|
293
|
+
copy_feedback_pos = event ? { x: event.clientX, y: event.clientY } : null
|
|
223
294
|
try {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
295
|
+
await navigator.clipboard.writeText(text)
|
|
296
|
+
copy_feedback_error = false
|
|
297
|
+
oncopy?.(path, text)
|
|
298
|
+
} catch { // Clipboard API failed - still show feedback but as error
|
|
299
|
+
copy_feedback_error = true
|
|
227
300
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
copy_feedback_path = path; // Show feedback regardless of success/failure
|
|
232
|
-
if (copy_feedback_timeout)
|
|
233
|
-
clearTimeout(copy_feedback_timeout);
|
|
301
|
+
copy_feedback_path = path // Show feedback regardless of success/failure
|
|
302
|
+
if (copy_feedback_timeout) clearTimeout(copy_feedback_timeout)
|
|
234
303
|
copy_feedback_timeout = setTimeout(() => {
|
|
235
|
-
|
|
236
|
-
}, 1000)
|
|
237
|
-
}
|
|
238
|
-
|
|
304
|
+
copy_feedback_path = null
|
|
305
|
+
}, 1000)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function register_path(path: string): void {
|
|
239
309
|
if (!registered_paths_set.has(path)) {
|
|
240
|
-
|
|
241
|
-
|
|
310
|
+
registered_paths_set.add(path)
|
|
311
|
+
registered_paths_list = [...registered_paths_list, path]
|
|
242
312
|
}
|
|
243
|
-
}
|
|
244
|
-
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function unregister_path(path: string): void {
|
|
245
316
|
if (registered_paths_set.has(path)) {
|
|
246
|
-
|
|
247
|
-
|
|
317
|
+
registered_paths_set.delete(path)
|
|
318
|
+
registered_paths_list = registered_paths_list.filter((entry) => entry !== path)
|
|
248
319
|
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Helper to get value at a path (for onselect callback)
|
|
323
|
+
function get_value_at_path(path: string): unknown {
|
|
324
|
+
if (!path || path === root_label) return value
|
|
325
|
+
|
|
326
|
+
const segments = parse_path(path)
|
|
327
|
+
let current: unknown = value
|
|
328
|
+
const start_idx = segments[0] === root_label ? 1 : 0
|
|
329
|
+
|
|
257
330
|
for (let idx = start_idx; idx < segments.length; idx++) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
else {
|
|
272
|
-
return undefined;
|
|
273
|
-
}
|
|
331
|
+
const segment = segments[idx]
|
|
332
|
+
if (current == null) return undefined
|
|
333
|
+
|
|
334
|
+
// Map/Set use numeric indexing
|
|
335
|
+
if (current instanceof Map || current instanceof Set) {
|
|
336
|
+
const index = typeof segment === `number` ? segment : Number(segment)
|
|
337
|
+
if (Number.isNaN(index)) return undefined
|
|
338
|
+
current = Array.from(current.values())[index]
|
|
339
|
+
} else if (typeof current === `object`) {
|
|
340
|
+
current = (current as Record<string | number, unknown>)[segment]
|
|
341
|
+
} else {
|
|
342
|
+
return undefined
|
|
343
|
+
}
|
|
274
344
|
}
|
|
275
|
-
return current
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return compute_diff(compare_value, value, root_path)
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
345
|
+
return current
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Compute diff map when compare_value is provided
|
|
349
|
+
let diff_map = $derived.by((): Map<string, DiffEntry> | null => {
|
|
350
|
+
if (compare_value === undefined) return null
|
|
351
|
+
return compute_diff(compare_value, value, root_path)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
// Pre-compute ghost children map for O(1) lookup per node
|
|
355
|
+
let ghost_map = $derived(diff_map ? build_ghost_map(diff_map) : new Map())
|
|
356
|
+
|
|
357
|
+
// Collapse all descendants but keep the given node expanded (single batch)
|
|
358
|
+
function collapse_children_only(target_path: string): void {
|
|
287
359
|
for (const desc of get_descendants(target_path)) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
360
|
+
if (desc === target_path) {
|
|
361
|
+
collapsed_paths.delete(desc)
|
|
362
|
+
force_expanded.add(desc)
|
|
363
|
+
} else {
|
|
364
|
+
force_expanded.delete(desc)
|
|
365
|
+
collapsed_paths.add(desc)
|
|
366
|
+
}
|
|
296
367
|
}
|
|
297
|
-
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
298
|
-
force_expanded = new SvelteSet(force_expanded)
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
368
|
+
collapsed_paths = new SvelteSet(collapsed_paths)
|
|
369
|
+
force_expanded = new SvelteSet(force_expanded)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Context menu handlers
|
|
373
|
+
function show_context_menu(
|
|
374
|
+
event: MouseEvent,
|
|
375
|
+
ctx_path: string,
|
|
376
|
+
ctx_value: unknown,
|
|
377
|
+
expandable: boolean,
|
|
378
|
+
is_collapsed: boolean,
|
|
379
|
+
): void {
|
|
380
|
+
event.preventDefault()
|
|
303
381
|
context_menu_state = {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
382
|
+
x: event.clientX,
|
|
383
|
+
y: event.clientY,
|
|
384
|
+
path: ctx_path,
|
|
385
|
+
value: ctx_value,
|
|
386
|
+
expandable,
|
|
387
|
+
is_collapsed,
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function close_context_menu(): void {
|
|
392
|
+
context_menu_state = null
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Run action with current context menu state, then close
|
|
396
|
+
function ctx_menu_action(
|
|
397
|
+
action: (state: NonNullable<typeof context_menu_state>) => void,
|
|
398
|
+
): void {
|
|
399
|
+
if (context_menu_state) action(context_menu_state)
|
|
400
|
+
close_context_menu()
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Pin/unpin a path for quick reference
|
|
404
|
+
function toggle_pin(pin_path: string): void {
|
|
405
|
+
if (pinned_paths.has(pin_path)) pinned_paths.delete(pin_path)
|
|
406
|
+
else pinned_paths.add(pin_path)
|
|
407
|
+
pinned_paths = new SvelteSet(pinned_paths)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Toggle selection of a path (with shift for range select)
|
|
411
|
+
function toggle_select(select_path: string, shift: boolean): void {
|
|
331
412
|
if (shift && last_selected_path) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
selected_paths = new SvelteSet(selected_paths);
|
|
413
|
+
const start_idx = registered_paths_list.indexOf(last_selected_path)
|
|
414
|
+
const end_idx = registered_paths_list.indexOf(select_path)
|
|
415
|
+
if (start_idx !== -1 && end_idx !== -1) {
|
|
416
|
+
const [from, to] = start_idx < end_idx
|
|
417
|
+
? [start_idx, end_idx]
|
|
418
|
+
: [end_idx, start_idx]
|
|
419
|
+
for (let idx = from; idx <= to; idx++) {
|
|
420
|
+
selected_paths.add(registered_paths_list[idx])
|
|
342
421
|
}
|
|
422
|
+
selected_paths = new SvelteSet(selected_paths)
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
if (selected_paths.has(select_path)) selected_paths.delete(select_path)
|
|
426
|
+
else selected_paths.add(select_path)
|
|
427
|
+
selected_paths = new SvelteSet(selected_paths)
|
|
343
428
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
last_selected_path = select_path;
|
|
352
|
-
}
|
|
353
|
-
// Copy all selected node values to clipboard
|
|
354
|
-
function copy_selected() {
|
|
355
|
-
if (selected_paths.size === 0)
|
|
356
|
-
return;
|
|
429
|
+
last_selected_path = select_path
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Copy all selected node values to clipboard
|
|
433
|
+
function copy_selected(): void {
|
|
434
|
+
if (selected_paths.size === 0) return
|
|
357
435
|
const text = [...selected_paths]
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
copy_to_clipboard(`[selection]`, text)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
436
|
+
.map((sel_path) => serialize_for_copy(get_value_at_path(sel_path)))
|
|
437
|
+
.join(`\n`)
|
|
438
|
+
copy_to_clipboard(`[selection]`, text)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Create context
|
|
442
|
+
const context: JsonTreeContext = {
|
|
364
443
|
get settings() {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
444
|
+
return {
|
|
445
|
+
default_fold_level,
|
|
446
|
+
auto_fold_arrays,
|
|
447
|
+
auto_fold_objects,
|
|
448
|
+
show_data_types,
|
|
449
|
+
show_array_indices,
|
|
450
|
+
sort_keys,
|
|
451
|
+
max_string_length,
|
|
452
|
+
highlight_changes,
|
|
453
|
+
editable,
|
|
454
|
+
}
|
|
376
455
|
},
|
|
377
456
|
get collapsed() {
|
|
378
|
-
|
|
457
|
+
return collapsed_paths
|
|
379
458
|
},
|
|
380
459
|
get force_expanded() {
|
|
381
|
-
|
|
460
|
+
return force_expanded
|
|
382
461
|
},
|
|
383
462
|
get search_query() {
|
|
384
|
-
|
|
463
|
+
return search_query
|
|
385
464
|
},
|
|
386
465
|
get search_matches() {
|
|
387
|
-
|
|
466
|
+
return search_matches
|
|
388
467
|
},
|
|
389
468
|
get current_match_path() {
|
|
390
|
-
|
|
469
|
+
return current_match_path
|
|
391
470
|
},
|
|
392
471
|
get focused_path() {
|
|
393
|
-
|
|
472
|
+
return focused_path
|
|
394
473
|
},
|
|
395
474
|
previous_values,
|
|
396
475
|
toggle_collapse,
|
|
@@ -399,112 +478,119 @@ const context = {
|
|
|
399
478
|
collapse_all,
|
|
400
479
|
collapse_to_level,
|
|
401
480
|
set_focused,
|
|
402
|
-
copy_value: (val_path, val, event) =>
|
|
403
|
-
|
|
481
|
+
copy_value: (val_path: string, val: unknown, event?: CopyEventPosition) =>
|
|
482
|
+
copy_to_clipboard(val_path, serialize_for_copy(val), event),
|
|
483
|
+
copy_path: (cp_path: string, event?: CopyEventPosition) =>
|
|
484
|
+
copy_to_clipboard(cp_path, cp_path, event),
|
|
404
485
|
register_path,
|
|
405
486
|
unregister_path,
|
|
406
487
|
show_context_menu,
|
|
407
488
|
get pinned_paths() {
|
|
408
|
-
|
|
489
|
+
return pinned_paths
|
|
409
490
|
},
|
|
410
491
|
toggle_pin,
|
|
411
492
|
get selected_paths() {
|
|
412
|
-
|
|
493
|
+
return selected_paths
|
|
413
494
|
},
|
|
414
495
|
toggle_select,
|
|
415
496
|
copy_selected,
|
|
416
497
|
get diff_map() {
|
|
417
|
-
|
|
498
|
+
return diff_map
|
|
418
499
|
},
|
|
419
500
|
get ghost_map() {
|
|
420
|
-
|
|
501
|
+
return ghost_map
|
|
421
502
|
},
|
|
422
503
|
collapse_children_only,
|
|
423
504
|
get onchange() {
|
|
424
|
-
|
|
505
|
+
return onchange
|
|
425
506
|
},
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
setContext(JSON_TREE_CONTEXT_KEY, context)
|
|
510
|
+
|
|
511
|
+
// Keyboard navigation at tree level
|
|
512
|
+
function handle_tree_keydown(event: KeyboardEvent) {
|
|
430
513
|
// Escape closes context menu first, then clears selection
|
|
431
514
|
if (event.key === `Escape`) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
515
|
+
if (context_menu_state) {
|
|
516
|
+
close_context_menu()
|
|
517
|
+
return
|
|
518
|
+
}
|
|
519
|
+
if (selected_paths.size > 0) {
|
|
520
|
+
selected_paths = new SvelteSet()
|
|
521
|
+
return
|
|
522
|
+
}
|
|
440
523
|
}
|
|
441
524
|
// Ctrl/Cmd+C with selection copies all selected
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
525
|
+
if (
|
|
526
|
+
(event.key === `c` || event.key === `C`) &&
|
|
527
|
+
(event.ctrlKey || event.metaKey) &&
|
|
528
|
+
selected_paths.size > 0
|
|
529
|
+
) {
|
|
530
|
+
event.preventDefault()
|
|
531
|
+
copy_selected()
|
|
532
|
+
return
|
|
448
533
|
}
|
|
534
|
+
|
|
449
535
|
if (!focused_path) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
536
|
+
// Focus first node on any arrow key
|
|
537
|
+
if (ARROW_KEYS.has(event.key)) {
|
|
538
|
+
event.preventDefault()
|
|
539
|
+
if (registered_paths_list.length > 0) {
|
|
540
|
+
set_focused(registered_paths_list[0])
|
|
456
541
|
}
|
|
457
|
-
|
|
542
|
+
}
|
|
543
|
+
return
|
|
458
544
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
545
|
+
|
|
546
|
+
const current_index = registered_paths_list.indexOf(focused_path)
|
|
547
|
+
if (current_index === -1) return
|
|
548
|
+
|
|
462
549
|
if (event.key === `ArrowDown`) {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
set_focused(registered_paths_list[prev_index]);
|
|
550
|
+
event.preventDefault()
|
|
551
|
+
const next_index = Math.min(current_index + 1, registered_paths_list.length - 1)
|
|
552
|
+
set_focused(registered_paths_list[next_index])
|
|
553
|
+
} else if (event.key === `ArrowUp`) {
|
|
554
|
+
event.preventDefault()
|
|
555
|
+
const prev_index = Math.max(current_index - 1, 0)
|
|
556
|
+
set_focused(registered_paths_list[prev_index])
|
|
471
557
|
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
search_input_value =
|
|
478
|
-
search_query =
|
|
479
|
-
current_match_index = -1
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Clear search
|
|
561
|
+
function clear_search() {
|
|
562
|
+
if (search_debounce_timeout) clearTimeout(search_debounce_timeout)
|
|
563
|
+
search_input_value = ``
|
|
564
|
+
search_query = ``
|
|
565
|
+
current_match_index = -1
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Copy entire JSON to clipboard
|
|
569
|
+
function copy_all(): void {
|
|
570
|
+
copy_to_clipboard(`[root]`, serialize_for_copy(value))
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Download JSON as file
|
|
574
|
+
function download_json(): void {
|
|
575
|
+
const json_str = serialize_for_copy(value)
|
|
576
|
+
const date_str = new Date().toISOString().slice(0, 10)
|
|
577
|
+
const filename = download_filename ?? `data-${date_str}.json`
|
|
578
|
+
download(json_str, filename, `application/json`)
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Handle keyboard events on search input
|
|
582
|
+
function handle_search_keydown(event: KeyboardEvent) {
|
|
494
583
|
if (event.key === `Escape`) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
go_to_prev_match();
|
|
504
|
-
else
|
|
505
|
-
go_to_next_match();
|
|
584
|
+
event.preventDefault()
|
|
585
|
+
clear_search()
|
|
586
|
+
search_input_element?.blur()
|
|
587
|
+
} else if (event.key === `Enter` || event.key === `F3`) {
|
|
588
|
+
event.stopPropagation() // Prevent bubbling to tree-level F3 handler
|
|
589
|
+
event.preventDefault()
|
|
590
|
+
if (event.shiftKey) go_to_prev_match()
|
|
591
|
+
else go_to_next_match()
|
|
506
592
|
}
|
|
507
|
-
}
|
|
593
|
+
}
|
|
508
594
|
</script>
|
|
509
595
|
|
|
510
596
|
<div
|