matterviz 0.3.6 → 0.4.0

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 (926) hide show
  1. package/dist/EmptyState.svelte.d.ts +9 -0
  2. package/dist/FilePicker.svelte +360 -0
  3. package/dist/FilePicker.svelte.d.ts +17 -0
  4. package/dist/Icon.svelte +44 -0
  5. package/dist/Icon.svelte.d.ts +13 -0
  6. package/dist/MillerIndexInput.svelte +66 -0
  7. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  8. package/dist/api/mp.d.ts +6 -0
  9. package/dist/api/mp.js +22 -0
  10. package/dist/api/optimade.d.ts +45 -0
  11. package/dist/api/optimade.js +141 -0
  12. package/dist/app.css +244 -0
  13. package/dist/brillouin/BrillouinZone.svelte +554 -0
  14. package/dist/brillouin/BrillouinZone.svelte.d.ts +84 -0
  15. package/dist/brillouin/BrillouinZoneControls.svelte +144 -0
  16. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +17 -0
  17. package/dist/brillouin/BrillouinZoneExportPane.svelte +146 -0
  18. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +15 -0
  19. package/dist/brillouin/BrillouinZoneInfoPane.svelte +146 -0
  20. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +13 -0
  21. package/dist/brillouin/BrillouinZoneScene.svelte +522 -0
  22. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +49 -0
  23. package/dist/brillouin/BrillouinZoneTooltip.svelte +83 -0
  24. package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +8 -0
  25. package/dist/brillouin/compute.d.ts +17 -0
  26. package/dist/brillouin/compute.js +422 -0
  27. package/dist/brillouin/index.d.ts +8 -0
  28. package/dist/brillouin/index.js +7 -0
  29. package/dist/brillouin/types.d.ts +43 -0
  30. package/dist/brillouin/types.js +1 -0
  31. package/dist/chempot-diagram/ChemPotDiagram.svelte +328 -0
  32. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  33. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +843 -0
  34. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  35. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3191 -0
  36. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  37. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  38. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  39. package/dist/chempot-diagram/async-compute.svelte.js +80 -0
  40. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  41. package/dist/chempot-diagram/chempot-worker.js +12 -0
  42. package/dist/chempot-diagram/color.d.ts +10 -0
  43. package/dist/chempot-diagram/color.js +32 -0
  44. package/dist/chempot-diagram/compute.d.ts +48 -0
  45. package/dist/chempot-diagram/compute.js +804 -0
  46. package/dist/chempot-diagram/index.d.ts +6 -0
  47. package/dist/chempot-diagram/index.js +6 -0
  48. package/dist/chempot-diagram/pointer.d.ts +16 -0
  49. package/dist/chempot-diagram/pointer.js +40 -0
  50. package/dist/chempot-diagram/temperature.d.ts +15 -0
  51. package/dist/chempot-diagram/temperature.js +34 -0
  52. package/dist/chempot-diagram/types.d.ts +81 -0
  53. package/dist/chempot-diagram/types.js +28 -0
  54. package/dist/colors/index.d.ts +47 -0
  55. package/dist/colors/index.js +204 -0
  56. package/dist/composition/BarChart.svelte +297 -0
  57. package/dist/composition/BarChart.svelte.d.ts +39 -0
  58. package/dist/composition/BubbleChart.svelte +218 -0
  59. package/dist/composition/BubbleChart.svelte.d.ts +28 -0
  60. package/dist/composition/Composition.svelte +165 -0
  61. package/dist/composition/Composition.svelte.d.ts +15 -0
  62. package/dist/composition/Formula.svelte +268 -0
  63. package/dist/composition/Formula.svelte.d.ts +19 -0
  64. package/dist/composition/FormulaFilter.svelte +1263 -0
  65. package/dist/composition/FormulaFilter.svelte.d.ts +51 -0
  66. package/dist/composition/PieChart.svelte +324 -0
  67. package/dist/composition/PieChart.svelte.d.ts +37 -0
  68. package/dist/composition/chem-sys.d.ts +8 -0
  69. package/dist/composition/chem-sys.js +85 -0
  70. package/dist/composition/format.d.ts +15 -0
  71. package/dist/composition/format.js +111 -0
  72. package/dist/composition/index.d.ts +21 -0
  73. package/dist/composition/index.js +15 -0
  74. package/dist/composition/parse.d.ts +56 -0
  75. package/dist/composition/parse.js +486 -0
  76. package/dist/constants.d.ts +29 -0
  77. package/dist/constants.js +99 -0
  78. package/dist/controls.d.ts +14 -0
  79. package/dist/controls.js +30 -0
  80. package/dist/convex-hull/ConvexHull.svelte +157 -0
  81. package/dist/convex-hull/ConvexHull.svelte.d.ts +13 -0
  82. package/dist/convex-hull/ConvexHull2D.svelte +827 -0
  83. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +11 -0
  84. package/dist/convex-hull/ConvexHull3D.svelte +1801 -0
  85. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +8 -0
  86. package/dist/convex-hull/ConvexHull4D.svelte +1394 -0
  87. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +8 -0
  88. package/dist/convex-hull/ConvexHullControls.svelte +535 -0
  89. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +48 -0
  90. package/dist/convex-hull/ConvexHullInfoPane.svelte +125 -0
  91. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +20 -0
  92. package/dist/convex-hull/ConvexHullStats.svelte +929 -0
  93. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +17 -0
  94. package/dist/convex-hull/ConvexHullTooltip.svelte +131 -0
  95. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +33 -0
  96. package/dist/convex-hull/GasPressureControls.svelte +247 -0
  97. package/dist/convex-hull/GasPressureControls.svelte.d.ts +11 -0
  98. package/dist/convex-hull/StructurePopup.svelte +151 -0
  99. package/dist/convex-hull/StructurePopup.svelte.d.ts +18 -0
  100. package/dist/convex-hull/TemperatureSlider.svelte +140 -0
  101. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +8 -0
  102. package/dist/convex-hull/barycentric-coords.d.ts +18 -0
  103. package/dist/convex-hull/barycentric-coords.js +182 -0
  104. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  105. package/dist/convex-hull/demo-temperature.js +38 -0
  106. package/dist/convex-hull/gas-thermodynamics.d.ts +16 -0
  107. package/dist/convex-hull/gas-thermodynamics.js +306 -0
  108. package/dist/convex-hull/helpers.d.ts +117 -0
  109. package/dist/convex-hull/helpers.js +718 -0
  110. package/dist/convex-hull/index.d.ts +119 -0
  111. package/dist/convex-hull/index.js +58 -0
  112. package/dist/convex-hull/thermodynamics.d.ts +67 -0
  113. package/dist/convex-hull/thermodynamics.js +1757 -0
  114. package/dist/convex-hull/types.d.ts +162 -0
  115. package/dist/convex-hull/types.js +36 -0
  116. package/dist/coordination/CoordinationBarPlot.svelte +311 -0
  117. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +30 -0
  118. package/dist/coordination/calc-coordination.d.ts +15 -0
  119. package/dist/coordination/calc-coordination.js +63 -0
  120. package/dist/coordination/index.d.ts +8 -0
  121. package/dist/coordination/index.js +7 -0
  122. package/dist/effects.svelte.d.ts +12 -0
  123. package/dist/effects.svelte.js +37 -0
  124. package/dist/element/BohrAtom.svelte.d.ts +20 -0
  125. package/dist/element/ElementHeading.svelte +26 -0
  126. package/dist/element/ElementHeading.svelte.d.ts +8 -0
  127. package/dist/element/ElementPhoto.svelte +57 -0
  128. package/dist/element/ElementPhoto.svelte.d.ts +9 -0
  129. package/dist/element/ElementStats.svelte +80 -0
  130. package/dist/element/ElementStats.svelte.d.ts +8 -0
  131. package/dist/element/ElementTile.svelte +484 -0
  132. package/dist/element/ElementTile.svelte.d.ts +29 -0
  133. package/dist/element/Nucleus.svelte.d.ts +17 -0
  134. package/dist/element/data.d.ts +2 -0
  135. package/dist/element/data.js +2 -0
  136. package/dist/element/index.d.ts +8 -0
  137. package/dist/element/index.js +7 -0
  138. package/dist/element/types.d.ts +57 -0
  139. package/dist/element/types.js +1 -0
  140. package/dist/feedback/ClickFeedback.svelte +58 -0
  141. package/dist/feedback/ClickFeedback.svelte.d.ts +12 -0
  142. package/dist/feedback/DragOverlay.svelte +42 -0
  143. package/dist/feedback/DragOverlay.svelte.d.ts +7 -0
  144. package/dist/feedback/Spinner.svelte.d.ts +7 -0
  145. package/dist/feedback/StatusMessage.svelte.d.ts +9 -0
  146. package/dist/feedback/index.d.ts +4 -0
  147. package/dist/feedback/index.js +4 -0
  148. package/dist/fermi-surface/FermiSlice.svelte +197 -0
  149. package/dist/fermi-surface/FermiSlice.svelte.d.ts +24 -0
  150. package/dist/fermi-surface/FermiSurface.svelte +606 -0
  151. package/dist/fermi-surface/FermiSurface.svelte.d.ts +83 -0
  152. package/dist/fermi-surface/FermiSurfaceControls.svelte +448 -0
  153. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +35 -0
  154. package/dist/fermi-surface/FermiSurfaceScene.svelte +797 -0
  155. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +50 -0
  156. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +85 -0
  157. package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +8 -0
  158. package/dist/fermi-surface/compute.d.ts +5 -0
  159. package/dist/fermi-surface/compute.js +538 -0
  160. package/dist/fermi-surface/constants.d.ts +9 -0
  161. package/dist/fermi-surface/constants.js +27 -0
  162. package/dist/fermi-surface/export.d.ts +5 -0
  163. package/dist/fermi-surface/export.js +51 -0
  164. package/dist/fermi-surface/index.d.ts +12 -0
  165. package/dist/fermi-surface/index.js +13 -0
  166. package/dist/fermi-surface/marching-cubes.d.ts +2 -0
  167. package/dist/fermi-surface/marching-cubes.js +2 -0
  168. package/dist/fermi-surface/parse.d.ts +2 -0
  169. package/dist/fermi-surface/parse.js +494 -0
  170. package/dist/fermi-surface/symmetry.d.ts +3 -0
  171. package/dist/fermi-surface/symmetry.js +46 -0
  172. package/dist/fermi-surface/types.d.ts +111 -0
  173. package/dist/fermi-surface/types.js +4 -0
  174. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1547 -0
  175. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  176. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  177. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  178. package/dist/heatmap-matrix/index.d.ts +53 -0
  179. package/dist/heatmap-matrix/index.js +100 -0
  180. package/dist/heatmap-matrix/shared.d.ts +2 -0
  181. package/dist/heatmap-matrix/shared.js +4 -0
  182. package/dist/icons.d.ts +569 -0
  183. package/dist/icons.js +648 -0
  184. package/dist/index.d.ts +39 -0
  185. package/dist/index.js +39 -0
  186. package/dist/io/decompress.d.ts +11 -0
  187. package/dist/io/decompress.js +76 -0
  188. package/dist/io/export.d.ts +16 -0
  189. package/dist/io/export.js +338 -0
  190. package/dist/io/fetch.d.ts +5 -0
  191. package/dist/io/fetch.js +43 -0
  192. package/dist/io/file-drop.d.ts +7 -0
  193. package/dist/io/file-drop.js +42 -0
  194. package/dist/io/index.d.ts +7 -0
  195. package/dist/io/index.js +6 -0
  196. package/dist/io/is-binary.d.ts +1 -0
  197. package/dist/io/is-binary.js +20 -0
  198. package/dist/io/types.d.ts +8 -0
  199. package/dist/io/types.js +1 -0
  200. package/dist/io/url-drop.d.ts +2 -0
  201. package/dist/io/url-drop.js +154 -0
  202. package/dist/isosurface/Isosurface.svelte +285 -0
  203. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  204. package/dist/isosurface/IsosurfaceControls.svelte +277 -0
  205. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  206. package/dist/isosurface/index.d.ts +5 -0
  207. package/dist/isosurface/index.js +6 -0
  208. package/dist/isosurface/parse.d.ts +6 -0
  209. package/dist/isosurface/parse.js +552 -0
  210. package/dist/isosurface/slice.d.ts +11 -0
  211. package/dist/isosurface/slice.js +141 -0
  212. package/dist/isosurface/types.d.ts +56 -0
  213. package/dist/isosurface/types.js +227 -0
  214. package/dist/keyboard.d.ts +3 -0
  215. package/dist/keyboard.js +23 -0
  216. package/dist/labels.d.ts +53 -0
  217. package/dist/labels.js +278 -0
  218. package/dist/layout/FullscreenToggle.svelte +50 -0
  219. package/dist/layout/FullscreenToggle.svelte.d.ts +7 -0
  220. package/dist/layout/InfoCard.svelte +120 -0
  221. package/dist/layout/InfoCard.svelte.d.ts +21 -0
  222. package/dist/layout/InfoTag.svelte +185 -0
  223. package/dist/layout/InfoTag.svelte.d.ts +19 -0
  224. package/dist/layout/PropertyFilter.svelte +247 -0
  225. package/dist/layout/PropertyFilter.svelte.d.ts +24 -0
  226. package/dist/layout/SettingsSection.svelte +148 -0
  227. package/dist/layout/SettingsSection.svelte.d.ts +17 -0
  228. package/dist/layout/SubpageGrid.svelte +82 -0
  229. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  230. package/dist/layout/fullscreen.d.ts +9 -0
  231. package/dist/layout/fullscreen.js +53 -0
  232. package/dist/layout/index.d.ts +10 -0
  233. package/dist/layout/index.js +8 -0
  234. package/dist/layout/json-tree/JsonNode.svelte +548 -0
  235. package/dist/layout/json-tree/JsonNode.svelte.d.ts +11 -0
  236. package/dist/layout/json-tree/JsonTree.svelte +1230 -0
  237. package/dist/layout/json-tree/JsonTree.svelte.d.ts +6 -0
  238. package/dist/layout/json-tree/JsonValue.svelte.d.ts +9 -0
  239. package/dist/layout/json-tree/index.d.ts +3 -0
  240. package/dist/layout/json-tree/index.js +3 -0
  241. package/dist/layout/json-tree/types.d.ts +74 -0
  242. package/dist/layout/json-tree/types.js +2 -0
  243. package/dist/layout/json-tree/utils.d.ts +29 -0
  244. package/dist/layout/json-tree/utils.js +642 -0
  245. package/dist/marching-cubes.d.ts +14 -0
  246. package/dist/marching-cubes.js +535 -0
  247. package/dist/math.d.ts +105 -0
  248. package/dist/math.js +920 -0
  249. package/dist/overlays/ContextMenu.svelte +162 -0
  250. package/dist/overlays/ContextMenu.svelte.d.ts +25 -0
  251. package/dist/overlays/CopyButton.svelte +45 -0
  252. package/dist/overlays/CopyButton.svelte.d.ts +8 -0
  253. package/dist/overlays/DragControlTab.svelte +98 -0
  254. package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
  255. package/dist/overlays/DraggablePane.svelte +487 -0
  256. package/dist/overlays/DraggablePane.svelte.d.ts +36 -0
  257. package/dist/overlays/InfoPaneCards.svelte +149 -0
  258. package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
  259. package/dist/overlays/index.d.ts +3 -0
  260. package/dist/overlays/index.js +3 -0
  261. package/dist/periodic-table/PeriodicTable.svelte +480 -0
  262. package/dist/periodic-table/PeriodicTable.svelte.d.ts +55 -0
  263. package/dist/periodic-table/PeriodicTableControls.svelte +557 -0
  264. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +24 -0
  265. package/dist/periodic-table/PropertySelect.svelte +38 -0
  266. package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
  267. package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
  268. package/dist/periodic-table/index.d.ts +10 -0
  269. package/dist/periodic-table/index.js +4 -0
  270. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1092 -0
  271. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +44 -0
  272. package/dist/phase-diagram/PhaseDiagramControls.svelte +444 -0
  273. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +30 -0
  274. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +127 -0
  275. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  276. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
  277. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +19 -0
  278. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
  279. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +16 -0
  280. package/dist/phase-diagram/TdbInfoPanel.svelte +203 -0
  281. package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +12 -0
  282. package/dist/phase-diagram/build-diagram.d.ts +11 -0
  283. package/dist/phase-diagram/build-diagram.js +160 -0
  284. package/dist/phase-diagram/colors.d.ts +35 -0
  285. package/dist/phase-diagram/colors.js +51 -0
  286. package/dist/phase-diagram/diagram-input.d.ts +29 -0
  287. package/dist/phase-diagram/diagram-input.js +3 -0
  288. package/dist/phase-diagram/index.d.ts +13 -0
  289. package/dist/phase-diagram/index.js +11 -0
  290. package/dist/phase-diagram/parse.d.ts +55 -0
  291. package/dist/phase-diagram/parse.js +273 -0
  292. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  293. package/dist/phase-diagram/svg-to-diagram.js +867 -0
  294. package/dist/phase-diagram/types.d.ts +93 -0
  295. package/dist/phase-diagram/types.js +1 -0
  296. package/dist/phase-diagram/utils.d.ts +118 -0
  297. package/dist/phase-diagram/utils.js +600 -0
  298. package/dist/plot/bar/BarPlot.svelte +1755 -0
  299. package/dist/plot/bar/BarPlot.svelte.d.ts +84 -0
  300. package/dist/plot/bar/BarPlotControls.svelte +67 -0
  301. package/dist/plot/bar/BarPlotControls.svelte.d.ts +18 -0
  302. package/dist/plot/bar/SpacegroupBarPlot.svelte +293 -0
  303. package/dist/plot/bar/SpacegroupBarPlot.svelte.d.ts +9 -0
  304. package/dist/plot/bar/data.d.ts +40 -0
  305. package/dist/plot/bar/data.js +154 -0
  306. package/dist/plot/bar/geometry.d.ts +39 -0
  307. package/dist/plot/bar/geometry.js +60 -0
  308. package/dist/plot/bar/index.d.ts +3 -0
  309. package/dist/plot/bar/index.js +3 -0
  310. package/dist/plot/box/BoxPlot.svelte +1462 -0
  311. package/dist/plot/box/BoxPlot.svelte.d.ts +94 -0
  312. package/dist/plot/box/BoxPlotControls.svelte +109 -0
  313. package/dist/plot/box/BoxPlotControls.svelte.d.ts +19 -0
  314. package/dist/plot/box/Violin.svelte +14 -0
  315. package/dist/plot/box/Violin.svelte.d.ts +70 -0
  316. package/dist/plot/box/box-plot.d.ts +55 -0
  317. package/dist/plot/box/box-plot.js +126 -0
  318. package/dist/plot/box/index.d.ts +5 -0
  319. package/dist/plot/box/index.js +5 -0
  320. package/dist/plot/box/kde.d.ts +16 -0
  321. package/dist/plot/box/kde.js +160 -0
  322. package/dist/plot/box/quantile.d.ts +3 -0
  323. package/dist/plot/box/quantile.js +53 -0
  324. package/dist/plot/core/auto-place.d.ts +43 -0
  325. package/dist/plot/core/auto-place.js +122 -0
  326. package/dist/plot/core/axis-utils.d.ts +46 -0
  327. package/dist/plot/core/axis-utils.js +110 -0
  328. package/dist/plot/core/components/AxisLabel.svelte +51 -0
  329. package/dist/plot/core/components/AxisLabel.svelte.d.ts +16 -0
  330. package/dist/plot/core/components/ColorBar.svelte +724 -0
  331. package/dist/plot/core/components/ColorBar.svelte.d.ts +31 -0
  332. package/dist/plot/core/components/ColorScaleSelect.svelte +55 -0
  333. package/dist/plot/core/components/ColorScaleSelect.svelte.d.ts +15 -0
  334. package/dist/plot/core/components/ControlPane.svelte +46 -0
  335. package/dist/plot/core/components/ControlPane.svelte.d.ts +13 -0
  336. package/dist/plot/core/components/FillArea.svelte +234 -0
  337. package/dist/plot/core/components/FillArea.svelte.d.ts +21 -0
  338. package/dist/plot/core/components/InteractiveAxisLabel.svelte +96 -0
  339. package/dist/plot/core/components/InteractiveAxisLabel.svelte.d.ts +14 -0
  340. package/dist/plot/core/components/Line.svelte +101 -0
  341. package/dist/plot/core/components/Line.svelte.d.ts +15 -0
  342. package/dist/plot/core/components/PlotAxis.svelte +171 -0
  343. package/dist/plot/core/components/PlotAxis.svelte.d.ts +25 -0
  344. package/dist/plot/core/components/PlotControls.svelte +525 -0
  345. package/dist/plot/core/components/PlotControls.svelte.d.ts +4 -0
  346. package/dist/plot/core/components/PlotLegend.svelte +580 -0
  347. package/dist/plot/core/components/PlotLegend.svelte.d.ts +30 -0
  348. package/dist/plot/core/components/PlotTooltip.svelte +83 -0
  349. package/dist/plot/core/components/PlotTooltip.svelte.d.ts +25 -0
  350. package/dist/plot/core/components/PortalSelect.svelte +257 -0
  351. package/dist/plot/core/components/PortalSelect.svelte.d.ts +16 -0
  352. package/dist/plot/core/components/ReferenceLine.svelte +204 -0
  353. package/dist/plot/core/components/ReferenceLine.svelte.d.ts +20 -0
  354. package/dist/plot/core/components/ReferenceLine3D.svelte +156 -0
  355. package/dist/plot/core/components/ReferenceLine3D.svelte.d.ts +14 -0
  356. package/dist/plot/core/components/ReferencePlane.svelte +175 -0
  357. package/dist/plot/core/components/ReferencePlane.svelte.d.ts +14 -0
  358. package/dist/plot/core/components/ZeroLines.svelte +97 -0
  359. package/dist/plot/core/components/ZeroLines.svelte.d.ts +33 -0
  360. package/dist/plot/core/components/ZoomRect.svelte +23 -0
  361. package/dist/plot/core/components/ZoomRect.svelte.d.ts +8 -0
  362. package/dist/plot/core/components/index.d.ts +17 -0
  363. package/dist/plot/core/components/index.js +17 -0
  364. package/dist/plot/core/data-cleaning.d.ts +107 -0
  365. package/dist/plot/core/data-cleaning.js +853 -0
  366. package/dist/plot/core/data-transform.d.ts +16 -0
  367. package/dist/plot/core/data-transform.js +45 -0
  368. package/dist/plot/core/fill-utils.d.ts +33 -0
  369. package/dist/plot/core/fill-utils.js +388 -0
  370. package/dist/plot/core/hover-lock.svelte.d.ts +14 -0
  371. package/dist/plot/core/hover-lock.svelte.js +45 -0
  372. package/dist/plot/core/index.d.ts +10 -0
  373. package/dist/plot/core/index.js +11 -0
  374. package/dist/plot/core/interactions.d.ts +35 -0
  375. package/dist/plot/core/interactions.js +195 -0
  376. package/dist/plot/core/layout.d.ts +79 -0
  377. package/dist/plot/core/layout.js +281 -0
  378. package/dist/plot/core/reference-line.d.ts +60 -0
  379. package/dist/plot/core/reference-line.js +301 -0
  380. package/dist/plot/core/scales.d.ts +48 -0
  381. package/dist/plot/core/scales.js +480 -0
  382. package/dist/plot/core/svg.d.ts +2 -0
  383. package/dist/plot/core/svg.js +41 -0
  384. package/dist/plot/core/types.d.ts +771 -0
  385. package/dist/plot/core/types.js +99 -0
  386. package/dist/plot/core/utils/label-placement.d.ts +68 -0
  387. package/dist/plot/core/utils/label-placement.js +326 -0
  388. package/dist/plot/core/utils/series-visibility.d.ts +26 -0
  389. package/dist/plot/core/utils/series-visibility.js +112 -0
  390. package/dist/plot/core/utils.d.ts +11 -0
  391. package/dist/plot/core/utils.js +27 -0
  392. package/dist/plot/histogram/Histogram.svelte +1418 -0
  393. package/dist/plot/histogram/Histogram.svelte.d.ts +50 -0
  394. package/dist/plot/histogram/HistogramControls.svelte +212 -0
  395. package/dist/plot/histogram/HistogramControls.svelte.d.ts +22 -0
  396. package/dist/plot/histogram/index.d.ts +2 -0
  397. package/dist/plot/histogram/index.js +2 -0
  398. package/dist/plot/index.d.ts +8 -0
  399. package/dist/plot/index.js +10 -0
  400. package/dist/plot/sankey/Sankey.svelte +700 -0
  401. package/dist/plot/sankey/Sankey.svelte.d.ts +74 -0
  402. package/dist/plot/sankey/SankeyControls.svelte +98 -0
  403. package/dist/plot/sankey/SankeyControls.svelte.d.ts +19 -0
  404. package/dist/plot/sankey/index.d.ts +4 -0
  405. package/dist/plot/sankey/index.js +3 -0
  406. package/dist/plot/sankey/sankey-types.d.ts +42 -0
  407. package/dist/plot/sankey/sankey-types.js +4 -0
  408. package/dist/plot/sankey/sankey.d.ts +52 -0
  409. package/dist/plot/sankey/sankey.js +187 -0
  410. package/dist/plot/scatter/BinnedScatterPlot.svelte +1116 -0
  411. package/dist/plot/scatter/BinnedScatterPlot.svelte.d.ts +66 -0
  412. package/dist/plot/scatter/ElementScatter.svelte +63 -0
  413. package/dist/plot/scatter/ElementScatter.svelte.d.ts +14 -0
  414. package/dist/plot/scatter/ScatterPlot.svelte +2357 -0
  415. package/dist/plot/scatter/ScatterPlot.svelte.d.ts +96 -0
  416. package/dist/plot/scatter/ScatterPlotControls.svelte +307 -0
  417. package/dist/plot/scatter/ScatterPlotControls.svelte.d.ts +17 -0
  418. package/dist/plot/scatter/ScatterPoint.svelte +182 -0
  419. package/dist/plot/scatter/ScatterPoint.svelte.d.ts +22 -0
  420. package/dist/plot/scatter/adaptive-density.d.ts +79 -0
  421. package/dist/plot/scatter/adaptive-density.js +217 -0
  422. package/dist/plot/scatter/binned-scatter-types.d.ts +59 -0
  423. package/dist/plot/scatter/binned-scatter-types.js +1 -0
  424. package/dist/plot/scatter/index.d.ts +7 -0
  425. package/dist/plot/scatter/index.js +5 -0
  426. package/dist/plot/scatter/scatter-data.d.ts +19 -0
  427. package/dist/plot/scatter/scatter-data.js +212 -0
  428. package/dist/plot/scatter-3d/ScatterPlot3D.svelte +531 -0
  429. package/dist/plot/scatter-3d/ScatterPlot3D.svelte.d.ts +95 -0
  430. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte +438 -0
  431. package/dist/plot/scatter-3d/ScatterPlot3DControls.svelte.d.ts +20 -0
  432. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte +912 -0
  433. package/dist/plot/scatter-3d/ScatterPlot3DScene.svelte.d.ts +74 -0
  434. package/dist/plot/scatter-3d/Surface3D.svelte +197 -0
  435. package/dist/plot/scatter-3d/Surface3D.svelte.d.ts +13 -0
  436. package/dist/plot/scatter-3d/index.d.ts +4 -0
  437. package/dist/plot/scatter-3d/index.js +4 -0
  438. package/dist/plot/sunburst/Sunburst.svelte +1045 -0
  439. package/dist/plot/sunburst/Sunburst.svelte.d.ts +96 -0
  440. package/dist/plot/sunburst/SunburstControls.svelte +200 -0
  441. package/dist/plot/sunburst/SunburstControls.svelte.d.ts +26 -0
  442. package/dist/plot/sunburst/index.d.ts +4 -0
  443. package/dist/plot/sunburst/index.js +4 -0
  444. package/dist/plot/sunburst/render.d.ts +34 -0
  445. package/dist/plot/sunburst/render.js +122 -0
  446. package/dist/plot/sunburst/sunburst.d.ts +62 -0
  447. package/dist/plot/sunburst/sunburst.js +266 -0
  448. package/dist/rdf/RdfPlot.svelte +248 -0
  449. package/dist/rdf/RdfPlot.svelte.d.ts +27 -0
  450. package/dist/rdf/calc-rdf.d.ts +4 -0
  451. package/dist/rdf/calc-rdf.js +98 -0
  452. package/dist/rdf/index.d.ts +23 -0
  453. package/dist/rdf/index.js +2 -0
  454. package/dist/sanitize.d.ts +6 -0
  455. package/dist/sanitize.js +116 -0
  456. package/dist/settings.d.ts +319 -0
  457. package/dist/settings.js +1394 -0
  458. package/dist/spectral/Bands.svelte +1050 -0
  459. package/dist/spectral/Bands.svelte.d.ts +39 -0
  460. package/dist/spectral/BandsAndDos.svelte +134 -0
  461. package/dist/spectral/BandsAndDos.svelte.d.ts +18 -0
  462. package/dist/spectral/BrillouinBandsDos.svelte +264 -0
  463. package/dist/spectral/BrillouinBandsDos.svelte.d.ts +20 -0
  464. package/dist/spectral/Dos.svelte +688 -0
  465. package/dist/spectral/Dos.svelte.d.ts +29 -0
  466. package/dist/spectral/helpers.d.ts +121 -0
  467. package/dist/spectral/helpers.js +1098 -0
  468. package/dist/spectral/index.d.ts +6 -0
  469. package/dist/spectral/index.js +6 -0
  470. package/dist/spectral/types.d.ts +84 -0
  471. package/dist/spectral/types.js +2 -0
  472. package/dist/state.svelte.d.ts +25 -0
  473. package/dist/state.svelte.js +45 -0
  474. package/dist/structure/Arrow.svelte +72 -0
  475. package/dist/structure/Arrow.svelte.d.ts +15 -0
  476. package/dist/structure/AtomLegend.svelte +814 -0
  477. package/dist/structure/AtomLegend.svelte.d.ts +35 -0
  478. package/dist/structure/Bond.svelte +140 -0
  479. package/dist/structure/Bond.svelte.d.ts +9 -0
  480. package/dist/structure/CanvasTooltip.svelte +33 -0
  481. package/dist/structure/CanvasTooltip.svelte.d.ts +12 -0
  482. package/dist/structure/CellSelect.svelte +348 -0
  483. package/dist/structure/CellSelect.svelte.d.ts +13 -0
  484. package/dist/structure/Cylinder.svelte +49 -0
  485. package/dist/structure/Cylinder.svelte.d.ts +13 -0
  486. package/dist/structure/Lattice.svelte +196 -0
  487. package/dist/structure/Lattice.svelte.d.ts +17 -0
  488. package/dist/structure/Structure.svelte +2254 -0
  489. package/dist/structure/Structure.svelte.d.ts +89 -0
  490. package/dist/structure/StructureControls.svelte +1273 -0
  491. package/dist/structure/StructureControls.svelte.d.ts +31 -0
  492. package/dist/structure/StructureExportPane.svelte +252 -0
  493. package/dist/structure/StructureExportPane.svelte.d.ts +17 -0
  494. package/dist/structure/StructureInfoPane.svelte +736 -0
  495. package/dist/structure/StructureInfoPane.svelte.d.ts +19 -0
  496. package/dist/structure/StructureScene.svelte +2256 -0
  497. package/dist/structure/StructureScene.svelte.d.ts +111 -0
  498. package/dist/structure/atom-properties.d.ts +37 -0
  499. package/dist/structure/atom-properties.js +200 -0
  500. package/dist/structure/bond-order-perception.d.ts +13 -0
  501. package/dist/structure/bond-order-perception.js +384 -0
  502. package/dist/structure/bonding.d.ts +69 -0
  503. package/dist/structure/bonding.js +724 -0
  504. package/dist/structure/export.d.ts +20 -0
  505. package/dist/structure/export.js +731 -0
  506. package/dist/structure/index.d.ts +124 -0
  507. package/dist/structure/index.js +167 -0
  508. package/dist/structure/label-placement.d.ts +14 -0
  509. package/dist/structure/label-placement.js +72 -0
  510. package/dist/structure/measure.d.ts +7 -0
  511. package/dist/structure/measure.js +30 -0
  512. package/dist/structure/parse.d.ts +66 -0
  513. package/dist/structure/parse.js +1410 -0
  514. package/dist/structure/partial-occupancy.d.ts +25 -0
  515. package/dist/structure/partial-occupancy.js +99 -0
  516. package/dist/structure/pbc.d.ts +9 -0
  517. package/dist/structure/pbc.js +127 -0
  518. package/dist/structure/supercell.d.ts +8 -0
  519. package/dist/structure/supercell.js +170 -0
  520. package/dist/structure/validation.d.ts +2 -0
  521. package/dist/structure/validation.js +10 -0
  522. package/dist/symmetry/SymmetryStats.svelte +226 -0
  523. package/dist/symmetry/SymmetryStats.svelte.d.ts +21 -0
  524. package/dist/symmetry/WyckoffTable.svelte +120 -0
  525. package/dist/symmetry/WyckoffTable.svelte.d.ts +11 -0
  526. package/dist/symmetry/cell-transform.d.ts +12 -0
  527. package/dist/symmetry/cell-transform.js +91 -0
  528. package/dist/symmetry/index.d.ts +43 -0
  529. package/dist/symmetry/index.js +226 -0
  530. package/dist/symmetry/spacegroups.d.ts +16 -0
  531. package/dist/symmetry/spacegroups.js +429 -0
  532. package/dist/table/HeatmapTable.svelte +1885 -0
  533. package/dist/table/HeatmapTable.svelte.d.ts +49 -0
  534. package/dist/table/ToggleMenu.svelte +385 -0
  535. package/dist/table/ToggleMenu.svelte.d.ts +11 -0
  536. package/dist/table/index.d.ts +72 -0
  537. package/dist/table/index.js +38 -0
  538. package/dist/theme/ThemeControl.svelte +53 -0
  539. package/dist/theme/ThemeControl.svelte.d.ts +9 -0
  540. package/dist/theme/index.d.ts +29 -0
  541. package/dist/theme/index.js +79 -0
  542. package/dist/time.d.ts +4 -0
  543. package/dist/time.js +70 -0
  544. package/dist/tooltip/KCoords.svelte +45 -0
  545. package/dist/tooltip/KCoords.svelte.d.ts +8 -0
  546. package/dist/tooltip/TooltipContent.svelte +58 -0
  547. package/dist/tooltip/TooltipContent.svelte.d.ts +31 -0
  548. package/dist/tooltip/index.d.ts +3 -0
  549. package/dist/tooltip/index.js +2 -0
  550. package/dist/tooltip/types.d.ts +8 -0
  551. package/dist/tooltip/types.js +1 -0
  552. package/dist/trajectory/Trajectory.svelte +1571 -0
  553. package/dist/trajectory/Trajectory.svelte.d.ts +78 -0
  554. package/dist/trajectory/TrajectoryError.svelte +128 -0
  555. package/dist/trajectory/TrajectoryError.svelte.d.ts +13 -0
  556. package/dist/trajectory/TrajectoryExportPane.svelte +358 -0
  557. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +17 -0
  558. package/dist/trajectory/TrajectoryInfoPane.svelte +314 -0
  559. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +17 -0
  560. package/dist/trajectory/constants.d.ts +6 -0
  561. package/dist/trajectory/constants.js +7 -0
  562. package/dist/trajectory/extract.d.ts +5 -0
  563. package/dist/trajectory/extract.js +162 -0
  564. package/dist/trajectory/format-detect.d.ts +10 -0
  565. package/dist/trajectory/format-detect.js +90 -0
  566. package/dist/trajectory/frame-reader.d.ts +17 -0
  567. package/dist/trajectory/frame-reader.js +299 -0
  568. package/dist/trajectory/helpers.d.ts +15 -0
  569. package/dist/trajectory/helpers.js +164 -0
  570. package/dist/trajectory/index.d.ts +63 -0
  571. package/dist/trajectory/index.js +126 -0
  572. package/dist/trajectory/parse/ase.d.ts +2 -0
  573. package/dist/trajectory/parse/ase.js +73 -0
  574. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  575. package/dist/trajectory/parse/hdf5.js +127 -0
  576. package/dist/trajectory/parse/index.d.ts +12 -0
  577. package/dist/trajectory/parse/index.js +306 -0
  578. package/dist/trajectory/parse/lammps.d.ts +5 -0
  579. package/dist/trajectory/parse/lammps.js +179 -0
  580. package/dist/trajectory/parse/vasp.d.ts +2 -0
  581. package/dist/trajectory/parse/vasp.js +87 -0
  582. package/dist/trajectory/parse/xyz.d.ts +26 -0
  583. package/dist/trajectory/parse/xyz.js +123 -0
  584. package/dist/trajectory/plotting.d.ts +28 -0
  585. package/dist/trajectory/plotting.js +423 -0
  586. package/dist/trajectory/types.d.ts +11 -0
  587. package/dist/trajectory/types.js +1 -0
  588. package/dist/utils.d.ts +7 -0
  589. package/dist/utils.js +47 -0
  590. package/dist/xrd/XrdPlot.svelte +616 -0
  591. package/dist/xrd/XrdPlot.svelte.d.ts +28 -0
  592. package/dist/xrd/broadening.d.ts +20 -0
  593. package/dist/xrd/broadening.js +97 -0
  594. package/dist/xrd/calc-xrd.d.ts +37 -0
  595. package/dist/xrd/calc-xrd.js +339 -0
  596. package/dist/xrd/index.d.ts +37 -0
  597. package/dist/xrd/index.js +4 -0
  598. package/dist/xrd/parse.d.ts +13 -0
  599. package/dist/xrd/parse.js +749 -0
  600. package/license +1 -1
  601. package/package.json +237 -1458
  602. package/readme.md +98 -171
  603. package/.vscode/launch.json +0 -13
  604. package/.vscodeignore +0 -7
  605. package/dist/assets/STLExporter-BpTH3YHE.js +0 -8
  606. package/dist/assets/browser-DdDecX_W.js +0 -1
  607. package/dist/assets/export-qgn-H9y6.js +0 -2
  608. package/dist/assets/main-DiKYzti2.css +0 -1
  609. package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
  610. package/dist/extension.js +0 -31293
  611. package/dist/src/lib/FilePicker.svelte +0 -360
  612. package/dist/src/lib/Icon.svelte +0 -41
  613. package/dist/src/lib/MillerIndexInput.svelte +0 -66
  614. package/dist/src/lib/api/mp.ts +0 -26
  615. package/dist/src/lib/api/optimade.ts +0 -204
  616. package/dist/src/lib/app.css +0 -247
  617. package/dist/src/lib/brillouin/BrillouinZone.svelte +0 -549
  618. package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +0 -144
  619. package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +0 -146
  620. package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +0 -146
  621. package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +0 -476
  622. package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +0 -92
  623. package/dist/src/lib/brillouin/compute.ts +0 -529
  624. package/dist/src/lib/brillouin/index.ts +0 -8
  625. package/dist/src/lib/brillouin/types.ts +0 -51
  626. package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +0 -327
  627. package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +0 -846
  628. package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +0 -3193
  629. package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +0 -94
  630. package/dist/src/lib/chempot-diagram/chempot-worker.ts +0 -11
  631. package/dist/src/lib/chempot-diagram/color.ts +0 -42
  632. package/dist/src/lib/chempot-diagram/compute.ts +0 -1014
  633. package/dist/src/lib/chempot-diagram/index.ts +0 -6
  634. package/dist/src/lib/chempot-diagram/pointer.ts +0 -56
  635. package/dist/src/lib/chempot-diagram/temperature.ts +0 -77
  636. package/dist/src/lib/chempot-diagram/types.ts +0 -130
  637. package/dist/src/lib/colors/index.ts +0 -249
  638. package/dist/src/lib/composition/BarChart.svelte +0 -297
  639. package/dist/src/lib/composition/BubbleChart.svelte +0 -218
  640. package/dist/src/lib/composition/Composition.svelte +0 -165
  641. package/dist/src/lib/composition/Formula.svelte +0 -268
  642. package/dist/src/lib/composition/FormulaFilter.svelte +0 -1257
  643. package/dist/src/lib/composition/PieChart.svelte +0 -323
  644. package/dist/src/lib/composition/format.ts +0 -155
  645. package/dist/src/lib/composition/index.ts +0 -37
  646. package/dist/src/lib/composition/parse.ts +0 -605
  647. package/dist/src/lib/constants.ts +0 -134
  648. package/dist/src/lib/controls.ts +0 -42
  649. package/dist/src/lib/convex-hull/ConvexHull.svelte +0 -157
  650. package/dist/src/lib/convex-hull/ConvexHull2D.svelte +0 -825
  651. package/dist/src/lib/convex-hull/ConvexHull3D.svelte +0 -1801
  652. package/dist/src/lib/convex-hull/ConvexHull4D.svelte +0 -1398
  653. package/dist/src/lib/convex-hull/ConvexHullControls.svelte +0 -535
  654. package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +0 -125
  655. package/dist/src/lib/convex-hull/ConvexHullStats.svelte +0 -929
  656. package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +0 -131
  657. package/dist/src/lib/convex-hull/GasPressureControls.svelte +0 -247
  658. package/dist/src/lib/convex-hull/StructurePopup.svelte +0 -151
  659. package/dist/src/lib/convex-hull/TemperatureSlider.svelte +0 -140
  660. package/dist/src/lib/convex-hull/barycentric-coords.ts +0 -246
  661. package/dist/src/lib/convex-hull/demo-temperature.ts +0 -63
  662. package/dist/src/lib/convex-hull/gas-thermodynamics.ts +0 -405
  663. package/dist/src/lib/convex-hull/helpers.ts +0 -932
  664. package/dist/src/lib/convex-hull/index.ts +0 -202
  665. package/dist/src/lib/convex-hull/thermodynamics.ts +0 -2192
  666. package/dist/src/lib/convex-hull/types.ts +0 -267
  667. package/dist/src/lib/coordination/CoordinationBarPlot.svelte +0 -311
  668. package/dist/src/lib/coordination/calc-coordination.ts +0 -93
  669. package/dist/src/lib/coordination/index.ts +0 -9
  670. package/dist/src/lib/effects.svelte.ts +0 -48
  671. package/dist/src/lib/element/ElementHeading.svelte +0 -26
  672. package/dist/src/lib/element/ElementPhoto.svelte +0 -57
  673. package/dist/src/lib/element/ElementStats.svelte +0 -80
  674. package/dist/src/lib/element/ElementTile.svelte +0 -484
  675. package/dist/src/lib/element/data.ts +0 -14
  676. package/dist/src/lib/element/index.ts +0 -8
  677. package/dist/src/lib/element/types.ts +0 -62
  678. package/dist/src/lib/feedback/ClickFeedback.svelte +0 -58
  679. package/dist/src/lib/feedback/DragOverlay.svelte +0 -42
  680. package/dist/src/lib/feedback/index.ts +0 -4
  681. package/dist/src/lib/fermi-surface/FermiSlice.svelte +0 -189
  682. package/dist/src/lib/fermi-surface/FermiSurface.svelte +0 -600
  683. package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +0 -448
  684. package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +0 -794
  685. package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
  686. package/dist/src/lib/fermi-surface/compute.ts +0 -728
  687. package/dist/src/lib/fermi-surface/constants.ts +0 -32
  688. package/dist/src/lib/fermi-surface/export.ts +0 -64
  689. package/dist/src/lib/fermi-surface/index.ts +0 -14
  690. package/dist/src/lib/fermi-surface/marching-cubes.ts +0 -3
  691. package/dist/src/lib/fermi-surface/parse.ts +0 -574
  692. package/dist/src/lib/fermi-surface/symmetry.ts +0 -56
  693. package/dist/src/lib/fermi-surface/types.ts +0 -159
  694. package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +0 -1545
  695. package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
  696. package/dist/src/lib/heatmap-matrix/index.ts +0 -167
  697. package/dist/src/lib/heatmap-matrix/shared.ts +0 -7
  698. package/dist/src/lib/icons.ts +0 -650
  699. package/dist/src/lib/index.ts +0 -61
  700. package/dist/src/lib/io/decompress.ts +0 -92
  701. package/dist/src/lib/io/export.ts +0 -385
  702. package/dist/src/lib/io/fetch.ts +0 -46
  703. package/dist/src/lib/io/file-drop.ts +0 -51
  704. package/dist/src/lib/io/index.ts +0 -7
  705. package/dist/src/lib/io/is-binary.ts +0 -24
  706. package/dist/src/lib/io/types.ts +0 -8
  707. package/dist/src/lib/io/url-drop.ts +0 -141
  708. package/dist/src/lib/isosurface/Isosurface.svelte +0 -285
  709. package/dist/src/lib/isosurface/IsosurfaceControls.svelte +0 -277
  710. package/dist/src/lib/isosurface/index.ts +0 -7
  711. package/dist/src/lib/isosurface/parse.ts +0 -656
  712. package/dist/src/lib/isosurface/slice.ts +0 -175
  713. package/dist/src/lib/isosurface/types.ts +0 -309
  714. package/dist/src/lib/labels.ts +0 -320
  715. package/dist/src/lib/layout/FullscreenToggle.svelte +0 -50
  716. package/dist/src/lib/layout/InfoCard.svelte +0 -120
  717. package/dist/src/lib/layout/InfoTag.svelte +0 -185
  718. package/dist/src/lib/layout/PropertyFilter.svelte +0 -246
  719. package/dist/src/lib/layout/SettingsSection.svelte +0 -148
  720. package/dist/src/lib/layout/SubpageGrid.svelte +0 -82
  721. package/dist/src/lib/layout/fullscreen.ts +0 -65
  722. package/dist/src/lib/layout/index.ts +0 -11
  723. package/dist/src/lib/layout/json-tree/JsonNode.svelte +0 -548
  724. package/dist/src/lib/layout/json-tree/JsonTree.svelte +0 -1230
  725. package/dist/src/lib/layout/json-tree/index.ts +0 -3
  726. package/dist/src/lib/layout/json-tree/types.ts +0 -126
  727. package/dist/src/lib/layout/json-tree/utils.ts +0 -682
  728. package/dist/src/lib/marching-cubes.ts +0 -614
  729. package/dist/src/lib/math.ts +0 -1081
  730. package/dist/src/lib/overlays/ContextMenu.svelte +0 -162
  731. package/dist/src/lib/overlays/CopyButton.svelte +0 -45
  732. package/dist/src/lib/overlays/DragControlTab.svelte +0 -98
  733. package/dist/src/lib/overlays/DraggablePane.svelte +0 -487
  734. package/dist/src/lib/overlays/InfoPaneCards.svelte +0 -149
  735. package/dist/src/lib/overlays/index.ts +0 -3
  736. package/dist/src/lib/periodic-table/PeriodicTable.svelte +0 -469
  737. package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +0 -557
  738. package/dist/src/lib/periodic-table/PropertySelect.svelte +0 -37
  739. package/dist/src/lib/periodic-table/index.ts +0 -12
  740. package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
  741. package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +0 -444
  742. package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
  743. package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +0 -184
  744. package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +0 -391
  745. package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +0 -203
  746. package/dist/src/lib/phase-diagram/build-diagram.ts +0 -186
  747. package/dist/src/lib/phase-diagram/colors.ts +0 -58
  748. package/dist/src/lib/phase-diagram/diagram-input.ts +0 -40
  749. package/dist/src/lib/phase-diagram/index.ts +0 -13
  750. package/dist/src/lib/phase-diagram/parse.ts +0 -348
  751. package/dist/src/lib/phase-diagram/svg-to-diagram.ts +0 -1023
  752. package/dist/src/lib/phase-diagram/types.ts +0 -144
  753. package/dist/src/lib/phase-diagram/utils.ts +0 -775
  754. package/dist/src/lib/plot/AxisLabel.svelte +0 -51
  755. package/dist/src/lib/plot/BarPlot.svelte +0 -2113
  756. package/dist/src/lib/plot/BarPlotControls.svelte +0 -66
  757. package/dist/src/lib/plot/BinnedScatterPlot.svelte +0 -1114
  758. package/dist/src/lib/plot/ColorBar.svelte +0 -721
  759. package/dist/src/lib/plot/ColorScaleSelect.svelte +0 -54
  760. package/dist/src/lib/plot/ElementScatter.svelte +0 -63
  761. package/dist/src/lib/plot/FillArea.svelte +0 -223
  762. package/dist/src/lib/plot/Histogram.svelte +0 -1558
  763. package/dist/src/lib/plot/HistogramControls.svelte +0 -212
  764. package/dist/src/lib/plot/InteractiveAxisLabel.svelte +0 -96
  765. package/dist/src/lib/plot/Line.svelte +0 -84
  766. package/dist/src/lib/plot/PlotAxis.svelte +0 -169
  767. package/dist/src/lib/plot/PlotControls.svelte +0 -537
  768. package/dist/src/lib/plot/PlotLegend.svelte +0 -569
  769. package/dist/src/lib/plot/PlotTooltip.svelte +0 -67
  770. package/dist/src/lib/plot/PortalSelect.svelte +0 -253
  771. package/dist/src/lib/plot/ReferenceLine.svelte +0 -204
  772. package/dist/src/lib/plot/ReferenceLine3D.svelte +0 -156
  773. package/dist/src/lib/plot/ReferencePlane.svelte +0 -175
  774. package/dist/src/lib/plot/ScatterPlot.svelte +0 -2778
  775. package/dist/src/lib/plot/ScatterPlot3D.svelte +0 -529
  776. package/dist/src/lib/plot/ScatterPlot3DControls.svelte +0 -437
  777. package/dist/src/lib/plot/ScatterPlot3DScene.svelte +0 -912
  778. package/dist/src/lib/plot/ScatterPlotControls.svelte +0 -306
  779. package/dist/src/lib/plot/ScatterPoint.svelte +0 -182
  780. package/dist/src/lib/plot/SpacegroupBarPlot.svelte +0 -293
  781. package/dist/src/lib/plot/Surface3D.svelte +0 -197
  782. package/dist/src/lib/plot/ZeroLines.svelte +0 -97
  783. package/dist/src/lib/plot/ZoomRect.svelte +0 -23
  784. package/dist/src/lib/plot/adaptive-density.ts +0 -316
  785. package/dist/src/lib/plot/auto-place.ts +0 -184
  786. package/dist/src/lib/plot/axis-utils.ts +0 -122
  787. package/dist/src/lib/plot/binned-scatter-types.ts +0 -83
  788. package/dist/src/lib/plot/data-cleaning.ts +0 -1069
  789. package/dist/src/lib/plot/data-transform.ts +0 -69
  790. package/dist/src/lib/plot/defaults.ts +0 -9
  791. package/dist/src/lib/plot/fill-utils.ts +0 -494
  792. package/dist/src/lib/plot/hover-lock.svelte.ts +0 -60
  793. package/dist/src/lib/plot/index.ts +0 -53
  794. package/dist/src/lib/plot/interactions.ts +0 -119
  795. package/dist/src/lib/plot/layout.ts +0 -425
  796. package/dist/src/lib/plot/reference-line.ts +0 -426
  797. package/dist/src/lib/plot/scales.ts +0 -654
  798. package/dist/src/lib/plot/svg.ts +0 -23
  799. package/dist/src/lib/plot/types.ts +0 -1144
  800. package/dist/src/lib/plot/utils/label-placement.ts +0 -541
  801. package/dist/src/lib/plot/utils/series-visibility.ts +0 -140
  802. package/dist/src/lib/plot/utils.ts +0 -11
  803. package/dist/src/lib/rdf/RdfPlot.svelte +0 -247
  804. package/dist/src/lib/rdf/calc-rdf.ts +0 -167
  805. package/dist/src/lib/rdf/index.ts +0 -27
  806. package/dist/src/lib/sanitize.ts +0 -126
  807. package/dist/src/lib/settings.ts +0 -1479
  808. package/dist/src/lib/spectral/Bands.svelte +0 -1040
  809. package/dist/src/lib/spectral/BandsAndDos.svelte +0 -134
  810. package/dist/src/lib/spectral/BrillouinBandsDos.svelte +0 -252
  811. package/dist/src/lib/spectral/Dos.svelte +0 -697
  812. package/dist/src/lib/spectral/helpers.ts +0 -1381
  813. package/dist/src/lib/spectral/index.ts +0 -8
  814. package/dist/src/lib/spectral/types.ts +0 -112
  815. package/dist/src/lib/state.svelte.ts +0 -64
  816. package/dist/src/lib/structure/Arrow.svelte +0 -72
  817. package/dist/src/lib/structure/AtomLegend.svelte +0 -815
  818. package/dist/src/lib/structure/Bond.svelte +0 -140
  819. package/dist/src/lib/structure/CanvasTooltip.svelte +0 -33
  820. package/dist/src/lib/structure/CellSelect.svelte +0 -349
  821. package/dist/src/lib/structure/Cylinder.svelte +0 -45
  822. package/dist/src/lib/structure/Lattice.svelte +0 -196
  823. package/dist/src/lib/structure/Structure.svelte +0 -2248
  824. package/dist/src/lib/structure/StructureControls.svelte +0 -1273
  825. package/dist/src/lib/structure/StructureExportPane.svelte +0 -252
  826. package/dist/src/lib/structure/StructureInfoPane.svelte +0 -737
  827. package/dist/src/lib/structure/StructureScene.svelte +0 -2255
  828. package/dist/src/lib/structure/atom-properties.ts +0 -316
  829. package/dist/src/lib/structure/bond-order-perception.ts +0 -447
  830. package/dist/src/lib/structure/bonding.ts +0 -944
  831. package/dist/src/lib/structure/export.ts +0 -861
  832. package/dist/src/lib/structure/index.ts +0 -291
  833. package/dist/src/lib/structure/label-placement.ts +0 -130
  834. package/dist/src/lib/structure/measure.ts +0 -45
  835. package/dist/src/lib/structure/parse.ts +0 -1705
  836. package/dist/src/lib/structure/partial-occupancy.ts +0 -183
  837. package/dist/src/lib/structure/pbc.ts +0 -164
  838. package/dist/src/lib/structure/supercell.ts +0 -226
  839. package/dist/src/lib/structure/validation.ts +0 -11
  840. package/dist/src/lib/symmetry/SymmetryStats.svelte +0 -226
  841. package/dist/src/lib/symmetry/WyckoffTable.svelte +0 -120
  842. package/dist/src/lib/symmetry/cell-transform.ts +0 -118
  843. package/dist/src/lib/symmetry/index.ts +0 -348
  844. package/dist/src/lib/symmetry/spacegroups.ts +0 -404
  845. package/dist/src/lib/table/HeatmapTable.svelte +0 -1833
  846. package/dist/src/lib/table/ToggleMenu.svelte +0 -385
  847. package/dist/src/lib/table/index.ts +0 -139
  848. package/dist/src/lib/theme/ThemeControl.svelte +0 -53
  849. package/dist/src/lib/theme/index.ts +0 -107
  850. package/dist/src/lib/time.ts +0 -71
  851. package/dist/src/lib/tooltip/TooltipContent.svelte +0 -58
  852. package/dist/src/lib/tooltip/index.ts +0 -2
  853. package/dist/src/lib/tooltip/types.ts +0 -13
  854. package/dist/src/lib/trajectory/Trajectory.svelte +0 -1545
  855. package/dist/src/lib/trajectory/TrajectoryError.svelte +0 -128
  856. package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +0 -357
  857. package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +0 -313
  858. package/dist/src/lib/trajectory/constants.ts +0 -7
  859. package/dist/src/lib/trajectory/extract.ts +0 -196
  860. package/dist/src/lib/trajectory/format-detect.ts +0 -96
  861. package/dist/src/lib/trajectory/frame-reader.ts +0 -456
  862. package/dist/src/lib/trajectory/helpers.ts +0 -217
  863. package/dist/src/lib/trajectory/index.ts +0 -218
  864. package/dist/src/lib/trajectory/parse/ase.ts +0 -109
  865. package/dist/src/lib/trajectory/parse/hdf5.ts +0 -173
  866. package/dist/src/lib/trajectory/parse/index.ts +0 -411
  867. package/dist/src/lib/trajectory/parse/lammps.ts +0 -215
  868. package/dist/src/lib/trajectory/parse/vasp.ts +0 -102
  869. package/dist/src/lib/trajectory/parse/xyz.ts +0 -143
  870. package/dist/src/lib/trajectory/plotting.ts +0 -599
  871. package/dist/src/lib/trajectory/types.ts +0 -13
  872. package/dist/src/lib/utils.ts +0 -56
  873. package/dist/src/lib/xrd/XrdPlot.svelte +0 -615
  874. package/dist/src/lib/xrd/broadening.ts +0 -130
  875. package/dist/src/lib/xrd/calc-xrd.ts +0 -397
  876. package/dist/src/lib/xrd/index.ts +0 -38
  877. package/dist/src/lib/xrd/parse.ts +0 -858
  878. package/dist/webview.js +0 -29421
  879. package/icon.png +0 -0
  880. package/matterviz-0.3.2.vsix +0 -0
  881. package/matterviz-0.3.4.vsix +0 -0
  882. package/matterviz-0.3.5.vsix +0 -0
  883. package/scripts/sync-config.ts +0 -101
  884. package/src/declarations.d.ts +0 -2
  885. package/src/extension.ts +0 -972
  886. package/src/node-io.ts +0 -65
  887. package/src/types.ts +0 -17
  888. package/src/webview/JsonBrowser.svelte +0 -1079
  889. package/src/webview/PlotPanel.svelte +0 -346
  890. package/src/webview/detect.ts +0 -444
  891. package/src/webview/main.ts +0 -764
  892. package/src/webview/plot-utils.ts +0 -250
  893. package/test-fixtures/all-viz-types.json.gz +0 -0
  894. package/test-fixtures/plot-demo-data.json.gz +0 -0
  895. package/tests/detect.test.ts +0 -604
  896. package/tests/extension.test.ts +0 -2041
  897. package/tests/node-io.test.ts +0 -39
  898. package/tests/plot-utils.test.ts +0 -302
  899. package/tests/vite-plugin-json-gz.test.ts +0 -114
  900. package/tests/vscode-mock.ts +0 -18
  901. package/tests/webview.test.ts +0 -231
  902. package/tsconfig.json +0 -20
  903. package/vite-plugin-json-gz.ts +0 -29
  904. package/vite.config.ts +0 -34
  905. package/vite.extension.config.ts +0 -34
  906. /package/dist/{src/lib/EmptyState.svelte → EmptyState.svelte} +0 -0
  907. /package/dist/{src/lib/chempot-diagram → chempot-diagram}/ChemPotScene3D.svelte +0 -0
  908. /package/dist/{src/lib/colors → colors}/alloy-colors.json +0 -0
  909. /package/dist/{src/lib/colors → colors}/dark-mode-colors.json +0 -0
  910. /package/dist/{src/lib/colors → colors}/jmol-colors.json +0 -0
  911. /package/dist/{src/lib/colors → colors}/muted-colors.json +0 -0
  912. /package/dist/{src/lib/colors → colors}/pastel-colors.json +0 -0
  913. /package/dist/{src/lib/colors → colors}/vesta-colors.json +0 -0
  914. /package/dist/{src/lib/element → element}/BohrAtom.svelte +0 -0
  915. /package/dist/{src/lib/element → element}/Nucleus.svelte +0 -0
  916. /package/dist/{src/lib/element → element}/data.json +0 -0
  917. /package/dist/{src/lib/element → element}/data.json.gz +0 -0
  918. /package/dist/{src/lib/element → element}/data.json.gz.d.ts +0 -0
  919. /package/dist/{src/lib/element → element}/data.schema.json +0 -0
  920. /package/dist/{src/lib/element-image-urls.json → element-image-urls.json} +0 -0
  921. /package/dist/{src/lib/feedback → feedback}/Spinner.svelte +0 -0
  922. /package/dist/{src/lib/feedback → feedback}/StatusMessage.svelte +0 -0
  923. /package/dist/{src/lib/layout → layout}/json-tree/JsonValue.svelte +0 -0
  924. /package/dist/{src/lib/periodic-table → periodic-table}/TableInset.svelte +0 -0
  925. /package/dist/{src/lib/theme → theme}/themes.mjs +0 -0
  926. /package/dist/{src/lib/xrd → xrd}/atomic_scattering_params.json +0 -0
