matterviz 0.3.1 → 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 (358) hide show
  1. package/dist/EmptyState.svelte +10 -2
  2. package/dist/FilePicker.svelte +154 -96
  3. package/dist/Icon.svelte +20 -14
  4. package/dist/MillerIndexInput.svelte +27 -21
  5. package/dist/api/optimade.js +6 -6
  6. package/dist/app.css +216 -178
  7. package/dist/brillouin/BrillouinZone.svelte +299 -198
  8. package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
  9. package/dist/brillouin/BrillouinZoneControls.svelte +32 -5
  10. package/dist/brillouin/BrillouinZoneExportPane.svelte +74 -55
  11. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +1 -1
  12. package/dist/brillouin/BrillouinZoneInfoPane.svelte +99 -68
  13. package/dist/brillouin/BrillouinZoneScene.svelte +277 -165
  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 +327 -0
  18. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  19. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +847 -0
  20. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  21. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3194 -0
  22. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  23. package/dist/chempot-diagram/ChemPotScene3D.svelte +11 -0
  24. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  25. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  26. package/dist/chempot-diagram/async-compute.svelte.js +77 -0
  27. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  28. package/dist/chempot-diagram/chempot-worker.js +11 -0
  29. package/dist/chempot-diagram/color.d.ts +10 -0
  30. package/dist/chempot-diagram/color.js +32 -0
  31. package/dist/chempot-diagram/compute.d.ts +48 -0
  32. package/dist/chempot-diagram/compute.js +812 -0
  33. package/dist/chempot-diagram/index.d.ts +6 -0
  34. package/dist/chempot-diagram/index.js +6 -0
  35. package/dist/chempot-diagram/pointer.d.ts +16 -0
  36. package/dist/chempot-diagram/pointer.js +40 -0
  37. package/dist/chempot-diagram/temperature.d.ts +15 -0
  38. package/dist/chempot-diagram/temperature.js +36 -0
  39. package/dist/chempot-diagram/types.d.ts +86 -0
  40. package/dist/chempot-diagram/types.js +28 -0
  41. package/dist/colors/index.d.ts +3 -1
  42. package/dist/colors/index.js +9 -3
  43. package/dist/composition/BarChart.svelte +141 -77
  44. package/dist/composition/BubbleChart.svelte +107 -52
  45. package/dist/composition/Composition.svelte +100 -79
  46. package/dist/composition/Formula.svelte +108 -62
  47. package/dist/composition/FormulaFilter.svelte +973 -353
  48. package/dist/composition/FormulaFilter.svelte.d.ts +35 -1
  49. package/dist/composition/PieChart.svelte +199 -99
  50. package/dist/composition/PieChart.svelte.d.ts +1 -1
  51. package/dist/composition/format.d.ts +5 -0
  52. package/dist/composition/format.js +20 -3
  53. package/dist/composition/parse.js +14 -9
  54. package/dist/convex-hull/ConvexHull.svelte +93 -38
  55. package/dist/convex-hull/ConvexHull2D.svelte +551 -393
  56. package/dist/convex-hull/ConvexHull3D.svelte +1303 -825
  57. package/dist/convex-hull/ConvexHull4D.svelte +1012 -686
  58. package/dist/convex-hull/ConvexHullControls.svelte +115 -28
  59. package/dist/convex-hull/ConvexHullInfoPane.svelte +29 -3
  60. package/dist/convex-hull/ConvexHullStats.svelte +821 -249
  61. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
  62. package/dist/convex-hull/ConvexHullTooltip.svelte +41 -16
  63. package/dist/convex-hull/GasPressureControls.svelte +104 -61
  64. package/dist/convex-hull/StructurePopup.svelte +25 -4
  65. package/dist/convex-hull/TemperatureSlider.svelte +45 -25
  66. package/dist/convex-hull/barycentric-coords.js +13 -7
  67. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  68. package/dist/convex-hull/demo-temperature.js +40 -0
  69. package/dist/convex-hull/gas-thermodynamics.js +17 -12
  70. package/dist/convex-hull/helpers.d.ts +10 -1
  71. package/dist/convex-hull/helpers.js +79 -38
  72. package/dist/convex-hull/index.d.ts +1 -0
  73. package/dist/convex-hull/index.js +1 -0
  74. package/dist/convex-hull/thermodynamics.d.ts +8 -21
  75. package/dist/convex-hull/thermodynamics.js +163 -69
  76. package/dist/convex-hull/types.d.ts +12 -12
  77. package/dist/convex-hull/types.js +0 -12
  78. package/dist/coordination/CoordinationBarPlot.svelte +232 -176
  79. package/dist/element/BohrAtom.svelte +56 -13
  80. package/dist/element/ElementHeading.svelte +7 -2
  81. package/dist/element/ElementPhoto.svelte +15 -9
  82. package/dist/element/ElementStats.svelte +10 -4
  83. package/dist/element/ElementTile.svelte +137 -73
  84. package/dist/element/Nucleus.svelte +39 -11
  85. package/dist/element/data.js +2 -14
  86. package/dist/element/data.json.gz +0 -0
  87. package/dist/element/types.d.ts +1 -0
  88. package/dist/feedback/ClickFeedback.svelte +16 -5
  89. package/dist/feedback/DragOverlay.svelte +10 -2
  90. package/dist/feedback/Spinner.svelte +4 -2
  91. package/dist/feedback/StatusMessage.svelte +8 -2
  92. package/dist/fermi-surface/FermiSlice.svelte +118 -88
  93. package/dist/fermi-surface/FermiSurface.svelte +336 -239
  94. package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
  95. package/dist/fermi-surface/FermiSurfaceControls.svelte +113 -46
  96. package/dist/fermi-surface/FermiSurfaceScene.svelte +536 -343
  97. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
  98. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +14 -5
  99. package/dist/fermi-surface/compute.js +16 -20
  100. package/dist/fermi-surface/parse.js +37 -33
  101. package/dist/fermi-surface/symmetry.js +2 -7
  102. package/dist/fermi-surface/types.d.ts +3 -5
  103. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1527 -0
  104. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  105. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  106. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  107. package/dist/heatmap-matrix/index.d.ts +53 -0
  108. package/dist/heatmap-matrix/index.js +100 -0
  109. package/dist/heatmap-matrix/shared.d.ts +2 -0
  110. package/dist/heatmap-matrix/shared.js +4 -0
  111. package/dist/icons.d.ts +111 -0
  112. package/dist/icons.js +158 -0
  113. package/dist/index.d.ts +5 -2
  114. package/dist/index.js +5 -2
  115. package/dist/io/decompress.js +1 -1
  116. package/dist/io/export.d.ts +3 -0
  117. package/dist/io/export.js +138 -140
  118. package/dist/io/file-drop.d.ts +7 -0
  119. package/dist/io/file-drop.js +43 -0
  120. package/dist/io/index.d.ts +2 -2
  121. package/dist/io/index.js +2 -112
  122. package/dist/io/is-binary.js +2 -3
  123. package/dist/io/types.d.ts +1 -0
  124. package/dist/io/url-drop.d.ts +2 -0
  125. package/dist/io/url-drop.js +117 -0
  126. package/dist/isosurface/Isosurface.svelte +220 -110
  127. package/dist/isosurface/IsosurfaceControls.svelte +65 -28
  128. package/dist/isosurface/parse.js +104 -56
  129. package/dist/isosurface/slice.d.ts +2 -1
  130. package/dist/isosurface/slice.js +8 -13
  131. package/dist/isosurface/types.d.ts +14 -1
  132. package/dist/isosurface/types.js +152 -5
  133. package/dist/labels.d.ts +2 -1
  134. package/dist/labels.js +12 -8
  135. package/dist/layout/FullscreenToggle.svelte +11 -2
  136. package/dist/layout/InfoCard.svelte +38 -6
  137. package/dist/layout/InfoTag.svelte +125 -94
  138. package/dist/layout/PropertyFilter.svelte +82 -37
  139. package/dist/layout/SettingsSection.svelte +85 -55
  140. package/dist/layout/SubpageGrid.svelte +82 -0
  141. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  142. package/dist/layout/index.d.ts +1 -0
  143. package/dist/layout/index.js +1 -0
  144. package/dist/layout/json-tree/JsonNode.svelte +266 -223
  145. package/dist/layout/json-tree/JsonTree.svelte +516 -429
  146. package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
  147. package/dist/layout/json-tree/JsonValue.svelte +281 -173
  148. package/dist/layout/json-tree/types.d.ts +10 -2
  149. package/dist/layout/json-tree/utils.d.ts +2 -0
  150. package/dist/layout/json-tree/utils.js +37 -2
  151. package/dist/marching-cubes.js +25 -2
  152. package/dist/math.d.ts +20 -17
  153. package/dist/math.js +474 -57
  154. package/dist/overlays/ContextMenu.svelte +66 -40
  155. package/dist/overlays/DraggablePane.svelte +331 -154
  156. package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
  157. package/dist/periodic-table/PeriodicTable.svelte +278 -145
  158. package/dist/periodic-table/PeriodicTableControls.svelte +178 -128
  159. package/dist/periodic-table/PropertySelect.svelte +25 -7
  160. package/dist/periodic-table/TableInset.svelte +8 -3
  161. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +559 -267
  162. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
  163. package/dist/phase-diagram/PhaseDiagramControls.svelte +131 -51
  164. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
  165. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  166. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  167. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +160 -110
  168. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +8 -1
  169. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +217 -86
  170. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
  171. package/dist/phase-diagram/TdbInfoPanel.svelte +28 -4
  172. package/dist/phase-diagram/build-diagram.js +9 -9
  173. package/dist/phase-diagram/colors.js +1 -3
  174. package/dist/phase-diagram/index.d.ts +2 -0
  175. package/dist/phase-diagram/index.js +2 -0
  176. package/dist/phase-diagram/parse.js +10 -9
  177. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  178. package/dist/phase-diagram/svg-to-diagram.js +869 -0
  179. package/dist/phase-diagram/types.d.ts +10 -0
  180. package/dist/phase-diagram/utils.d.ts +8 -4
  181. package/dist/phase-diagram/utils.js +219 -74
  182. package/dist/plot/AxisLabel.svelte +51 -0
  183. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  184. package/dist/plot/BarPlot.svelte +1461 -768
  185. package/dist/plot/BarPlot.svelte.d.ts +3 -3
  186. package/dist/plot/BarPlotControls.svelte +33 -6
  187. package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
  188. package/dist/plot/ColorBar.svelte +533 -383
  189. package/dist/plot/ColorBar.svelte.d.ts +1 -1
  190. package/dist/plot/ColorScaleSelect.svelte +28 -7
  191. package/dist/plot/ElementScatter.svelte +38 -16
  192. package/dist/plot/FillArea.svelte +152 -92
  193. package/dist/plot/Histogram.svelte +1162 -709
  194. package/dist/plot/Histogram.svelte.d.ts +1 -1
  195. package/dist/plot/HistogramControls.svelte +81 -18
  196. package/dist/plot/HistogramControls.svelte.d.ts +6 -2
  197. package/dist/plot/InteractiveAxisLabel.svelte +34 -11
  198. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +1 -1
  199. package/dist/plot/Line.svelte +63 -28
  200. package/dist/plot/PlotControls.svelte +221 -96
  201. package/dist/plot/PlotControls.svelte.d.ts +1 -1
  202. package/dist/plot/PlotLegend.svelte +174 -91
  203. package/dist/plot/PlotTooltip.svelte +45 -6
  204. package/dist/plot/PortalSelect.svelte +175 -146
  205. package/dist/plot/ReferenceLine.svelte +77 -22
  206. package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
  207. package/dist/plot/ReferenceLine3D.svelte +132 -107
  208. package/dist/plot/ReferencePlane.svelte +146 -123
  209. package/dist/plot/ScatterPlot.svelte +1880 -1156
  210. package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
  211. package/dist/plot/ScatterPlot3D.svelte +256 -131
  212. package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
  213. package/dist/plot/ScatterPlot3DControls.svelte +300 -297
  214. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +2 -1
  215. package/dist/plot/ScatterPlot3DScene.svelte +608 -406
  216. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
  217. package/dist/plot/ScatterPlotControls.svelte +150 -70
  218. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  219. package/dist/plot/ScatterPoint.svelte +98 -26
  220. package/dist/plot/ScatterPoint.svelte.d.ts +1 -0
  221. package/dist/plot/SpacegroupBarPlot.svelte +142 -85
  222. package/dist/plot/Surface3D.svelte +159 -108
  223. package/dist/plot/ZeroLines.svelte +96 -0
  224. package/dist/plot/ZeroLines.svelte.d.ts +32 -0
  225. package/dist/plot/ZoomRect.svelte +23 -0
  226. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  227. package/dist/plot/axis-utils.d.ts +1 -1
  228. package/dist/plot/axis-utils.js +1 -3
  229. package/dist/plot/data-cleaning.js +12 -28
  230. package/dist/plot/data-transform.js +2 -1
  231. package/dist/plot/fill-utils.js +2 -0
  232. package/dist/plot/index.d.ts +6 -2
  233. package/dist/plot/index.js +6 -2
  234. package/dist/plot/interactions.d.ts +8 -10
  235. package/dist/plot/interactions.js +2 -3
  236. package/dist/plot/layout.d.ts +11 -2
  237. package/dist/plot/layout.js +44 -17
  238. package/dist/plot/reference-line.d.ts +5 -22
  239. package/dist/plot/reference-line.js +12 -84
  240. package/dist/plot/scales.js +24 -36
  241. package/dist/plot/types.d.ts +53 -40
  242. package/dist/plot/types.js +12 -7
  243. package/dist/plot/utils/label-placement.d.ts +32 -15
  244. package/dist/plot/utils/label-placement.js +227 -63
  245. package/dist/plot/utils/series-visibility.js +2 -3
  246. package/dist/plot/utils.d.ts +1 -0
  247. package/dist/plot/utils.js +14 -0
  248. package/dist/rdf/RdfPlot.svelte +173 -132
  249. package/dist/rdf/calc-rdf.js +4 -5
  250. package/dist/sanitize.d.ts +4 -0
  251. package/dist/sanitize.js +107 -0
  252. package/dist/settings.d.ts +21 -6
  253. package/dist/settings.js +63 -19
  254. package/dist/spectral/Bands.svelte +963 -412
  255. package/dist/spectral/Bands.svelte.d.ts +22 -2
  256. package/dist/spectral/BandsAndDos.svelte +90 -49
  257. package/dist/spectral/BrillouinBandsDos.svelte +151 -93
  258. package/dist/spectral/Dos.svelte +389 -258
  259. package/dist/spectral/helpers.d.ts +23 -1
  260. package/dist/spectral/helpers.js +119 -51
  261. package/dist/spectral/types.d.ts +2 -0
  262. package/dist/state.svelte.d.ts +1 -1
  263. package/dist/state.svelte.js +3 -2
  264. package/dist/structure/Arrow.svelte +59 -20
  265. package/dist/structure/AtomLegend.svelte +231 -129
  266. package/dist/structure/AtomLegend.svelte.d.ts +1 -1
  267. package/dist/structure/Bond.svelte +73 -47
  268. package/dist/structure/CanvasTooltip.svelte +10 -2
  269. package/dist/structure/CellSelect.svelte +148 -51
  270. package/dist/structure/Cylinder.svelte +33 -17
  271. package/dist/structure/Lattice.svelte +88 -33
  272. package/dist/structure/Structure.svelte +1077 -821
  273. package/dist/structure/Structure.svelte.d.ts +1 -1
  274. package/dist/structure/StructureControls.svelte +373 -139
  275. package/dist/structure/StructureControls.svelte.d.ts +1 -1
  276. package/dist/structure/StructureExportPane.svelte +124 -89
  277. package/dist/structure/StructureExportPane.svelte.d.ts +1 -1
  278. package/dist/structure/StructureInfoPane.svelte +304 -231
  279. package/dist/structure/StructureScene.svelte +919 -445
  280. package/dist/structure/StructureScene.svelte.d.ts +16 -7
  281. package/dist/structure/atom-properties.d.ts +6 -2
  282. package/dist/structure/atom-properties.js +42 -29
  283. package/dist/structure/bonding.js +6 -7
  284. package/dist/structure/export.js +22 -34
  285. package/dist/structure/ferrox-wasm-types.d.ts +3 -2
  286. package/dist/structure/ferrox-wasm-types.js +0 -3
  287. package/dist/structure/ferrox-wasm.d.ts +3 -2
  288. package/dist/structure/ferrox-wasm.js +2 -3
  289. package/dist/structure/index.d.ts +16 -0
  290. package/dist/structure/index.js +88 -6
  291. package/dist/structure/measure.d.ts +2 -2
  292. package/dist/structure/measure.js +4 -44
  293. package/dist/structure/parse.js +130 -155
  294. package/dist/structure/partial-occupancy.d.ts +25 -0
  295. package/dist/structure/partial-occupancy.js +99 -0
  296. package/dist/structure/pbc.d.ts +1 -0
  297. package/dist/structure/pbc.js +16 -6
  298. package/dist/structure/supercell.d.ts +2 -2
  299. package/dist/structure/supercell.js +12 -22
  300. package/dist/structure/validation.js +5 -3
  301. package/dist/symmetry/SymmetryStats.svelte +94 -37
  302. package/dist/symmetry/WyckoffTable.svelte +42 -14
  303. package/dist/symmetry/cell-transform.js +5 -3
  304. package/dist/symmetry/index.d.ts +7 -4
  305. package/dist/symmetry/index.js +87 -21
  306. package/dist/symmetry/spacegroups.js +148 -148
  307. package/dist/table/HeatmapTable.svelte +1112 -516
  308. package/dist/table/HeatmapTable.svelte.d.ts +12 -1
  309. package/dist/table/ToggleMenu.svelte +125 -90
  310. package/dist/table/index.d.ts +2 -0
  311. package/dist/table/index.js +2 -4
  312. package/dist/theme/ThemeControl.svelte +21 -12
  313. package/dist/time.js +4 -1
  314. package/dist/tooltip/TooltipContent.svelte +33 -8
  315. package/dist/trajectory/Trajectory.svelte +889 -687
  316. package/dist/trajectory/TrajectoryError.svelte +14 -3
  317. package/dist/trajectory/TrajectoryExportPane.svelte +148 -90
  318. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
  319. package/dist/trajectory/TrajectoryInfoPane.svelte +272 -143
  320. package/dist/trajectory/constants.d.ts +6 -0
  321. package/dist/trajectory/constants.js +7 -0
  322. package/dist/trajectory/extract.js +13 -31
  323. package/dist/trajectory/format-detect.d.ts +9 -0
  324. package/dist/trajectory/format-detect.js +76 -0
  325. package/dist/trajectory/frame-reader.d.ts +17 -0
  326. package/dist/trajectory/frame-reader.js +332 -0
  327. package/dist/trajectory/helpers.d.ts +14 -0
  328. package/dist/trajectory/helpers.js +172 -0
  329. package/dist/trajectory/index.d.ts +1 -0
  330. package/dist/trajectory/index.js +23 -14
  331. package/dist/trajectory/parse/ase.d.ts +2 -0
  332. package/dist/trajectory/parse/ase.js +77 -0
  333. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  334. package/dist/trajectory/parse/hdf5.js +129 -0
  335. package/dist/trajectory/parse/index.d.ts +12 -0
  336. package/dist/trajectory/parse/index.js +299 -0
  337. package/dist/trajectory/parse/lammps.d.ts +5 -0
  338. package/dist/trajectory/parse/lammps.js +179 -0
  339. package/dist/trajectory/parse/vasp.d.ts +2 -0
  340. package/dist/trajectory/parse/vasp.js +68 -0
  341. package/dist/trajectory/parse/xyz.d.ts +2 -0
  342. package/dist/trajectory/parse/xyz.js +110 -0
  343. package/dist/trajectory/plotting.js +13 -8
  344. package/dist/trajectory/types.d.ts +11 -0
  345. package/dist/trajectory/types.js +1 -0
  346. package/dist/utils.d.ts +3 -0
  347. package/dist/utils.js +17 -0
  348. package/dist/xrd/XrdPlot.svelte +337 -245
  349. package/dist/xrd/broadening.js +14 -9
  350. package/dist/xrd/calc-xrd.js +12 -19
  351. package/dist/xrd/parse.d.ts +1 -1
  352. package/dist/xrd/parse.js +17 -17
  353. package/package.json +103 -101
  354. package/readme.md +4 -4
  355. package/dist/trajectory/parse.d.ts +0 -42
  356. package/dist/trajectory/parse.js +0 -1267
  357. /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
  358. /package/dist/theme/{themes.js → themes.mjs} +0 -0
