matterviz 0.3.6 → 0.3.7

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 (863) 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.d.ts +13 -0
  5. package/dist/MillerIndexInput.svelte +66 -0
  6. package/dist/MillerIndexInput.svelte.d.ts +7 -0
  7. package/dist/api/mp.d.ts +6 -0
  8. package/dist/api/mp.js +22 -0
  9. package/dist/api/optimade.d.ts +45 -0
  10. package/dist/api/optimade.js +135 -0
  11. package/dist/brillouin/BrillouinZone.svelte +549 -0
  12. package/dist/brillouin/BrillouinZone.svelte.d.ts +83 -0
  13. package/dist/brillouin/BrillouinZoneControls.svelte +144 -0
  14. package/dist/brillouin/BrillouinZoneControls.svelte.d.ts +17 -0
  15. package/dist/brillouin/BrillouinZoneExportPane.svelte +146 -0
  16. package/dist/brillouin/BrillouinZoneExportPane.svelte.d.ts +15 -0
  17. package/dist/brillouin/BrillouinZoneInfoPane.svelte +146 -0
  18. package/dist/brillouin/BrillouinZoneInfoPane.svelte.d.ts +13 -0
  19. package/dist/brillouin/BrillouinZoneScene.svelte +476 -0
  20. package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +48 -0
  21. package/dist/brillouin/BrillouinZoneTooltip.svelte +92 -0
  22. package/dist/brillouin/BrillouinZoneTooltip.svelte.d.ts +8 -0
  23. package/dist/brillouin/compute.d.ts +17 -0
  24. package/dist/brillouin/compute.js +426 -0
  25. package/dist/brillouin/index.d.ts +8 -0
  26. package/dist/brillouin/index.js +7 -0
  27. package/dist/brillouin/types.d.ts +43 -0
  28. package/dist/brillouin/types.js +1 -0
  29. package/dist/chempot-diagram/ChemPotDiagram.svelte +327 -0
  30. package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
  31. package/dist/chempot-diagram/ChemPotDiagram2D.svelte +846 -0
  32. package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
  33. package/dist/chempot-diagram/ChemPotDiagram3D.svelte +3193 -0
  34. package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
  35. package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
  36. package/dist/chempot-diagram/async-compute.svelte.d.ts +3 -0
  37. package/dist/chempot-diagram/async-compute.svelte.js +78 -0
  38. package/dist/chempot-diagram/chempot-worker.d.ts +1 -0
  39. package/dist/chempot-diagram/chempot-worker.js +11 -0
  40. package/dist/chempot-diagram/color.d.ts +10 -0
  41. package/dist/chempot-diagram/color.js +32 -0
  42. package/dist/chempot-diagram/compute.d.ts +48 -0
  43. package/dist/chempot-diagram/compute.js +806 -0
  44. package/dist/chempot-diagram/index.d.ts +6 -0
  45. package/dist/chempot-diagram/index.js +6 -0
  46. package/dist/chempot-diagram/pointer.d.ts +16 -0
  47. package/dist/chempot-diagram/pointer.js +40 -0
  48. package/dist/chempot-diagram/temperature.d.ts +15 -0
  49. package/dist/chempot-diagram/temperature.js +34 -0
  50. package/dist/chempot-diagram/types.d.ts +81 -0
  51. package/dist/chempot-diagram/types.js +28 -0
  52. package/dist/colors/index.d.ts +47 -0
  53. package/dist/colors/index.js +203 -0
  54. package/dist/composition/BarChart.svelte +297 -0
  55. package/dist/composition/BarChart.svelte.d.ts +39 -0
  56. package/dist/composition/BubbleChart.svelte +218 -0
  57. package/dist/composition/BubbleChart.svelte.d.ts +28 -0
  58. package/dist/composition/Composition.svelte +165 -0
  59. package/dist/composition/Composition.svelte.d.ts +15 -0
  60. package/dist/composition/Formula.svelte +268 -0
  61. package/dist/composition/Formula.svelte.d.ts +19 -0
  62. package/dist/composition/FormulaFilter.svelte +1257 -0
  63. package/dist/composition/FormulaFilter.svelte.d.ts +51 -0
  64. package/dist/composition/PieChart.svelte +323 -0
  65. package/dist/composition/PieChart.svelte.d.ts +37 -0
  66. package/dist/composition/format.d.ts +15 -0
  67. package/dist/composition/format.js +109 -0
  68. package/dist/composition/index.d.ts +20 -0
  69. package/dist/composition/index.js +14 -0
  70. package/dist/composition/parse.d.ts +56 -0
  71. package/dist/composition/parse.js +474 -0
  72. package/dist/constants.d.ts +29 -0
  73. package/dist/constants.js +99 -0
  74. package/dist/controls.d.ts +14 -0
  75. package/dist/controls.js +30 -0
  76. package/dist/convex-hull/ConvexHull.svelte +157 -0
  77. package/dist/convex-hull/ConvexHull.svelte.d.ts +13 -0
  78. package/dist/convex-hull/ConvexHull2D.svelte +825 -0
  79. package/dist/convex-hull/ConvexHull2D.svelte.d.ts +11 -0
  80. package/dist/convex-hull/ConvexHull3D.svelte +1801 -0
  81. package/dist/convex-hull/ConvexHull3D.svelte.d.ts +8 -0
  82. package/dist/convex-hull/ConvexHull4D.svelte +1398 -0
  83. package/dist/convex-hull/ConvexHull4D.svelte.d.ts +8 -0
  84. package/dist/convex-hull/ConvexHullControls.svelte +535 -0
  85. package/dist/convex-hull/ConvexHullControls.svelte.d.ts +48 -0
  86. package/dist/convex-hull/ConvexHullInfoPane.svelte +125 -0
  87. package/dist/convex-hull/ConvexHullInfoPane.svelte.d.ts +20 -0
  88. package/dist/convex-hull/ConvexHullStats.svelte +929 -0
  89. package/dist/convex-hull/ConvexHullStats.svelte.d.ts +17 -0
  90. package/dist/convex-hull/ConvexHullTooltip.svelte +131 -0
  91. package/dist/convex-hull/ConvexHullTooltip.svelte.d.ts +33 -0
  92. package/dist/convex-hull/GasPressureControls.svelte +247 -0
  93. package/dist/convex-hull/GasPressureControls.svelte.d.ts +11 -0
  94. package/dist/convex-hull/StructurePopup.svelte +151 -0
  95. package/dist/convex-hull/StructurePopup.svelte.d.ts +18 -0
  96. package/dist/convex-hull/TemperatureSlider.svelte.d.ts +8 -0
  97. package/dist/convex-hull/barycentric-coords.d.ts +18 -0
  98. package/dist/convex-hull/barycentric-coords.js +182 -0
  99. package/dist/convex-hull/demo-temperature.d.ts +6 -0
  100. package/dist/convex-hull/demo-temperature.js +40 -0
  101. package/dist/convex-hull/gas-thermodynamics.d.ts +16 -0
  102. package/dist/convex-hull/gas-thermodynamics.js +314 -0
  103. package/dist/convex-hull/helpers.d.ts +114 -0
  104. package/dist/convex-hull/helpers.js +710 -0
  105. package/dist/convex-hull/index.d.ts +119 -0
  106. package/dist/convex-hull/index.js +58 -0
  107. package/dist/convex-hull/thermodynamics.d.ts +67 -0
  108. package/dist/convex-hull/thermodynamics.js +1752 -0
  109. package/dist/convex-hull/types.d.ts +162 -0
  110. package/dist/convex-hull/types.js +36 -0
  111. package/dist/coordination/CoordinationBarPlot.svelte +311 -0
  112. package/dist/coordination/CoordinationBarPlot.svelte.d.ts +30 -0
  113. package/dist/coordination/calc-coordination.d.ts +15 -0
  114. package/dist/coordination/calc-coordination.js +63 -0
  115. package/dist/coordination/index.d.ts +8 -0
  116. package/dist/coordination/index.js +7 -0
  117. package/dist/effects.svelte.d.ts +12 -0
  118. package/dist/effects.svelte.js +37 -0
  119. package/dist/element/BohrAtom.svelte.d.ts +20 -0
  120. package/dist/element/ElementHeading.svelte +26 -0
  121. package/dist/element/ElementHeading.svelte.d.ts +8 -0
  122. package/dist/element/ElementPhoto.svelte +57 -0
  123. package/dist/element/ElementPhoto.svelte.d.ts +9 -0
  124. package/dist/element/ElementStats.svelte +80 -0
  125. package/dist/element/ElementStats.svelte.d.ts +8 -0
  126. package/dist/element/ElementTile.svelte +484 -0
  127. package/dist/element/ElementTile.svelte.d.ts +29 -0
  128. package/dist/element/Nucleus.svelte.d.ts +17 -0
  129. package/dist/element/data.d.ts +2 -0
  130. package/dist/element/data.js +2 -0
  131. package/dist/element/index.d.ts +8 -0
  132. package/dist/element/index.js +7 -0
  133. package/dist/element/types.d.ts +57 -0
  134. package/dist/element/types.js +1 -0
  135. package/dist/feedback/ClickFeedback.svelte +58 -0
  136. package/dist/feedback/ClickFeedback.svelte.d.ts +12 -0
  137. package/dist/feedback/DragOverlay.svelte +42 -0
  138. package/dist/feedback/DragOverlay.svelte.d.ts +7 -0
  139. package/dist/feedback/Spinner.svelte.d.ts +7 -0
  140. package/dist/feedback/StatusMessage.svelte.d.ts +9 -0
  141. package/dist/feedback/index.d.ts +4 -0
  142. package/dist/feedback/index.js +4 -0
  143. package/dist/fermi-surface/FermiSlice.svelte +189 -0
  144. package/dist/fermi-surface/FermiSlice.svelte.d.ts +24 -0
  145. package/dist/fermi-surface/FermiSurface.svelte +600 -0
  146. package/dist/fermi-surface/FermiSurface.svelte.d.ts +83 -0
  147. package/dist/fermi-surface/FermiSurfaceControls.svelte +448 -0
  148. package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +35 -0
  149. package/dist/fermi-surface/FermiSurfaceScene.svelte +794 -0
  150. package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +50 -0
  151. package/dist/fermi-surface/FermiSurfaceTooltip.svelte +111 -0
  152. package/dist/fermi-surface/FermiSurfaceTooltip.svelte.d.ts +8 -0
  153. package/dist/fermi-surface/compute.d.ts +5 -0
  154. package/dist/fermi-surface/compute.js +538 -0
  155. package/dist/fermi-surface/constants.d.ts +9 -0
  156. package/dist/fermi-surface/constants.js +27 -0
  157. package/dist/fermi-surface/export.d.ts +5 -0
  158. package/dist/fermi-surface/export.js +50 -0
  159. package/dist/fermi-surface/index.d.ts +12 -0
  160. package/dist/fermi-surface/index.js +13 -0
  161. package/dist/fermi-surface/marching-cubes.d.ts +2 -0
  162. package/dist/fermi-surface/marching-cubes.js +2 -0
  163. package/dist/fermi-surface/parse.d.ts +2 -0
  164. package/dist/fermi-surface/parse.js +491 -0
  165. package/dist/fermi-surface/symmetry.d.ts +3 -0
  166. package/dist/fermi-surface/symmetry.js +46 -0
  167. package/dist/fermi-surface/types.d.ts +110 -0
  168. package/dist/fermi-surface/types.js +4 -0
  169. package/dist/heatmap-matrix/HeatmapMatrix.svelte +1545 -0
  170. package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
  171. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +225 -0
  172. package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +30 -0
  173. package/dist/heatmap-matrix/index.d.ts +53 -0
  174. package/dist/heatmap-matrix/index.js +100 -0
  175. package/dist/heatmap-matrix/shared.d.ts +2 -0
  176. package/dist/heatmap-matrix/shared.js +4 -0
  177. package/dist/icons.d.ts +569 -0
  178. package/dist/icons.js +648 -0
  179. package/dist/index.d.ts +39 -0
  180. package/dist/index.js +39 -0
  181. package/dist/io/decompress.d.ts +11 -0
  182. package/dist/io/decompress.js +74 -0
  183. package/dist/io/export.d.ts +16 -0
  184. package/dist/io/export.js +316 -0
  185. package/dist/io/fetch.d.ts +5 -0
  186. package/dist/io/fetch.js +39 -0
  187. package/dist/io/file-drop.d.ts +7 -0
  188. package/dist/io/file-drop.js +43 -0
  189. package/dist/io/index.d.ts +7 -0
  190. package/dist/io/index.js +6 -0
  191. package/dist/io/is-binary.d.ts +1 -0
  192. package/dist/io/is-binary.js +20 -0
  193. package/dist/io/types.d.ts +8 -0
  194. package/dist/io/types.js +1 -0
  195. package/dist/io/url-drop.d.ts +2 -0
  196. package/dist/io/url-drop.js +123 -0
  197. package/dist/isosurface/Isosurface.svelte +285 -0
  198. package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
  199. package/dist/isosurface/IsosurfaceControls.svelte +277 -0
  200. package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
  201. package/dist/isosurface/index.d.ts +5 -0
  202. package/dist/isosurface/index.js +6 -0
  203. package/dist/isosurface/parse.d.ts +6 -0
  204. package/dist/isosurface/parse.js +553 -0
  205. package/dist/isosurface/slice.d.ts +11 -0
  206. package/dist/isosurface/slice.js +140 -0
  207. package/dist/isosurface/types.d.ts +56 -0
  208. package/dist/isosurface/types.js +227 -0
  209. package/dist/labels.d.ts +53 -0
  210. package/dist/labels.js +277 -0
  211. package/dist/layout/FullscreenToggle.svelte +50 -0
  212. package/dist/layout/FullscreenToggle.svelte.d.ts +7 -0
  213. package/dist/layout/InfoCard.svelte +120 -0
  214. package/dist/layout/InfoCard.svelte.d.ts +21 -0
  215. package/dist/layout/InfoTag.svelte +185 -0
  216. package/dist/layout/InfoTag.svelte.d.ts +19 -0
  217. package/dist/layout/PropertyFilter.svelte +246 -0
  218. package/dist/layout/PropertyFilter.svelte.d.ts +24 -0
  219. package/dist/layout/SettingsSection.svelte +148 -0
  220. package/dist/layout/SettingsSection.svelte.d.ts +17 -0
  221. package/dist/layout/SubpageGrid.svelte +82 -0
  222. package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
  223. package/dist/layout/fullscreen.d.ts +9 -0
  224. package/dist/layout/fullscreen.js +53 -0
  225. package/dist/layout/index.d.ts +10 -0
  226. package/dist/layout/index.js +8 -0
  227. package/dist/layout/json-tree/JsonNode.svelte +548 -0
  228. package/dist/layout/json-tree/JsonNode.svelte.d.ts +11 -0
  229. package/dist/layout/json-tree/JsonTree.svelte +1230 -0
  230. package/dist/layout/json-tree/JsonTree.svelte.d.ts +6 -0
  231. package/dist/layout/json-tree/JsonValue.svelte.d.ts +9 -0
  232. package/dist/layout/json-tree/index.d.ts +3 -0
  233. package/dist/layout/json-tree/index.js +3 -0
  234. package/dist/layout/json-tree/types.d.ts +74 -0
  235. package/dist/layout/json-tree/types.js +2 -0
  236. package/dist/layout/json-tree/utils.d.ts +29 -0
  237. package/dist/layout/json-tree/utils.js +641 -0
  238. package/dist/marching-cubes.d.ts +14 -0
  239. package/dist/marching-cubes.js +540 -0
  240. package/dist/math.d.ts +101 -0
  241. package/dist/math.js +905 -0
  242. package/dist/overlays/ContextMenu.svelte +162 -0
  243. package/dist/overlays/ContextMenu.svelte.d.ts +25 -0
  244. package/dist/overlays/CopyButton.svelte +45 -0
  245. package/dist/overlays/CopyButton.svelte.d.ts +8 -0
  246. package/dist/overlays/DragControlTab.svelte +98 -0
  247. package/dist/overlays/DragControlTab.svelte.d.ts +8 -0
  248. package/dist/overlays/DraggablePane.svelte +487 -0
  249. package/dist/overlays/DraggablePane.svelte.d.ts +36 -0
  250. package/dist/overlays/InfoPaneCards.svelte +149 -0
  251. package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
  252. package/dist/overlays/index.d.ts +3 -0
  253. package/dist/overlays/index.js +3 -0
  254. package/dist/periodic-table/PeriodicTable.svelte +469 -0
  255. package/dist/periodic-table/PeriodicTable.svelte.d.ts +55 -0
  256. package/dist/periodic-table/PeriodicTableControls.svelte +557 -0
  257. package/dist/periodic-table/PeriodicTableControls.svelte.d.ts +24 -0
  258. package/dist/periodic-table/PropertySelect.svelte +37 -0
  259. package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
  260. package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
  261. package/dist/periodic-table/index.d.ts +10 -0
  262. package/dist/periodic-table/index.js +4 -0
  263. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +1086 -0
  264. package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +44 -0
  265. package/dist/phase-diagram/PhaseDiagramControls.svelte +444 -0
  266. package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +30 -0
  267. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +126 -0
  268. package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
  269. package/dist/phase-diagram/PhaseDiagramExportPane.svelte +184 -0
  270. package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +19 -0
  271. package/dist/phase-diagram/PhaseDiagramTooltip.svelte +391 -0
  272. package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +16 -0
  273. package/dist/phase-diagram/TdbInfoPanel.svelte +203 -0
  274. package/dist/phase-diagram/TdbInfoPanel.svelte.d.ts +12 -0
  275. package/dist/phase-diagram/build-diagram.d.ts +11 -0
  276. package/dist/phase-diagram/build-diagram.js +160 -0
  277. package/dist/phase-diagram/colors.d.ts +35 -0
  278. package/dist/phase-diagram/colors.js +51 -0
  279. package/dist/phase-diagram/diagram-input.d.ts +29 -0
  280. package/dist/phase-diagram/diagram-input.js +3 -0
  281. package/dist/phase-diagram/index.d.ts +13 -0
  282. package/dist/phase-diagram/index.js +11 -0
  283. package/dist/phase-diagram/parse.d.ts +55 -0
  284. package/dist/phase-diagram/parse.js +272 -0
  285. package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
  286. package/dist/phase-diagram/svg-to-diagram.js +867 -0
  287. package/dist/phase-diagram/types.d.ts +93 -0
  288. package/dist/phase-diagram/types.js +1 -0
  289. package/dist/phase-diagram/utils.d.ts +118 -0
  290. package/dist/phase-diagram/utils.js +604 -0
  291. package/dist/plot/AxisLabel.svelte +51 -0
  292. package/dist/plot/AxisLabel.svelte.d.ts +16 -0
  293. package/dist/plot/BarPlot.svelte +2113 -0
  294. package/dist/plot/BarPlot.svelte.d.ts +84 -0
  295. package/dist/plot/BarPlotControls.svelte +66 -0
  296. package/dist/plot/BarPlotControls.svelte.d.ts +18 -0
  297. package/dist/plot/BinnedScatterPlot.svelte +1114 -0
  298. package/dist/plot/BinnedScatterPlot.svelte.d.ts +66 -0
  299. package/dist/plot/ColorBar.svelte +721 -0
  300. package/dist/plot/ColorBar.svelte.d.ts +31 -0
  301. package/dist/plot/ColorScaleSelect.svelte +54 -0
  302. package/dist/plot/ColorScaleSelect.svelte.d.ts +15 -0
  303. package/dist/plot/ElementScatter.svelte +63 -0
  304. package/dist/plot/ElementScatter.svelte.d.ts +14 -0
  305. package/dist/plot/FillArea.svelte.d.ts +21 -0
  306. package/dist/plot/Histogram.svelte +1558 -0
  307. package/dist/plot/Histogram.svelte.d.ts +50 -0
  308. package/dist/plot/HistogramControls.svelte +212 -0
  309. package/dist/plot/HistogramControls.svelte.d.ts +22 -0
  310. package/dist/plot/InteractiveAxisLabel.svelte +96 -0
  311. package/dist/plot/InteractiveAxisLabel.svelte.d.ts +14 -0
  312. package/dist/plot/Line.svelte +84 -0
  313. package/dist/plot/Line.svelte.d.ts +15 -0
  314. package/dist/plot/PlotAxis.svelte +169 -0
  315. package/dist/plot/PlotAxis.svelte.d.ts +24 -0
  316. package/dist/plot/PlotControls.svelte +537 -0
  317. package/dist/plot/PlotControls.svelte.d.ts +4 -0
  318. package/dist/plot/PlotLegend.svelte +569 -0
  319. package/dist/plot/PlotLegend.svelte.d.ts +29 -0
  320. package/dist/plot/PlotTooltip.svelte +67 -0
  321. package/dist/plot/PlotTooltip.svelte.d.ts +17 -0
  322. package/dist/plot/PortalSelect.svelte +253 -0
  323. package/dist/plot/PortalSelect.svelte.d.ts +16 -0
  324. package/dist/plot/ReferenceLine.svelte.d.ts +20 -0
  325. package/dist/plot/ReferenceLine3D.svelte +156 -0
  326. package/dist/plot/ReferenceLine3D.svelte.d.ts +14 -0
  327. package/dist/plot/ReferencePlane.svelte +175 -0
  328. package/dist/plot/ReferencePlane.svelte.d.ts +14 -0
  329. package/dist/plot/ScatterPlot.svelte +2778 -0
  330. package/dist/plot/ScatterPlot.svelte.d.ts +96 -0
  331. package/dist/plot/ScatterPlot3D.svelte +529 -0
  332. package/dist/plot/ScatterPlot3D.svelte.d.ts +95 -0
  333. package/dist/plot/ScatterPlot3DControls.svelte +437 -0
  334. package/dist/plot/ScatterPlot3DControls.svelte.d.ts +20 -0
  335. package/dist/plot/ScatterPlot3DScene.svelte +912 -0
  336. package/dist/plot/ScatterPlot3DScene.svelte.d.ts +74 -0
  337. package/dist/plot/ScatterPlotControls.svelte +306 -0
  338. package/dist/plot/ScatterPlotControls.svelte.d.ts +17 -0
  339. package/dist/plot/ScatterPoint.svelte +182 -0
  340. package/dist/plot/ScatterPoint.svelte.d.ts +22 -0
  341. package/dist/plot/SpacegroupBarPlot.svelte +293 -0
  342. package/dist/plot/SpacegroupBarPlot.svelte.d.ts +9 -0
  343. package/dist/plot/Surface3D.svelte +197 -0
  344. package/dist/plot/Surface3D.svelte.d.ts +13 -0
  345. package/dist/plot/ZeroLines.svelte +97 -0
  346. package/dist/plot/ZeroLines.svelte.d.ts +33 -0
  347. package/dist/plot/ZoomRect.svelte +23 -0
  348. package/dist/plot/ZoomRect.svelte.d.ts +8 -0
  349. package/dist/plot/adaptive-density.d.ts +69 -0
  350. package/dist/plot/adaptive-density.js +191 -0
  351. package/dist/plot/auto-place.d.ts +43 -0
  352. package/dist/plot/auto-place.js +122 -0
  353. package/dist/plot/axis-utils.d.ts +19 -0
  354. package/dist/plot/axis-utils.js +78 -0
  355. package/dist/plot/binned-scatter-types.d.ts +59 -0
  356. package/dist/plot/binned-scatter-types.js +1 -0
  357. package/dist/plot/data-cleaning.d.ts +37 -0
  358. package/dist/plot/data-cleaning.js +855 -0
  359. package/dist/plot/data-transform.d.ts +16 -0
  360. package/dist/plot/data-transform.js +45 -0
  361. package/dist/plot/defaults.d.ts +19 -0
  362. package/dist/plot/defaults.js +9 -0
  363. package/dist/plot/fill-utils.d.ts +46 -0
  364. package/dist/plot/fill-utils.js +322 -0
  365. package/dist/plot/hover-lock.svelte.d.ts +14 -0
  366. package/dist/plot/hover-lock.svelte.js +46 -0
  367. package/dist/plot/index.d.ts +41 -0
  368. package/dist/plot/index.js +39 -0
  369. package/dist/plot/interactions.d.ts +12 -0
  370. package/dist/plot/interactions.js +101 -0
  371. package/dist/plot/layout.d.ts +78 -0
  372. package/dist/plot/layout.js +273 -0
  373. package/dist/plot/reference-line.d.ts +60 -0
  374. package/dist/plot/reference-line.js +314 -0
  375. package/dist/plot/scales.d.ts +48 -0
  376. package/dist/plot/scales.js +481 -0
  377. package/dist/plot/svg.d.ts +1 -0
  378. package/dist/plot/svg.js +11 -0
  379. package/dist/plot/types.d.ts +831 -0
  380. package/dist/plot/types.js +99 -0
  381. package/dist/plot/utils/label-placement.d.ts +68 -0
  382. package/dist/plot/utils/label-placement.js +326 -0
  383. package/dist/plot/utils/series-visibility.d.ts +15 -0
  384. package/dist/plot/utils/series-visibility.js +85 -0
  385. package/dist/plot/utils.d.ts +1 -0
  386. package/dist/plot/utils.js +14 -0
  387. package/dist/rdf/RdfPlot.svelte +247 -0
  388. package/dist/rdf/RdfPlot.svelte.d.ts +27 -0
  389. package/dist/rdf/calc-rdf.d.ts +4 -0
  390. package/dist/rdf/calc-rdf.js +111 -0
  391. package/dist/rdf/index.d.ts +23 -0
  392. package/dist/rdf/index.js +2 -0
  393. package/dist/sanitize.d.ts +6 -0
  394. package/dist/sanitize.js +116 -0
  395. package/dist/settings.d.ts +255 -0
  396. package/dist/settings.js +1132 -0
  397. package/dist/spectral/Bands.svelte +1040 -0
  398. package/dist/spectral/Bands.svelte.d.ts +40 -0
  399. package/dist/spectral/BandsAndDos.svelte +134 -0
  400. package/dist/spectral/BandsAndDos.svelte.d.ts +18 -0
  401. package/dist/spectral/BrillouinBandsDos.svelte +252 -0
  402. package/dist/spectral/BrillouinBandsDos.svelte.d.ts +20 -0
  403. package/dist/spectral/Dos.svelte +697 -0
  404. package/dist/spectral/Dos.svelte.d.ts +29 -0
  405. package/dist/spectral/helpers.d.ts +119 -0
  406. package/dist/spectral/helpers.js +1032 -0
  407. package/dist/spectral/index.d.ts +6 -0
  408. package/dist/spectral/index.js +6 -0
  409. package/dist/spectral/types.d.ts +84 -0
  410. package/dist/spectral/types.js +2 -0
  411. package/dist/state.svelte.d.ts +25 -0
  412. package/dist/state.svelte.js +45 -0
  413. package/dist/structure/Arrow.svelte +72 -0
  414. package/dist/structure/Arrow.svelte.d.ts +15 -0
  415. package/dist/structure/AtomLegend.svelte +815 -0
  416. package/dist/structure/AtomLegend.svelte.d.ts +35 -0
  417. package/dist/structure/Bond.svelte +140 -0
  418. package/dist/structure/Bond.svelte.d.ts +9 -0
  419. package/dist/structure/CanvasTooltip.svelte +33 -0
  420. package/dist/structure/CanvasTooltip.svelte.d.ts +12 -0
  421. package/dist/structure/CellSelect.svelte +349 -0
  422. package/dist/structure/CellSelect.svelte.d.ts +13 -0
  423. package/dist/structure/Cylinder.svelte +45 -0
  424. package/dist/structure/Cylinder.svelte.d.ts +10 -0
  425. package/dist/structure/Lattice.svelte +196 -0
  426. package/dist/structure/Lattice.svelte.d.ts +17 -0
  427. package/dist/structure/Structure.svelte +2248 -0
  428. package/dist/structure/Structure.svelte.d.ts +89 -0
  429. package/dist/structure/StructureControls.svelte +1273 -0
  430. package/dist/structure/StructureControls.svelte.d.ts +31 -0
  431. package/dist/structure/StructureExportPane.svelte +252 -0
  432. package/dist/structure/StructureExportPane.svelte.d.ts +17 -0
  433. package/dist/structure/StructureInfoPane.svelte +737 -0
  434. package/dist/structure/StructureInfoPane.svelte.d.ts +19 -0
  435. package/dist/structure/StructureScene.svelte +2255 -0
  436. package/dist/structure/StructureScene.svelte.d.ts +111 -0
  437. package/dist/structure/atom-properties.d.ts +37 -0
  438. package/dist/structure/atom-properties.js +200 -0
  439. package/dist/structure/bond-order-perception.d.ts +13 -0
  440. package/dist/structure/bond-order-perception.js +384 -0
  441. package/dist/structure/bonding.d.ts +68 -0
  442. package/dist/structure/bonding.js +696 -0
  443. package/dist/structure/export.d.ts +20 -0
  444. package/dist/structure/export.js +727 -0
  445. package/dist/structure/index.d.ts +126 -0
  446. package/dist/structure/index.js +169 -0
  447. package/dist/structure/label-placement.d.ts +14 -0
  448. package/dist/structure/label-placement.js +72 -0
  449. package/dist/structure/measure.d.ts +6 -0
  450. package/dist/structure/measure.js +29 -0
  451. package/dist/structure/parse.d.ts +66 -0
  452. package/dist/structure/parse.js +1392 -0
  453. package/dist/structure/partial-occupancy.d.ts +25 -0
  454. package/dist/structure/partial-occupancy.js +99 -0
  455. package/dist/structure/pbc.d.ts +9 -0
  456. package/dist/structure/pbc.js +123 -0
  457. package/dist/structure/supercell.d.ts +8 -0
  458. package/dist/structure/supercell.js +170 -0
  459. package/dist/structure/validation.d.ts +2 -0
  460. package/dist/structure/validation.js +10 -0
  461. package/dist/symmetry/SymmetryStats.svelte +226 -0
  462. package/dist/symmetry/SymmetryStats.svelte.d.ts +21 -0
  463. package/dist/symmetry/WyckoffTable.svelte +120 -0
  464. package/dist/symmetry/WyckoffTable.svelte.d.ts +11 -0
  465. package/dist/symmetry/cell-transform.d.ts +12 -0
  466. package/dist/symmetry/cell-transform.js +91 -0
  467. package/dist/symmetry/index.d.ts +43 -0
  468. package/dist/symmetry/index.js +228 -0
  469. package/dist/symmetry/spacegroups.d.ts +9 -0
  470. package/dist/symmetry/spacegroups.js +394 -0
  471. package/dist/table/HeatmapTable.svelte +1833 -0
  472. package/dist/table/HeatmapTable.svelte.d.ts +49 -0
  473. package/dist/table/ToggleMenu.svelte +385 -0
  474. package/dist/table/ToggleMenu.svelte.d.ts +11 -0
  475. package/dist/table/index.d.ts +74 -0
  476. package/dist/table/index.js +38 -0
  477. package/dist/theme/ThemeControl.svelte +53 -0
  478. package/dist/theme/ThemeControl.svelte.d.ts +9 -0
  479. package/dist/theme/index.d.ts +29 -0
  480. package/dist/theme/index.js +79 -0
  481. package/dist/time.d.ts +4 -0
  482. package/dist/time.js +70 -0
  483. package/dist/tooltip/TooltipContent.svelte +58 -0
  484. package/dist/tooltip/TooltipContent.svelte.d.ts +31 -0
  485. package/dist/tooltip/index.d.ts +2 -0
  486. package/dist/tooltip/index.js +1 -0
  487. package/dist/tooltip/types.d.ts +8 -0
  488. package/dist/tooltip/types.js +1 -0
  489. package/dist/trajectory/Trajectory.svelte +1545 -0
  490. package/dist/trajectory/Trajectory.svelte.d.ts +77 -0
  491. package/dist/trajectory/TrajectoryError.svelte +128 -0
  492. package/dist/trajectory/TrajectoryError.svelte.d.ts +13 -0
  493. package/dist/trajectory/TrajectoryExportPane.svelte +357 -0
  494. package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +17 -0
  495. package/dist/trajectory/TrajectoryInfoPane.svelte +313 -0
  496. package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +17 -0
  497. package/dist/trajectory/constants.d.ts +6 -0
  498. package/dist/trajectory/constants.js +7 -0
  499. package/dist/trajectory/extract.d.ts +5 -0
  500. package/dist/trajectory/extract.js +162 -0
  501. package/dist/trajectory/format-detect.d.ts +9 -0
  502. package/dist/trajectory/format-detect.js +76 -0
  503. package/dist/trajectory/frame-reader.d.ts +17 -0
  504. package/dist/trajectory/frame-reader.js +332 -0
  505. package/dist/trajectory/helpers.d.ts +15 -0
  506. package/dist/trajectory/helpers.js +164 -0
  507. package/dist/trajectory/index.d.ts +63 -0
  508. package/dist/trajectory/index.js +126 -0
  509. package/dist/trajectory/parse/ase.d.ts +2 -0
  510. package/dist/trajectory/parse/ase.js +73 -0
  511. package/dist/trajectory/parse/hdf5.d.ts +2 -0
  512. package/dist/trajectory/parse/hdf5.js +127 -0
  513. package/dist/trajectory/parse/index.d.ts +12 -0
  514. package/dist/trajectory/parse/index.js +298 -0
  515. package/dist/trajectory/parse/lammps.d.ts +5 -0
  516. package/dist/trajectory/parse/lammps.js +179 -0
  517. package/dist/trajectory/parse/vasp.d.ts +2 -0
  518. package/dist/trajectory/parse/vasp.js +68 -0
  519. package/dist/trajectory/parse/xyz.d.ts +2 -0
  520. package/dist/trajectory/parse/xyz.js +110 -0
  521. package/dist/trajectory/plotting.d.ts +28 -0
  522. package/dist/trajectory/plotting.js +423 -0
  523. package/dist/trajectory/types.d.ts +11 -0
  524. package/dist/trajectory/types.js +1 -0
  525. package/dist/utils.d.ts +6 -0
  526. package/dist/utils.js +45 -0
  527. package/dist/xrd/XrdPlot.svelte +615 -0
  528. package/dist/xrd/XrdPlot.svelte.d.ts +28 -0
  529. package/dist/xrd/broadening.d.ts +20 -0
  530. package/dist/xrd/broadening.js +97 -0
  531. package/dist/xrd/calc-xrd.d.ts +37 -0
  532. package/dist/xrd/calc-xrd.js +336 -0
  533. package/dist/xrd/index.d.ts +37 -0
  534. package/dist/xrd/index.js +4 -0
  535. package/dist/xrd/parse.d.ts +13 -0
  536. package/dist/xrd/parse.js +749 -0
  537. package/license +1 -1
  538. package/package.json +232 -1457
  539. package/readme.md +98 -171
  540. package/.vscode/launch.json +0 -13
  541. package/.vscodeignore +0 -7
  542. package/dist/assets/STLExporter-BpTH3YHE.js +0 -8
  543. package/dist/assets/browser-DdDecX_W.js +0 -1
  544. package/dist/assets/export-qgn-H9y6.js +0 -2
  545. package/dist/assets/main-DiKYzti2.css +0 -1
  546. package/dist/assets/moyo_wasm_bg-0ocwg7xY.wasm +0 -0
  547. package/dist/extension.js +0 -31293
  548. package/dist/src/lib/FilePicker.svelte +0 -360
  549. package/dist/src/lib/MillerIndexInput.svelte +0 -66
  550. package/dist/src/lib/api/mp.ts +0 -26
  551. package/dist/src/lib/api/optimade.ts +0 -204
  552. package/dist/src/lib/brillouin/BrillouinZone.svelte +0 -549
  553. package/dist/src/lib/brillouin/BrillouinZoneControls.svelte +0 -144
  554. package/dist/src/lib/brillouin/BrillouinZoneExportPane.svelte +0 -146
  555. package/dist/src/lib/brillouin/BrillouinZoneInfoPane.svelte +0 -146
  556. package/dist/src/lib/brillouin/BrillouinZoneScene.svelte +0 -476
  557. package/dist/src/lib/brillouin/BrillouinZoneTooltip.svelte +0 -92
  558. package/dist/src/lib/brillouin/compute.ts +0 -529
  559. package/dist/src/lib/brillouin/index.ts +0 -8
  560. package/dist/src/lib/brillouin/types.ts +0 -51
  561. package/dist/src/lib/chempot-diagram/ChemPotDiagram.svelte +0 -327
  562. package/dist/src/lib/chempot-diagram/ChemPotDiagram2D.svelte +0 -846
  563. package/dist/src/lib/chempot-diagram/ChemPotDiagram3D.svelte +0 -3193
  564. package/dist/src/lib/chempot-diagram/async-compute.svelte.ts +0 -94
  565. package/dist/src/lib/chempot-diagram/chempot-worker.ts +0 -11
  566. package/dist/src/lib/chempot-diagram/color.ts +0 -42
  567. package/dist/src/lib/chempot-diagram/compute.ts +0 -1014
  568. package/dist/src/lib/chempot-diagram/index.ts +0 -6
  569. package/dist/src/lib/chempot-diagram/pointer.ts +0 -56
  570. package/dist/src/lib/chempot-diagram/temperature.ts +0 -77
  571. package/dist/src/lib/chempot-diagram/types.ts +0 -130
  572. package/dist/src/lib/colors/index.ts +0 -249
  573. package/dist/src/lib/composition/BarChart.svelte +0 -297
  574. package/dist/src/lib/composition/BubbleChart.svelte +0 -218
  575. package/dist/src/lib/composition/Composition.svelte +0 -165
  576. package/dist/src/lib/composition/Formula.svelte +0 -268
  577. package/dist/src/lib/composition/FormulaFilter.svelte +0 -1257
  578. package/dist/src/lib/composition/PieChart.svelte +0 -323
  579. package/dist/src/lib/composition/format.ts +0 -155
  580. package/dist/src/lib/composition/index.ts +0 -37
  581. package/dist/src/lib/composition/parse.ts +0 -605
  582. package/dist/src/lib/constants.ts +0 -134
  583. package/dist/src/lib/controls.ts +0 -42
  584. package/dist/src/lib/convex-hull/ConvexHull.svelte +0 -157
  585. package/dist/src/lib/convex-hull/ConvexHull2D.svelte +0 -825
  586. package/dist/src/lib/convex-hull/ConvexHull3D.svelte +0 -1801
  587. package/dist/src/lib/convex-hull/ConvexHull4D.svelte +0 -1398
  588. package/dist/src/lib/convex-hull/ConvexHullControls.svelte +0 -535
  589. package/dist/src/lib/convex-hull/ConvexHullInfoPane.svelte +0 -125
  590. package/dist/src/lib/convex-hull/ConvexHullStats.svelte +0 -929
  591. package/dist/src/lib/convex-hull/ConvexHullTooltip.svelte +0 -131
  592. package/dist/src/lib/convex-hull/GasPressureControls.svelte +0 -247
  593. package/dist/src/lib/convex-hull/StructurePopup.svelte +0 -151
  594. package/dist/src/lib/convex-hull/barycentric-coords.ts +0 -246
  595. package/dist/src/lib/convex-hull/demo-temperature.ts +0 -63
  596. package/dist/src/lib/convex-hull/gas-thermodynamics.ts +0 -405
  597. package/dist/src/lib/convex-hull/helpers.ts +0 -932
  598. package/dist/src/lib/convex-hull/index.ts +0 -202
  599. package/dist/src/lib/convex-hull/thermodynamics.ts +0 -2192
  600. package/dist/src/lib/convex-hull/types.ts +0 -267
  601. package/dist/src/lib/coordination/CoordinationBarPlot.svelte +0 -311
  602. package/dist/src/lib/coordination/calc-coordination.ts +0 -93
  603. package/dist/src/lib/coordination/index.ts +0 -9
  604. package/dist/src/lib/effects.svelte.ts +0 -48
  605. package/dist/src/lib/element/ElementHeading.svelte +0 -26
  606. package/dist/src/lib/element/ElementPhoto.svelte +0 -57
  607. package/dist/src/lib/element/ElementStats.svelte +0 -80
  608. package/dist/src/lib/element/ElementTile.svelte +0 -484
  609. package/dist/src/lib/element/data.ts +0 -14
  610. package/dist/src/lib/element/index.ts +0 -8
  611. package/dist/src/lib/element/types.ts +0 -62
  612. package/dist/src/lib/feedback/ClickFeedback.svelte +0 -58
  613. package/dist/src/lib/feedback/DragOverlay.svelte +0 -42
  614. package/dist/src/lib/feedback/index.ts +0 -4
  615. package/dist/src/lib/fermi-surface/FermiSlice.svelte +0 -189
  616. package/dist/src/lib/fermi-surface/FermiSurface.svelte +0 -600
  617. package/dist/src/lib/fermi-surface/FermiSurfaceControls.svelte +0 -448
  618. package/dist/src/lib/fermi-surface/FermiSurfaceScene.svelte +0 -794
  619. package/dist/src/lib/fermi-surface/FermiSurfaceTooltip.svelte +0 -111
  620. package/dist/src/lib/fermi-surface/compute.ts +0 -728
  621. package/dist/src/lib/fermi-surface/constants.ts +0 -32
  622. package/dist/src/lib/fermi-surface/export.ts +0 -64
  623. package/dist/src/lib/fermi-surface/index.ts +0 -14
  624. package/dist/src/lib/fermi-surface/marching-cubes.ts +0 -3
  625. package/dist/src/lib/fermi-surface/parse.ts +0 -574
  626. package/dist/src/lib/fermi-surface/symmetry.ts +0 -56
  627. package/dist/src/lib/fermi-surface/types.ts +0 -159
  628. package/dist/src/lib/heatmap-matrix/HeatmapMatrix.svelte +0 -1545
  629. package/dist/src/lib/heatmap-matrix/HeatmapMatrixControls.svelte +0 -225
  630. package/dist/src/lib/heatmap-matrix/index.ts +0 -167
  631. package/dist/src/lib/heatmap-matrix/shared.ts +0 -7
  632. package/dist/src/lib/icons.ts +0 -650
  633. package/dist/src/lib/index.ts +0 -61
  634. package/dist/src/lib/io/decompress.ts +0 -92
  635. package/dist/src/lib/io/export.ts +0 -385
  636. package/dist/src/lib/io/fetch.ts +0 -46
  637. package/dist/src/lib/io/file-drop.ts +0 -51
  638. package/dist/src/lib/io/index.ts +0 -7
  639. package/dist/src/lib/io/is-binary.ts +0 -24
  640. package/dist/src/lib/io/types.ts +0 -8
  641. package/dist/src/lib/io/url-drop.ts +0 -141
  642. package/dist/src/lib/isosurface/Isosurface.svelte +0 -285
  643. package/dist/src/lib/isosurface/IsosurfaceControls.svelte +0 -277
  644. package/dist/src/lib/isosurface/index.ts +0 -7
  645. package/dist/src/lib/isosurface/parse.ts +0 -656
  646. package/dist/src/lib/isosurface/slice.ts +0 -175
  647. package/dist/src/lib/isosurface/types.ts +0 -309
  648. package/dist/src/lib/labels.ts +0 -320
  649. package/dist/src/lib/layout/FullscreenToggle.svelte +0 -50
  650. package/dist/src/lib/layout/InfoCard.svelte +0 -120
  651. package/dist/src/lib/layout/InfoTag.svelte +0 -185
  652. package/dist/src/lib/layout/PropertyFilter.svelte +0 -246
  653. package/dist/src/lib/layout/SettingsSection.svelte +0 -148
  654. package/dist/src/lib/layout/SubpageGrid.svelte +0 -82
  655. package/dist/src/lib/layout/fullscreen.ts +0 -65
  656. package/dist/src/lib/layout/index.ts +0 -11
  657. package/dist/src/lib/layout/json-tree/JsonNode.svelte +0 -548
  658. package/dist/src/lib/layout/json-tree/JsonTree.svelte +0 -1230
  659. package/dist/src/lib/layout/json-tree/index.ts +0 -3
  660. package/dist/src/lib/layout/json-tree/types.ts +0 -126
  661. package/dist/src/lib/layout/json-tree/utils.ts +0 -682
  662. package/dist/src/lib/marching-cubes.ts +0 -614
  663. package/dist/src/lib/math.ts +0 -1081
  664. package/dist/src/lib/overlays/ContextMenu.svelte +0 -162
  665. package/dist/src/lib/overlays/CopyButton.svelte +0 -45
  666. package/dist/src/lib/overlays/DragControlTab.svelte +0 -98
  667. package/dist/src/lib/overlays/DraggablePane.svelte +0 -487
  668. package/dist/src/lib/overlays/InfoPaneCards.svelte +0 -149
  669. package/dist/src/lib/overlays/index.ts +0 -3
  670. package/dist/src/lib/periodic-table/PeriodicTable.svelte +0 -469
  671. package/dist/src/lib/periodic-table/PeriodicTableControls.svelte +0 -557
  672. package/dist/src/lib/periodic-table/PropertySelect.svelte +0 -37
  673. package/dist/src/lib/periodic-table/index.ts +0 -12
  674. package/dist/src/lib/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +0 -1086
  675. package/dist/src/lib/phase-diagram/PhaseDiagramControls.svelte +0 -444
  676. package/dist/src/lib/phase-diagram/PhaseDiagramEditorPane.svelte +0 -126
  677. package/dist/src/lib/phase-diagram/PhaseDiagramExportPane.svelte +0 -184
  678. package/dist/src/lib/phase-diagram/PhaseDiagramTooltip.svelte +0 -391
  679. package/dist/src/lib/phase-diagram/TdbInfoPanel.svelte +0 -203
  680. package/dist/src/lib/phase-diagram/build-diagram.ts +0 -186
  681. package/dist/src/lib/phase-diagram/colors.ts +0 -58
  682. package/dist/src/lib/phase-diagram/diagram-input.ts +0 -40
  683. package/dist/src/lib/phase-diagram/index.ts +0 -13
  684. package/dist/src/lib/phase-diagram/parse.ts +0 -348
  685. package/dist/src/lib/phase-diagram/svg-to-diagram.ts +0 -1023
  686. package/dist/src/lib/phase-diagram/types.ts +0 -144
  687. package/dist/src/lib/phase-diagram/utils.ts +0 -775
  688. package/dist/src/lib/plot/AxisLabel.svelte +0 -51
  689. package/dist/src/lib/plot/BarPlot.svelte +0 -2113
  690. package/dist/src/lib/plot/BarPlotControls.svelte +0 -66
  691. package/dist/src/lib/plot/BinnedScatterPlot.svelte +0 -1114
  692. package/dist/src/lib/plot/ColorBar.svelte +0 -721
  693. package/dist/src/lib/plot/ColorScaleSelect.svelte +0 -54
  694. package/dist/src/lib/plot/ElementScatter.svelte +0 -63
  695. package/dist/src/lib/plot/Histogram.svelte +0 -1558
  696. package/dist/src/lib/plot/HistogramControls.svelte +0 -212
  697. package/dist/src/lib/plot/InteractiveAxisLabel.svelte +0 -96
  698. package/dist/src/lib/plot/Line.svelte +0 -84
  699. package/dist/src/lib/plot/PlotAxis.svelte +0 -169
  700. package/dist/src/lib/plot/PlotControls.svelte +0 -537
  701. package/dist/src/lib/plot/PlotLegend.svelte +0 -569
  702. package/dist/src/lib/plot/PlotTooltip.svelte +0 -67
  703. package/dist/src/lib/plot/PortalSelect.svelte +0 -253
  704. package/dist/src/lib/plot/ReferenceLine3D.svelte +0 -156
  705. package/dist/src/lib/plot/ReferencePlane.svelte +0 -175
  706. package/dist/src/lib/plot/ScatterPlot.svelte +0 -2778
  707. package/dist/src/lib/plot/ScatterPlot3D.svelte +0 -529
  708. package/dist/src/lib/plot/ScatterPlot3DControls.svelte +0 -437
  709. package/dist/src/lib/plot/ScatterPlot3DScene.svelte +0 -912
  710. package/dist/src/lib/plot/ScatterPlotControls.svelte +0 -306
  711. package/dist/src/lib/plot/ScatterPoint.svelte +0 -182
  712. package/dist/src/lib/plot/SpacegroupBarPlot.svelte +0 -293
  713. package/dist/src/lib/plot/Surface3D.svelte +0 -197
  714. package/dist/src/lib/plot/ZeroLines.svelte +0 -97
  715. package/dist/src/lib/plot/ZoomRect.svelte +0 -23
  716. package/dist/src/lib/plot/adaptive-density.ts +0 -316
  717. package/dist/src/lib/plot/auto-place.ts +0 -184
  718. package/dist/src/lib/plot/axis-utils.ts +0 -122
  719. package/dist/src/lib/plot/binned-scatter-types.ts +0 -83
  720. package/dist/src/lib/plot/data-cleaning.ts +0 -1069
  721. package/dist/src/lib/plot/data-transform.ts +0 -69
  722. package/dist/src/lib/plot/defaults.ts +0 -9
  723. package/dist/src/lib/plot/fill-utils.ts +0 -494
  724. package/dist/src/lib/plot/hover-lock.svelte.ts +0 -60
  725. package/dist/src/lib/plot/index.ts +0 -53
  726. package/dist/src/lib/plot/interactions.ts +0 -119
  727. package/dist/src/lib/plot/layout.ts +0 -425
  728. package/dist/src/lib/plot/reference-line.ts +0 -426
  729. package/dist/src/lib/plot/scales.ts +0 -654
  730. package/dist/src/lib/plot/svg.ts +0 -23
  731. package/dist/src/lib/plot/types.ts +0 -1144
  732. package/dist/src/lib/plot/utils/label-placement.ts +0 -541
  733. package/dist/src/lib/plot/utils/series-visibility.ts +0 -140
  734. package/dist/src/lib/plot/utils.ts +0 -11
  735. package/dist/src/lib/rdf/RdfPlot.svelte +0 -247
  736. package/dist/src/lib/rdf/calc-rdf.ts +0 -167
  737. package/dist/src/lib/rdf/index.ts +0 -27
  738. package/dist/src/lib/sanitize.ts +0 -126
  739. package/dist/src/lib/settings.ts +0 -1479
  740. package/dist/src/lib/spectral/Bands.svelte +0 -1040
  741. package/dist/src/lib/spectral/BandsAndDos.svelte +0 -134
  742. package/dist/src/lib/spectral/BrillouinBandsDos.svelte +0 -252
  743. package/dist/src/lib/spectral/Dos.svelte +0 -697
  744. package/dist/src/lib/spectral/helpers.ts +0 -1381
  745. package/dist/src/lib/spectral/index.ts +0 -8
  746. package/dist/src/lib/spectral/types.ts +0 -112
  747. package/dist/src/lib/state.svelte.ts +0 -64
  748. package/dist/src/lib/structure/Arrow.svelte +0 -72
  749. package/dist/src/lib/structure/AtomLegend.svelte +0 -815
  750. package/dist/src/lib/structure/Bond.svelte +0 -140
  751. package/dist/src/lib/structure/CanvasTooltip.svelte +0 -33
  752. package/dist/src/lib/structure/CellSelect.svelte +0 -349
  753. package/dist/src/lib/structure/Cylinder.svelte +0 -45
  754. package/dist/src/lib/structure/Lattice.svelte +0 -196
  755. package/dist/src/lib/structure/Structure.svelte +0 -2248
  756. package/dist/src/lib/structure/StructureControls.svelte +0 -1273
  757. package/dist/src/lib/structure/StructureExportPane.svelte +0 -252
  758. package/dist/src/lib/structure/StructureInfoPane.svelte +0 -737
  759. package/dist/src/lib/structure/StructureScene.svelte +0 -2255
  760. package/dist/src/lib/structure/atom-properties.ts +0 -316
  761. package/dist/src/lib/structure/bond-order-perception.ts +0 -447
  762. package/dist/src/lib/structure/bonding.ts +0 -944
  763. package/dist/src/lib/structure/export.ts +0 -861
  764. package/dist/src/lib/structure/index.ts +0 -291
  765. package/dist/src/lib/structure/label-placement.ts +0 -130
  766. package/dist/src/lib/structure/measure.ts +0 -45
  767. package/dist/src/lib/structure/parse.ts +0 -1705
  768. package/dist/src/lib/structure/partial-occupancy.ts +0 -183
  769. package/dist/src/lib/structure/pbc.ts +0 -164
  770. package/dist/src/lib/structure/supercell.ts +0 -226
  771. package/dist/src/lib/structure/validation.ts +0 -11
  772. package/dist/src/lib/symmetry/SymmetryStats.svelte +0 -226
  773. package/dist/src/lib/symmetry/WyckoffTable.svelte +0 -120
  774. package/dist/src/lib/symmetry/cell-transform.ts +0 -118
  775. package/dist/src/lib/symmetry/index.ts +0 -348
  776. package/dist/src/lib/symmetry/spacegroups.ts +0 -404
  777. package/dist/src/lib/table/HeatmapTable.svelte +0 -1833
  778. package/dist/src/lib/table/ToggleMenu.svelte +0 -385
  779. package/dist/src/lib/table/index.ts +0 -139
  780. package/dist/src/lib/theme/ThemeControl.svelte +0 -53
  781. package/dist/src/lib/theme/index.ts +0 -107
  782. package/dist/src/lib/time.ts +0 -71
  783. package/dist/src/lib/tooltip/TooltipContent.svelte +0 -58
  784. package/dist/src/lib/tooltip/index.ts +0 -2
  785. package/dist/src/lib/tooltip/types.ts +0 -13
  786. package/dist/src/lib/trajectory/Trajectory.svelte +0 -1545
  787. package/dist/src/lib/trajectory/TrajectoryError.svelte +0 -128
  788. package/dist/src/lib/trajectory/TrajectoryExportPane.svelte +0 -357
  789. package/dist/src/lib/trajectory/TrajectoryInfoPane.svelte +0 -313
  790. package/dist/src/lib/trajectory/constants.ts +0 -7
  791. package/dist/src/lib/trajectory/extract.ts +0 -196
  792. package/dist/src/lib/trajectory/format-detect.ts +0 -96
  793. package/dist/src/lib/trajectory/frame-reader.ts +0 -456
  794. package/dist/src/lib/trajectory/helpers.ts +0 -217
  795. package/dist/src/lib/trajectory/index.ts +0 -218
  796. package/dist/src/lib/trajectory/parse/ase.ts +0 -109
  797. package/dist/src/lib/trajectory/parse/hdf5.ts +0 -173
  798. package/dist/src/lib/trajectory/parse/index.ts +0 -411
  799. package/dist/src/lib/trajectory/parse/lammps.ts +0 -215
  800. package/dist/src/lib/trajectory/parse/vasp.ts +0 -102
  801. package/dist/src/lib/trajectory/parse/xyz.ts +0 -143
  802. package/dist/src/lib/trajectory/plotting.ts +0 -599
  803. package/dist/src/lib/trajectory/types.ts +0 -13
  804. package/dist/src/lib/utils.ts +0 -56
  805. package/dist/src/lib/xrd/XrdPlot.svelte +0 -615
  806. package/dist/src/lib/xrd/broadening.ts +0 -130
  807. package/dist/src/lib/xrd/calc-xrd.ts +0 -397
  808. package/dist/src/lib/xrd/index.ts +0 -38
  809. package/dist/src/lib/xrd/parse.ts +0 -858
  810. package/dist/webview.js +0 -29421
  811. package/icon.png +0 -0
  812. package/matterviz-0.3.2.vsix +0 -0
  813. package/matterviz-0.3.4.vsix +0 -0
  814. package/matterviz-0.3.5.vsix +0 -0
  815. package/scripts/sync-config.ts +0 -101
  816. package/src/declarations.d.ts +0 -2
  817. package/src/extension.ts +0 -972
  818. package/src/node-io.ts +0 -65
  819. package/src/types.ts +0 -17
  820. package/src/webview/JsonBrowser.svelte +0 -1079
  821. package/src/webview/PlotPanel.svelte +0 -346
  822. package/src/webview/detect.ts +0 -444
  823. package/src/webview/main.ts +0 -764
  824. package/src/webview/plot-utils.ts +0 -250
  825. package/test-fixtures/all-viz-types.json.gz +0 -0
  826. package/test-fixtures/plot-demo-data.json.gz +0 -0
  827. package/tests/detect.test.ts +0 -604
  828. package/tests/extension.test.ts +0 -2041
  829. package/tests/node-io.test.ts +0 -39
  830. package/tests/plot-utils.test.ts +0 -302
  831. package/tests/vite-plugin-json-gz.test.ts +0 -114
  832. package/tests/vscode-mock.ts +0 -18
  833. package/tests/webview.test.ts +0 -231
  834. package/tsconfig.json +0 -20
  835. package/vite-plugin-json-gz.ts +0 -29
  836. package/vite.config.ts +0 -34
  837. package/vite.extension.config.ts +0 -34
  838. /package/dist/{src/lib/EmptyState.svelte → EmptyState.svelte} +0 -0
  839. /package/dist/{src/lib/Icon.svelte → Icon.svelte} +0 -0
  840. /package/dist/{src/lib/app.css → app.css} +0 -0
  841. /package/dist/{src/lib/chempot-diagram → chempot-diagram}/ChemPotScene3D.svelte +0 -0
  842. /package/dist/{src/lib/colors → colors}/alloy-colors.json +0 -0
  843. /package/dist/{src/lib/colors → colors}/dark-mode-colors.json +0 -0
  844. /package/dist/{src/lib/colors → colors}/jmol-colors.json +0 -0
  845. /package/dist/{src/lib/colors → colors}/muted-colors.json +0 -0
  846. /package/dist/{src/lib/colors → colors}/pastel-colors.json +0 -0
  847. /package/dist/{src/lib/colors → colors}/vesta-colors.json +0 -0
  848. /package/dist/{src/lib/convex-hull → convex-hull}/TemperatureSlider.svelte +0 -0
  849. /package/dist/{src/lib/element → element}/BohrAtom.svelte +0 -0
  850. /package/dist/{src/lib/element → element}/Nucleus.svelte +0 -0
  851. /package/dist/{src/lib/element → element}/data.json +0 -0
  852. /package/dist/{src/lib/element → element}/data.json.gz +0 -0
  853. /package/dist/{src/lib/element → element}/data.json.gz.d.ts +0 -0
  854. /package/dist/{src/lib/element → element}/data.schema.json +0 -0
  855. /package/dist/{src/lib/element-image-urls.json → element-image-urls.json} +0 -0
  856. /package/dist/{src/lib/feedback → feedback}/Spinner.svelte +0 -0
  857. /package/dist/{src/lib/feedback → feedback}/StatusMessage.svelte +0 -0
  858. /package/dist/{src/lib/layout → layout}/json-tree/JsonValue.svelte +0 -0
  859. /package/dist/{src/lib/periodic-table → periodic-table}/TableInset.svelte +0 -0
  860. /package/dist/{src/lib/plot → plot}/FillArea.svelte +0 -0
  861. /package/dist/{src/lib/plot → plot}/ReferenceLine.svelte +0 -0
  862. /package/dist/{src/lib/theme → theme}/themes.mjs +0 -0
  863. /package/dist/{src/lib/xrd → xrd}/atomic_scattering_params.json +0 -0