@@ -0,0 +1,1885 @@
1
+ <script lang="ts">
2
+ import { luminance, watch_dark_mode } from '../colors'
3
+ import Icon from '../Icon.svelte'
4
+ import { format_num } from '../labels'
5
+ import { SettingsSection } from '../layout'
6
+ import ContextMenu from '../overlays/ContextMenu.svelte'
7
+ import DraggablePane from '../overlays/DraggablePane.svelte'
8
+ import type {
9
+ CellSnippet,
10
+ CellVal,
11
+ ExportData,
12
+ InitialSort,
13
+ Label,
14
+ MultiSortState,
15
+ Pagination,
16
+ RowData,
17
+ Search,
18
+ SortHint,
19
+ SortState,
20
+ SpecialCells,
21
+ } from './'
22
+ import { calc_cell_color, strip_html } from './'
23
+ import { sanitize_html } from '../sanitize'
24
+ import { normalize_unicode_minus } from '../utils'
25
+ import type { Snippet } from 'svelte'
26
+ import { tooltip } from 'svelte-multiselect/attachments'
27
+ import { flip } from 'svelte/animate'
28
+ import type { HTMLAttributes } from 'svelte/elements'
29
+ import { SvelteMap } from 'svelte/reactivity'
30
+
31
+ // Helper to check if value is invalid (null, undefined, NaN)
32
+ const is_invalid = (val: unknown) =>
33
+ val == null || (typeof val === `number` && Number.isNaN(val))
34
+
35
+ // tooltip() wires [title]/[aria-label]/[data-title] elements once when it runs.
36
+ // Table cells are replaced when the table re-renders (sort, filter, data or
37
+ // pagination changes), which would silently drop their tooltips. Observe the
38
+ // container and incrementally wire newly added elements / unwire removed ones,
39
+ // instead of tearing down and rebuilding every tooltip on each unrelated DOM
40
+ // mutation (dropdowns, panes, pagination, context menu).
41
+ const tooltip_selector = `[title], [aria-label], [data-title]`
42
+ function table_tooltips(node: HTMLElement) {
43
+ const options = { allow_html: true } as const
44
+ // Per-element cleanups so individual nodes can be unwired as they leave the DOM.
45
+ const wired = new SvelteMap<Element, () => void>()
46
+
47
+ const wire = (root: Element) => {
48
+ const targets = root.matches(tooltip_selector)
49
+ ? [root, ...root.querySelectorAll(tooltip_selector)]
50
+ : [...root.querySelectorAll(tooltip_selector)]
51
+ for (const el of targets) {
52
+ if (!(el instanceof HTMLElement) || wired.has(el)) continue
53
+ // tooltip() only mutates attributes (title -> data-original-title), never
54
+ // childList, so wiring here can't re-trigger the childList observer below.
55
+ const cleanup = tooltip(options)(el)
56
+ if (cleanup) wired.set(el, cleanup)
57
+ }
58
+ }
59
+
60
+ wire(node)
61
+ const observer = new MutationObserver((mutations) => {
62
+ // Unwire elements that left the DOM. isConnected stays true for moved nodes
63
+ // (e.g. row reordering on sort), so those keep their tooltips without churn.
64
+ // Deleting the current entry mid-iteration is safe for Map.
65
+ for (const [el, cleanup] of wired) {
66
+ if (!el.isConnected) {
67
+ cleanup()
68
+ wired.delete(el)
69
+ }
70
+ }
71
+ // Wire only the freshly added subtrees, not the whole container.
72
+ for (const { addedNodes } of mutations) {
73
+ for (const added of addedNodes) {
74
+ if (added instanceof Element) wire(added)
75
+ }
76
+ }
77
+ })
78
+ observer.observe(node, { childList: true, subtree: true })
79
+ return () => {
80
+ observer.disconnect()
81
+ for (const cleanup of wired.values()) cleanup()
82
+ wired.clear()
83
+ }
84
+ }
85
+
86
+ const NUMERIC_WITH_ERROR_RE =
87
+ /^([-+−]?(?:\d+\.?\d*|\d*\.\d+)(?:[eE][-+−]?\d+)?)\s*(?:±|\+[-−]|\()/
88
+
89
+ const parse_numeric_string = (val: string): number | null => {
90
+ const numeric_str = val.match(NUMERIC_WITH_ERROR_RE)?.[1] ?? val
91
+ if (numeric_str.trim() === ``) return null
92
+ const num = Number(normalize_unicode_minus(numeric_str))
93
+ return isNaN(num) ? null : num
94
+ }
95
+
96
+ // Get sort value from a cell (handles HTML data-sort-value and numbers with errors)
97
+ const get_sort_val = (val: CellVal): string | number => {
98
+ if (typeof val === `string`) {
99
+ // Check for HTML data-sort-value attribute first
100
+ const sort_attr_match = val.match(/data-sort-value="([^"]*)"/)
101
+ if (sort_attr_match) {
102
+ const num = Number(sort_attr_match[1])
103
+ return isNaN(num) ? sort_attr_match[1] : num
104
+ }
105
+ const num = parse_numeric_string(val)
106
+ if (num !== null) return num
107
+ }
108
+ return val as string | number
109
+ }
110
+
111
+ let {
112
+ data = $bindable([]),
113
+ columns = [],
114
+ sort_hint = undefined,
115
+ cell,
116
+ special_cells,
117
+ controls,
118
+ initial_sort = undefined,
119
+ sort = $bindable({ column: ``, dir: `asc` }), // allows external control/sync of sorting
120
+ fixed_header = false,
121
+ default_num_format = `.3`,
122
+ show_heatmap = $bindable(true),
123
+ heatmap_class = `heatmap`,
124
+ onrowclick,
125
+ onrowdblclick,
126
+ column_order = $bindable([]),
127
+ export_data = false,
128
+ show_column_toggle = false,
129
+ search = false,
130
+ show_row_select = false,
131
+ pagination = false,
132
+ selected_rows = $bindable([]),
133
+ hidden_columns = $bindable([]),
134
+ scroll_style,
135
+ root_style,
136
+ onsort = undefined,
137
+ onsorterror = undefined,
138
+ loading = $bindable(false),
139
+ sort_data = true,
140
+ heatmap_opacity = $bindable(1),
141
+ empty_message = `No data`,
142
+ show_row_numbers = false,
143
+ allow_better_toggle = false,
144
+ show_controls = $bindable(false),
145
+ controls_open = $bindable(false),
146
+ header_cell,
147
+ footer,
148
+ ...rest
149
+ }: HTMLAttributes<HTMLDivElement> & {
150
+ data: RowData[]
151
+ columns?: Label[]
152
+ sort_hint?: SortHint
153
+ cell?: CellSnippet
154
+ special_cells?: SpecialCells
155
+ controls?: Snippet
156
+ initial_sort?: InitialSort
157
+ sort?: { column: string; dir: `asc` | `desc` }
158
+ fixed_header?: boolean
159
+ default_num_format?: string
160
+ show_heatmap?: boolean
161
+ heatmap_class?: string
162
+ onrowclick?: (event: MouseEvent | KeyboardEvent, row: RowData) => void
163
+ onrowdblclick?: (event: MouseEvent, row: RowData) => void
164
+ // Array of column IDs to control display order. IDs are derived as:
165
+ // - Ungrouped columns: col.key ?? col.label
166
+ // - Grouped columns: `${col.key ?? col.label} (${col.group})`
167
+ // This allows persisting/restoring column order across sessions.
168
+ column_order?: string[]
169
+ export_data?: ExportData
170
+ show_column_toggle?: boolean
171
+ search?: Search
172
+ show_row_select?: boolean
173
+ pagination?: Pagination
174
+ selected_rows?: RowData[]
175
+ hidden_columns?: string[]
176
+ scroll_style?: string
177
+ // Inline styles for the root table container (merged with rest.style). Use instead of global CSS overrides.
178
+ root_style?: string
179
+ // Async callback for server-side sorting. When provided, client-side sorting is skipped
180
+ // and the callback is called with (column_id, direction) to fetch new data from server.
181
+ onsort?: (column: string, dir: `asc` | `desc`) => Promise<RowData[]>
182
+ // Callback when onsort fails, receives the error for parent handling (e.g. toast notification)
183
+ onsorterror?: (error: unknown, column: string, dir: `asc` | `desc`) => void
184
+ // Loading state during async sort operations
185
+ loading?: boolean
186
+ // Whether to sort data client-side. Set to false when parent handles sorting externally.
187
+ // When onsort is provided, sort_data behavior is implicitly false.
188
+ sort_data?: boolean
189
+ // Heatmap cell background opacity (0–1). Controls both the visual fade via CSS
190
+ // color-mix() and the JS text contrast correction. Default 1 (fully opaque).
191
+ heatmap_opacity?: number
192
+ // Message shown when the table has no data rows. Set to empty string to hide.
193
+ empty_message?: string
194
+ // Show a row number column as the first column
195
+ show_row_numbers?: boolean
196
+ // When true, show a toggle in colored column headers to cycle gradient direction
197
+ allow_better_toggle?: boolean
198
+ // Whether the gear icon for the controls pane is visible
199
+ show_controls?: boolean
200
+ // Whether the controls pane is expanded
201
+ controls_open?: boolean
202
+ // Custom snippet for rendering header cells. Falls back to {@html col.label}.
203
+ header_cell?: Snippet<[{ col: Label }]>
204
+ // Footer snippet rendered inside <tfoot> below the table body
205
+ footer?: Snippet
206
+ } = $props()
207
+
208
+ let container_el = $state<HTMLDivElement>()
209
+
210
+ // Read --page-bg from computed style for text contrast calculation.
211
+ // Recalculates on mount and when the theme changes (dark/light mode toggle).
212
+ let page_bg_lum = $state(luminance(`white`))
213
+ $effect(() => {
214
+ if (!container_el) return
215
+ const read_page_bg = () => {
216
+ if (!container_el) return
217
+ const page_bg = getComputedStyle(container_el).getPropertyValue(`--page-bg`)
218
+ .trim()
219
+ page_bg_lum = luminance(page_bg || `white`)
220
+ }
221
+ read_page_bg()
222
+ return watch_dark_mode(read_page_bg)
223
+ })
224
+
225
+ // Detect HTML to prevent setting raw HTML as data-sort-value. Simple string matching
226
+ // suffices since false positives just skip setting the attr (sorting still works by inner data-sort-value).
227
+ function is_html_str(val: unknown): boolean {
228
+ if (typeof val !== `string`) return false
229
+ return (
230
+ (val.includes(`<`) && val.includes(`>`)) || // Has angle brackets
231
+ val.startsWith(`&lt;`) || // Has HTML entity for <
232
+ val.includes(`href=`) || // Has href attribute
233
+ val.includes(`class=`) // Has class attribute
234
+ )
235
+ }
236
+
237
+ // Normalize initial_sort config
238
+ let initial_sort_config = $derived(
239
+ initial_sort
240
+ ? typeof initial_sort === `string`
241
+ ? { column: initial_sort, direction: `asc` as const }
242
+ : { direction: `asc` as const, ...initial_sort }
243
+ : null,
244
+ )
245
+
246
+ // Normalize pagination config
247
+ let pagination_config = $derived(
248
+ pagination
249
+ ? { page_size: 25, ...(typeof pagination === `object` ? pagination : {}) }
250
+ : null,
251
+ )
252
+
253
+ // Mutable page size — writable $derived allows user to change via dropdown
254
+ let effective_page_size = $derived(pagination_config?.page_size ?? 25)
255
+
256
+ // Normalize search config
257
+ let search_config = $derived(
258
+ search
259
+ ? {
260
+ placeholder: `Filter...`,
261
+ expanded: false,
262
+ ...(typeof search === `object` ? search : {}),
263
+ }
264
+ : null,
265
+ )
266
+
267
+ // Normalize export_data config
268
+ type ExportFormat = `csv` | `json`
269
+ const default_formats: ExportFormat[] = [`csv`, `json`]
270
+ let export_config = $derived(
271
+ export_data
272
+ ? {
273
+ formats: default_formats,
274
+ filename: `table-export`,
275
+ ...(typeof export_data === `object` ? export_data : {}),
276
+ }
277
+ : null,
278
+ )
279
+
280
+ // Derive sort_state from bindable prop, falling back to initial_sort if sort not yet set
281
+ // This ensures immediate sorting on first render without waiting for effects
282
+ let sort_state = $derived<SortState>({
283
+ column: sort.column || initial_sort_config?.column || ``,
284
+ ascending: sort.column
285
+ ? sort.dir !== `desc`
286
+ : initial_sort_config?.direction !== `desc`,
287
+ })
288
+
289
+ // Multi-column sort state (for Shift+click)
290
+ let multi_sort = $state<MultiSortState>([])
291
+
292
+ // Search/filter state
293
+ let search_query = $state(``)
294
+ let search_expanded = $derived(search_config?.expanded ?? false)
295
+
296
+ // Pagination state
297
+ let current_page = $state(1)
298
+
299
+ // Dropdown states
300
+ let show_column_dropdown = $state(false)
301
+ let show_export_dropdown = $state(false)
302
+
303
+ // Per-column gradient direction overrides (user-toggled via header)
304
+ let better_overrides = new SvelteMap<string, `higher` | `lower`>()
305
+
306
+ // Per-column color scale overrides
307
+ let color_scale_overrides = new SvelteMap<string, string>()
308
+
309
+ const color_scale_options = [
310
+ `interpolateViridis`,
311
+ `interpolatePlasma`,
312
+ `interpolateInferno`,
313
+ `interpolateCividis`,
314
+ `interpolateTurbo`,
315
+ `interpolateBlues`,
316
+ `interpolateGreens`,
317
+ `interpolateReds`,
318
+ `interpolateYlOrRd`,
319
+ ] as const
320
+
321
+ // Columns that have a color gradient
322
+ let colored_columns = $derived(
323
+ columns.filter((col) =>
324
+ col.color_scale !== null && col.color_scale !== undefined
325
+ ),
326
+ )
327
+
328
+ // Column resize state
329
+ let resize_col_id = $state<string | null>(null)
330
+ let resize_start_x = $state(0)
331
+ let resize_start_width = $state(0)
332
+ let column_widths = $state<Record<string, number>>({})
333
+
334
+ // Auto-discover columns from data keys when none are provided
335
+ $effect.pre(() => {
336
+ if (columns.length > 0 || data.length === 0) return
337
+ const seen: Record<string, true> = {}
338
+ for (const row of data.slice(0, 50)) {
339
+ for (const key of Object.keys(row)) {
340
+ if (key !== `style` && key !== `class`) seen[key] = true
341
+ }
342
+ }
343
+ columns = Object.keys(seen).map((key) => ({ label: key }))
344
+ })
345
+
346
+ // Helper to make column IDs (needed since column labels in different groups can be repeated)
347
+ const get_col_id = (col: Label) =>
348
+ col.group ? `${col.key ?? col.label} (${col.group})` : (col.key ?? col.label)
349
+
350
+ // Sync column_order with columns: initialize if empty, remove stale IDs, append new IDs
351
+ $effect(() => {
352
+ if (columns.length === 0) return
353
+ const col_ids = columns.map(get_col_id)
354
+
355
+ // Case 1: First render - initialize with default order
356
+ if (column_order.length === 0) {
357
+ column_order = col_ids
358
+ return
359
+ }
360
+
361
+ // Case 2: Sync needed - keep valid IDs in their order, append any new ones
362
+ const valid_ids = new Set(col_ids)
363
+ const kept = column_order.filter((id) => valid_ids.has(id))
364
+ const new_ids = col_ids.filter((id) => !kept.includes(id))
365
+ const new_order = [...kept, ...new_ids]
366
+
367
+ // Skip assignment if content is unchanged to prevent infinite effect loop.
368
+ // After drag reorder, column_order differs from col_ids (default order) but the
369
+ // computed new_order equals the current column_order — assigning a new array
370
+ // reference would re-trigger this effect endlessly.
371
+ if (new_order.length === column_order.length &&
372
+ new_order.every((id, idx) => id === column_order[idx])) return
373
+
374
+ column_order = new_order
375
+ })
376
+
377
+ // Reorder columns based on column_order
378
+ let ordered_columns = $derived.by(() => {
379
+ if (column_order.length === 0) return columns
380
+
381
+ const col_map = new SvelteMap(columns.map((col) => [get_col_id(col), col]))
382
+
383
+ // Add columns in specified order, then any remaining columns that weren't in the order list
384
+ const ordered = column_order
385
+ .map((id) => col_map.get(id))
386
+ .filter((col): col is Label => col != null)
387
+
388
+ const ordered_ids = new Set(ordered.map(get_col_id))
389
+ const remaining = columns.filter((col) => !ordered_ids.has(get_col_id(col)))
390
+
391
+ return [...ordered, ...remaining]
392
+ })
393
+
394
+ let drag_col_id = $state<string | null>(null)
395
+ let drag_over_col_id = $state<string | null>(null)
396
+
397
+ // Merge root_style with rest.style for root div; omit style from rest to avoid duplicate
398
+ let rest_props = $derived.by(() => {
399
+ const { style: rest_style, ...other_props } = rest
400
+ const merged = [rest_style, root_style].filter(Boolean).join(`; `)
401
+ return { ...other_props, ...(merged ? { style: merged } : {}) }
402
+ })
403
+
404
+ // WeakMap to assign stable unique IDs to row objects for efficient comparison and keying
405
+ // This avoids O(n) JSON.stringify calls and prevents unnecessary re-renders
406
+ const row_id_map = new WeakMap<RowData, string>()
407
+ let row_id_counter = 0
408
+
409
+ function get_row_id(row: RowData): string {
410
+ let id = row_id_map.get(row)
411
+ if (id === undefined) {
412
+ id = `row_${row_id_counter++}`
413
+ row_id_map.set(row, id)
414
+ }
415
+ return id
416
+ }
417
+
418
+ // Returns 'left' or 'right' to indicate which side of target to insert dragged column
419
+ function get_drag_side(target_col_id: string): `left` | `right` | null {
420
+ if (!drag_col_id) return null
421
+ const drag_idx = column_order.indexOf(drag_col_id)
422
+ const target_idx = column_order.indexOf(target_col_id)
423
+ if (drag_idx === -1 || target_idx === -1) return null
424
+ return drag_idx < target_idx ? `right` : `left`
425
+ }
426
+
427
+ function reset_drag_state() {
428
+ drag_col_id = null
429
+ drag_over_col_id = null
430
+ }
431
+
432
+ const get_drag_col_group = () =>
433
+ ordered_columns.find((col) => get_col_id(col) === drag_col_id)?.group
434
+
435
+ function handle_drag_start(event: DragEvent, col: Label) {
436
+ if (!event.dataTransfer) return
437
+ drag_col_id = get_col_id(col)
438
+ event.dataTransfer.effectAllowed = `move`
439
+ event.dataTransfer.setData(`text/html`, ``)
440
+ }
441
+
442
+ function handle_drag_over(event: DragEvent, col: Label) {
443
+ event.preventDefault()
444
+ if (!event.dataTransfer) return
445
+ event.dataTransfer.dropEffect = `move`
446
+
447
+ // Prevent cross-group drag-over to keep group headers contiguous
448
+ if (get_drag_col_group() !== col.group) {
449
+ event.dataTransfer.dropEffect = `none`
450
+ drag_over_col_id = null
451
+ return
452
+ }
453
+
454
+ drag_over_col_id = get_col_id(col)
455
+ }
456
+
457
+ function handle_drop(event: DragEvent, target_col: Label) {
458
+ event.preventDefault()
459
+
460
+ // Block cross-group (or group→ungroup) reorders to preserve group contiguity
461
+ if (!drag_col_id || drag_col_id === get_col_id(target_col)) {
462
+ reset_drag_state()
463
+ return
464
+ }
465
+
466
+ // Block cross-group reorders to preserve group contiguity
467
+ if (get_drag_col_group() !== target_col.group) {
468
+ reset_drag_state()
469
+ return
470
+ }
471
+
472
+ const target_col_id = get_col_id(target_col)
473
+ const drag_idx = column_order.indexOf(drag_col_id)
474
+ const target_idx = column_order.indexOf(target_col_id)
475
+
476
+ if (drag_idx === -1 || target_idx === -1) {
477
+ reset_drag_state()
478
+ return
479
+ }
480
+
481
+ // Reorder: remove dragged column, then insert at target position
482
+ // When dragging left-to-right (drag_idx < target_idx), removing the dragged
483
+ // element shifts all subsequent indices down by 1, so we must adjust target_idx
484
+ const new_order = [...column_order]
485
+ new_order.splice(drag_idx, 1)
486
+ const adjusted_target = drag_idx < target_idx ? target_idx - 1 : target_idx
487
+ new_order.splice(adjusted_target, 0, drag_col_id)
488
+ column_order = new_order
489
+ reset_drag_state()
490
+ }
491
+
492
+ // Filter data based on search query
493
+ let filtered_data = $derived.by(() => {
494
+ const base_data = data?.filter?.((row) =>
495
+ Object.values(row).some((val) => val !== undefined)
496
+ ) ?? []
497
+
498
+ if (!search_query.trim()) return base_data
499
+
500
+ const query = search_query.toLowerCase().trim()
501
+ return base_data.filter((row) =>
502
+ Object.values(row).some((val) => {
503
+ if (val == null) return false
504
+ const clean_val = strip_html(String(val)).toLowerCase()
505
+ return clean_val.includes(query)
506
+ })
507
+ )
508
+ })
509
+
510
+ let sorted_data = $derived.by(() => {
511
+ // Skip client-side sorting when using async onsort callback or sort_data is false
512
+ if (onsort || !sort_data) return filtered_data
513
+
514
+ if (!sort_state.column && multi_sort.length === 0) return filtered_data
515
+
516
+ // Build sort criteria: multi_sort takes precedence, fallback to single sort
517
+ const sort_criteria = multi_sort.length > 0
518
+ ? multi_sort
519
+ : sort_state.column
520
+ ? [sort_state]
521
+ : []
522
+
523
+ if (sort_criteria.length === 0) return filtered_data
524
+
525
+ return [...filtered_data].sort((row1, row2) => {
526
+ for (const { column, ascending } of sort_criteria) {
527
+ const matched_col = ordered_columns.find((col) => get_col_id(col) === column)
528
+ if (!matched_col) continue
529
+
530
+ const col_id = get_col_id(matched_col)
531
+ const val1 = row1[col_id]
532
+ const val2 = row2[col_id]
533
+
534
+ if (val1 === val2) continue
535
+
536
+ // Push invalid values to bottom
537
+ if (is_invalid(val1) || is_invalid(val2)) {
538
+ return Number(is_invalid(val1)) - Number(is_invalid(val2))
539
+ }
540
+
541
+ const sort_val1 = get_sort_val(val1)
542
+ const sort_val2 = get_sort_val(val2)
543
+ const modifier = ascending ? 1 : -1
544
+
545
+ if (typeof sort_val1 === `string` && typeof sort_val2 === `string`) {
546
+ const cmp = sort_val1.localeCompare(sort_val2, undefined, {
547
+ numeric: true,
548
+ sensitivity: `base`,
549
+ })
550
+ if (cmp !== 0) return cmp * modifier
551
+ } else if (typeof sort_val1 !== typeof sort_val2) {
552
+ // number<string is false both ways, breaking the comparator: numbers sort first
553
+ return (typeof sort_val1 === `number` ? -1 : 1) * modifier
554
+ } else if (sort_val1 !== sort_val2) {
555
+ return (sort_val1 ?? 0) < (sort_val2 ?? 0) ? -modifier : modifier
556
+ }
557
+ }
558
+ return 0
559
+ })
560
+ })
561
+
562
+ // Paginated data
563
+ let paginated_data = $derived.by(() => {
564
+ if (!pagination_config) return sorted_data
565
+ const start = (current_page - 1) * effective_page_size
566
+ return sorted_data.slice(start, start + effective_page_size)
567
+ })
568
+
569
+ let total_pages = $derived(
570
+ Math.ceil(sorted_data.length / effective_page_size),
571
+ )
572
+
573
+ // Track previous values to detect actual changes
574
+ let prev_search_query = $state(``)
575
+ let prev_data_length = $state(0)
576
+
577
+ // Track async sort requests to prevent race conditions
578
+ let sort_request_id = 0
579
+
580
+ // Reset to page 1 when search query or data length actually changes
581
+ $effect(() => {
582
+ const query_changed = search_query !== prev_search_query
583
+ const data_changed = sorted_data.length !== prev_data_length
584
+
585
+ if (query_changed || data_changed) {
586
+ current_page = 1
587
+ prev_search_query = search_query
588
+ prev_data_length = sorted_data.length
589
+ } else if (total_pages > 0 && current_page > total_pages) {
590
+ // Clamp when total pages decreases (e.g., page size increase)
591
+ current_page = total_pages
592
+ }
593
+ })
594
+
595
+ async function sort_rows(
596
+ column: string,
597
+ group: string | undefined,
598
+ event: MouseEvent | KeyboardEvent,
599
+ ) {
600
+ // Find the column using both label and group if provided
601
+ const col = ordered_columns.find(
602
+ (candidate_col) => candidate_col.label === column && candidate_col.group === group,
603
+ )
604
+
605
+ if (!col) return // Skip if column not found
606
+ if (col.sortable === false) return // Skip sorting if column marked as unsortable
607
+
608
+ const col_id = get_col_id(col)
609
+
610
+ // Shift+click for multi-column sort
611
+ if (event.shiftKey) {
612
+ const existing_idx = multi_sort.findIndex((sort_entry) => sort_entry.column === col_id)
613
+ if (existing_idx !== -1) {
614
+ // Toggle direction or remove if clicked again
615
+ const existing = multi_sort[existing_idx]
616
+ if (existing.ascending === (col.better === `lower`)) {
617
+ // Remove from multi-sort
618
+ multi_sort = multi_sort.filter((_, idx) => idx !== existing_idx)
619
+ } else {
620
+ // Toggle direction
621
+ multi_sort = multi_sort.map((sort_entry, idx) =>
622
+ idx === existing_idx ? { ...sort_entry, ascending: !sort_entry.ascending } : sort_entry
623
+ )
624
+ }
625
+ } else {
626
+ // Add to multi-sort
627
+ multi_sort = [...multi_sort, {
628
+ column: col_id,
629
+ ascending: col.better === `lower`,
630
+ }]
631
+ }
632
+ // Clear single sort when using multi-sort
633
+ sort = { column: ``, dir: `asc` }
634
+ } else {
635
+ // Regular click - single column sort
636
+ multi_sort = [] // Clear multi-sort
637
+ // Use sort_state.column for comparison since it includes initial_sort fallback
638
+ const new_dir = sort_state.column !== col_id
639
+ ? (col.better === `lower` ? `asc` : `desc`)
640
+ : (sort_state.ascending ? `desc` : `asc`)
641
+
642
+ // Save previous sort state in case we need to revert on error
643
+ const prev_sort = { ...sort }
644
+ sort = { column: col_id, dir: new_dir }
645
+
646
+ // If onsort callback provided, fetch new data from server
647
+ if (onsort) {
648
+ loading = true
649
+ const request_id = ++sort_request_id
650
+ try {
651
+ const result = await onsort(col_id, new_dir)
652
+ // Only update if this is still the most recent request (avoid race condition)
653
+ if (request_id === sort_request_id) {
654
+ data = result
655
+ }
656
+ } catch (err) {
657
+ console.error(`Sort callback failed:`, err)
658
+ // Revert sort state on failure so UI doesn't show wrong direction
659
+ if (request_id === sort_request_id) {
660
+ sort = prev_sort
661
+ onsorterror?.(err, col_id, new_dir)
662
+ }
663
+ } finally {
664
+ // Only clear loading if this is still the most recent request
665
+ if (request_id === sort_request_id) {
666
+ loading = false
667
+ }
668
+ }
669
+ }
670
+ }
671
+ }
672
+
673
+ // Extract numeric value from strings with uncertainty notation: "1.23 ± 0.05", "1.23 +- 0.05", "1.23(5)"
674
+ function parse_numeric_val(val: CellVal): number | null {
675
+ if (typeof val === `number`) return Number.isNaN(val) ? null : val
676
+ return typeof val === `string` ? parse_numeric_string(val) : null
677
+ }
678
+
679
+ // Memoize parsed column values to avoid O(N²) re-parsing in calc_color
680
+ let parsed_column_values = $derived.by(() => {
681
+ const result = new SvelteMap<string, (number | null)[]>()
682
+ for (const col of ordered_columns) {
683
+ if (col.color_scale === null) continue
684
+ const col_id = get_col_id(col)
685
+ result.set(col_id, sorted_data.map((row) => parse_numeric_val(row[col_id])))
686
+ }
687
+ return result
688
+ })
689
+
690
+ function calc_color(val: CellVal, col: Label) {
691
+ if (!show_heatmap || col.color_scale === null) {
692
+ return { bg: null, text: null }
693
+ }
694
+
695
+ // Parse numeric value from strings with uncertainty notation
696
+ const numeric_val = parse_numeric_val(val)
697
+ if (numeric_val === null) return { bg: null, text: null }
698
+
699
+ const col_id = get_col_id(col)
700
+ // Use memoized parsed values for the column
701
+ const numeric_vals = parsed_column_values.get(col_id) ?? []
702
+
703
+ const better = better_overrides.get(col_id) ?? col.better
704
+ const scale = (color_scale_overrides.get(col_id) ?? col.color_scale ??
705
+ `interpolateViridis`) as Parameters<typeof calc_cell_color>[3]
706
+ const color = calc_cell_color(
707
+ numeric_val,
708
+ numeric_vals,
709
+ better,
710
+ scale,
711
+ col.scale_type || `linear`,
712
+ )
713
+
714
+ // Recompute text contrast against effective bg (cell bg blended with page bg by opacity).
715
+ // Approximation: blend luminances directly; accurate enough for black/white text choice.
716
+ if (color.bg && heatmap_opacity < 1) {
717
+ const blended_lum = luminance(color.bg) * heatmap_opacity +
718
+ page_bg_lum * (1 - heatmap_opacity)
719
+ color.text = blended_lum > 0.7 ? `black` : `white`
720
+ }
721
+ return color
722
+ }
723
+
724
+ let visible_columns = $derived(
725
+ ordered_columns.filter((col) =>
726
+ col.visible !== false && !hidden_columns.includes(get_col_id(col))
727
+ ),
728
+ )
729
+
730
+ const sort_indicator = (col: Label, current_sort_state: SortState) => {
731
+ const hide_sort_indicator = col.show_sort_indicator === false ||
732
+ col.style?.includes(`--hide-sort-indicator`)
733
+ if (hide_sort_indicator) return ``
734
+
735
+ const col_id = get_col_id(col)
736
+
737
+ // Check multi-sort first
738
+ const multi_idx = multi_sort.findIndex((sort_entry) => sort_entry.column === col_id)
739
+ if (multi_idx !== -1) {
740
+ const arrow = multi_sort[multi_idx].ascending ? `↓` : `↑`
741
+ const badge = multi_sort.length > 1 ? `<sup>${multi_idx + 1}</sup>` : ``
742
+ return `<span style="font-size: 0.8em;">${arrow}${badge}</span>`
743
+ }
744
+
745
+ const is_sorted = current_sort_state.column === col_id
746
+ if (!is_sorted) return ``
747
+ // Show indicator only for actively sorted columns.
748
+ const arrow = current_sort_state.ascending ? `↓` : `↑`
749
+
750
+ return arrow ? `<span style="font-size: 0.8em;">${arrow}</span>` : ``
751
+ }
752
+
753
+ // Context menu state for column header right-click
754
+ let context_menu_col = $state<string | null>(null)
755
+ let context_menu_pos = $state({ x: 0, y: 0 })
756
+
757
+ const better_sections = [
758
+ {
759
+ title: `Gradient direction`,
760
+ options: [
761
+ { value: `higher`, label: `▲ Higher is better` },
762
+ { value: `lower`, label: `▼ Lower is better` },
763
+ ],
764
+ },
765
+ ] as const
766
+
767
+ // Row selection using WeakMap-based ID lookup instead of O(n) JSON.stringify comparison
768
+ function toggle_row_select(row: RowData) {
769
+ const row_id = get_row_id(row)
770
+ const idx = selected_rows.findIndex((selected_row) => get_row_id(selected_row) === row_id)
771
+ if (idx !== -1) {
772
+ selected_rows = selected_rows.filter((_, i) => i !== idx)
773
+ } else {
774
+ selected_rows = [...selected_rows, row]
775
+ }
776
+ }
777
+
778
+ function is_row_selected(row: RowData): boolean {
779
+ const row_id = get_row_id(row)
780
+ return selected_rows.some((selected_row) => get_row_id(selected_row) === row_id)
781
+ }
782
+
783
+ // Select-all: checks if every row on the current page is selected
784
+ let all_page_selected = $derived(
785
+ paginated_data.length > 0 && paginated_data.every((row) => is_row_selected(row)),
786
+ )
787
+
788
+ function toggle_select_all() {
789
+ if (all_page_selected) {
790
+ const page_ids = new Set(paginated_data.map(get_row_id))
791
+ selected_rows = selected_rows.filter((row) => !page_ids.has(get_row_id(row)))
792
+ } else {
793
+ const already = new Set(selected_rows.map(get_row_id))
794
+ const new_rows = paginated_data.filter((row) => !already.has(get_row_id(row)))
795
+ selected_rows = [...selected_rows, ...new_rows]
796
+ }
797
+ }
798
+
799
+ // Data source for exports: selected rows when any are selected, otherwise all sorted data
800
+ let export_rows = $derived(
801
+ show_row_select && selected_rows.length > 0 ? selected_rows : sorted_data,
802
+ )
803
+
804
+ // Serialize table as delimited text (shared by CSV export and clipboard copy)
805
+ // Per RFC 4180, fields containing commas, double quotes, or newlines must be quoted
806
+ function serialize_table(delimiter: string, csv_quote = false): string {
807
+ const quote = (str: string) => {
808
+ if (!csv_quote) return str
809
+ if (str.includes(`,`) || str.includes(`"`) || str.includes(`\n`)) {
810
+ return `"${str.replaceAll('"', `""`)}"`
811
+ }
812
+ return str
813
+ }
814
+ const headers = visible_columns.map((col) => quote(strip_html(col.label)))
815
+ const rows = export_rows.map((row) =>
816
+ visible_columns.map((col) => {
817
+ const val = row[get_col_id(col)]
818
+ if (val == null) return ``
819
+ return quote(strip_html(String(val)))
820
+ })
821
+ )
822
+ return [headers.join(delimiter), ...rows.map((row) => row.join(delimiter))].join(`\n`)
823
+ }
824
+
825
+ function export_csv(filename = `table-export`) {
826
+ download_file(serialize_table(`,`, true), `${filename}.csv`, `text/csv`)
827
+ }
828
+
829
+ function export_json(filename = `table-export`) {
830
+ const rows = export_rows.map((row) => {
831
+ const clean_row: Record<string, unknown> = {}
832
+ for (const col of visible_columns) {
833
+ const col_id = get_col_id(col)
834
+ const val = row[col_id]
835
+ clean_row[strip_html(col.label)] = typeof val === `string`
836
+ ? strip_html(val)
837
+ : val
838
+ }
839
+ return clean_row
840
+ })
841
+ download_file(
842
+ JSON.stringify(rows, null, 2),
843
+ `${filename}.json`,
844
+ `application/json`,
845
+ )
846
+ }
847
+
848
+ function download_file(content: string, filename: string, mime_type: string) {
849
+ const blob = new Blob([content], { type: mime_type })
850
+ const url = URL.createObjectURL(blob)
851
+ const link = document.createElement(`a`)
852
+ link.href = url
853
+ link.download = filename
854
+ document.body.append(link)
855
+ link.click()
856
+ document.body.removeChild(link)
857
+ URL.revokeObjectURL(url)
858
+ }
859
+
860
+ function copy_to_clipboard() {
861
+ navigator.clipboard.writeText(serialize_table(`\t`))
862
+ }
863
+
864
+ // Column visibility toggle
865
+ function toggle_column(col_id: string) {
866
+ if (hidden_columns.includes(col_id)) {
867
+ hidden_columns = hidden_columns.filter((id) => id !== col_id)
868
+ } else {
869
+ hidden_columns = [...hidden_columns, col_id]
870
+ }
871
+ }
872
+
873
+ // Column resize handlers
874
+ function start_resize(event: MouseEvent, col: Label) {
875
+ event.preventDefault()
876
+ event.stopPropagation()
877
+ resize_col_id = get_col_id(col)
878
+ resize_start_x = event.clientX
879
+ const th = event.target instanceof Element ? event.target.parentElement : null
880
+ resize_start_width = th?.offsetWidth ?? 100
881
+
882
+ document.addEventListener(`mousemove`, handle_resize)
883
+ document.addEventListener(`mouseup`, stop_resize)
884
+ }
885
+
886
+ function handle_resize(event: MouseEvent) {
887
+ if (!resize_col_id) return
888
+ const delta = event.clientX - resize_start_x
889
+ const new_width = Math.min(500, Math.max(50, resize_start_width + delta))
890
+ column_widths = { ...column_widths, [resize_col_id]: new_width }
891
+ }
892
+
893
+ function stop_resize() {
894
+ resize_col_id = null
895
+ document.removeEventListener(`mousemove`, handle_resize)
896
+ document.removeEventListener(`mouseup`, stop_resize)
897
+ }
898
+
899
+ // Normalize sort_hint to a config object with defaults
900
+ let hint_config = $derived(
901
+ sort_hint
902
+ ? {
903
+ position: `bottom` as const,
904
+ permanent: false,
905
+ ...(typeof sort_hint === `string` ? { text: sort_hint } : sort_hint),
906
+ }
907
+ : null,
908
+ )
909
+ </script>
910
+
911
+ {#snippet sort_hint_element(pos: `top` | `bottom`)}
912
+ {#if hint_config?.position === pos}
913
+ <div
914
+ class="sort-hint {hint_config.class ?? ``}"
915
+ class:permanent={hint_config.permanent}
916
+ style={hint_config.style}
917
+ >
918
+ {hint_config.text}
919
+ </div>
920
+ {/if}
921
+ {/snippet}
922
+
923
+ <div
924
+ {@attach table_tooltips}
925
+ {...rest_props}
926
+ bind:this={container_el}
927
+ class="table-container {rest_props.class ?? ``}"
928
+ style:--heatmap-opacity="{heatmap_opacity * 100}%"
929
+ onmouseleave={() => {
930
+ show_column_dropdown = false
931
+ show_export_dropdown = false
932
+ context_menu_col = null
933
+ }}
934
+ >
935
+ <!-- Floating control buttons -->
936
+ <section class="control-buttons">
937
+ {#if search_config}
938
+ {#if search_expanded || search_query}
939
+ <input
940
+ type="search"
941
+ class="search-input"
942
+ placeholder={search_config.placeholder}
943
+ bind:value={search_query}
944
+ onblur={() => {
945
+ if (!search_query) search_expanded = false
946
+ }}
947
+ />
948
+ <button
949
+ class="icon-btn"
950
+ onclick={() => {
951
+ search_query = ``
952
+ search_expanded = false
953
+ }}
954
+ {@attach tooltip({ content: `Clear`, placement: `top` })}
955
+ >
956
+ <Icon icon="Cross" style="width: 10px" />
957
+ </button>
958
+ {:else}
959
+ <button
960
+ class="icon-btn"
961
+ onclick={() => search_expanded = true}
962
+ {@attach tooltip({ content: `Search`, placement: `top` })}
963
+ >
964
+ <Icon icon="Search" style="width: 14px" />
965
+ </button>
966
+ {/if}
967
+ {/if}
968
+
969
+ {#if show_column_toggle}
970
+ <div class="dropdown-wrapper">
971
+ <button
972
+ class="icon-btn"
973
+ class:active={show_column_dropdown}
974
+ onclick={() => show_column_dropdown = !show_column_dropdown}
975
+ {@attach tooltip({ content: `Columns`, placement: `top` })}
976
+ >
977
+ <Icon icon="Columns" style="width: 14px" />
978
+ </button>
979
+ {#if show_column_dropdown}
980
+ <div class="dropdown-pane">
981
+ {#each ordered_columns as col (get_col_id(col))}
982
+ {@const col_id = get_col_id(col)}
983
+ <label class="dropdown-option">
984
+ <input
985
+ type="checkbox"
986
+ checked={!hidden_columns.includes(col_id)}
987
+ onchange={() => toggle_column(col_id)}
988
+ />
989
+ {@html sanitize_html(col.label)}
990
+ </label>
991
+ {/each}
992
+ </div>
993
+ {/if}
994
+ </div>
995
+ {/if}
996
+
997
+ {#if export_config}
998
+ <div class="dropdown-wrapper">
999
+ <button
1000
+ class="icon-btn"
1001
+ class:active={show_export_dropdown}
1002
+ onclick={() => show_export_dropdown = !show_export_dropdown}
1003
+ {@attach tooltip({ content: `Export`, placement: `top` })}
1004
+ >
1005
+ <Icon icon="Export" style="width: 14px" />
1006
+ </button>
1007
+ {#if show_export_dropdown}
1008
+ <div class="dropdown-pane">
1009
+ {#if export_config.formats.includes(`csv`)}
1010
+ <button
1011
+ class="dropdown-option"
1012
+ onclick={() => {
1013
+ export_csv(export_config.filename)
1014
+ show_export_dropdown = false
1015
+ }}
1016
+ >
1017
+ <Icon icon="Download" style="width: 12px" /> CSV
1018
+ </button>
1019
+ {/if}
1020
+ {#if export_config.formats.includes(`json`)}
1021
+ <button
1022
+ class="dropdown-option"
1023
+ onclick={() => {
1024
+ export_json(export_config.filename)
1025
+ show_export_dropdown = false
1026
+ }}
1027
+ >
1028
+ <Icon icon="Download" style="width: 12px" /> JSON
1029
+ </button>
1030
+ {/if}
1031
+ <button
1032
+ class="dropdown-option"
1033
+ onclick={() => {
1034
+ copy_to_clipboard()
1035
+ show_export_dropdown = false
1036
+ }}
1037
+ >
1038
+ <Icon icon="Copy" style="width: 12px" /> Copy
1039
+ </button>
1040
+ </div>
1041
+ {/if}
1042
+ </div>
1043
+ {/if}
1044
+
1045
+ {#if show_row_select && selected_rows.length > 0}
1046
+ <button
1047
+ class="icon-btn selection-badge"
1048
+ onclick={() => selected_rows = []}
1049
+ title="Clear {selected_rows.length} selected rows"
1050
+ >
1051
+ <span class="badge">{selected_rows.length}</span>
1052
+ <Icon icon="Cross" style="width: 10px" />
1053
+ </button>
1054
+ {/if}
1055
+
1056
+ {#if controls}
1057
+ {@render controls()}
1058
+ {/if}
1059
+ </section>
1060
+
1061
+ {#if show_controls}
1062
+ <DraggablePane
1063
+ bind:show={controls_open}
1064
+ closed_icon="Settings"
1065
+ open_icon="Cross"
1066
+ toggle_props={{
1067
+ title: `${controls_open ? `Close` : `Open`} table controls`,
1068
+ style: `position: absolute; top: 5pt; right: 1ex; z-index: 10`,
1069
+ }}
1070
+ pane_props={{ style: `max-height: 60vh; overflow-y: auto; font-size: 0.85em` }}
1071
+ >
1072
+ <SettingsSection
1073
+ title="Heatmap"
1074
+ current_values={{ show_heatmap, heatmap_opacity }}
1075
+ on_reset={() => {
1076
+ show_heatmap = true
1077
+ heatmap_opacity = 1
1078
+ }}
1079
+ >
1080
+ <label><input type="checkbox" bind:checked={show_heatmap} /> Show heatmap</label>
1081
+ {#if show_heatmap}
1082
+ <label>
1083
+ Opacity
1084
+ <input
1085
+ type="range"
1086
+ min="0"
1087
+ max="1"
1088
+ step="0.05"
1089
+ bind:value={heatmap_opacity}
1090
+ />
1091
+ <input
1092
+ type="number"
1093
+ min="0"
1094
+ max="1"
1095
+ step="0.05"
1096
+ bind:value={heatmap_opacity}
1097
+ style="width: 3.5em"
1098
+ />
1099
+ </label>
1100
+ {/if}
1101
+ </SettingsSection>
1102
+
1103
+ <SettingsSection
1104
+ title="Display"
1105
+ current_values={{ show_row_numbers }}
1106
+ on_reset={() => {
1107
+ show_row_numbers = false
1108
+ }}
1109
+ >
1110
+ <label><input type="checkbox" bind:checked={show_row_numbers} /> Row
1111
+ numbers</label>
1112
+ </SettingsSection>
1113
+
1114
+ {#if colored_columns.length > 0}
1115
+ <SettingsSection
1116
+ title="Column Colors"
1117
+ current_values={Object.fromEntries([...better_overrides, ...color_scale_overrides])}
1118
+ on_reset={() => {
1119
+ better_overrides.clear()
1120
+ color_scale_overrides.clear()
1121
+ }}
1122
+ >
1123
+ {#each colored_columns as col (get_col_id(col))}
1124
+ {@const col_id = get_col_id(col)}
1125
+ <div class="col-color-row">
1126
+ <span class="col-color-label">{@html sanitize_html(col.label)}</span>
1127
+ <select
1128
+ value={color_scale_overrides.get(col_id) ?? col.color_scale ??
1129
+ `interpolateViridis`}
1130
+ onchange={(event) => {
1131
+ const val = event.currentTarget.value
1132
+ if (
1133
+ val === (col.color_scale ?? `interpolateViridis`)
1134
+ ) color_scale_overrides.delete(col_id)
1135
+ else color_scale_overrides.set(col_id, val)
1136
+ }}
1137
+ >
1138
+ {#each color_scale_options as scale (scale)}
1139
+ <option value={scale}>{scale.replace(`interpolate`, ``)}</option>
1140
+ {/each}
1141
+ </select>
1142
+ <select
1143
+ value={better_overrides.get(col_id) ?? col.better ?? ``}
1144
+ onchange={(event) => {
1145
+ const val = event.currentTarget.value
1146
+ if (!val) better_overrides.delete(col_id)
1147
+ else better_overrides.set(col_id, val as `higher` | `lower`)
1148
+ }}
1149
+ >
1150
+ <option value="">Default</option>
1151
+ <option value="higher">▲ High</option>
1152
+ <option value="lower">▼ Low</option>
1153
+ </select>
1154
+ </div>
1155
+ {/each}
1156
+ </SettingsSection>
1157
+ {/if}
1158
+ </DraggablePane>
1159
+ {/if}
1160
+
1161
+ {@render sort_hint_element(`top`)}
1162
+
1163
+ <div
1164
+ class="table-scroll"
1165
+ style={scroll_style}
1166
+ class:has-scroll={scroll_style}
1167
+ >
1168
+ {#if loading}
1169
+ <div class="loading-overlay">
1170
+ <div class="loading-spinner"></div>
1171
+ </div>
1172
+ {/if}
1173
+ <table class:fixed-header={fixed_header} class={heatmap_class}>
1174
+ <thead>
1175
+ <!-- Don't add a table row for group headers if there are none -->
1176
+ {#if visible_columns.some((col) => col.group)}
1177
+ <!-- First level headers -->
1178
+ <tr class="group-header">
1179
+ {#if show_row_select}
1180
+ <th class="select-col"></th>
1181
+ {/if}
1182
+ {#if show_row_numbers}
1183
+ <th class="row-num-col"></th>
1184
+ {/if}
1185
+ {#each visible_columns as col (get_col_id(col))}
1186
+ {#if !col.group}
1187
+ <th class:sticky-col={col.sticky}></th>
1188
+ {:else}
1189
+ {@const group_cols = visible_columns.filter((column) =>
1190
+ column.group === col.group
1191
+ )}
1192
+ <!-- Only render the group header once for each group by checking if this is the first column of this group -->
1193
+ {#if visible_columns.findIndex((column) => column.group === col.group) ===
1194
+ visible_columns.findIndex((column) =>
1195
+ column.group === col.group && column.label === col.label
1196
+ )}
1197
+ <th title={col.description} colspan={group_cols.length}>
1198
+ {@html sanitize_html(col.group)}
1199
+ </th>
1200
+ {/if}
1201
+ {/if}
1202
+ {/each}
1203
+ </tr>
1204
+ {/if}
1205
+ <!-- Second level headers -->
1206
+ <tr>
1207
+ {#if show_row_select}
1208
+ <th
1209
+ class="select-col"
1210
+ title={all_page_selected ? `Deselect all` : `Select all on this page`}
1211
+ >
1212
+ <input
1213
+ type="checkbox"
1214
+ checked={all_page_selected}
1215
+ onchange={toggle_select_all}
1216
+ />
1217
+ </th>
1218
+ {/if}
1219
+ {#if show_row_numbers}
1220
+ <th class="row-num-col">#</th>
1221
+ {/if}
1222
+ {#each visible_columns as col (get_col_id(col))}
1223
+ {@const col_id = get_col_id(col)}
1224
+ {@const drag_side = drag_over_col_id === col_id
1225
+ ? get_drag_side(col_id)
1226
+ : null}
1227
+ {@const col_width = column_widths[col_id]}
1228
+ <th
1229
+ title={col.description}
1230
+ tabindex={col.sortable === false ? undefined : 0}
1231
+ role={col.sortable === false ? undefined : `button`}
1232
+ oncontextmenu={(event) => {
1233
+ if (
1234
+ !allow_better_toggle || col.color_scale === null ||
1235
+ col.color_scale === undefined
1236
+ ) return
1237
+ event.preventDefault()
1238
+ event.stopPropagation()
1239
+ context_menu_col = col_id
1240
+ const rect = container_el?.getBoundingClientRect()
1241
+ context_menu_pos = {
1242
+ x: event.clientX - (rect?.left ?? 0),
1243
+ y: event.clientY - (rect?.top ?? 0),
1244
+ }
1245
+ }}
1246
+ onclick={(event) => {
1247
+ if (!drag_col_id && !resize_col_id) {
1248
+ sort_rows(
1249
+ col.label,
1250
+ col.group,
1251
+ event,
1252
+ )
1253
+ }
1254
+ }}
1255
+ onkeydown={(event) => {
1256
+ if (
1257
+ (event.key === `Enter` || event.key === ` `) &&
1258
+ !drag_col_id && !resize_col_id
1259
+ ) {
1260
+ event.preventDefault()
1261
+ sort_rows(col.label, col.group, event)
1262
+ }
1263
+ }}
1264
+ style={`${col.style ?? ``}${
1265
+ col_width
1266
+ ? `; width: ${col_width}px; min-width: ${col_width}px`
1267
+ : ``
1268
+ }`}
1269
+ class:sticky-col={col.sticky}
1270
+ class:not-sortable={col.sortable === false}
1271
+ class:dragging={drag_col_id === col_id}
1272
+ class:resizing={resize_col_id === col_id}
1273
+ data-drag-side={drag_side}
1274
+ draggable="true"
1275
+ aria-dropeffect="move"
1276
+ aria-sort={sort_state.column === col_id
1277
+ ? (sort_state.ascending ? `ascending` : `descending`)
1278
+ : `none`}
1279
+ ondragstart={(event: DragEvent & { currentTarget: HTMLElement }) => {
1280
+ handle_drag_start(event, col)
1281
+ event.currentTarget.setAttribute(`aria-grabbed`, `true`)
1282
+ }}
1283
+ ondragover={(event) => handle_drag_over(event, col)}
1284
+ ondragleave={() => (drag_over_col_id = null)}
1285
+ ondrop={(event) => handle_drop(event, col)}
1286
+ ondragend={(event: DragEvent & { currentTarget: HTMLElement }) => {
1287
+ reset_drag_state()
1288
+ event.currentTarget.removeAttribute(`aria-grabbed`)
1289
+ }}
1290
+ >
1291
+ {#if header_cell}
1292
+ {@render header_cell({ col })}
1293
+ {:else}
1294
+ {@html sanitize_html(col.label)}
1295
+ {/if}
1296
+ {@html sanitize_html(sort_indicator(col, sort_state))}
1297
+ <!-- Column resize handle -->
1298
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
1299
+ <span
1300
+ class="resize-handle"
1301
+ onmousedown={(event) => start_resize(event, col)}
1302
+ role="separator"
1303
+ aria-orientation="vertical"
1304
+ aria-valuenow={column_widths[get_col_id(col)] ?? 100}
1305
+ aria-valuemin={50}
1306
+ aria-valuemax={500}
1307
+ ></span>
1308
+ </th>
1309
+ {/each}
1310
+ </tr>
1311
+ </thead>
1312
+ <tbody>
1313
+ {#each paginated_data as row, row_idx (get_row_id(row))}
1314
+ {@const row_selected = show_row_select && is_row_selected(row)}
1315
+ <tr
1316
+ animate:flip={{ duration: 500 }}
1317
+ style={row.style}
1318
+ class={row.class ?? ``}
1319
+ class:selected={row_selected}
1320
+ tabindex={onrowclick ? 0 : undefined}
1321
+ onclick={onrowclick ? (event) => onrowclick(event, row) : undefined}
1322
+ ondblclick={onrowdblclick ? (event) => onrowdblclick(event, row) : undefined}
1323
+ onkeydown={onrowclick
1324
+ ? (event) => {
1325
+ if (event.key === `Enter` || event.key === ` `) {
1326
+ event.preventDefault()
1327
+ onrowclick(event, row)
1328
+ } else if (event.key === `ArrowDown`) {
1329
+ event.preventDefault()
1330
+ const next = event.currentTarget.nextElementSibling
1331
+ if (next instanceof HTMLElement) next.focus()
1332
+ } else if (event.key === `ArrowUp`) {
1333
+ event.preventDefault()
1334
+ const prev = event.currentTarget.previousElementSibling
1335
+ if (prev instanceof HTMLElement) prev.focus()
1336
+ }
1337
+ }
1338
+ : undefined}
1339
+ >
1340
+ {#if show_row_select}
1341
+ <td class="select-col">
1342
+ <input
1343
+ type="checkbox"
1344
+ checked={row_selected}
1345
+ onchange={() => toggle_row_select(row)}
1346
+ />
1347
+ </td>
1348
+ {/if}
1349
+ {#if show_row_numbers}
1350
+ <td class="row-num-col">
1351
+ {(current_page - 1) * effective_page_size + row_idx + 1}
1352
+ </td>
1353
+ {/if}
1354
+ {#each visible_columns as col (get_col_id(col))}
1355
+ {@const val = row[get_col_id(col)]}
1356
+ {@const color = calc_color(val, col)}
1357
+ {@const col_width = column_widths[get_col_id(col)]}
1358
+ <td
1359
+ data-col={col.label}
1360
+ data-sort-value={is_html_str(val) ? null : val}
1361
+ class:sticky-col={col.sticky}
1362
+ style:--cell-bg={color.bg}
1363
+ style:color={color.text}
1364
+ style={`${col.cell_style ?? col.style ?? ``}${
1365
+ col_width
1366
+ ? `; width: ${col_width}px; max-width: ${col_width}px`
1367
+ : ``
1368
+ }`}
1369
+ >
1370
+ {#if special_cells?.[col.label]}
1371
+ {@render special_cells[col.label]({ row, col, val })}
1372
+ {:else if cell}
1373
+ {@render cell({ row, col, val })}
1374
+ {:else if typeof val === `number` && !Number.isNaN(val)}
1375
+ {format_num(val, col.format ?? default_num_format)}
1376
+ {:else if val === undefined || val === null || Number.isNaN(val)}
1377
+ <span {@attach tooltip({ content: `Not available` })}>
1378
+ n/a
1379
+ </span>
1380
+ {:else}
1381
+ {@html sanitize_html(val)}
1382
+ {/if}
1383
+ </td>
1384
+ {/each}
1385
+ </tr>
1386
+ {:else}
1387
+ {#if empty_message}
1388
+ <tr class="empty-row">
1389
+ <td
1390
+ colspan={visible_columns.length + (show_row_select ? 1 : 0) +
1391
+ (show_row_numbers ? 1 : 0)}
1392
+ >
1393
+ {empty_message}
1394
+ </td>
1395
+ </tr>
1396
+ {/if}
1397
+ {/each}
1398
+ </tbody>
1399
+ {#if footer}
1400
+ <tfoot>
1401
+ {@render footer()}
1402
+ </tfoot>
1403
+ {/if}
1404
+ </table>
1405
+ </div>
1406
+
1407
+ {@render sort_hint_element(`bottom`)}
1408
+
1409
+ {#if pagination_config && total_pages > 1}
1410
+ <div class="pagination">
1411
+ <button
1412
+ class="page-btn"
1413
+ disabled={current_page === 1}
1414
+ onclick={() => current_page = 1}
1415
+ title="First page"
1416
+ >
1417
+ «
1418
+ </button>
1419
+ <button
1420
+ class="page-btn"
1421
+ disabled={current_page === 1}
1422
+ onclick={() => current_page--}
1423
+ title="Previous page"
1424
+ >
1425
+
1426
+ </button>
1427
+ <span class="page-info">
1428
+ Page
1429
+ <input
1430
+ type="number"
1431
+ class="page-input"
1432
+ min="1"
1433
+ max={total_pages}
1434
+ value={current_page}
1435
+ onchange={(event) => {
1436
+ const val = parseInt(event.currentTarget.value, 10)
1437
+ current_page = Math.max(1, Math.min(total_pages, isNaN(val) ? 1 : val))
1438
+ event.currentTarget.value = String(current_page)
1439
+ }}
1440
+ />
1441
+ of {total_pages}
1442
+ <span class="row-count">({sorted_data.length} rows)</span>
1443
+ </span>
1444
+ <button
1445
+ class="page-btn"
1446
+ disabled={current_page === total_pages}
1447
+ onclick={() => current_page++}
1448
+ title="Next page"
1449
+ >
1450
+
1451
+ </button>
1452
+ <button
1453
+ class="page-btn"
1454
+ disabled={current_page === total_pages}
1455
+ onclick={() => current_page = total_pages}
1456
+ title="Last page"
1457
+ >
1458
+ »
1459
+ </button>
1460
+ {#if pagination_config.page_sizes}
1461
+ <select
1462
+ class="page-size-select"
1463
+ onchange={(event) => {
1464
+ effective_page_size = parseInt(event.currentTarget.value, 10)
1465
+ current_page = 1
1466
+ }}
1467
+ >
1468
+ {#each pagination_config.page_sizes as size (size)}
1469
+ <option value={size} selected={size === effective_page_size}>
1470
+ {size} / page
1471
+ </option>
1472
+ {/each}
1473
+ </select>
1474
+ {/if}
1475
+ </div>
1476
+ {/if}
1477
+
1478
+ <ContextMenu
1479
+ sections={better_sections}
1480
+ selected_values={{ 'Gradient direction': better_overrides.get(context_menu_col ?? ``) ?? `` }}
1481
+ position={context_menu_pos}
1482
+ visible={context_menu_col !== null}
1483
+ on_close={() => context_menu_col = null}
1484
+ style={[
1485
+ `--surface-bg: light-dark(#fff, #1e1e1e)`,
1486
+ `--border-color: light-dark(rgba(0,0,0,0.15), rgba(255,255,255,0.15))`,
1487
+ `--text-color: light-dark(#333, #eee)`,
1488
+ `--text-color-muted: light-dark(#888, #999)`,
1489
+ `--surface-bg-hover: light-dark(rgba(0,0,0,0.06), rgba(255,255,255,0.1))`,
1490
+ `--accent-color: light-dark(rgba(0,0,0,0.1), rgba(255,255,255,0.15))`,
1491
+ `z-index: 200`,
1492
+ ].join(`; `)}
1493
+ on_select={(_, option) => {
1494
+ if (!context_menu_col) return
1495
+ const current = better_overrides.get(context_menu_col)
1496
+ if (current === option.value) better_overrides.delete(context_menu_col)
1497
+ else better_overrides.set(context_menu_col, option.value as `higher` | `lower`)
1498
+ context_menu_col = null
1499
+ }}
1500
+ />
1501
+ </div>
1502
+
1503
+ <style>
1504
+ .table-container {
1505
+ font-size: var(--heatmap-font-size, 0.9em);
1506
+ width: fit-content;
1507
+ max-width: 100%;
1508
+ max-height: inherit;
1509
+ margin: 0 auto;
1510
+ position: relative;
1511
+ display: flex;
1512
+ flex-direction: column;
1513
+ }
1514
+ .table-scroll {
1515
+ position: relative;
1516
+ overflow: auto;
1517
+ }
1518
+ .table-scroll.has-scroll {
1519
+ border: 1px solid light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.12));
1520
+ border-radius: var(--border-radius, 3pt);
1521
+ overflow-x: hidden;
1522
+ overflow-y: auto;
1523
+ }
1524
+ table {
1525
+ border-collapse: separate;
1526
+ border-spacing: 0;
1527
+ display: table; /* Override global display: block to enable sticky headers */
1528
+ }
1529
+ th, td {
1530
+ padding: var(--heatmap-cell-padding, 1pt 5pt);
1531
+ text-align: var(--heatmap-text-align, left);
1532
+ border: var(--heatmap-cell-border, none);
1533
+ white-space: nowrap;
1534
+ overflow: hidden;
1535
+ text-overflow: ellipsis;
1536
+ /* --cell-bg is set inline per-cell by calc_color(); --heatmap-opacity is set
1537
+ on the container from the heatmap_opacity prop to fade cell backgrounds */
1538
+ background-color: color-mix(
1539
+ in srgb,
1540
+ var(--cell-bg, transparent) var(--heatmap-opacity, 100%),
1541
+ transparent
1542
+ );
1543
+ }
1544
+ th {
1545
+ background: var(--heatmap-header-bg, var(--page-bg, Canvas));
1546
+ position: sticky;
1547
+ top: 0;
1548
+ z-index: 2;
1549
+ cursor: pointer;
1550
+ user-select: none;
1551
+ }
1552
+ th:hover {
1553
+ background: var(--heatmap-header-hover-bg, var(--nav-bg));
1554
+ }
1555
+ th.dragging {
1556
+ opacity: 0.4;
1557
+ cursor: grabbing;
1558
+ }
1559
+ th[data-drag-side='left'] {
1560
+ border-left: 4px solid var(--highlight, #4a9eff);
1561
+ }
1562
+ th[data-drag-side='right'] {
1563
+ border-right: 4px solid var(--highlight, #4a9eff);
1564
+ }
1565
+ th[draggable='true'] {
1566
+ cursor: grab;
1567
+ }
1568
+ th.sticky-col {
1569
+ position: sticky;
1570
+ left: 0;
1571
+ top: 0;
1572
+ background: var(--heatmap-header-bg, var(--page-bg, Canvas));
1573
+ z-index: 4; /* Higher than regular th (2) to stay above when both scroll */
1574
+ border-right: 1px solid var(--border, #ddd);
1575
+ }
1576
+ td.sticky-col {
1577
+ position: sticky;
1578
+ left: 0;
1579
+ background: var(--page-bg, Canvas);
1580
+ z-index: 1;
1581
+ border-right: 1px solid var(--border, #ddd);
1582
+ }
1583
+ tbody tr:hover {
1584
+ filter: var(--heatmap-row-hover-filter, brightness(1.1));
1585
+ }
1586
+ tbody tr[tabindex] {
1587
+ cursor: pointer;
1588
+ }
1589
+ tbody tr:focus-visible {
1590
+ outline: 2px solid var(--highlight, #4a9eff);
1591
+ outline-offset: -2px;
1592
+ }
1593
+ td[data-sort-value] {
1594
+ cursor: default;
1595
+ }
1596
+ .group-header th {
1597
+ text-align: center;
1598
+ border-bottom: 1px solid var(--border);
1599
+ }
1600
+ /* Sticky cells in group header row need higher z-index to clip scrolling group headers */
1601
+ .group-header th.sticky-col {
1602
+ z-index: 5;
1603
+ }
1604
+ /* Floating control buttons above the table */
1605
+ .control-buttons {
1606
+ display: flex;
1607
+ justify-content: flex-end;
1608
+ align-items: center;
1609
+ gap: 2px;
1610
+ margin-bottom: 1px;
1611
+ opacity: 0;
1612
+ pointer-events: none;
1613
+ transition: opacity 0.15s;
1614
+ }
1615
+ .table-container:hover .control-buttons,
1616
+ .control-buttons:focus-within {
1617
+ opacity: 1;
1618
+ pointer-events: auto;
1619
+ }
1620
+ .icon-btn {
1621
+ padding: 2px 4px;
1622
+ border: none;
1623
+ border-radius: 3px;
1624
+ background: light-dark(rgba(0, 0, 0, 0.06), rgba(255, 255, 255, 0.1));
1625
+ color: light-dark(#333, #ddd);
1626
+ cursor: pointer;
1627
+ display: flex;
1628
+ align-items: center;
1629
+ justify-content: center;
1630
+ gap: 2px;
1631
+ font-size: 0.8em;
1632
+ }
1633
+ .icon-btn :global(svg) {
1634
+ width: 12px;
1635
+ height: 12px;
1636
+ }
1637
+ .icon-btn:hover {
1638
+ background: light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.2));
1639
+ }
1640
+ .icon-btn.active {
1641
+ background: light-dark(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.25));
1642
+ }
1643
+ .selection-badge {
1644
+ position: relative;
1645
+ }
1646
+ .selection-badge .badge {
1647
+ background: var(--highlight, #4a9eff);
1648
+ color: white;
1649
+ font-size: 0.7em;
1650
+ padding: 1px 4px;
1651
+ border-radius: 8px;
1652
+ min-width: 14px;
1653
+ text-align: center;
1654
+ }
1655
+ .dropdown-wrapper {
1656
+ position: relative;
1657
+ }
1658
+ .dropdown-pane {
1659
+ position: absolute;
1660
+ top: 100%;
1661
+ right: 0;
1662
+ margin-top: 4px;
1663
+ padding: 4px 0;
1664
+ background: light-dark(rgba(255, 255, 255, 0.98), rgba(30, 30, 30, 0.98));
1665
+ border: 1px solid light-dark(rgba(0, 0, 0, 0.12), rgba(255, 255, 255, 0.15));
1666
+ border-radius: 6px;
1667
+ box-shadow: 0 4px 12px light-dark(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.4));
1668
+ max-height: 280px;
1669
+ overflow-y: auto;
1670
+ z-index: 100;
1671
+ color: light-dark(#333, #eee);
1672
+ font-size: 0.95em;
1673
+ }
1674
+ .dropdown-option {
1675
+ display: flex;
1676
+ align-items: center;
1677
+ gap: 8px;
1678
+ padding: 3px 6px;
1679
+ cursor: pointer;
1680
+ font-size: 0.95em;
1681
+ white-space: nowrap;
1682
+ background: transparent;
1683
+ border: none;
1684
+ color: inherit;
1685
+ width: 100%;
1686
+ text-align: left;
1687
+ }
1688
+ .dropdown-option:hover {
1689
+ background: light-dark(rgba(0, 0, 0, 0.06), rgba(255, 255, 255, 0.1));
1690
+ }
1691
+ /* Column toggle labels - more compact */
1692
+ label.dropdown-option {
1693
+ padding: 4px 10px;
1694
+ gap: 6px;
1695
+ }
1696
+ .search-input {
1697
+ padding: 2px 4px;
1698
+ border: 1px solid light-dark(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.2));
1699
+ border-radius: 3px;
1700
+ background: light-dark(rgba(255, 255, 255, 0.9), rgba(0, 0, 0, 0.3));
1701
+ color: light-dark(#333, #eee);
1702
+ font-size: 0.8em;
1703
+ width: 110px;
1704
+ box-sizing: border-box;
1705
+ }
1706
+ .search-input:focus {
1707
+ outline: 1px solid var(--highlight, #4a9eff);
1708
+ }
1709
+ .search-input::placeholder {
1710
+ color: light-dark(#999, #666);
1711
+ }
1712
+ .sort-hint {
1713
+ text-align: center;
1714
+ font-size: 0.75em;
1715
+ color: var(--text-muted);
1716
+ padding: 4px 0;
1717
+ opacity: 0;
1718
+ transition: opacity 0.15s;
1719
+ }
1720
+ .table-container:hover .sort-hint,
1721
+ .sort-hint.permanent {
1722
+ opacity: 1;
1723
+ }
1724
+ .not-sortable {
1725
+ cursor: default;
1726
+ }
1727
+ tr.highlight {
1728
+ background-color: var(--nav-bg) !important;
1729
+ }
1730
+ tr.highlight, tr.highlight :global(a) {
1731
+ color: var(--highlight) !important;
1732
+ }
1733
+
1734
+ /* Row selection */
1735
+ .select-col {
1736
+ width: 30px;
1737
+ text-align: center;
1738
+ vertical-align: middle;
1739
+ padding: 2px !important;
1740
+ }
1741
+ .select-col :global(svg) {
1742
+ display: block;
1743
+ margin: auto;
1744
+ }
1745
+ tr.selected {
1746
+ background: var(--highlight-bg, rgba(74, 158, 255, 0.15)) !important;
1747
+ }
1748
+ tr.selected td {
1749
+ border-top: 1px solid var(--highlight, #4a9eff);
1750
+ border-bottom: 1px solid var(--highlight, #4a9eff);
1751
+ }
1752
+ /* Pagination */
1753
+ .pagination {
1754
+ display: flex;
1755
+ align-items: center;
1756
+ justify-content: center;
1757
+ gap: 8px;
1758
+ margin-top: 12px;
1759
+ padding-top: 12px;
1760
+ border-top: 1px solid var(--border);
1761
+ }
1762
+ .page-btn {
1763
+ padding: 4px 10px;
1764
+ border: 1px solid var(--border, #444);
1765
+ border-radius: 4px;
1766
+ background: var(--page-bg, Canvas);
1767
+ color: inherit;
1768
+ cursor: pointer;
1769
+ font-size: 1em;
1770
+ }
1771
+ .page-btn:hover:not(:disabled) {
1772
+ background: var(--nav-bg, #333);
1773
+ }
1774
+ .page-btn:disabled {
1775
+ opacity: 0.4;
1776
+ cursor: not-allowed;
1777
+ }
1778
+ .page-info {
1779
+ font-size: 0.9em;
1780
+ display: flex;
1781
+ align-items: center;
1782
+ gap: 4px;
1783
+ }
1784
+ .page-input {
1785
+ min-width: 1em !important; /* Override global min-width: 40px from app.css */
1786
+ padding: 2px 4px;
1787
+ border: 1px solid light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2));
1788
+ border-radius: 3px;
1789
+ background: light-dark(#fff, #333);
1790
+ color: inherit;
1791
+ font-size: inherit;
1792
+ text-align: center;
1793
+ -moz-appearance: textfield;
1794
+ appearance: textfield;
1795
+ }
1796
+ .page-input::-webkit-outer-spin-button,
1797
+ .page-input::-webkit-inner-spin-button {
1798
+ -webkit-appearance: none;
1799
+ appearance: none;
1800
+ margin: 0;
1801
+ }
1802
+ .page-input:focus {
1803
+ outline: 1px solid var(--highlight, #4a9eff);
1804
+ }
1805
+ .row-count {
1806
+ color: var(--text-muted);
1807
+ font-size: 0.85em;
1808
+ }
1809
+
1810
+ .col-color-row {
1811
+ display: flex;
1812
+ align-items: center;
1813
+ gap: 4px;
1814
+ padding: 2px 0;
1815
+ select {
1816
+ font-size: 0.85em;
1817
+ padding: 1px 2px;
1818
+ }
1819
+ }
1820
+ .col-color-label {
1821
+ flex: 1;
1822
+ overflow: hidden;
1823
+ text-overflow: ellipsis;
1824
+ white-space: nowrap;
1825
+ min-width: 0;
1826
+ }
1827
+ /* Column resize */
1828
+ .resize-handle {
1829
+ position: absolute;
1830
+ right: 0;
1831
+ top: 0;
1832
+ bottom: 0;
1833
+ width: 4px;
1834
+ cursor: col-resize;
1835
+ background: transparent;
1836
+ }
1837
+ .resize-handle:hover,
1838
+ th.resizing .resize-handle {
1839
+ background: var(--highlight, #4a9eff);
1840
+ }
1841
+ /* Loading overlay */
1842
+ .loading-overlay {
1843
+ position: absolute;
1844
+ inset: 0;
1845
+ background: light-dark(rgba(255, 255, 255, 0.7), rgba(0, 0, 0, 0.5));
1846
+ display: flex;
1847
+ align-items: center;
1848
+ justify-content: center;
1849
+ z-index: 10;
1850
+ }
1851
+ .loading-spinner {
1852
+ width: 24px;
1853
+ height: 24px;
1854
+ border: 3px solid light-dark(#e5e7eb, #444);
1855
+ border-top-color: var(--highlight, #3b82f6);
1856
+ border-radius: 50%;
1857
+ animation: spin 0.8s linear infinite;
1858
+ }
1859
+ @keyframes spin {
1860
+ to {
1861
+ transform: rotate(360deg);
1862
+ }
1863
+ }
1864
+ .empty-row td {
1865
+ text-align: center;
1866
+ padding: 2em !important;
1867
+ color: var(--text-muted, #888);
1868
+ font-style: italic;
1869
+ }
1870
+ .row-num-col {
1871
+ text-align: right;
1872
+ color: var(--text-muted, #888);
1873
+ font-size: 0.85em;
1874
+ width: 2em;
1875
+ padding-right: 8px !important;
1876
+ }
1877
+ .page-size-select {
1878
+ padding: 2px 4px;
1879
+ border: 1px solid light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2));
1880
+ border-radius: 3px;
1881
+ background: light-dark(#fff, #333);
1882
+ color: inherit;
1883
+ font-size: 0.9em;
1884
+ }
1885
+ </style>