@@ -1,395 +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, ...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);
217
+ collapsed_paths.delete(path)
218
+ force_expanded.add(path)
219
+ } else {
220
+ force_expanded.delete(path)
221
+ collapsed_paths.add(path)
161
222
  }
162
- else {
163
- force_expanded.delete(path);
164
- collapsed_paths.add(path);
165
- }
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);
227
- }
228
- catch { // Clipboard API failed - still show feedback but as error
229
- copy_feedback_error = true;
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
230
300
  }
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
- };
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
+ }
375
455
  },
376
456
  get collapsed() {
377
- return collapsed_paths;
457
+ return collapsed_paths
378
458
  },
379
459
  get force_expanded() {
380
- return force_expanded;
460
+ return force_expanded
381
461
  },
382
462
  get search_query() {
383
- return search_query;
463
+ return search_query
384
464
  },
385
465
  get search_matches() {
386
- return search_matches;
466
+ return search_matches
387
467
  },
388
468
  get current_match_path() {
389
- return current_match_path;
469
+ return current_match_path
390
470
  },
391
471
  get focused_path() {
392
- return focused_path;
472
+ return focused_path
393
473
  },
394
474
  previous_values,
395
475
  toggle_collapse,
@@ -398,109 +478,119 @@ const context = {
398
478
  collapse_all,
399
479
  collapse_to_level,
400
480
  set_focused,
401
- copy_value: (val_path, val, event) => copy_to_clipboard(val_path, serialize_for_copy(val), event),
402
- 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),
403
485
  register_path,