@@ -1,2041 +0,0 @@
1
- import {
2
- STRUCTURE_EXTENSIONS,
3
- TRAJ_EXTENSIONS,
4
- TRAJ_KEYWORDS,
5
- VASP_VOLUMETRIC_REGEX,
6
- } from '$lib/constants'
7
- import type { ThemeName } from '$lib/theme/index'
8
- import { is_trajectory_file } from '$lib/trajectory/parse'
9
- import { Buffer } from 'node:buffer'
10
- import { beforeEach, describe, expect, test, vi } from 'vitest'
11
- import type { ExtensionContext, Tab, TextEditor, Uri } from 'vscode'
12
- import pkg_json from '../package.json' with { type: 'json' }
13
- import {
14
- activate,
15
- create_html,
16
- get_defaults,
17
- get_file,
18
- get_theme,
19
- handle_msg,
20
- MessageData,
21
- read_file,
22
- render,
23
- should_auto_render,
24
- } from '../src/extension'
25
- import { VOLUMETRIC_VASP_RE } from '../src/types'
26
- import type { FileData } from '../src/webview/main'
27
-
28
- // Mock modules
29
- const mock_fs = vi.hoisted(() => ({
30
- readFileSync: vi.fn().mockReturnValue(`mock content`),
31
- existsSync: vi.fn().mockReturnValue(true),
32
- readdirSync: vi.fn().mockReturnValue([]),
33
- }))
34
-
35
- vi.mock(`node:fs`, () => mock_fs)
36
- vi.mock(`node:path`, async (importOriginal) => {
37
- const actual = (await importOriginal()) as Record<string, unknown>
38
- return {
39
- ...actual,
40
- basename: vi.fn((p: string) => p.split(`/`).pop() || ``),
41
- dirname: vi.fn((p: string) => p.split(`/`).slice(0, -1).join(`/`) || `/`),
42
- join: vi.fn((...paths: string[]) => paths.join(`/`)),
43
- isAbsolute: vi.fn((p: string) => p.startsWith(`/`)),
44
- }
45
- })
46
-
47
- const msg_args = {
48
- // generic placeholder arguments for all messages
49
- filename: `filename`,
50
- request_id: `request_id`,
51
- file_path: `file_path`,
52
- frame_index: 0,
53
- } as const
54
-
55
- const mock_vscode = vi.hoisted(() => ({
56
- window: {
57
- showInformationMessage: vi.fn(),
58
- showErrorMessage: vi.fn(),
59
- showTextDocument: vi.fn(),
60
- showSaveDialog: vi.fn(),
61
- createWebviewPanel: vi.fn(),
62
- activeTextEditor: null as TextEditor | null,
63
- tabGroups: { activeTabGroup: { activeTab: null as Tab | null } },
64
- registerCustomEditorProvider: vi.fn(),
65
- activeColorTheme: { kind: 1 }, // Light theme by default
66
- onDidChangeActiveColorTheme: vi.fn(() => ({ dispose: vi.fn() })),
67
- onDidChangeActiveTextEditor: vi.fn(() => ({ dispose: vi.fn() })),
68
- },
69
- workspace: {
70
- getConfiguration: vi.fn(() => ({
71
- get: vi.fn((_key: string, default_val: string) => default_val),
72
- })),
73
- onDidChangeConfiguration: vi.fn(() => ({ dispose: vi.fn() })),
74
- openTextDocument: vi.fn(),
75
- onDidOpenTextDocument: vi.fn(() => ({ dispose: vi.fn() })),
76
- onDidCreateFiles: vi.fn(() => ({ dispose: vi.fn() })),
77
- onDidRenameFiles: vi.fn(() => ({ dispose: vi.fn() })),
78
- createFileSystemWatcher: vi.fn(() => ({
79
- onDidChange: vi.fn(),
80
- onDidDelete: vi.fn(),
81
- dispose: vi.fn(),
82
- })),
83
- fs: { stat: vi.fn(), readFile: vi.fn(), writeFile: vi.fn() },
84
- },
85
- commands: { registerCommand: vi.fn(), executeCommand: vi.fn() },
86
- Uri: {
87
- file: vi.fn((p: string) => ({ fsPath: p })),
88
- joinPath: vi.fn((_base: unknown, ...paths: string[]) => ({
89
- fsPath: paths.join(`/`),
90
- })),
91
- parse: vi.fn((url: string) => ({ toString: () => url })),
92
- },
93
- ViewColumn: { Active: 1, Beside: 2 },
94
- ColorThemeKind: { Light: 1, Dark: 2, HighContrast: 3, HighContrastLight: 4 },
95
- UIKind: { Desktop: 1, Web: 2 },
96
- RelativePattern: class {
97
- constructor(
98
- public base: unknown,
99
- public pattern: string,
100
- ) {}
101
- },
102
- version: `1.99.0`,
103
- env: {
104
- appName: `VSCode`,
105
- remoteName: undefined,
106
- uiKind: 1, // Desktop
107
- clipboard: {
108
- writeText: vi.fn(() => Promise.resolve()),
109
- readText: vi.fn(() => Promise.resolve(``)),
110
- },
111
- openExternal: vi.fn(() => Promise.resolve(true)),
112
- },
113
- }))
114
-
115
- vi.mock(`vscode`, () => mock_vscode)
116
-
117
- describe(`MatterViz Extension`, () => {
118
- let mock_file_system_watcher: {
119
- onDidChange: ReturnType<typeof vi.fn>
120
- onDidDelete: ReturnType<typeof vi.fn>
121
- dispose: ReturnType<typeof vi.fn>
122
- }
123
- const supported_volumetric_filenames: [string, string][] = [
124
- [`CHGCAR`, `VASP volumetric`],
125
- [`AECCAR0`, `VASP volumetric`],
126
- [`AECCAR1`, `VASP volumetric`],
127
- [`AECCAR2`, `VASP volumetric`],
128
- [`ELFCAR`, `VASP volumetric`],
129
- [`LOCPOT`, `VASP volumetric`],
130
- [`PARCHG`, `VASP volumetric`],
131
- [`PARCHG.gz`, `VASP volumetric`],
132
- [`PARCHG.BAND_1`, `VASP volumetric`],
133
- [`run_PARCHG_001`, `VASP volumetric`],
134
- [`density.cube`, `Gaussian cube`],
135
- [`density.cube.gz`, `Gaussian cube`],
136
- ]
137
- const volumetric_auto_render_filenames: [string, boolean][] =
138
- supported_volumetric_filenames.map(([filename]) => [filename, true] as [string, boolean])
139
-
140
- test(`extensionKind should be configured as ["workspace"] to work locally and in remote SSH sessions`, () => {
141
- // https://github.com/janosh/matterviz/issues/129#issuecomment-3193473225
142
- expect(pkg_json.extensionKind).toEqual([`workspace`])
143
- })
144
-
145
- test(`extension volumetric regex stays in sync with app detection`, () => {
146
- expect(VOLUMETRIC_VASP_RE.source).toBe(VASP_VOLUMETRIC_REGEX.source)
147
- expect(VOLUMETRIC_VASP_RE.flags).toBe(VASP_VOLUMETRIC_REGEX.flags)
148
- })
149
-
150
- describe(`Custom Editor File Patterns`, () => {
151
- const custom_editors = pkg_json.contributes.customEditors
152
- const matterviz_editor = custom_editors.find(
153
- (editor) => editor.viewType === `matterviz.viewer`,
154
- )
155
- const patterns = matterviz_editor?.selector.map((sel) => sel.filenamePattern) ?? []
156
-
157
- // Tests if a filename matches any pattern (simplified glob matching).
158
- const matches_any_pattern = (filename: string): boolean => {
159
- for (const pattern of patterns) {
160
- // Convert glob pattern to regex
161
- const regex_str = pattern
162
- .replace(/\./g, `\\.`) // escape dots
163
- .replace(/\*/g, `.*`) // * → .*
164
- .replace(/\{([^}]+)\}/g, (_, group) => `(${group.split(`,`).join(`|`)})`) // {a,b} → (a|b)
165
- const regex = new RegExp(`^${regex_str}$`, `i`)
166
- if (regex.test(filename)) return true
167
- }
168
- return false
169
- }
170
-
171
- test(`should have matterviz.viewer custom editor defined`, () => {
172
- expect(matterviz_editor).toBeDefined()
173
- expect(matterviz_editor?.displayName).toBe(`MatterViz Viewer`)
174
- })
175
-
176
- // Tests auto-synced with library constants to prevent regressions
177
- test.each<[string, string]>([
178
- // All trajectory extensions from library
179
- ...TRAJ_EXTENSIONS.map(
180
- (ext: string) => [`test${ext}`, `TRAJ_EXT ${ext}`] as [string, string],
181
- ),
182
- // All structure extensions from library (uncompressed, .gz, .bz2)
183
- ...STRUCTURE_EXTENSIONS.flatMap(
184
- (ext: string) =>
185
- [
186
- [`test${ext}`, `STRUCT_EXT ${ext}`],
187
- [`test${ext}.gz`, `${ext}.gz`],
188
- ] as [string, string][],
189
- ),
190
- // All trajectory keywords in filenames
191
- ...TRAJ_KEYWORDS.map(
192
- (kw: string) => [`${kw}_output.dat`, `TRAJ_KW ${kw}`] as [string, string],
193
- ),
194
- // VASP special filenames + additional VS Code-only formats
195
- [`POSCAR`, `VASP`],
196
- [`CONTCAR`, `VASP`],
197
- [`XDATCAR`, `VASP`],
198
- [`OUTCAR`, `VASP`],
199
- [`simulation.h5`, `HDF5`],
200
- [`data.hdf5`, `HDF5`],
201
- [`dynamics.dcd`, `DCD`],
202
- [`run.trr`, `TRR`],
203
- [`molecule.xyz`, `XYZ`],
204
- [`atoms.extxyz`, `extXYZ`],
205
- ...supported_volumetric_filenames,
206
- ])(`pattern matches "%s" (%s)`, (filename) => {
207
- expect(matches_any_pattern(filename)).toBe(true)
208
- })
209
-
210
- test.each([
211
- [`myCHGCARfile`],
212
- [`prefixPARCHGsuffix`],
213
- [`notes_ELFCARbackup`],
214
- [`report-AECCARnotes`],
215
- [`density.cube.bz2`],
216
- [`structure.cif.bz2`],
217
- ])(`pattern does not match unsupported near miss "%s"`, (filename) => {
218
- expect(matches_any_pattern(filename)).toBe(false)
219
- })
220
- })
221
-
222
- beforeEach(async () => {
223
- vi.clearAllMocks()
224
-
225
- // Import extension module and clear all watchers
226
- const ext = await import(`../src/extension`)
227
- ext.active_watchers.clear()
228
- ext.active_frame_loaders.clear()
229
- ext.auto_render_timers.clear()
230
- ext.active_auto_render_panels.clear()
231
-
232
- mock_fs.readFileSync.mockReturnValue(`mock content`)
233
- mock_fs.existsSync.mockReturnValue(true)
234
- mock_fs.readdirSync.mockReturnValue([])
235
- mock_vscode.window.activeTextEditor = null
236
-
237
- // Reset theme to default Light theme to avoid inter-test coupling
238
- mock_vscode.window.activeColorTheme = { kind: 1 } // Light theme by default
239
-
240
- // Set up file system watcher mock
241
- mock_file_system_watcher = {
242
- onDidChange: vi.fn(),
243
- onDidDelete: vi.fn(),
244
- dispose: vi.fn(),
245
- }
246
- mock_vscode.workspace.createFileSystemWatcher.mockReturnValue(mock_file_system_watcher)
247
-
248
- // Set up default mock for vscode.workspace.fs.stat to return file stats
249
- mock_vscode.workspace.fs.stat.mockResolvedValue({ size: 1000, type: 1 })
250
- mock_vscode.workspace.fs.readFile.mockResolvedValue(
251
- new Uint8Array(Buffer.from(`mock content`)),
252
- )
253
- })
254
-
255
- // Test data consolidation
256
- const mock_webview = {
257
- cspSource: `vscode-webview:`,
258
- asWebviewUri: vi.fn(
259
- (uri: { fsPath: string }) =>
260
- `vscode-webview://unit-test${encodeURIComponent(uri.fsPath)}`,
261
- ),
262
- onDidReceiveMessage: vi.fn(),
263
- postMessage: vi.fn(),
264
- html: ``,
265
- }
266
- const mock_context = {
267
- extensionUri: { fsPath: `/test` },
268
- subscriptions: [],
269
- } as unknown as ExtensionContext
270
-
271
- test.each([
272
- [`test.gz`, true],
273
- [`test.h5`, true],
274
- [`test.traj`, true], // ASE binary files should be treated as compressed
275
- [`test.hdf5`, true],
276
- [`md_npt_300K.traj`, true], // Specific ASE ULM binary file
277
- [`ase-LiMnO2-chgnet-relax.traj`, true], // Another ASE ULM binary file
278
- [`test.cif`, false],
279
- [`test.xyz`, false], // .xyz files are text format, not compressed binary
280
- [`test.json`, false],
281
- [``, false],
282
- ])(`file reading: "%s" → compressed:%s`, async (filename, expected_compressed) => {
283
- const result = await read_file(`/test/${filename}`)
284
- expect(result.filename).toBe(filename)
285
- expect(result.is_base64).toBe(expected_compressed)
286
- // Assert payload differences instead of redundant API calls
287
- if (expected_compressed) {
288
- expect(result.content).toBe(Buffer.from(`mock content`).toString(`base64`))
289
- } else {
290
- expect(result.content).toBe(`mock content`)
291
- }
292
- })
293
-
294
- test(`file reading: large file should return sentinel`, async () => {
295
- const large_file_size = 2 * 1024 * 1024 * 1024 // 2GB, above MAX_VSCODE_FILE_SIZE (1GB)
296
- const filename = `large-structure.cif`
297
- const file_path = `/test/${filename}`
298
-
299
- // Mock fs.stat to return a size above the threshold
300
- mock_vscode.workspace.fs.stat.mockResolvedValue({
301
- size: large_file_size,
302
- type: 1,
303
- })
304
-
305
- const result = await read_file(file_path)
306
-
307
- expect(result.filename).toBe(filename)
308
- expect(result.content).toBe(`LARGE_FILE:${file_path}:${large_file_size}`)
309
- expect(result.is_base64).toBe(false)
310
- // readFile should not be called for large files
311
- expect(mock_vscode.workspace.fs.readFile).not.toHaveBeenCalled()
312
- })
313
-
314
- test(`file reading: large binary file should return sentinel with base64 flag`, async () => {
315
- const large_file_size = 2 * 1024 * 1024 * 1024 // 2GB, above MAX_VSCODE_FILE_SIZE (1GB)
316
- const filename = `large-trajectory.traj`
317
- const file_path = `/test/${filename}`
318
-
319
- // Mock fs.stat to return a size above the threshold
320
- mock_vscode.workspace.fs.stat.mockResolvedValue({
321
- size: large_file_size,
322
- type: 1,
323
- })
324
-
325
- const result = await read_file(file_path)
326
-
327
- expect(result.filename).toBe(filename)
328
- expect(result.content).toBe(`LARGE_FILE:${file_path}:${large_file_size}`)
329
- expect(result.is_base64).toBe(true) // Binary files should have base64 flag
330
- // readFile should not be called for large files
331
- expect(mock_vscode.workspace.fs.readFile).not.toHaveBeenCalled()
332
- })
333
-
334
- test.each([
335
- [`md_npt_300K.traj`, true, true], // ASE binary trajectory
336
- [`ase-LiMnO2-chgnet-relax.traj`, true, true], // ASE binary trajectory
337
- [`simulation_nvt_250K.traj`, true, true], // ASE binary trajectory
338
- [`water_cluster_md.traj`, true, true], // ASE binary trajectory
339
- [`optimization_relax.traj`, true, true], // ASE binary trajectory
340
- [`regular_text.traj`, true, true], // .traj files are always binary
341
- // filename-only based .xyz/.extxyz detection always assumes structure, requires file content to look for frames and recognize as trajectory
342
- [`test.xyz`, false, false],
343
- [`test.extxyz`, false, false],
344
- [`test.cif`, false, false], // Not a trajectory file
345
- ])(
346
- `ASE trajectory file handling: "%s" → trajectory:%s, binary:%s`,
347
- async (filename, is_trajectory, is_binary) => {
348
- expect(is_trajectory_file(filename)).toBe(is_trajectory)
349
- if (is_trajectory) {
350
- const result = await read_file(`/test/${filename}`)
351
- expect(result.is_base64).toBe(is_binary)
352
- }
353
- },
354
- )
355
-
356
- // Integration test for ASE trajectory file processing (simulates the exact failing scenario)
357
- test(`ASE trajectory file end-to-end processing`, async () => {
358
- const ase_filename = `ase-LiMnO2-chgnet-relax.traj`
359
-
360
- // Step 1: Extension should detect this as a trajectory file
361
- expect(is_trajectory_file(ase_filename)).toBe(true)
362
-
363
- // Step 2: Extension should read this as binary (compressed)
364
- const file_result = await read_file(`/test/${ase_filename}`)
365
- expect(file_result.filename).toBe(ase_filename)
366
- expect(file_result.is_base64).toBe(true)
367
- expect(file_result.content).toBe(Buffer.from(`mock content`).toString(`base64`)) // base64 encoded binary data
368
-
369
- // Step 3: Verify webview data structure matches expected format
370
- const webview_data = {
371
- type: `trajectory` as const,
372
- data: file_result,
373
- }
374
-
375
- // Step 4: HTML generation should work with this data
376
- const webview_data_with_theme = { ...webview_data, theme: `light` as const }
377
- const html = create_html(mock_webview, mock_context, webview_data_with_theme)
378
-
379
- expect(html).toContain(`<!DOCTYPE html>`)
380
- expect(html).toContain(JSON.stringify(webview_data_with_theme))
381
-
382
- // Step 5: Verify the exact data structure that would be sent to webview
383
- const parsed_data = JSON.parse(/matterviz_data=(\{[\s\S]*?\});/.exec(html)?.[1] ?? `{}`)
384
- expect(parsed_data.type).toBe(`trajectory`)
385
- expect(parsed_data.data.filename).toBe(ase_filename)
386
- expect(parsed_data.data.is_base64).toBe(true)
387
- expect(parsed_data.data.content).toBe(Buffer.from(`mock content`).toString(`base64`))
388
- expect(parsed_data.theme).toBe(`light`)
389
- })
390
-
391
- test.each([
392
- [{ fsPath: `/test/file.cif` } as unknown as Uri, `file.cif`],
393
- [{ fsPath: `/test/structure.xyz` } as unknown as Uri, `structure.xyz`],
394
- ])(`get_file with URI`, async (uri, expected_filename) => {
395
- const result = await get_file(uri)
396
- expect(result.filename).toBe(expected_filename)
397
- })
398
-
399
- test(`get_file with active editor`, async () => {
400
- mock_vscode.window.activeTextEditor = {
401
- document: { fileName: `/test/active.cif`, getText: () => `active content` },
402
- } as TextEditor
403
- const result = await get_file()
404
- expect(result.filename).toBe(`active.cif`)
405
- expect(result.content).toBe(`active content`)
406
- expect(result.is_base64).toBe(false)
407
- })
408
-
409
- test(`get_file with active tab`, async () => {
410
- mock_vscode.window.tabGroups.activeTabGroup.activeTab = {
411
- input: { uri: { fsPath: `/test/tab.cif` } },
412
- } as unknown as Tab
413
- const result = await get_file()
414
- expect(result.filename).toBe(`tab.cif`)
415
- })
416
-
417
- test(`get_file throws when no file found`, async () => {
418
- mock_vscode.window.tabGroups.activeTabGroup.activeTab = null
419
- await expect(get_file()).rejects.toThrow(
420
- `No file selected. MatterViz needs an active editor to know what to render.`,
421
- )
422
- })
423
-
424
- test.each([
425
- [`structure`, { filename: `test.cif`, content: `content`, is_base64: false }],
426
- [`trajectory`, { filename: `test.traj`, content: `YmluYXJ5`, is_base64: true }],
427
- [
428
- `structure`,
429
- {
430
- filename: `test"quotes.cif`,
431
- content: `content`,
432
- is_base64: false,
433
- },
434
- ],
435
- [`structure`, { filename: `test.cif`, content: ``, is_base64: false }],
436
- [
437
- `structure`,
438
- {
439
- filename: `test.cif`,
440
- content: `<script>alert("xss")</script>`,
441
- is_base64: false,
442
- },
443
- ],
444
- [
445
- `structure`,
446
- {
447
- filename: `large.cif`,
448
- content: `x`.repeat(100_000),
449
- is_base64: false,
450
- },
451
- ],
452
- ] as const)(`HTML generation: %s files`, (type, data) => {
453
- const webview_data = { type, data, theme: `light` } as const
454
- const html = create_html(mock_webview, mock_context, webview_data)
455
- expect(html).toContain(`<!DOCTYPE html>`)
456
- expect(html).toContain(`Content-Security-Policy`)
457
- expect(html).toContain(`default-src 'none'`)
458
- expect(html).toContain(`script-src 'nonce-`)
459
- expect(html).toMatch(/nonce="[a-zA-Z0-9]{8,32}"/)
460
- expect(html).toContain(JSON.stringify(webview_data).replace(/<\//g, `<\\/`))
461
- expect(html).toContain(`matterviz-app`)
462
- })
463
-
464
- test.each([
465
- [{ command: `info`, text: `Test message` }, `showInformationMessage`],
466
- [{ command: `error`, text: `Error message` }, `showErrorMessage`],
467
- [{ command: `info`, text: `"><script>alert(1)</script>` }, `showInformationMessage`],
468
- [{ command: `error`, text: `javascript:alert(1)` }, `showErrorMessage`],
469
- ] as const)(`message handling: %s`, async (message, expected_method) => {
470
- await handle_msg(message)
471
- expect(
472
- mock_vscode.window[expected_method as keyof typeof mock_vscode.window],
473
- ).toHaveBeenCalledWith(message.text)
474
- })
475
-
476
- test.each([
477
- [{ command: `saveAs`, content: `content`, filename: `test.cif` }, true, `Saved: save.cif`],
478
- [
479
- {
480
- command: `saveAs`,
481
- content: `<script>alert("XSS")</script>`,
482
- filename: `test.cif`,
483
- },
484
- true,
485
- `Saved: save.cif`,
486
- ],
487
- ] as const)(`saveAs success: %s`, async (message, should_succeed, expected_info) => {
488
- mock_vscode.window.showSaveDialog.mockResolvedValue({ fsPath: `/test/save.cif` })
489
- await handle_msg(message)
490
- if (should_succeed) {
491
- const enc = new TextEncoder()
492
- expect(mock_vscode.workspace.fs.writeFile).toHaveBeenCalledWith(
493
- { fsPath: `/test/save.cif` },
494
- enc.encode(message.content),
495
- )
496
- expect(mock_vscode.window.showInformationMessage).toHaveBeenCalledWith(expected_info)
497
- }
498
- })
499
-
500
- test.each([
501
- [
502
- `PNG image`,
503
- `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==`,
504
- `structure.png`,
505
- true,
506
- ],
507
- [
508
- `JPEG image`,
509
- `data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AP/Z`,
510
- `plot.jpg`,
511
- true,
512
- ],
513
- [
514
- `PDF document`,
515
- `data:application/pdf;base64,JVBERi0xLjQKJcOkw7zDtsO8DQoxIDAgb2JqDQo8PA0KL1R5cGUgL0NhdGFsb2cNCi9QYWdlcyAyIDAgUg0KPj4NCmVuZG9iag0KMiAwIG9iag0KPDwNCi9UeXBlIC9QYWdlcw0KL0tpZHMgWzMgMCBSXQ0KL0NvdW50IDENCi9NZWRpYUJveCBbMCAwIDYxMiA3OTJdDQo+Pg0KZW5kb2JqDQozIDAgb2JqDQo8PA0KL1R5cGUgL1BhZ2UNCi9QYXJlbnQgMiAwIFINCi9SZXNvdXJjZXMgPDwNCi9Gb250IDw8DQovRjEgNCAwIFINCj4+DQo+Pg0KL0NvbnRlbnRzIDUgMCBSDQo+Pg0KZW5kb2JqDQo0IDAgb2JqDQo8PA0KL1R5cGUgL0ZvbnQNCi9TdWJ0eXBlIC9UeXBlMQ0KL0Jhc2VGb250IC9IZWx2ZXRpY2ENCi9FbmNvZGluZyAvV2luQW5zaUVuY29kaW5nDQo+Pg0KZW5kb2JqDQo1IDAgb2JqDQo8PA0KL0xlbmd0aCA0NA0KPj4NCnN0cmVhbQ0KQlQNCjEyIDAgVGQKL0YxIDEyIFRqDQooSGVsbG8gV29ybGQpIFRqDQpFVA0KZW5kc3RyZWFtDQplbmRvYmoNCnhyZWYNCjAgNg0KMDAwMDAwMDAwMCA2NTUzNSBmDQowMDAwMDAwMDEwIDAwMDAwIG4NCjAwMDAwMDAwNzkgMDAwMDAgbg0KMDAwMDAwMDE3MyAwMDAwMCBuDQowMDAwMDAwMzAxIDAwMDAwIG4NCjAwMDAwMDAzODAgMDAwMDAgbg0KdHJhaWxlcg0KPDwNCi9TaXplIDYNCi9Sb290IDEgMCBSDQo+Pg0Kc3RhcnR4cmVmDQo0OTINCiUlRU9G`,
516
- `report.pdf`,
517
- true,
518
- ],
519
- ])(`saveAs binary data: %s`, async (_description, data_url, filename, is_binary) => {
520
- mock_vscode.window.showSaveDialog.mockResolvedValue({ fsPath: `/test/${filename}` })
521
- await handle_msg({
522
- command: `saveAs`,
523
- content: data_url,
524
- ...msg_args,
525
- filename,
526
- is_binary,
527
- })
528
- const base64_data = data_url.replace(/^data:[^;]+;base64,/, ``)
529
- const expected_buffer = Uint8Array.from(Buffer.from(base64_data, `base64`))
530
- expect(mock_vscode.workspace.fs.writeFile).toHaveBeenCalledWith(
531
- { fsPath: `/test/${filename}` },
532
- expected_buffer,
533
- )
534
- expect(mock_vscode.window.showInformationMessage).toHaveBeenCalledWith(
535
- `Saved: ${filename}`,
536
- )
537
- })
538
-
539
- test(`saveAs error handling`, async () => {
540
- mock_vscode.window.showSaveDialog.mockResolvedValue({
541
- fsPath: `/test/save.cif`,
542
- })
543
- mock_vscode.workspace.fs.writeFile.mockRejectedValue(new Error(`Write failed`))
544
-
545
- await handle_msg({
546
- command: `saveAs`,
547
- content: `content`,
548
- ...msg_args,
549
- filename: `test.cif`,
550
- })
551
- expect(mock_vscode.window.showErrorMessage).toHaveBeenCalledWith(
552
- `Failed to save text file: Write failed`,
553
- )
554
- })
555
-
556
- test(`saveAs user cancellation`, async () => {
557
- mock_vscode.window.showSaveDialog.mockResolvedValue(undefined)
558
-
559
- await handle_msg({
560
- command: `saveAs`,
561
- content: `content`,
562
- ...msg_args,
563
- filename: `test.cif`,
564
- })
565
- expect(mock_vscode.workspace.fs.writeFile).not.toHaveBeenCalled()
566
- })
567
-
568
- test(`saveAs binary data validation: empty base64 data`, async () => {
569
- mock_vscode.window.showSaveDialog.mockResolvedValue({ fsPath: `/test/test.png` })
570
-
571
- await handle_msg({
572
- command: `saveAs`,
573
- content: `data:image/png;base64,`,
574
- ...msg_args,
575
- filename: `test.png`,
576
- is_binary: true,
577
- })
578
-
579
- expect(mock_vscode.window.showErrorMessage).toHaveBeenCalledWith(
580
- `Failed to save binary data: Invalid data URL: missing base64 data`,
581
- )
582
- expect(mock_vscode.workspace.fs.writeFile).not.toHaveBeenCalled()
583
- })
584
-
585
- test.each([[{ command: `info` }], [{ command: `saveAs` }], [{ command: `unknown` }]])(
586
- `malformed message handling: %s`,
587
- async (msg) => {
588
- await expect(
589
- handle_msg({ ...msg, ...msg_args } as unknown as MessageData),
590
- ).resolves.toBeUndefined()
591
- },
592
- )
593
-
594
- test(`render creates webview panel`, async () => {
595
- const mock_panel = {
596
- webview: { ...mock_webview },
597
- onDidDispose: vi.fn(),
598
- }
599
- mock_vscode.window.createWebviewPanel.mockReturnValue(mock_panel)
600
- mock_vscode.window.activeTextEditor = {
601
- document: { fileName: `/test/active.cif`, getText: () => `content` },
602
- } as TextEditor
603
-
604
- await render(mock_context)
605
- expect(mock_vscode.window.createWebviewPanel).toHaveBeenCalledWith(
606
- `matterviz`,
607
- `MatterViz - active.cif`,
608
- mock_vscode.ViewColumn.Active,
609
- expect.any(Object),
610
- )
611
- })
612
-
613
- test(`render handles errors`, async () => {
614
- mock_vscode.window.activeTextEditor = null
615
- mock_vscode.window.tabGroups.activeTabGroup.activeTab = null
616
- await render(mock_context)
617
- expect(mock_vscode.window.showErrorMessage).toHaveBeenCalledWith(
618
- `Failed: No file selected. MatterViz needs an active editor to know what to render.`,
619
- )
620
- })
621
-
622
- test(`extension activation`, () => {
623
- activate(mock_context)
624
- expect(mock_vscode.commands.registerCommand).toHaveBeenCalledWith(
625
- `matterviz.open`,
626
- expect.any(Function),
627
- )
628
- expect(mock_vscode.commands.registerCommand).toHaveBeenCalledWith(
629
- `matterviz.report_bug`,
630
- expect.any(Function),
631
- )
632
- expect(mock_vscode.window.registerCustomEditorProvider).toHaveBeenCalledWith(
633
- `matterviz.viewer`,
634
- expect.any(Object),
635
- expect.any(Object),
636
- )
637
- })
638
-
639
- describe(`Bug Reporting`, () => {
640
- let mock_opened_document: { content: string; language: string } | null = null
641
- let report_bug_command: (() => Promise<void>) | null = null
642
- let mock_env: {
643
- appName: string
644
- remoteName: string | undefined
645
- uiKind: number
646
- clipboard: {
647
- writeText: ReturnType<typeof vi.fn>
648
- readText: ReturnType<typeof vi.fn>
649
- }
650
- openExternal: ReturnType<typeof vi.fn>
651
- }
652
-
653
- beforeEach(async () => {
654
- // Reset state
655
- mock_opened_document = null
656
- report_bug_command = null
657
-
658
- // Mock clipboard API (must be set up before activation)
659
- mock_env = {
660
- appName: `Cursor`,
661
- remoteName: undefined,
662
- uiKind: 1, // Desktop
663
- clipboard: {
664
- writeText: vi.fn(() => Promise.resolve()),
665
- readText: vi.fn(() => Promise.resolve(``)),
666
- },
667
- openExternal: vi.fn(() => Promise.resolve(true)),
668
- }
669
- mock_vscode.env = mock_env as unknown as typeof mock_vscode.env
670
-
671
- // Capture the report_bug command during activation
672
- const command_registry = new Map<string, () => Promise<void>>()
673
- mock_vscode.commands.registerCommand = vi.fn(
674
- (command_name: string, callback: () => Promise<void>) => {
675
- command_registry.set(command_name, callback)
676
- return { dispose: vi.fn() }
677
- },
678
- )
679
-
680
- // Mock workspace.openTextDocument to capture the document content (BEFORE activation)
681
- mock_vscode.workspace.openTextDocument = vi.fn(
682
- (options: { content: string; language: string }) => {
683
- mock_opened_document = {
684
- content: options.content,
685
- language: options.language,
686
- }
687
- return Promise.resolve({
688
- uri: { fsPath: `/tmp/bug-report.md` },
689
- getText: () => options.content,
690
- })
691
- },
692
- )
693
-
694
- // Mock window.showTextDocument (BEFORE activation)
695
- mock_vscode.window.showTextDocument = vi.fn(() => Promise.resolve())
696
-
697
- // Mock showInformationMessage to return action choices (BEFORE activation)
698
- mock_vscode.window.showInformationMessage = vi.fn(() => Promise.resolve(undefined))
699
-
700
- // Activate extension to register commands
701
- activate(mock_context)
702
-
703
- // Get the report_bug command
704
- report_bug_command = command_registry.get(`matterviz.report_bug`) ?? null
705
- })
706
-
707
- test(`should generate bug report with environment information`, async () => {
708
- expect(report_bug_command).not.toBeNull()
709
- if (!report_bug_command) return
710
-
711
- await report_bug_command()
712
-
713
- expect(mock_opened_document).not.toBeNull()
714
- expect(mock_opened_document?.language).toBe(`markdown`)
715
-
716
- const content = mock_opened_document?.content ?? ``
717
-
718
- // Check for main sections
719
- expect(content).toContain(`### Environment`)
720
- expect(content).toContain(`### System Resources`)
721
- expect(content).toContain(`### Active Files & Extension State`)
722
- expect(content).toContain(`### Console Logs`)
723
-
724
- // Check environment details
725
- expect(content).toContain(`- **Editor**: Cursor`)
726
- expect(content).toContain(`- **MatterViz Version**: ${pkg_json.version}`)
727
- expect(content).toContain(`- **UI Kind**: Desktop`)
728
- expect(content).toContain(`- **Remote Session**: No (Local)`)
729
-
730
- // Check system resources
731
- expect(content).toContain(`- **Total Memory**:`)
732
- expect(content).toContain(`- **Free Memory**:`)
733
- expect(content).toContain(`- **Process RSS**:`)
734
- expect(content).toContain(`- **Process Heap Used**:`)
735
- expect(content).toContain(`- **Process Heap Total**:`)
736
-
737
- // Check console logs instructions
738
- expect(content).toContain(`**Please check for console errors/warnings:**`)
739
- expect(content).toContain(`Toggle Developer Tools`)
740
-
741
- // Check GitHub link
742
- expect(content).toContain(`https://github.com/janosh/matterviz/issues`)
743
-
744
- // Check timestamp
745
- expect(content).toMatch(/\*\*Generated\*\*: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)
746
- })
747
-
748
- test(`should detect remote session correctly`, async () => {
749
- // Mock remote session
750
- mock_env.remoteName = `ssh-remote`
751
-
752
- expect(report_bug_command).not.toBeNull()
753
- if (!report_bug_command) return
754
-
755
- await report_bug_command()
756
-
757
- const content = mock_opened_document?.content ?? ``
758
- expect(content).toContain(`- **Remote Session**: Yes (ssh-remote)`)
759
- })
760
-
761
- test(`should include active files in report`, async () => {
762
- // Start watching some files first
763
- const file1_path = `/test/structure.cif`
764
- const file2_path = `/test/trajectory.traj`
765
-
766
- await handle_msg(
767
- {
768
- command: `startWatching`,
769
- file_path: file1_path,
770
- filename: `structure.cif`,
771
- request_id: `req1`,
772
- frame_index: 0,
773
- } as MessageData,
774
- mock_webview,
775
- )
776
-
777
- await handle_msg(
778
- {
779
- command: `startWatching`,
780
- file_path: file2_path,
781
- filename: `trajectory.traj`,
782
- request_id: `req2`,
783
- frame_index: 0,
784
- } as MessageData,
785
- mock_webview,
786
- )
787
-
788
- expect(report_bug_command).not.toBeNull()
789
- if (!report_bug_command) return
790
-
791
- await report_bug_command()
792
-
793
- const content = mock_opened_document?.content ?? ``
794
-
795
- // Should list both files with bold filenames (not headers)
796
- expect(content).toContain(`**structure.cif**`)
797
- expect(content).toContain(`**trajectory.traj**`)
798
- expect(content).toContain(`- **Path**: \`${file1_path}\``)
799
- expect(content).toContain(`- **Path**: \`${file2_path}\``)
800
- expect(content).toContain(`- **Has Watcher**: true`)
801
-
802
- // Check for combined section and extension state counters
803
- expect(content).toContain(`### Active Files & Extension State`)
804
- expect(content).toContain(`- **Active Watchers**: 2`)
805
- })
806
-
807
- test(`should handle files with no active watchers`, async () => {
808
- expect(report_bug_command).not.toBeNull()
809
- if (!report_bug_command) return
810
-
811
- await report_bug_command()
812
-
813
- const content = mock_opened_document?.content ?? ``
814
- expect(content).toContain(`No files currently being watched/rendered.`)
815
- })
816
-
817
- test(`should copy to clipboard when user selects that option`, async () => {
818
- mock_vscode.window.showInformationMessage = vi.fn(() =>
819
- Promise.resolve(`Copy to Clipboard`),
820
- )
821
-
822
- expect(report_bug_command).not.toBeNull()
823
- if (!report_bug_command) return
824
-
825
- await report_bug_command()
826
-
827
- const content = mock_opened_document?.content ?? ``
828
-
829
- // Should have called clipboard.writeText with the content
830
- expect(mock_env.clipboard.writeText).toHaveBeenCalledWith(content)
831
-
832
- // Should show success message
833
- expect(mock_vscode.window.showInformationMessage).toHaveBeenCalledWith(
834
- `Debug information copied to clipboard!`,
835
- )
836
- })
837
-
838
- test(`should open GitHub issues when user selects that option`, async () => {
839
- mock_vscode.window.showInformationMessage = vi.fn(() =>
840
- Promise.resolve(`Open GitHub Issues`),
841
- )
842
-
843
- expect(report_bug_command).not.toBeNull()
844
- if (!report_bug_command) return
845
-
846
- await report_bug_command()
847
-
848
- // Should have opened GitHub issues URL
849
- expect(mock_env.openExternal).toHaveBeenCalledWith(
850
- expect.objectContaining({
851
- toString: expect.any(Function),
852
- }),
853
- )
854
-
855
- // Verify the URL contains the GitHub issues path
856
- const call_args = mock_env.openExternal.mock.calls[0]
857
- expect(call_args[0].toString()).toContain(
858
- `https://github.com/janosh/matterviz/issues/new`,
859
- )
860
- })
861
-
862
- test(`should format file sizes correctly`, async () => {
863
- // Mock different file sizes
864
- const test_cases = [
865
- { size: 500, expected: `500 B` },
866
- { size: 1024, expected: `1.00 KiB` },
867
- { size: 1024 * 1024, expected: `1.00 MiB` },
868
- { size: 1024 * 1024 * 1024, expected: `1.00 GiB` },
869
- ]
870
-
871
- // Create a map to track sizes for each file
872
- const file_sizes = new Map<string, number>()
873
-
874
- // Set up persistent mock that uses the file_sizes map
875
- mock_vscode.workspace.fs.stat.mockImplementation((uri) => {
876
- const size = file_sizes.get(uri.fsPath) ?? 1000
877
- return Promise.resolve({ size, type: 1 })
878
- })
879
-
880
- // Add files to watchers with their sizes
881
- const watcher_promises = test_cases.map((test_case) => {
882
- const file_path = `/test/file_${test_case.size}.cif`
883
- file_sizes.set(file_path, test_case.size)
884
-
885
- return handle_msg(
886
- {
887
- command: `startWatching`,
888
- file_path,
889
- filename: `file_${test_case.size}.cif`,
890
- request_id: `req_${test_case.size}`,
891
- frame_index: 0,
892
- } as MessageData,
893
- mock_webview,
894
- )
895
- })
896
-
897
- await Promise.all(watcher_promises)
898
-
899
- expect(report_bug_command).not.toBeNull()
900
- if (!report_bug_command) return
901
-
902
- await report_bug_command()
903
-
904
- const content = mock_opened_document?.content ?? ``
905
-
906
- // Check that sizes are formatted correctly
907
- for (const test_case of test_cases) {
908
- if (test_case.size >= 1024) {
909
- // Only check KB and above (bytes might be rounded)
910
- expect(content).toContain(test_case.expected)
911
- }
912
- }
913
- })
914
-
915
- test(`should handle file stat errors gracefully`, async () => {
916
- // Mock file that exists but throws error on stat
917
- const file_path = `/test/error-file.cif`
918
-
919
- await handle_msg(
920
- {
921
- command: `startWatching`,
922
- file_path,
923
- filename: `error-file.cif`,
924
- request_id: `req_error`,
925
- frame_index: 0,
926
- } as MessageData,
927
- mock_webview,
928
- )
929
-
930
- // Mock stat to throw error for this specific file
931
- mock_vscode.workspace.fs.stat.mockRejectedValue(new Error(`File not found`))
932
-
933
- expect(report_bug_command).not.toBeNull()
934
- if (!report_bug_command) return
935
-
936
- await report_bug_command()
937
-
938
- const content = mock_opened_document?.content ?? ``
939
-
940
- // Should still include the file but with "Unknown" size
941
- expect(content).toContain(`**error-file.cif**`)
942
- expect(content).toContain(`- **Size**: Unknown`)
943
- })
944
-
945
- test(`should include extension state counters`, async () => {
946
- // Start watching multiple files
947
- await handle_msg(
948
- {
949
- command: `startWatching`,
950
- file_path: `/test/file1.cif`,
951
- filename: `file1.cif`,
952
- request_id: `req1`,
953
- frame_index: 0,
954
- } as MessageData,
955
- mock_webview,
956
- )
957
-
958
- await handle_msg(
959
- {
960
- command: `startWatching`,
961
- file_path: `/test/file2.cif`,
962
- filename: `file2.cif`,
963
- request_id: `req2`,
964
- frame_index: 0,
965
- } as MessageData,
966
- mock_webview,
967
- )
968
-
969
- expect(report_bug_command).not.toBeNull()
970
- if (!report_bug_command) return
971
-
972
- await report_bug_command()
973
-
974
- const content = mock_opened_document?.content ?? ``
975
-
976
- // Check combined Active Files & Extension State section
977
- expect(content).toContain(`### Active Files & Extension State`)
978
- expect(content).toContain(`- **Active Watchers**: 2`)
979
- expect(content).toMatch(/- \*\*Active Frame Loaders\*\*: \d+/)
980
- expect(content).toMatch(/- \*\*Auto-Render Timers\*\*: \d+/)
981
- expect(content).toMatch(/- \*\*Active Auto-Render Panels\*\*: \d+/)
982
- })
983
-
984
- test(`should handle errors during report generation`, async () => {
985
- // Mock openTextDocument to throw an error
986
- mock_vscode.workspace.openTextDocument = vi.fn(() =>
987
- Promise.reject(new Error(`Failed to create document`)),
988
- )
989
-
990
- expect(report_bug_command).not.toBeNull()
991
- if (!report_bug_command) return
992
-
993
- await report_bug_command()
994
-
995
- // Should show error message
996
- expect(mock_vscode.window.showErrorMessage).toHaveBeenCalledWith(
997
- expect.stringContaining(`Failed to collect debug information`),
998
- )
999
- })
1000
-
1001
- test(`should include console logs instructions in bug report`, async () => {
1002
- expect(report_bug_command).not.toBeNull()
1003
- if (!report_bug_command) return
1004
-
1005
- await report_bug_command()
1006
-
1007
- const content = mock_opened_document?.content ?? ``
1008
-
1009
- // Check for console logs section with instructions
1010
- expect(content).toContain(`### Console Logs`)
1011
- expect(content).toContain(`**Please check for console errors/warnings:**`)
1012
- expect(content).toContain(`Toggle Developer Tools`)
1013
- expect(content).toContain(`Tip: You can filter console messages`)
1014
- })
1015
- })
1016
-
1017
- test(`performance benchmarks`, () => {
1018
- // Trajectory detection performance
1019
- const filenames = Array.from({ length: 10000 }, (_, idx) => `test_${idx}.xyz`)
1020
- const start = performance.now()
1021
- filenames.forEach((name) => is_trajectory_file(name))
1022
- expect(performance.now() - start).toBeLessThan(100)
1023
-
1024
- // HTML generation performance
1025
- const large_data = {
1026
- type: `structure`,
1027
- data: { filename: `large.cif`, content: `x`.repeat(100_000), is_base64: false },
1028
- theme: `light`,
1029
- } as const
1030
- const html_start = performance.now()
1031
- create_html(mock_webview, mock_context, large_data)
1032
- expect(performance.now() - html_start).toBeLessThan(50)
1033
- })
1034
-
1035
- test(`nonce uniqueness`, () => {
1036
- const data = {
1037
- type: `structure`,
1038
- data: { filename: `test.cif`, content: `content`, is_base64: false },
1039
- theme: `light`,
1040
- } as const
1041
- const nonces = new Set<string>()
1042
-
1043
- for (let idx = 0; idx < 1000; idx++) {
1044
- const html = create_html(mock_webview, mock_context, data)
1045
- const nonce_match = /nonce="([a-zA-Z0-9]+)"/.exec(html)
1046
- if (nonce_match) nonces.add(nonce_match[1])
1047
- }
1048
-
1049
- expect(nonces.size).toBe(1000)
1050
- })
1051
-
1052
- test(`XSS prevention`, () => {
1053
- const dangerous_payloads = [
1054
- `<script>alert("XSS")</script>`,
1055
- `<img src="x" onerror="alert(1)">`,
1056
- `javascript:alert(1)`,
1057
- `"><script>alert(1)</script>`,
1058
- `';alert(1);//`,
1059
- `</script><script>alert(document.cookie)</script>`,
1060
- ]
1061
-
1062
- dangerous_payloads.forEach((payload) => {
1063
- const data = {
1064
- type: `structure`,
1065
- data: { filename: `test.cif`, content: payload, is_base64: false },
1066
- theme: `light`,
1067
- } as const
1068
- const html = create_html(mock_webview, mock_context, data)
1069
- const escaped_json = JSON.stringify(data).replace(/<\//g, `<\\/`)
1070
-
1071
- expect(html).toContain(escaped_json)
1072
- // Ensure no raw </script> inside the JSON data breaks out of the script tag
1073
- const data_script = /window\.matterviz_data=(.*?);/s.exec(html)
1074
- if (data_script) {
1075
- expect(data_script[1]).not.toContain(`</script>`)
1076
- }
1077
- })
1078
- })
1079
-
1080
- test(`concurrent operations`, async () => {
1081
- const promises = Array.from({ length: 50 }, (_, idx) =>
1082
- handle_msg({ command: `info`, text: `Message ${idx}`, ...msg_args }),
1083
- )
1084
- await Promise.all(promises)
1085
- expect(mock_vscode.window.showInformationMessage).toHaveBeenCalledTimes(50)
1086
- })
1087
-
1088
- describe(`Theme functionality`, () => {
1089
- test.each([
1090
- [mock_vscode.ColorThemeKind.Light, `auto`, `light`], // Light VSCode theme, auto setting → light
1091
- [mock_vscode.ColorThemeKind.Dark, `auto`, `dark`], // Dark VSCode theme, auto setting → dark
1092
- [mock_vscode.ColorThemeKind.HighContrast, `auto`, `black`], // High contrast VSCode theme, auto setting → black
1093
- [mock_vscode.ColorThemeKind.HighContrastLight, `auto`, `white`], // High contrast light VSCode theme, auto setting → white
1094
- [mock_vscode.ColorThemeKind.Light, `light`, `light`], // Light VSCode theme, light setting → light
1095
- [mock_vscode.ColorThemeKind.Light, `dark`, `dark`], // Light VSCode theme, dark setting → dark
1096
- [mock_vscode.ColorThemeKind.Light, `white`, `white`], // Light VSCode theme, white setting → white
1097
- [mock_vscode.ColorThemeKind.Light, `black`, `black`], // Light VSCode theme, black setting → black
1098
- [mock_vscode.ColorThemeKind.Dark, `light`, `light`], // Dark VSCode theme, light setting → light
1099
- [mock_vscode.ColorThemeKind.Dark, `dark`, `dark`], // Dark VSCode theme, dark setting → dark
1100
- [mock_vscode.ColorThemeKind.Dark, `white`, `white`], // Dark VSCode theme, white setting → white
1101
- [mock_vscode.ColorThemeKind.Dark, `black`, `black`], // Dark VSCode theme, black setting → black
1102
- ] as const)(
1103
- `theme detection: VSCode theme %i, setting '%s' → '%s'`,
1104
- (vscode_theme_kind: number, setting: string, expected: ThemeName) => {
1105
- const mock_config = {
1106
- get: vi.fn((key: string, default_value?: string) =>
1107
- key === `theme` ? setting : default_value,
1108
- ),
1109
- }
1110
- mock_vscode.workspace.getConfiguration = vi.fn(() => mock_config) as ReturnType<
1111
- typeof vi.fn
1112
- >
1113
- mock_vscode.window.activeColorTheme = { kind: vscode_theme_kind }
1114
-
1115
- const result = get_theme()
1116
- expect(result).toBe(expected)
1117
- },
1118
- )
1119
-
1120
- test(`webview data includes theme`, () => {
1121
- const mock_config = {
1122
- get: vi.fn((key: string, default_value?: string) =>
1123
- key === `theme` ? `dark` : default_value,
1124
- ),
1125
- }
1126
- mock_vscode.workspace.getConfiguration = vi.fn(() => mock_config) as ReturnType<
1127
- typeof vi.fn
1128
- >
1129
-
1130
- const data = {
1131
- type: `structure` as const,
1132
- data: { filename: `test.cif`, content: `content`, is_base64: false },
1133
- theme: get_theme(),
1134
- }
1135
-
1136
- const html = create_html(mock_webview, mock_context, data)
1137
-
1138
- const parsed_data = JSON.parse(/matterviz_data=(\{[\s\S]*?\});/.exec(html)?.[1] || `{}`)
1139
- expect(parsed_data.theme).toBe(`dark`)
1140
- })
1141
-
1142
- test(`invalid theme setting falls back to auto`, () => {
1143
- const mock_config = {
1144
- get: vi.fn((key: string, default_value?: string) =>
1145
- key === `theme` ? `invalid-theme` : default_value,
1146
- ),
1147
- }
1148
- mock_vscode.workspace.getConfiguration = vi.fn(() => mock_config) as ReturnType<
1149
- typeof vi.fn
1150
- >
1151
- mock_vscode.window.activeColorTheme = {
1152
- kind: mock_vscode.ColorThemeKind.Light,
1153
- }
1154
-
1155
- const result = get_theme()
1156
- expect(result).toBe(`light`) // Should fall back to system theme
1157
- })
1158
-
1159
- test(`high contrast themes are mapped correctly`, () => {
1160
- const mock_config = {
1161
- get: vi.fn((key: string, default_value?: string) =>
1162
- key === `theme` ? `auto` : default_value,
1163
- ),
1164
- }
1165
- mock_vscode.workspace.getConfiguration = vi.fn(() => mock_config) as ReturnType<
1166
- typeof vi.fn
1167
- >
1168
-
1169
- // Test high contrast dark → black
1170
- mock_vscode.window.activeColorTheme = {
1171
- kind: mock_vscode.ColorThemeKind.HighContrast,
1172
- }
1173
- expect(get_theme()).toBe(`black`)
1174
-
1175
- // Test high contrast light → white
1176
- mock_vscode.window.activeColorTheme = {
1177
- kind: mock_vscode.ColorThemeKind.HighContrastLight,
1178
- }
1179
- expect(get_theme()).toBe(`white`)
1180
- })
1181
- })
1182
-
1183
- describe(`Theme listener cleanup`, () => {
1184
- const setup_panel = (options = {}) => {
1185
- const mock_dispose = vi.fn()
1186
- const mock_panel = {
1187
- webview: { ...mock_webview },
1188
- onDidDispose: vi.fn(),
1189
- visible: true,
1190
- ...options,
1191
- }
1192
-
1193
- mock_vscode.window.createWebviewPanel.mockReturnValue(mock_panel)
1194
- mock_vscode.window.onDidChangeActiveColorTheme.mockReturnValue({
1195
- dispose: mock_dispose,
1196
- })
1197
- mock_vscode.workspace.onDidChangeConfiguration.mockReturnValue({
1198
- dispose: mock_dispose,
1199
- })
1200
- mock_vscode.window.activeTextEditor = {
1201
- document: { fileName: `/test/active.cif`, getText: () => `content` },
1202
- } as TextEditor
1203
-
1204
- return { mock_dispose, mock_panel }
1205
- }
1206
-
1207
- test(`sets up and cleans up theme listeners`, async () => {
1208
- const { mock_dispose, mock_panel } = setup_panel()
1209
-
1210
- await render(mock_context)
1211
-
1212
- expect(mock_vscode.window.onDidChangeActiveColorTheme).toHaveBeenCalled()
1213
- expect(mock_panel.onDidDispose).toHaveBeenCalled()
1214
-
1215
- // Test cleanup
1216
- mock_panel.onDidDispose.mock.calls[0][0]()
1217
- expect(mock_dispose).toHaveBeenCalledTimes(2)
1218
- })
1219
-
1220
- test(`respects panel visibility for theme updates`, async () => {
1221
- const mock_panel = {
1222
- webview: { ...mock_webview },
1223
- onDidDispose: vi.fn(),
1224
- visible: false,
1225
- }
1226
-
1227
- mock_vscode.window.createWebviewPanel.mockReturnValue(mock_panel)
1228
- mock_vscode.window.onDidChangeActiveColorTheme.mockReturnValue({
1229
- dispose: vi.fn(),
1230
- })
1231
- mock_vscode.workspace.onDidChangeConfiguration.mockReturnValue({
1232
- dispose: vi.fn(),
1233
- })
1234
- mock_vscode.window.activeTextEditor = {
1235
- document: { fileName: `/test/active.cif`, getText: () => `content` },
1236
- } as TextEditor
1237
-
1238
- await render(mock_context)
1239
-
1240
- // Store initial HTML after render (render always sets HTML initially)
1241
- const initial_html = mock_panel.webview.html
1242
-
1243
- const theme_calls = mock_vscode.window.onDidChangeActiveColorTheme.mock
1244
- .calls as unknown as unknown[][]
1245
- const theme_callback = theme_calls[0]?.[0] as (() => Promise<void>) | undefined
1246
- expect(theme_callback).toBeDefined()
1247
-
1248
- // Should not update when invisible
1249
- await theme_callback?.()
1250
- expect(mock_panel.webview.html).toBe(initial_html)
1251
-
1252
- // Should update when visible
1253
- mock_panel.visible = true
1254
- await theme_callback?.()
1255
- expect(mock_panel.webview.html).not.toBe(initial_html)
1256
- })
1257
-
1258
- test(`multiple panels dispose independently`, async () => {
1259
- const dispose1 = vi.fn()
1260
- const dispose2 = vi.fn()
1261
- const panel1 = { webview: { ...mock_webview }, onDidDispose: vi.fn() }
1262
- const panel2 = { webview: { ...mock_webview }, onDidDispose: vi.fn() }
1263
-
1264
- mock_vscode.window.createWebviewPanel
1265
- .mockReturnValueOnce(panel1)
1266
- .mockReturnValueOnce(panel2)
1267
- mock_vscode.window.onDidChangeActiveColorTheme
1268
- .mockReturnValueOnce({ dispose: dispose1 })
1269
- .mockReturnValueOnce({
1270
- dispose: dispose2,
1271
- })
1272
- mock_vscode.workspace.onDidChangeConfiguration
1273
- .mockReturnValueOnce({ dispose: dispose1 })
1274
- .mockReturnValueOnce({
1275
- dispose: dispose2,
1276
- })
1277
-
1278
- mock_vscode.window.activeTextEditor = {
1279
- document: { fileName: `/test/active.cif`, getText: () => `content` },
1280
- } as TextEditor
1281
-
1282
- await render(mock_context)
1283
- await render(mock_context)
1284
-
1285
- panel1.onDidDispose.mock.calls[0][0]()
1286
- expect(dispose1).toHaveBeenCalledTimes(2)
1287
- expect(dispose2).not.toHaveBeenCalled()
1288
- })
1289
- })
1290
-
1291
- describe(`File Watching`, () => {
1292
- describe(`message handling`, () => {
1293
- test(`should handle startWatching message`, async () => {
1294
- const message = {
1295
- command: `startWatching` as const,
1296
- ...msg_args,
1297
- file_path: `/test/file.cif`,
1298
- }
1299
- await handle_msg(message, mock_webview)
1300
-
1301
- expect(mock_vscode.workspace.createFileSystemWatcher).toHaveBeenCalledWith(
1302
- expect.objectContaining({
1303
- base: expect.anything(),
1304
- pattern: `file.cif`,
1305
- }),
1306
- )
1307
- expect(mock_file_system_watcher.onDidChange).toHaveBeenCalledWith(expect.any(Function))
1308
- expect(mock_file_system_watcher.onDidDelete).toHaveBeenCalledWith(expect.any(Function))
1309
- })
1310
-
1311
- test(`should handle stopWatching message`, async () => {
1312
- // First start watching
1313
- const start_message = {
1314
- command: `startWatching` as const,
1315
- ...msg_args,
1316
- file_path: `/test/file.cif`,
1317
- }
1318
- await handle_msg(start_message, mock_webview)
1319
-
1320
- // Then test stopping
1321
- const stop_message = {
1322
- command: `stopWatching` as const,
1323
- ...msg_args,
1324
- file_path: `/test/file.cif`,
1325
- }
1326
- await handle_msg(stop_message, mock_webview)
1327
-
1328
- expect(mock_file_system_watcher.dispose).toHaveBeenCalled()
1329
- })
1330
-
1331
- test(`should handle startWatching without webview gracefully`, async () => {
1332
- const message = {
1333
- command: `startWatching` as const,
1334
- ...msg_args,
1335
- file_path: `/test/file.cif`,
1336
- }
1337
-
1338
- await expect(handle_msg(message)).resolves.not.toThrow()
1339
- expect(mock_vscode.workspace.createFileSystemWatcher).not.toHaveBeenCalled()
1340
- })
1341
-
1342
- test(`should handle startWatching without file_path gracefully`, async () => {
1343
- const message = {
1344
- command: `startWatching` as const,
1345
- ...msg_args,
1346
- }
1347
-
1348
- await expect(handle_msg(message, mock_webview)).resolves.not.toThrow()
1349
- expect(mock_vscode.workspace.createFileSystemWatcher).not.toHaveBeenCalled()
1350
- })
1351
-
1352
- test(`should send error message when file watching fails`, async () => {
1353
- mock_vscode.workspace.createFileSystemWatcher.mockImplementation(() => {
1354
- throw new Error(`File system watcher creation failed`)
1355
- })
1356
-
1357
- const message = {
1358
- command: `startWatching` as const,
1359
- ...msg_args,
1360
- file_path: `/test/large-file.cif`,
1361
- }
1362
-
1363
- await handle_msg(message, mock_webview)
1364
-
1365
- expect(mock_webview.postMessage).toHaveBeenCalledWith({
1366
- command: `error`,
1367
- text: expect.stringContaining(`Failed to start watching file`),
1368
- })
1369
- })
1370
- })
1371
-
1372
- describe(`file change notifications`, () => {
1373
- test(`should send file change notification to webview`, async () => {
1374
- const message = {
1375
- command: `startWatching` as const,
1376
- ...msg_args,
1377
- file_path: `/test/file.cif`,
1378
- }
1379
-
1380
- await handle_msg(message, mock_webview)
1381
-
1382
- // Get the change handler
1383
- const change_handler = mock_file_system_watcher.onDidChange.mock.calls[0][0]
1384
-
1385
- // Trigger file change
1386
- await change_handler()
1387
-
1388
- // Wait for postMessage to be called (it's async)
1389
- await vi.waitFor(() => {
1390
- expect(mock_webview.postMessage).toHaveBeenCalledWith({
1391
- command: `fileUpdated`,
1392
- data: expect.objectContaining({
1393
- filename: `file.cif`,
1394
- content: `mock content`,
1395
- is_base64: false,
1396
- }),
1397
- type: `structure`,
1398
- ...msg_args,
1399
- file_path: `/test/file.cif`,
1400
- theme: `light`,
1401
- })
1402
- })
1403
- })
1404
-
1405
- test(`should handle file deletion notifications`, async () => {
1406
- const message = {
1407
- command: `startWatching` as const,
1408
- ...msg_args,
1409
- file_path: `/test/file.cif`,
1410
- }
1411
-
1412
- await handle_msg(message, mock_webview)
1413
-
1414
- // Get the delete handler
1415
- const delete_handler = mock_file_system_watcher.onDidDelete.mock.calls[0][0]
1416
-
1417
- // Trigger file deletion
1418
- delete_handler()
1419
-
1420
- expect(mock_webview.postMessage).toHaveBeenCalledWith(
1421
- expect.objectContaining({
1422
- command: `fileDeleted`,
1423
- file_path: `/test/file.cif`,
1424
- }),
1425
- )
1426
- })
1427
- })
1428
-
1429
- describe(`lifecycle management`, () => {
1430
- test(`should handle activation gracefully`, () => {
1431
- const mock_context = {
1432
- extensionUri: { fsPath: `/test/extension` },
1433
- subscriptions: [],
1434
- } as unknown as ExtensionContext
1435
-
1436
- expect(() => activate(mock_context)).not.toThrow()
1437
-
1438
- expect(mock_vscode.commands.registerCommand).toHaveBeenCalledWith(
1439
- `matterviz.open`,
1440
- expect.any(Function),
1441
- )
1442
- })
1443
- })
1444
- })
1445
-
1446
- describe(`Auto-Render Functionality`, () => {
1447
- test.each([
1448
- // Supported structure files
1449
- [`structure.cif`, true],
1450
- [`molecule.xyz`, true],
1451
- [`crystal.poscar`, true],
1452
- [`data.json`, false], // "data" is too broad, will not auto-render without structure-specific keywords in filename
1453
- [`structure.xml`, true],
1454
- [`molecule.pdb`, true],
1455
- [`compound.mol`, true],
1456
- [`structure.mol2`, true],
1457
- [`data.sdf`, true],
1458
- [`crystal.mmcif`, true],
1459
- // Supported trajectory files
1460
- [`trajectory.traj`, true],
1461
- [`simulation.h5`, true],
1462
- [`data.hdf5`, false],
1463
- [`traj.xtc`, true],
1464
- // Compressed supported files
1465
- [`trajectory.xyz.gz`, true],
1466
- [`data.json.gz`, false], // "data" is too broad, will not auto-render without structure-specific keywords in filename
1467
- [`structure.cif.gz`, true],
1468
- // Special filenames
1469
- [`POSCAR`, true],
1470
- [`CONTCAR`, true],
1471
- [`XDATCAR`, true],
1472
- [`trajectory.dat`, true],
1473
- [`md.xyz`, true],
1474
- [`relax.out`, true],
1475
- [`npt.log`, true],
1476
- [`nvt.data`, true],
1477
- [`nve.traj`, true],
1478
- // Files with special characters
1479
- [`structure (1).cif`, true],
1480
- [`trajectory[test].xyz.gz`, true],
1481
- [`crystal@test.poscar`, true],
1482
- [`molecule#test.xyz`, true],
1483
- [`structure$test.json`, true],
1484
- [`trajectory%test.h5`, true],
1485
- [`crystal^test.traj`, true],
1486
- [`molecule&test.extxyz`, true],
1487
- [`structure*test.xml`, true],
1488
- [`trajectory+test.pdb`, true],
1489
- [`crystal=test.mol`, true],
1490
- [`molecule|test.mol2`, true],
1491
- [`structure\`test.sdf`, true],
1492
- [`trajectory~test.mmcif`, true],
1493
- // Case sensitivity tests
1494
- [`STRUCTURE.CIF`, true],
1495
- [`structure.CIF`, true],
1496
- [`Structure.cif`, true],
1497
- [`TRAJECTORY.XYZ`, true],
1498
- [`trajectory.XYZ`, true],
1499
- [`Trajectory.xyz`, true],
1500
- [`POSCAR`, true],
1501
- [`poscar`, true],
1502
- [`Poscar`, true],
1503
- [`CONTCAR`, true],
1504
- [`contcar`, true],
1505
- [`Contcar`, true],
1506
- [`XDATCAR`, true],
1507
- [`xdatcar`, true],
1508
- [`Xdatcar`, true],
1509
- // Fermi surface files
1510
- [`band.bxsf`, true],
1511
- [`fermi.frmsf`, true],
1512
- [`BAND.BXSF`, true],
1513
- [`fermi.FRMSF`, true],
1514
- [`band.bxsf.gz`, true],
1515
- [`fermi.frmsf.gz`, true],
1516
- // Volumetric data files
1517
- ...volumetric_auto_render_filenames,
1518
- [`DENSITY.CUBE`, true],
1519
- [`CHGCAR.lobster`, true],
1520
- // Files that look like structure files but are supported
1521
- [`structure_copy.cif`, true],
1522
- [`trajectory_backup.xyz`, true],
1523
- [`trajectory.log`, true], // Contains "trajectory" keyword
1524
- // Very long filenames
1525
- [`structure`.repeat(100) + `.cif`, true],
1526
- // Unsupported files
1527
- [`config.yaml`, false],
1528
- [`simulation.trr`, false], // .trr files not supported
1529
- [`md.dcd`, false], // .dcd files not supported
1530
- [`document.txt`, false],
1531
- [`script.py`, false],
1532
- [`data.csv`, false],
1533
- [`image.png`, false],
1534
- [`archive.zip`, false],
1535
- [`fake.gz`, false],
1536
- [`config.ini`, false],
1537
- [`log.txt`, false],
1538
- [`README.md`, false],
1539
- [`readme.md`, false],
1540
- [`ReadMe.Md`, false],
1541
- [`vite.config.ts`, false],
1542
- [`test.spec.ts`, false],
1543
- [`index.html`, false],
1544
- [`style.css`, false],
1545
- [`app.js`, false],
1546
- [`data.sql`, false],
1547
- [`backup.tar`, false],
1548
- [`compressed.7z`, false],
1549
- [`binary.bin`, false],
1550
- [`.pre-commit-config.yaml`, false],
1551
- [`changelog.md`, false],
1552
- [`.prettierrc`, false],
1553
- [`.gitignore`, false],
1554
- [`dockerfile`, false],
1555
- [`makefile`, false],
1556
- [`.env`, false],
1557
- [`.env.local`, false],
1558
- [`.env.production`, false],
1559
- [`.github/workflows/ci.yml`, false],
1560
- [`dist/bundle.js`, false],
1561
- [`build/index.html`, false],
1562
- [`coverage/lcov.info`, false],
1563
- [`.cache/build.js`, false],
1564
- [`structure.json.bak`, false],
1565
- [`crystal.poscar.lock`, true],
1566
- [`simulation.log`, true],
1567
- [`backup.old`, false],
1568
- [`original.orig`, false],
1569
- [`patch.diff`, false],
1570
- [`structure.txt`, false],
1571
- [`crystal.md`, false],
1572
- [`molecule.doc`, false],
1573
- [`poscar.bak`, true],
1574
- [`contcar.old`, true],
1575
- [`document.txt.gz`, false],
1576
- [`script.py.gz`, false],
1577
- [`data.csv.gz`, false],
1578
- [`image.png.gz`, false],
1579
- [`archive.zip.gz`, false],
1580
- [`structure.cif.bz2`, false],
1581
- [`density.cube.bz2`, false],
1582
- [`PARCHG.bz2`, false],
1583
- [`myCHGCARfile`, false],
1584
- [`prefixPARCHGsuffix`, false],
1585
- [`structure.cif.bak`, false],
1586
- [`crystal.poscar.old`, true],
1587
- [`molecule.xyz~`, false],
1588
- [`structure.cif.swp`, false],
1589
- [`DOCUMENT.TXT`, false],
1590
- [`document.TXT`, false],
1591
- [`Document.txt`, false],
1592
- [`SCRIPT.PY`, false],
1593
- [`script.PY`, false],
1594
- [`Script.py`, false],
1595
- [`DATA.CSV`, false],
1596
- [`data.CSV`, false],
1597
- [`Data.csv`, false],
1598
- // Configuration files that should never auto-render
1599
- [`package.json`, false],
1600
- [`tsconfig.json`, false],
1601
- [`vite.config.ts`, false],
1602
- [`webpack.config.js`, false],
1603
- [`rollup.config.js`, false],
1604
- [`eslint.config.js`, false],
1605
- [`prettier.config.js`, false],
1606
- [`babel.config.js`, false],
1607
- [`jest.config.js`, false],
1608
- [`karma.conf.js`, false],
1609
- [`cypress.json`, false],
1610
- [`playwright.config.ts`, false],
1611
- [`.eslintrc.json`, false],
1612
- [`.prettierrc`, false],
1613
- [`.babelrc`, false],
1614
- [`.jest.config.js`, false],
1615
- [`.karma.conf.js`, false],
1616
- [`.cypress.json`, false],
1617
- [`.playwright.config.ts`, false],
1618
- [`.npmrc`, false],
1619
- [`.yarnrc`, false],
1620
- [`.vscode/settings.json`, false],
1621
- [`.idea/workspace.xml`, false],
1622
- [`.nyc_output/coverage.json`, false],
1623
- [`.tmp/temp.json`, false],
1624
- [`.temp/structure.json`, false],
1625
- [`node_modules/package.json`, false],
1626
- // Edge cases
1627
- [``, false],
1628
- [` `, false],
1629
- [`.`, false],
1630
- [`..`, false],
1631
- [`/`, false],
1632
- [`\\`, false],
1633
- [`a`.repeat(1000) + `.txt`, false],
1634
- // Null/undefined inputs
1635
- [null as unknown as string, false],
1636
- [undefined as unknown as string, false],
1637
- ])(`should detect auto-render for "%s" as %s`, (filename, expected) => {
1638
- expect(should_auto_render(filename)).toBe(expected)
1639
- })
1640
-
1641
- test(`should register auto-render functionality`, () => {
1642
- const mock_context = {
1643
- subscriptions: { push: vi.fn() },
1644
- } as unknown as ExtensionContext
1645
- activate(mock_context)
1646
- expect(mock_vscode.workspace.onDidOpenTextDocument).toHaveBeenCalledWith(
1647
- expect.any(Function),
1648
- )
1649
- })
1650
-
1651
- test(`should handle rapid file detection efficiently`, () => {
1652
- const filenames = Array.from({ length: 100 }, (_, idx) => `test_${idx}.cif`)
1653
- const start = performance.now()
1654
- filenames.forEach(should_auto_render)
1655
- expect(performance.now() - start).toBeLessThan(10)
1656
- })
1657
-
1658
- test(`should not trigger on non-file URIs`, () => {
1659
- const mock_context = {
1660
- subscriptions: { push: vi.fn() },
1661
- } as unknown as ExtensionContext
1662
-
1663
- activate(mock_context)
1664
-
1665
- // Get the registered callback
1666
- const open_doc_calls = mock_vscode.workspace.onDidOpenTextDocument.mock
1667
- .calls as unknown as unknown[][]
1668
- const on_did_open_text_document_callback = open_doc_calls[0]?.[0] as
1669
- | ((doc: unknown) => void)
1670
- | undefined
1671
- expect(on_did_open_text_document_callback).toBeDefined()
1672
-
1673
- // Mock document with non-file URI
1674
- const mock_document = {
1675
- uri: { scheme: `untitled` },
1676
- }
1677
-
1678
- expect(() => on_did_open_text_document_callback?.(mock_document)).not.toThrow()
1679
- })
1680
-
1681
- test(`should respect auto_render configuration setting`, () => {
1682
- const mock_context = {
1683
- subscriptions: { push: vi.fn() },
1684
- } as unknown as ExtensionContext
1685
-
1686
- // Mock configuration to disable auto_render
1687
- mock_vscode.workspace.getConfiguration.mockReturnValue({
1688
- get: vi.fn((key: string, default_val: string) => {
1689
- if (key === `auto_render`) return false
1690
- return default_val
1691
- }),
1692
- })
1693
-
1694
- activate(mock_context)
1695
-
1696
- const open_doc_calls = mock_vscode.workspace.onDidOpenTextDocument.mock
1697
- .calls as unknown as unknown[][]
1698
- const on_did_open_text_document_callback = open_doc_calls[0]?.[0] as
1699
- | ((doc: unknown) => void)
1700
- | undefined
1701
- expect(on_did_open_text_document_callback).toBeDefined()
1702
-
1703
- // Mock document with supported file
1704
- const mock_document = {
1705
- uri: { scheme: `file`, fsPath: `/test/structure.cif` },
1706
- }
1707
-
1708
- expect(() => on_did_open_text_document_callback?.(mock_document)).not.toThrow()
1709
- })
1710
-
1711
- test(`should handle file reading errors gracefully during auto-render`, async () => {
1712
- const mock_context = {
1713
- subscriptions: { push: vi.fn() },
1714
- } as unknown as ExtensionContext
1715
-
1716
- // Mock vscode.workspace.fs.stat to throw an error
1717
- mock_vscode.workspace.fs.stat.mockRejectedValue(new Error(`File not found`))
1718
-
1719
- // Enable auto_render in config
1720
- mock_vscode.workspace.getConfiguration.mockReturnValue({
1721
- get: vi.fn((_key: string, default_val: string) =>
1722
- _key === `auto_render` ? `true` : default_val,
1723
- ),
1724
- })
1725
-
1726
- activate(mock_context)
1727
-
1728
- const open_doc_calls = mock_vscode.workspace.onDidOpenTextDocument.mock
1729
- .calls as unknown as unknown[][]
1730
- const on_did_open_text_document_callback = open_doc_calls[0]?.[0] as
1731
- | ((doc: unknown) => void)
1732
- | undefined
1733
- expect(on_did_open_text_document_callback).toBeDefined()
1734
-
1735
- // Mock document with supported file
1736
- const mock_document = {
1737
- uri: { scheme: `file`, fsPath: `/test/structure.cif` },
1738
- }
1739
-
1740
- // Should show error message when file reading fails
1741
- expect(() => on_did_open_text_document_callback?.(mock_document)).not.toThrow()
1742
-
1743
- await vi.waitFor(() => {
1744
- expect(mock_vscode.window.showErrorMessage).toHaveBeenCalledWith(
1745
- expect.stringContaining(`MatterViz auto-render failed:`),
1746
- )
1747
- })
1748
- })
1749
- })
1750
-
1751
- describe(`Multi-frame xyz/extxyz handling`, () => {
1752
- test(`should correctly identify multi-frame XYZ as trajectory using content`, () => {
1753
- // Multi-frame XYZ content (2 frames)
1754
- const multi_frame_xyz_content = `3
1755
- frame 1
1756
- H 0.0 0.0 0.0
1757
- O 0.0 0.0 1.0
1758
- H 0.0 1.0 0.0
1759
- 3
1760
- frame 2
1761
- H 0.1 0.0 0.0
1762
- O 0.0 0.1 1.0
1763
- H 0.0 1.0 0.1`
1764
-
1765
- // Single-frame XYZ content
1766
- const single_frame_xyz_content = `3
1767
- water molecule
1768
- H 0.0 0.0 0.0
1769
- O 0.0 0.0 1.0
1770
- H 0.0 1.0 0.0`
1771
-
1772
- // Test 1: Verify is_trajectory_file directly detects multi-frame content
1773
- expect(is_trajectory_file(`multi-frame.xyz`, multi_frame_xyz_content)).toBe(true)
1774
- expect(is_trajectory_file(`single-frame.xyz`, single_frame_xyz_content)).toBe(false)
1775
-
1776
- // Test 2: Verify filename-only detection doesn't identify .xyz as trajectory
1777
- expect(is_trajectory_file(`multi-frame.xyz`)).toBe(false) // filename-only should be false
1778
- expect(is_trajectory_file(`single-frame.xyz`)).toBe(false) // filename-only should be false
1779
-
1780
- // Test 3: Test with FileData objects (simulating what infer_view_type receives)
1781
- const multi_frame_file: FileData = {
1782
- filename: `multi-frame.xyz`,
1783
- content: multi_frame_xyz_content,
1784
- is_base64: false,
1785
- }
1786
-
1787
- const single_frame_file: FileData = {
1788
- filename: `single-frame.xyz`,
1789
- content: single_frame_xyz_content,
1790
- is_base64: false,
1791
- }
1792
-
1793
- const compressed_file: FileData = {
1794
- filename: `trajectory.xyz.gz`,
1795
- content: `base64encodedcontent`,
1796
- is_base64: true,
1797
- }
1798
-
1799
- // Test what infer_view_type logic would do:
1800
- // For non-compressed files, pass content
1801
- expect(is_trajectory_file(multi_frame_file.filename, multi_frame_file.content)).toBe(
1802
- true,
1803
- )
1804
- expect(is_trajectory_file(single_frame_file.filename, single_frame_file.content)).toBe(
1805
- false,
1806
- )
1807
-
1808
- // For compressed files, don't pass content (falls back to filename-only)
1809
- expect(is_trajectory_file(compressed_file.filename)).toBe(true) // .xyz.gz with trajectory keyword is detected as trajectory by filename
1810
-
1811
- // Test 4: Test webview creation scenario directly with content-based detection
1812
- const multi_frame_html = create_html(mock_webview, mock_context, {
1813
- type: is_trajectory_file(multi_frame_file.filename, multi_frame_file.content)
1814
- ? `trajectory`
1815
- : `structure`,
1816
- data: multi_frame_file,
1817
- theme: `light`,
1818
- })
1819
-
1820
- const multi_frame_parsed_data = JSON.parse(
1821
- /matterviz_data=(\{[\s\S]*?\});/.exec(multi_frame_html)?.[1] ?? `{}`,
1822
- )
1823
-
1824
- expect(multi_frame_parsed_data.type).toBe(`trajectory`)
1825
- expect(multi_frame_parsed_data.data.filename).toBe(`multi-frame.xyz`)
1826
- expect(multi_frame_parsed_data.data.content).toBe(multi_frame_xyz_content)
1827
-
1828
- // Test 5: Test single-frame for comparison
1829
- const single_frame_html = create_html(mock_webview, mock_context, {
1830
- type: is_trajectory_file(single_frame_file.filename, single_frame_file.content)
1831
- ? `trajectory`
1832
- : `structure`,
1833
- data: single_frame_file,
1834
- theme: `light`,
1835
- })
1836
-
1837
- const single_frame_parsed_data = JSON.parse(
1838
- /matterviz_data=(\{[\s\S]*?\});/.exec(single_frame_html)?.[1] ?? `{}`,
1839
- )
1840
-
1841
- expect(single_frame_parsed_data.type).toBe(`structure`)
1842
-
1843
- // Test 6: Test compressed file falls back correctly
1844
- const compressed_html = create_html(mock_webview, mock_context, {
1845
- type: is_trajectory_file(compressed_file.filename) ? `trajectory` : `structure`,
1846
- data: compressed_file,
1847
- theme: `light`,
1848
- })
1849
-
1850
- const compressed_parsed_data = JSON.parse(
1851
- /matterviz_data=(\{[\s\S]*?\});/.exec(compressed_html)?.[1] ?? `{}`,
1852
- )
1853
-
1854
- expect(compressed_parsed_data.type).toBe(`trajectory`) // Should be trajectory since filename contains trajectory keyword
1855
- })
1856
-
1857
- test(`should handle compressed XYZ files by falling back to filename-only detection`, () => {
1858
- // Test compressed file (should fall back to filename-only detection)
1859
- const compressed_file = {
1860
- filename: `trajectory.xyz.gz`,
1861
- content: `base64encodedcontent`, // This is binary/compressed
1862
- is_base64: true,
1863
- }
1864
-
1865
- // For compressed files, infer_view_type should fall back to filename-only detection
1866
- // Since .xyz.gz with trajectory keyword is detected as trajectory by filename, it should be 'trajectory'
1867
- expect(is_trajectory_file(compressed_file.filename)).toBe(true) // filename-only detection
1868
-
1869
- // Test the HTML generation scenario
1870
- const html = create_html(mock_webview, mock_context, {
1871
- type: is_trajectory_file(compressed_file.filename) ? `trajectory` : `structure`,
1872
- data: compressed_file,
1873
- theme: `light`,
1874
- })
1875
-
1876
- const parsed_data = JSON.parse(/matterviz_data=(\{[\s\S]*?\});/.exec(html)?.[1] ?? `{}`)
1877
-
1878
- // Should be 'trajectory' since filename contains trajectory keyword
1879
- expect(parsed_data.type).toBe(`trajectory`)
1880
- })
1881
- })
1882
-
1883
- describe(`Default Settings`, () => {
1884
- // Helper to create mock config and test setting
1885
- const test_setting = (
1886
- result_path: string,
1887
- expected_value: unknown,
1888
- config_key: string,
1889
- ) => {
1890
- const parts = config_key.split(`.`)
1891
-
1892
- const mock_config = {
1893
- get: vi.fn((key: string, default_val?: unknown): unknown => {
1894
- if (parts.length === 2 && key === parts[0]) {
1895
- return { [parts[1]]: expected_value }
1896
- } else if (parts.length === 1 && key === parts[0]) return expected_value
1897
- return default_val
1898
- }),
1899
- }
1900
- // @ts-expect-error: Mock type override needed for testing
1901
- mock_vscode.workspace.getConfiguration.mockReturnValue(mock_config)
1902
-
1903
- const result = get_defaults()
1904
- const value = result_path
1905
- .split(`.`)
1906
- .reduce(
1907
- (obj: Record<string, unknown>, key: string) => obj?.[key] as Record<string, unknown>,
1908
- result,
1909
- )
1910
-
1911
- return Array.isArray(expected_value)
1912
- ? expect(value).toEqual(expected_value)
1913
- : expect(value).toBe(expected_value)
1914
- }
1915
-
1916
- test(`should merge user settings with defaults`, () => {
1917
- const user_config = {
1918
- structure: { atom_radius: 1.5, show_bonds: `always`, bond_color: `#ff0000` },
1919
- trajectory: { auto_play: true },
1920
- }
1921
- const mock_config = {
1922
- get: vi.fn((key: string, default_val?: unknown) => {
1923
- if (key === `structure`) return user_config.structure
1924
- if (key === `trajectory`) return user_config.trajectory
1925
- return default_val
1926
- }),
1927
- }
1928
- // @ts-expect-error: Mock type override needed for testing
1929
- mock_vscode.workspace.getConfiguration.mockReturnValue(mock_config)
1930
-
1931
- const result = get_defaults()
1932
-
1933
- expect(result.structure.atom_radius).toBe(1.5)
1934
- expect(result.structure.show_bonds).toBe(`always`)
1935
- expect(result.structure.bond_color).toBe(`#ff0000`)
1936
- expect(result.trajectory.auto_play).toBe(true)
1937
- expect(result.structure.same_size_atoms).toBe(false) // Falls back to default
1938
- })
1939
-
1940
- test.each([
1941
- // Numbers
1942
- [`structure.atom_radius`, 1.5],
1943
- [`structure.sphere_segments`, 24],
1944
- [`structure.bond_thickness`, 0.2],
1945
- [`structure.rotation_damping`, 0.2],
1946
- [`structure.zoom_speed`, 1.0],
1947
- [`structure.pan_speed`, 1.0],
1948
- [`structure.auto_rotate`, 2.0],
1949
- [`structure.site_label_size`, 14],
1950
- [`structure.site_label_padding`, 4],
1951
- [`structure.ambient_light`, 0.6],
1952
- [`structure.directional_light`, 0.8],
1953
- [`structure.vector_scale`, 2.0],
1954
- [`structure.vector_origin_gap`, 0.25],
1955
- [`structure.cell_edge_opacity`, 0.5],
1956
- [`structure.cell_surface_opacity`, 0.2],
1957
- [`background_opacity`, 0.8],
1958
- [`trajectory.fps`, 10],
1959
- [`trajectory.step_labels`, 10],
1960
-
1961
- // Booleans
1962
- [`structure.same_size_atoms`, true],
1963
- [`structure.show_atoms`, false],
1964
- [`structure.show_bonds`, `always`],
1965
- [`structure.show_site_labels`, true],
1966
- [`structure.show_cell`, true],
1967
- [`structure.show_cell_vectors`, true],
1968
- [`structure.show_image_atoms`, true],
1969
- [`structure.show_gizmo`, false],
1970
- [`trajectory.auto_play`, true],
1971
- [`trajectory.show_controls`, false],
1972
-
1973
- // Colors (strings)
1974
- [`structure.bond_color`, `#ff0000`],
1975
- [`structure.site_label_color`, `#00ff00`],
1976
- [`structure.site_label_bg_color`, `#333333`],
1977
- [`structure.cell_edge_color`, `#aaaaaa`],
1978
- [`structure.cell_surface_color`, `#bbbbbb`],
1979
- [`structure.vector_color`, `#ffff00`],
1980
- [`background_color`, `#111111`],
1981
-
1982
- // String enums
1983
- [`structure.bonding_strategy`, `solid_angle`],
1984
- [`structure.camera_projection`, `orthographic`],
1985
- [`color_scheme`, `Jmol`],
1986
- [`composition.color_scheme`, `Alloy`],
1987
- [`trajectory.display_mode`, `scatter`],
1988
- [`trajectory.layout`, `vertical`],
1989
- [`composition.display_mode`, `bar`],
1990
-
1991
- // Arrays
1992
- [`structure.camera_position`, [1, 2, 3]],
1993
- [`structure.site_label_offset`, [0.5, 1.0, 0]],
1994
- [`trajectory.fps_range`, [0.5, 60]],
1995
- ])(`should handle setting: %s = %s`, (result_path, expected_value) => {
1996
- test_setting(result_path, expected_value, result_path)
1997
- })
1998
-
1999
- test.each([
2000
- [{ get: vi.fn(() => undefined) }, `missing config`],
2001
- [
2002
- {
2003
- get: vi.fn((key: string, default_val?: unknown) =>
2004
- key === `defaults`
2005
- ? {
2006
- structure: {
2007
- atom_radius: `invalid`,
2008
- show_bonds: `invalid-value`,
2009
- bond_color: 123,
2010
- },
2011
- }
2012
- : default_val,
2013
- ),
2014
- },
2015
- `invalid values`,
2016
- ],
2017
- ])(`should handle %s gracefully`, (mock_config, _description) => {
2018
- // @ts-expect-error: Mock type override needed for testing
2019
- mock_vscode.workspace.getConfiguration.mockReturnValue(mock_config)
2020
-
2021
- expect(() => get_defaults()).not.toThrow()
2022
- const result = get_defaults()
2023
-
2024
- expect(result).toEqual(
2025
- expect.objectContaining({
2026
- structure: expect.any(Object),
2027
- trajectory: expect.any(Object),
2028
- composition: expect.any(Object),
2029
- }),
2030
- )
2031
- })
2032
-
2033
- test(`should handle workspace config errors`, () => {
2034
- mock_vscode.workspace.getConfiguration.mockImplementation(() => {
2035
- throw new Error(`Config access failed`)
2036
- })
2037
-
2038
- expect(() => get_defaults()).not.toThrow()
2039
- })
2040
- })
2041
- })