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