404
486
  unregister_path,
405
487
  show_context_menu,
406
488
  get pinned_paths() {
407
- return pinned_paths;
489
+ return pinned_paths
408
490
  },
409
491
  toggle_pin,
410
492
  get selected_paths() {
411
- return selected_paths;
493
+ return selected_paths
412
494
  },
413
495
  toggle_select,
414
496
  copy_selected,
415
497
  get diff_map() {
416
- return diff_map;
498
+ return diff_map
417
499
  },
418
500
  get ghost_map() {
419
- return ghost_map;
501
+ return ghost_map
420
502
  },
421
503
  collapse_children_only,
422
- };
423
- setContext(JSON_TREE_CONTEXT_KEY, context);
424
- // Keyboard navigation at tree level
425
- function handle_tree_keydown(event) {
504
+ get onchange() {
505
+ return onchange
506
+ },
507
+ }
508
+
509
+ setContext(JSON_TREE_CONTEXT_KEY, context)
510
+
511
+ // Keyboard navigation at tree level
512
+ function handle_tree_keydown(event: KeyboardEvent) {
426
513
  // Escape closes context menu first, then clears selection
427
514
  if (event.key === `Escape`) {
428
- if (context_menu_state) {
429
- close_context_menu();
430
- return;
431
- }
432
- if (selected_paths.size > 0) {
433
- selected_paths = new SvelteSet();
434
- return;
435
- }
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
+ }
436
523
  }
437
524
  // Ctrl/Cmd+C with selection copies all selected
438
- if ((event.key === `c` || event.key === `C`) &&
439
- (event.ctrlKey || event.metaKey) &&
440
- selected_paths.size > 0) {
441
- event.preventDefault();
442
- copy_selected();
443
- 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
444
533
  }
534
+
445
535
  if (!focused_path) {
446
- // Focus first node on any arrow key
447
- if (ARROW_KEYS.has(event.key)) {
448
- event.preventDefault();
449
- if (registered_paths_list.length > 0) {
450
- set_focused(registered_paths_list[0]);
451
- }
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])
452
541
  }
453
- return;
542
+ }
543
+ return
454
544
  }
455
- const current_index = registered_paths_list.indexOf(focused_path);
456
- if (current_index === -1)
457
- return;
545
+
546
+ const current_index = registered_paths_list.indexOf(focused_path)
547
+ if (current_index === -1) return
548
+
458
549
  if (event.key === `ArrowDown`) {
459
- event.preventDefault();
460
- const next_index = Math.min(current_index + 1, registered_paths_list.length - 1);
461
- set_focused(registered_paths_list[next_index]);
462
- }
463
- else if (event.key === `ArrowUp`) {
464
- event.preventDefault();
465
- const prev_index = Math.max(current_index - 1, 0);
466
- 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])
467
557
  }
468
- }
469
- // Clear search
470
- function clear_search() {
471
- if (search_debounce_timeout)
472
- clearTimeout(search_debounce_timeout);
473
- search_input_value = ``;
474
- search_query = ``;
475
- current_match_index = -1;
476
- }
477
- // Copy entire JSON to clipboard
478
- function copy_all() {
479
- copy_to_clipboard(`[root]`, serialize_for_copy(value));
480
- }
481
- // Download JSON as file
482
- function download_json() {
483
- const json_str = serialize_for_copy(value);
484
- const date_str = new Date().toISOString().slice(0, 10);
485
- const filename = download_filename ?? `data-${date_str}.json`;
486
- download(json_str, filename, `application/json`);
487
- }
488
- // Handle keyboard events on search input
489
- 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) {
490
583
  if (event.key === `Escape`) {
491
- event.preventDefault();
492
- clear_search();
493
- search_input_element?.blur();
494
- }
495
- else if (event.key === `Enter` || event.key === `F3`) {
496
- event.stopPropagation(); // Prevent bubbling to tree-level F3 handler
497
- event.preventDefault();
498
- if (event.shiftKey)
499
- go_to_prev_match();
500
- else
501
- 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()
502
592
  }
503
- }
593
+ }
504
594
  </script>
505
595
 
506
596
  <div
@@ -832,12 +922,14 @@ function handle_search_keydown(event) {
832
922
  border-radius: var(--jt-border-radius, 4px);
833
923
  overflow: hidden;
834
924
  }
925
+ /* --jt-header-bg, --jt-header-border, --jt-btn-bg, --jt-btn-hover-bg, --jt-btn-active-bg
926
+ intentionally removed in favor of transparent/opacity-based button styling.
927
+ Use --jt-hover-bg to customize the button hover background. */
835
928
  .json-tree-header {
836
929
  display: flex;
837
930
  align-items: center;
838
931
  gap: 8px;
839
932
  padding: 6px 8px;
840
- background: var(--jt-header-bg);
841
933
  flex-wrap: wrap;
842
934
  }
843
935
  .search-wrapper {
@@ -898,26 +990,22 @@ function handle_search_keydown(event) {
898
990
  min-width: 24px;
899
991
  height: 24px;
900
992
  padding: 2px 6px;
901
- border: 1px solid var(--jt-header-border);
902
- background: var(--jt-btn-bg, light-dark(white, rgba(255, 255, 255, 0.1)));
993
+ border: none;
994
+ background: transparent;
903
995
  border-radius: 3px;
904
996
  cursor: pointer;
905
997
  font-size: 11px;
906
998
  font-weight: 500;
907
999
  color: inherit;
908
- transition: background 0.15s;
1000
+ opacity: 0.6;
1001
+ transition: opacity 0.15s, background 0.15s;
909
1002
  }
910
1003
  .controls button:hover {
911
- background: var(
912
- --jt-btn-hover-bg,
913
- light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.15))
914
- );
1004
+ opacity: 1;
1005
+ background: var(--jt-hover-bg);
915
1006
  }
916
1007
  .controls button.active {
917
- background: var(
918
- --jt-btn-active-bg,
919
- light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2))
920
- );
1008
+ opacity: 1;
921
1009
  }
922
1010
  .match-nav {
923
1011
  display: flex;
@@ -931,17 +1019,16 @@ function handle_search_keydown(event) {
931
1019
  width: 20px;
932
1020
  height: 20px;
933
1021
  padding: 0;
934
- border: 1px solid var(--jt-header-border);
935
- background: var(--jt-btn-bg, light-dark(white, rgba(255, 255, 255, 0.1)));
1022
+ border: none;
1023
+ background: transparent;
936
1024
  border-radius: 3px;
937
1025
  cursor: pointer;
938
1026
  color: inherit;
1027
+ opacity: 0.6;
939
1028
  }
940
1029
  .nav-btn:hover {
941
- background: var(
942
- --jt-btn-hover-bg,
943
- light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.15))
944
- );
1030
+ opacity: 1;
1031
+ background: var(--jt-hover-bg);
945
1032
  }
946
1033
  .match-count {
947
1034
  font-size: 11px;