react-cheminfo 0.66.0 → 0.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/README.md +22 -11
  2. package/lib/download/ui/FigureDownloadPanel.d.ts +7 -1
  3. package/lib/download/ui/FigureDownloadPanel.d.ts.map +1 -1
  4. package/lib/download/ui/FigureDownloadPanel.js +2 -2
  5. package/lib/download/ui/FigureDownloadPanel.js.map +1 -1
  6. package/lib/ecosystem/core/sites.js +3 -3
  7. package/lib/ecosystem/core/sites.js.map +1 -1
  8. package/lib/ecosystem/core/types.d.ts +1 -1
  9. package/lib/ecosystem/core/types.d.ts.map +1 -1
  10. package/lib/ecosystem/ui/glyphs.js +1 -1
  11. package/lib/ecosystem/ui/glyphs.js.map +1 -1
  12. package/lib/i18n/core/chromeCatalog.d.ts +1 -1
  13. package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
  14. package/lib/i18n/core/messageCatalog.d.ts +2 -2
  15. package/lib/i18n/vite/localeCatalog.d.ts +1 -1
  16. package/lib/ir/core/index.d.ts +2 -2
  17. package/lib/ir/core/index.d.ts.map +1 -1
  18. package/lib/ir/core/index.js.map +1 -1
  19. package/lib/ir/core/irEditorApi.d.ts +12 -0
  20. package/lib/ir/core/irEditorApi.d.ts.map +1 -1
  21. package/lib/ir/core/irReducer.d.ts +2 -2
  22. package/lib/ir/core/irReducer.d.ts.map +1 -1
  23. package/lib/ir/core/irSidePanels.d.ts +7 -1
  24. package/lib/ir/core/irSidePanels.d.ts.map +1 -1
  25. package/lib/ir/core/irState.d.ts +8 -5
  26. package/lib/ir/core/irState.d.ts.map +1 -1
  27. package/lib/ir/core/irState.js.map +1 -1
  28. package/lib/ir/ui/IrCanvas.d.ts +7 -0
  29. package/lib/ir/ui/IrCanvas.d.ts.map +1 -1
  30. package/lib/ir/ui/IrCanvas.js +8 -3
  31. package/lib/ir/ui/IrCanvas.js.map +1 -1
  32. package/lib/ir/ui/IrChart.d.ts.map +1 -1
  33. package/lib/ir/ui/IrChart.js +5 -1
  34. package/lib/ir/ui/IrChart.js.map +1 -1
  35. package/lib/ir/ui/IrEditor.d.ts +22 -4
  36. package/lib/ir/ui/IrEditor.d.ts.map +1 -1
  37. package/lib/ir/ui/IrEditor.js.map +1 -1
  38. package/lib/ir/ui/IrShell.d.ts +9 -1
  39. package/lib/ir/ui/IrShell.d.ts.map +1 -1
  40. package/lib/ir/ui/IrShell.js +4 -19
  41. package/lib/ir/ui/IrShell.js.map +1 -1
  42. package/lib/ir/ui/IrSidePanel.d.ts +11 -0
  43. package/lib/ir/ui/IrSidePanel.d.ts.map +1 -1
  44. package/lib/ir/ui/IrSidePanel.js +12 -3
  45. package/lib/ir/ui/IrSidePanel.js.map +1 -1
  46. package/lib/ir/ui/RamanEditor.d.ts +22 -4
  47. package/lib/ir/ui/RamanEditor.d.ts.map +1 -1
  48. package/lib/ir/ui/RamanEditor.js.map +1 -1
  49. package/lib/ir/ui/VibrationalEditor.d.ts +18 -4
  50. package/lib/ir/ui/VibrationalEditor.d.ts.map +1 -1
  51. package/lib/ir/ui/VibrationalEditor.js +4 -2
  52. package/lib/ir/ui/VibrationalEditor.js.map +1 -1
  53. package/lib/ir/ui/index.d.ts +1 -0
  54. package/lib/ir/ui/index.d.ts.map +1 -1
  55. package/lib/ir/ui/index.js.map +1 -1
  56. package/lib/ir/ui/irActions.d.ts +3 -3
  57. package/lib/ir/ui/irActions.d.ts.map +1 -1
  58. package/lib/ir/ui/irChartProps.d.ts +7 -0
  59. package/lib/ir/ui/irChartProps.d.ts.map +1 -1
  60. package/lib/ir/ui/irHostPanels.d.ts +53 -0
  61. package/lib/ir/ui/irHostPanels.d.ts.map +1 -0
  62. package/lib/ir/ui/irHostPanels.js +41 -0
  63. package/lib/ir/ui/irHostPanels.js.map +1 -0
  64. package/lib/ir/ui/useIrSide.d.ts +27 -0
  65. package/lib/ir/ui/useIrSide.d.ts.map +1 -0
  66. package/lib/ir/ui/useIrSide.js +42 -0
  67. package/lib/ir/ui/useIrSide.js.map +1 -0
  68. package/lib/ir/ui/useIrZoom.d.ts +8 -0
  69. package/lib/ir/ui/useIrZoom.d.ts.map +1 -1
  70. package/lib/ir/ui/useIrZoom.js +2 -1
  71. package/lib/ir/ui/useIrZoom.js.map +1 -1
  72. package/lib/locales/de.json +9 -1
  73. package/lib/locales/en.json +9 -1
  74. package/lib/locales/es.json +9 -1
  75. package/lib/locales/fr.json +9 -1
  76. package/lib/locales/it.json +9 -1
  77. package/lib/molecule3d/core/animatedPng.d.ts +25 -0
  78. package/lib/molecule3d/core/animatedPng.d.ts.map +1 -0
  79. package/lib/molecule3d/core/animatedPng.js +59 -0
  80. package/lib/molecule3d/core/animatedPng.js.map +1 -0
  81. package/lib/molecule3d/core/file.d.ts +24 -0
  82. package/lib/molecule3d/core/file.d.ts.map +1 -0
  83. package/lib/molecule3d/core/file.js +2 -0
  84. package/lib/molecule3d/core/file.js.map +1 -0
  85. package/lib/molecule3d/core/index.d.ts +4 -1
  86. package/lib/molecule3d/core/index.d.ts.map +1 -1
  87. package/lib/molecule3d/core/index.js +1 -0
  88. package/lib/molecule3d/core/index.js.map +1 -1
  89. package/lib/molecule3d/core/settings.d.ts +0 -7
  90. package/lib/molecule3d/core/settings.d.ts.map +1 -1
  91. package/lib/molecule3d/core/settings.js.map +1 -1
  92. package/lib/molecule3d/ui/Molecule3DExport.d.ts +10 -2
  93. package/lib/molecule3d/ui/Molecule3DExport.d.ts.map +1 -1
  94. package/lib/molecule3d/ui/Molecule3DExport.js +15 -2
  95. package/lib/molecule3d/ui/Molecule3DExport.js.map +1 -1
  96. package/lib/molecule3d/ui/Molecule3DToolbar.js +9 -2
  97. package/lib/molecule3d/ui/Molecule3DToolbar.js.map +1 -1
  98. package/lib/molecule3d/ui/MoleculeCanvas3D.d.ts.map +1 -1
  99. package/lib/molecule3d/ui/MoleculeCanvas3D.js +23 -41
  100. package/lib/molecule3d/ui/MoleculeCanvas3D.js.map +1 -1
  101. package/lib/molecule3d/ui/drawScene.d.ts +2 -1
  102. package/lib/molecule3d/ui/drawScene.d.ts.map +1 -1
  103. package/lib/molecule3d/ui/drawScene.js.map +1 -1
  104. package/lib/molecule3d/ui/exportMoleculeImage.d.ts +15 -1
  105. package/lib/molecule3d/ui/exportMoleculeImage.d.ts.map +1 -1
  106. package/lib/molecule3d/ui/exportMoleculeImage.js +14 -2
  107. package/lib/molecule3d/ui/exportMoleculeImage.js.map +1 -1
  108. package/lib/molecule3d/ui/moleculeFrames.d.ts +47 -0
  109. package/lib/molecule3d/ui/moleculeFrames.d.ts.map +1 -0
  110. package/lib/molecule3d/ui/moleculeFrames.js +65 -0
  111. package/lib/molecule3d/ui/moleculeFrames.js.map +1 -0
  112. package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts +14 -1
  113. package/lib/molecule3d/ui/moleculeViewer3DProps.d.ts.map +1 -1
  114. package/lib/molecule3d/ui/renderMolecule.d.ts +3 -3
  115. package/lib/molecule3d/ui/renderMolecule.d.ts.map +1 -1
  116. package/lib/molecule3d/ui/renderMolecule.js +2 -1
  117. package/lib/molecule3d/ui/renderMolecule.js.map +1 -1
  118. package/lib/molecule3d/ui/sceneDrawer.d.ts +2 -1
  119. package/lib/molecule3d/ui/sceneDrawer.d.ts.map +1 -1
  120. package/lib/molecule3d/ui/sceneDrawer.js +9 -1
  121. package/lib/molecule3d/ui/sceneDrawer.js.map +1 -1
  122. package/lib/molecule3d/ui/useImageExport.d.ts +8 -3
  123. package/lib/molecule3d/ui/useImageExport.d.ts.map +1 -1
  124. package/lib/molecule3d/ui/useImageExport.js +13 -4
  125. package/lib/molecule3d/ui/useImageExport.js.map +1 -1
  126. package/lib/molecule3d/ui/useMoleculeScene.d.ts +2 -1
  127. package/lib/molecule3d/ui/useMoleculeScene.d.ts.map +1 -1
  128. package/lib/molecule3d/ui/useMoleculeScene.js.map +1 -1
  129. package/lib/molecule3d/ui/useMoleculeViewer.d.ts +16 -0
  130. package/lib/molecule3d/ui/useMoleculeViewer.d.ts.map +1 -0
  131. package/lib/molecule3d/ui/useMoleculeViewer.js +37 -0
  132. package/lib/molecule3d/ui/useMoleculeViewer.js.map +1 -0
  133. package/lib/molecule3d/ui/usePolarSurfaceArea.d.ts +3 -3
  134. package/lib/molecule3d/ui/usePolarSurfaceArea.d.ts.map +1 -1
  135. package/lib/molecule3d/ui/usePolarSurfaceArea.js +4 -3
  136. package/lib/molecule3d/ui/usePolarSurfaceArea.js.map +1 -1
  137. package/lib/molecule3d/ui/viewer.d.ts +4 -1
  138. package/lib/molecule3d/ui/viewer.d.ts.map +1 -1
  139. package/lib/molecule3d/ui/viewer.js +21 -19
  140. package/lib/molecule3d/ui/viewer.js.map +1 -1
  141. package/lib/molecule3d/ui/viewerTypes.d.ts +6 -0
  142. package/lib/molecule3d/ui/viewerTypes.d.ts.map +1 -1
  143. package/lib/molstar/core/captureScene.d.ts +19 -1
  144. package/lib/molstar/core/captureScene.d.ts.map +1 -1
  145. package/lib/molstar/core/captureScene.js +57 -2
  146. package/lib/molstar/core/captureScene.js.map +1 -1
  147. package/lib/molstar/core/index.d.ts +5 -2
  148. package/lib/molstar/core/index.d.ts.map +1 -1
  149. package/lib/molstar/core/index.js +4 -2
  150. package/lib/molstar/core/index.js.map +1 -1
  151. package/lib/molstar/core/trajectory.d.ts +56 -0
  152. package/lib/molstar/core/trajectory.d.ts.map +1 -0
  153. package/lib/molstar/core/trajectory.js +96 -0
  154. package/lib/molstar/core/trajectory.js.map +1 -0
  155. package/lib/panel/ui/PanelRail.d.ts.map +1 -1
  156. package/lib/panel/ui/PanelRail.js +1 -1
  157. package/lib/panel/ui/PanelRail.js.map +1 -1
  158. package/lib/raman.d.ts +2 -1
  159. package/lib/raman.d.ts.map +1 -1
  160. package/lib/raman.js.map +1 -1
  161. package/lib/router/core/basePath.d.ts +2 -2
  162. package/lib/router/core/basePath.js +2 -2
  163. package/lib/router/core/siteAddresses.d.ts +1 -1
  164. package/lib/router/core/siteAddresses.js +1 -1
  165. package/lib/seo/core/siteFiles.js +1 -1
  166. package/lib/seo/core/siteFiles.js.map +1 -1
  167. package/lib/server/answerAddresses.d.ts +3 -3
  168. package/lib/server/answerAddresses.js +3 -3
  169. package/lib/structure/core/index.d.ts +2 -0
  170. package/lib/structure/core/index.d.ts.map +1 -1
  171. package/lib/structure/core/index.js +1 -0
  172. package/lib/structure/core/index.js.map +1 -1
  173. package/lib/structure/core/structureTransfer.d.ts +29 -0
  174. package/lib/structure/core/structureTransfer.d.ts.map +1 -0
  175. package/lib/structure/core/structureTransfer.js +26 -0
  176. package/lib/structure/core/structureTransfer.js.map +1 -0
  177. package/lib/structure/ui/EditorImportButton.d.ts +20 -0
  178. package/lib/structure/ui/EditorImportButton.d.ts.map +1 -0
  179. package/lib/structure/ui/EditorImportButton.js +24 -0
  180. package/lib/structure/ui/EditorImportButton.js.map +1 -0
  181. package/lib/structure/ui/EditorImportProblem.d.ts +17 -0
  182. package/lib/structure/ui/EditorImportProblem.d.ts.map +1 -0
  183. package/lib/structure/ui/EditorImportProblem.js +32 -0
  184. package/lib/structure/ui/EditorImportProblem.js.map +1 -0
  185. package/lib/structure/ui/StructureDrop.d.ts +39 -0
  186. package/lib/structure/ui/StructureDrop.d.ts.map +1 -0
  187. package/lib/structure/ui/StructureDrop.js +19 -0
  188. package/lib/structure/ui/StructureDrop.js.map +1 -0
  189. package/lib/structure/ui/StructureDropOverlay.d.ts +16 -0
  190. package/lib/structure/ui/StructureDropOverlay.d.ts.map +1 -0
  191. package/lib/structure/ui/StructureDropOverlay.js +29 -0
  192. package/lib/structure/ui/StructureDropOverlay.js.map +1 -0
  193. package/lib/structure/ui/StructureEditor.d.ts +3 -80
  194. package/lib/structure/ui/StructureEditor.d.ts.map +1 -1
  195. package/lib/structure/ui/StructureEditor.js +21 -33
  196. package/lib/structure/ui/StructureEditor.js.map +1 -1
  197. package/lib/structure/ui/importedStructure.d.ts +25 -0
  198. package/lib/structure/ui/importedStructure.d.ts.map +1 -0
  199. package/lib/structure/ui/importedStructure.js +74 -0
  200. package/lib/structure/ui/importedStructure.js.map +1 -0
  201. package/lib/structure/ui/index.d.ts +2 -0
  202. package/lib/structure/ui/index.d.ts.map +1 -1
  203. package/lib/structure/ui/index.js +1 -0
  204. package/lib/structure/ui/index.js.map +1 -1
  205. package/lib/structure/ui/structureEditorProps.d.ts +98 -0
  206. package/lib/structure/ui/structureEditorProps.d.ts.map +1 -0
  207. package/lib/structure/ui/structureEditorProps.js +2 -0
  208. package/lib/structure/ui/structureEditorProps.js.map +1 -0
  209. package/lib/structure/ui/useDebouncedChange.d.ts +16 -0
  210. package/lib/structure/ui/useDebouncedChange.d.ts.map +1 -0
  211. package/lib/structure/ui/useDebouncedChange.js +30 -0
  212. package/lib/structure/ui/useDebouncedChange.js.map +1 -0
  213. package/lib/structure/ui/useStructureDrop.d.ts +30 -0
  214. package/lib/structure/ui/useStructureDrop.d.ts.map +1 -0
  215. package/lib/structure/ui/useStructureDrop.js +49 -0
  216. package/lib/structure/ui/useStructureDrop.js.map +1 -0
  217. package/lib/structure/ui/useStructureImport.d.ts +45 -0
  218. package/lib/structure/ui/useStructureImport.d.ts.map +1 -0
  219. package/lib/structure/ui/useStructureImport.js +62 -0
  220. package/lib/structure/ui/useStructureImport.js.map +1 -0
  221. package/package.json +4 -1
  222. package/src/download/ui/FigureDownloadPanel.tsx +9 -2
  223. package/src/ecosystem/core/sites.ts +3 -3
  224. package/src/ecosystem/core/types.ts +1 -1
  225. package/src/ecosystem/ui/glyphs.tsx +1 -1
  226. package/src/i18n/core/messageCatalog.ts +2 -2
  227. package/src/i18n/vite/localeCatalog.ts +1 -1
  228. package/src/ir/core/index.ts +6 -2
  229. package/src/ir/core/irEditorApi.ts +13 -0
  230. package/src/ir/core/irReducer.ts +2 -2
  231. package/src/ir/core/irSidePanels.ts +8 -1
  232. package/src/ir/core/irState.ts +8 -5
  233. package/src/ir/ui/IrCanvas.tsx +27 -2
  234. package/src/ir/ui/IrChart.tsx +11 -0
  235. package/src/ir/ui/IrEditor.tsx +22 -4
  236. package/src/ir/ui/IrShell.tsx +21 -22
  237. package/src/ir/ui/IrSidePanel.tsx +36 -10
  238. package/src/ir/ui/RamanEditor.tsx +22 -4
  239. package/src/ir/ui/VibrationalEditor.tsx +24 -5
  240. package/src/ir/ui/index.ts +1 -0
  241. package/src/ir/ui/irActions.ts +3 -3
  242. package/src/ir/ui/irChartProps.ts +7 -0
  243. package/src/ir/ui/irHostPanels.ts +76 -0
  244. package/src/ir/ui/useIrSide.ts +60 -0
  245. package/src/ir/ui/useIrZoom.ts +10 -0
  246. package/src/locales/de.json +9 -1
  247. package/src/locales/en.json +9 -1
  248. package/src/locales/es.json +9 -1
  249. package/src/locales/fr.json +9 -1
  250. package/src/locales/it.json +9 -1
  251. package/src/molecule3d/core/animatedPng.ts +79 -0
  252. package/src/molecule3d/core/file.ts +23 -0
  253. package/src/molecule3d/core/index.ts +3 -1
  254. package/src/molecule3d/core/settings.ts +0 -8
  255. package/src/molecule3d/ui/Molecule3DExport.tsx +43 -5
  256. package/src/molecule3d/ui/Molecule3DToolbar.tsx +9 -2
  257. package/src/molecule3d/ui/MoleculeCanvas3D.tsx +24 -38
  258. package/src/molecule3d/ui/drawScene.ts +2 -1
  259. package/src/molecule3d/ui/exportMoleculeImage.ts +28 -3
  260. package/src/molecule3d/ui/moleculeFrames.ts +88 -0
  261. package/src/molecule3d/ui/moleculeViewer3DProps.ts +14 -5
  262. package/src/molecule3d/ui/renderMolecule.ts +5 -3
  263. package/src/molecule3d/ui/sceneDrawer.ts +14 -2
  264. package/src/molecule3d/ui/useImageExport.ts +22 -5
  265. package/src/molecule3d/ui/useMoleculeScene.ts +2 -1
  266. package/src/molecule3d/ui/useMoleculeViewer.ts +46 -0
  267. package/src/molecule3d/ui/usePolarSurfaceArea.ts +6 -4
  268. package/src/molecule3d/ui/viewer.ts +34 -27
  269. package/src/molecule3d/ui/viewerTypes.ts +6 -0
  270. package/src/molstar/core/captureScene.ts +85 -2
  271. package/src/molstar/core/index.ts +13 -2
  272. package/src/molstar/core/trajectory.ts +117 -0
  273. package/src/panel/ui/PanelRail.tsx +3 -0
  274. package/src/raman.ts +2 -1
  275. package/src/router/core/basePath.ts +2 -2
  276. package/src/router/core/siteAddresses.ts +1 -1
  277. package/src/seo/core/siteFiles.ts +1 -1
  278. package/src/server/answerAddresses.ts +3 -3
  279. package/src/structure/core/index.ts +8 -0
  280. package/src/structure/core/structureTransfer.ts +37 -0
  281. package/src/structure/ui/EditorImportButton.tsx +59 -0
  282. package/src/structure/ui/EditorImportProblem.tsx +63 -0
  283. package/src/structure/ui/StructureDrop.tsx +66 -0
  284. package/src/structure/ui/StructureDropOverlay.tsx +50 -0
  285. package/src/structure/ui/StructureEditor.tsx +49 -134
  286. package/src/structure/ui/importedStructure.ts +83 -0
  287. package/src/structure/ui/index.ts +2 -0
  288. package/src/structure/ui/structureEditorProps.ts +103 -0
  289. package/src/structure/ui/useDebouncedChange.ts +41 -0
  290. package/src/structure/ui/useStructureDrop.ts +76 -0
  291. package/src/structure/ui/useStructureImport.ts +122 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The frames of a trajectory: how many there are, which one is shown, and
3
+ * playing them in a loop.
4
+ *
5
+ * molstar keeps a trajectory as one data node and shows one of its frames
6
+ * through a `ModelFromTrajectory` node whose `modelIndex` says which. Playing
7
+ * is molstar's own model-index animation, which steps that index; showing one
8
+ * frame sets it.
9
+ */
10
+
11
+ import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
12
+ import { AnimateModelIndex } from 'molstar/lib/mol-plugin-state/animation/built-in/model-index.js';
13
+ import { PluginStateObject } from 'molstar/lib/mol-plugin-state/objects.js';
14
+ import { ModelFromTrajectory } from 'molstar/lib/mol-plugin-state/transforms/model.js';
15
+ import { StateSelection } from 'molstar/lib/mol-state/index.js';
16
+
17
+ /** How fast molstar's model-index animation can step, frames per second. */
18
+ export const PLAYBACK_FPS_RANGE = { minimum: 5, maximum: 60 } as const;
19
+
20
+ /**
21
+ * How many frames the trajectory on screen holds.
22
+ * @param plugin - The molstar context.
23
+ * @returns The most frames of any trajectory shown; 0 when none is.
24
+ */
25
+ export function trajectoryFrameCount(plugin: PluginContext): number {
26
+ const state = plugin.state.data;
27
+ let count = 0;
28
+ for (const model of modelCells(plugin)) {
29
+ const trajectory = StateSelection.findAncestorOfType(
30
+ state.tree,
31
+ state.cells,
32
+ model.transform.ref,
33
+ PluginStateObject.Molecule.Trajectory,
34
+ );
35
+ count = Math.max(count, trajectory?.obj?.data.frameCount ?? 0);
36
+ }
37
+ return count;
38
+ }
39
+
40
+ /**
41
+ * Show one frame of every trajectory on screen, and everything built on it:
42
+ * the drawn model, its surface and the measurements over it.
43
+ * @param plugin - The molstar context.
44
+ * @param index - The frame, from 0.
45
+ * @returns Nothing; resolves once the state tree is updated.
46
+ */
47
+ export async function showTrajectoryFrame(
48
+ plugin: PluginContext,
49
+ index: number,
50
+ ): Promise<void> {
51
+ const update = plugin.build();
52
+ for (const model of modelCells(plugin)) {
53
+ update.to(model).update({ modelIndex: index });
54
+ }
55
+ await update.commit();
56
+ }
57
+
58
+ /**
59
+ * The frame on screen.
60
+ * @param plugin - The molstar context.
61
+ * @returns The index of the frame the first trajectory shows; 0 when none is.
62
+ */
63
+ export function shownTrajectoryFrame(plugin: PluginContext): number {
64
+ return modelCells(plugin)[0]?.transform.params?.modelIndex ?? 0;
65
+ }
66
+
67
+ /**
68
+ * Play the frames in a loop, or stop them where they stand.
69
+ * @param plugin - The molstar context.
70
+ * @param playing - Whether to play. A trajectory of one frame never plays.
71
+ * @param framesPerSecond - How fast, clamped into {@link PLAYBACK_FPS_RANGE}.
72
+ * @returns Nothing; resolves once the animation is started or stopped.
73
+ */
74
+ export async function setTrajectoryPlayback(
75
+ plugin: PluginContext,
76
+ playing: boolean,
77
+ framesPerSecond: number,
78
+ ): Promise<void> {
79
+ if (!playing || trajectoryFrameCount(plugin) < 2) {
80
+ await stopTrajectory(plugin);
81
+ return;
82
+ }
83
+ await plugin.managers.animation.play(AnimateModelIndex, {
84
+ mode: { name: 'loop', params: { direction: 'forward' } },
85
+ duration: {
86
+ name: 'sequential',
87
+ params: { maxFps: clampFramesPerSecond(framesPerSecond) },
88
+ },
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Stop the frames where they stand.
94
+ * @param plugin - The molstar context.
95
+ * @returns Nothing; resolves once the animation is stopped.
96
+ */
97
+ export async function stopTrajectory(plugin: PluginContext): Promise<void> {
98
+ const animation = plugin.managers.animation;
99
+ if (animation.isAnimating) await animation.stop();
100
+ }
101
+
102
+ /**
103
+ * A frame rate molstar's model-index animation accepts.
104
+ * @param framesPerSecond - Any rate.
105
+ * @returns The rate, rounded and clamped into {@link PLAYBACK_FPS_RANGE}.
106
+ */
107
+ export function clampFramesPerSecond(framesPerSecond: number): number {
108
+ const { minimum, maximum } = PLAYBACK_FPS_RANGE;
109
+ if (!Number.isFinite(framesPerSecond)) return minimum;
110
+ return Math.min(maximum, Math.max(minimum, Math.round(framesPerSecond)));
111
+ }
112
+
113
+ function modelCells(plugin: PluginContext) {
114
+ return plugin.state.data.select(
115
+ StateSelection.Generators.ofTransformer(ModelFromTrajectory),
116
+ );
117
+ }
@@ -55,6 +55,9 @@ export function PanelRail<TId extends string>(
55
55
  id={panel.id}
56
56
  icon={panel.icon}
57
57
  tooltip={panel.title}
58
+ // A tooltip is not an accessible name: without this every toggle is
59
+ // an icon-only button with no name at all.
60
+ aria-label={panel.title}
58
61
  active={openIds.includes(panel.id)}
59
62
  onClick={() => onToggle(panel.id)}
60
63
  />
package/src/raman.ts CHANGED
@@ -9,9 +9,10 @@
9
9
  */
10
10
 
11
11
  export type { LoadedIrSpectrum } from './ir/core/analysisSpectra.ts';
12
- export type { IrEditorHandle } from './ir/core/irEditorApi.ts';
12
+ export type { IrEditorHandle, IrPlotClick } from './ir/core/irEditorApi.ts';
13
13
  export type { IrSpectrum } from './ir/core/irSpectrum.ts';
14
14
  export { toIrSpectra } from './ir/core/toIrSpectrum.ts';
15
+ export type { IrHostPanel } from './ir/ui/irHostPanels.ts';
15
16
  export type {
16
17
  RamanEditorHandle,
17
18
  RamanEditorProps,
@@ -4,8 +4,8 @@ import { trimTrailingSlash } from './address.ts';
4
4
  * Where a site is mounted, read off the page rather than off the build.
5
5
  *
6
6
  * The build is mount-agnostic — vite is given a relative base, so the same
7
- * `dist` serves `https://surge.cheminfo.org/` and
8
- * `https://www.cheminfo.org/surge/`. What tells the two apart is the `<base>`
7
+ * `dist` serves `https://isomers.cheminfo.org/` and
8
+ * `https://www.cheminfo.org/isomers/`. What tells the two apart is the `<base>`
9
9
  * the deployment stamps into the page it hands out, and `document.baseURI`
10
10
  * resolves it against the address actually opened. So this answers correctly
11
11
  * whether or not a slash closes the address, and on an address the SPA
@@ -16,7 +16,7 @@ import {
16
16
  *
17
17
  * The mount is read off the page, not off the build: the build carries no
18
18
  * mount, and the `<base>` a deployment stamps into its pages is what tells
19
- * `https://surge.cheminfo.org/` from `https://www.cheminfo.org/surge/`. Under
19
+ * `https://isomers.cheminfo.org/` from `https://www.cheminfo.org/isomers/`. Under
20
20
  * Node there is no page, and the mount is the root.
21
21
  * @param siteId - The site, as `ECOSYSTEM_SITES` names it.
22
22
  * @param options - A mount path to use instead of the one read off the page.
@@ -130,7 +130,7 @@ export function originOf(options: SiteOrigin): string {
130
130
  const origin = options.origin ?? `https://${resolveSite(options.site).host}`;
131
131
  if (!HTTP_ORIGIN.test(origin) || !URL.canParse(origin)) {
132
132
  throw new Error(
133
- `an origin is an absolute address, e.g. https://surge.cheminfo.org: ${JSON.stringify(origin)}`,
133
+ `an origin is an absolute address, e.g. https://isomers.cheminfo.org: ${JSON.stringify(origin)}`,
134
134
  );
135
135
  }
136
136
  return trimTrailingSlash(origin);
@@ -6,9 +6,9 @@
6
6
  * The two are written however the deployment writes them — an origin, an
7
7
  * origin and a mount, with or without a trailing slash — and a page is named
8
8
  * from the site's own root, `/titration` and `titration` alike: every helper
9
- * joins the two with exactly one slash, so `https://www.cheminfo.org/surge`
10
- * and `https://www.cheminfo.org/surge/` both put `/generator` at
11
- * `https://www.cheminfo.org/surge/generator`.
9
+ * joins the two with exactly one slash, so `https://www.cheminfo.org/isomers`
10
+ * and `https://www.cheminfo.org/isomers/` both put `/generator` at
11
+ * `https://www.cheminfo.org/isomers/generator`.
12
12
  */
13
13
 
14
14
  import { trimTrailingSlash } from '../router/core/address.ts';
@@ -82,3 +82,11 @@ export type {
82
82
  StructureSourceInput,
83
83
  } from './structureSource.ts';
84
84
  export { structureSource } from './structureSource.ts';
85
+ export type {
86
+ StructureTransfer,
87
+ StructureTransferSource,
88
+ } from './structureTransfer.ts';
89
+ export {
90
+ carriesStructure,
91
+ readStructureTransfer,
92
+ } from './structureTransfer.ts';
@@ -0,0 +1,37 @@
1
+ /** What a paste or a drop carried that a structure can be read from. */
2
+ export type StructureTransfer =
3
+ { kind: 'files'; files: File[] } | { kind: 'text'; text: string };
4
+
5
+ /** The part of a `DataTransfer` a structure is read from. */
6
+ export interface StructureTransferSource {
7
+ files: ArrayLike<File>;
8
+ getData: (format: string) => string;
9
+ }
10
+
11
+ /**
12
+ * What a paste or a drop holds: its files when it carries any, otherwise its
13
+ * text — a SMILES, an idCode, a molfile. A file copied in the file manager also
14
+ * carries its name as text, so the files win.
15
+ * @param transfer - The clipboard or drag data, `null` when the event has none.
16
+ * @returns The files or the trimmed text, or `null` when there is nothing.
17
+ */
18
+ export function readStructureTransfer(
19
+ transfer: StructureTransferSource | null,
20
+ ): StructureTransfer | null {
21
+ if (transfer === null) return null;
22
+ if (transfer.files.length > 0) {
23
+ return { kind: 'files', files: Array.from(transfer.files) };
24
+ }
25
+ const text = transfer.getData('text').trim();
26
+ return text === '' ? null : { kind: 'text', text };
27
+ }
28
+
29
+ /**
30
+ * Whether a drag in progress may carry a structure. Until the drop only the
31
+ * types are readable, never the content, so a file or plain text is enough.
32
+ * @param types - `DataTransfer.types` of the drag.
33
+ * @returns Whether it is worth offering to take the drop.
34
+ */
35
+ export function carriesStructure(types: readonly string[]): boolean {
36
+ return types.includes('Files') || types.includes('text/plain');
37
+ }
@@ -0,0 +1,59 @@
1
+ import { Button } from '@blueprintjs/core';
2
+ import type { ReactElement } from 'react';
3
+ import { useRef } from 'react';
4
+
5
+ import { useChromeT } from '../../i18n/ui/useT.ts';
6
+ import { ActionTooltip } from '../../shared/ui/ActionTooltip.tsx';
7
+ import type { StructureTransfer } from '../core/structureTransfer.ts';
8
+
9
+ /** What {@link EditorImportButton} needs. */
10
+ export interface EditorImportButtonProps {
11
+ /** Called with the files chosen. */
12
+ onImport: (transfer: StructureTransfer) => void;
13
+ /**
14
+ * The file types the picker offers, as an `accept` attribute.
15
+ * @default undefined
16
+ */
17
+ accept?: string;
18
+ }
19
+
20
+ /**
21
+ * The editor's open button, beside the export one, for a visitor who cannot
22
+ * drag a file onto the canvas — on a phone, or from a picker.
23
+ * @param props - See {@link EditorImportButtonProps}.
24
+ * @returns The button and its hidden file input.
25
+ */
26
+ export function EditorImportButton(
27
+ props: EditorImportButtonProps,
28
+ ): ReactElement {
29
+ const { onImport, accept } = props;
30
+ const t = useChromeT();
31
+ const input = useRef<HTMLInputElement>(null);
32
+
33
+ return (
34
+ <>
35
+ <ActionTooltip content={t('structure.import.open')} placement="left">
36
+ <Button
37
+ variant="minimal"
38
+ size="small"
39
+ icon="import"
40
+ aria-label={t('structure.import.open')}
41
+ onClick={() => input.current?.click()}
42
+ />
43
+ </ActionTooltip>
44
+ <input
45
+ ref={input}
46
+ type="file"
47
+ accept={accept}
48
+ hidden
49
+ data-testid="structure-editor-file"
50
+ onChange={(event) => {
51
+ const files = Array.from(event.currentTarget.files ?? []);
52
+ // Cleared, so choosing the same file again is a change.
53
+ event.currentTarget.value = '';
54
+ if (files.length > 0) onImport({ kind: 'files', files });
55
+ }}
56
+ />
57
+ </>
58
+ );
59
+ }
@@ -0,0 +1,63 @@
1
+ import { Button, Callout } from '@blueprintjs/core';
2
+ import type { CSSProperties, ReactElement } from 'react';
3
+
4
+ import { useChromeT } from '../../i18n/ui/useT.ts';
5
+
6
+ import type { StructureImportProblem } from './useStructureImport.ts';
7
+
8
+ /** What {@link EditorImportProblem} needs. */
9
+ export interface EditorImportProblemProps {
10
+ /** What went wrong. */
11
+ problem: StructureImportProblem;
12
+ /** Forget it. */
13
+ onDismiss: () => void;
14
+ }
15
+
16
+ /**
17
+ * The line along the bottom of the canvas that says a drop loaded nothing,
18
+ * and that the drawing is still there.
19
+ * @param props - See {@link EditorImportProblemProps}.
20
+ * @returns The line.
21
+ */
22
+ export function EditorImportProblem(
23
+ props: EditorImportProblemProps,
24
+ ): ReactElement {
25
+ const { problem, onDismiss } = props;
26
+ const t = useChromeT();
27
+ return (
28
+ <div role="alert" style={PLACE}>
29
+ <Callout intent="danger" icon={null} compact style={LINE}>
30
+ <span style={TEXT}>
31
+ {problem.file === null
32
+ ? t('structure.import.unreadableText')
33
+ : t('structure.import.unreadableFile', { name: problem.file })}
34
+ </span>
35
+ <Button
36
+ variant="minimal"
37
+ size="small"
38
+ icon="cross"
39
+ aria-label={t('overlay.dismiss')}
40
+ onClick={onDismiss}
41
+ />
42
+ </Callout>
43
+ </div>
44
+ );
45
+ }
46
+
47
+ /** Clear of the tool palette, two 21px columns in a 2px border, at the left. */
48
+ const PLACE: CSSProperties = {
49
+ position: 'absolute',
50
+ right: 4,
51
+ bottom: 4,
52
+ left: 52,
53
+ zIndex: 10,
54
+ };
55
+
56
+ const LINE: CSSProperties = {
57
+ display: 'flex',
58
+ alignItems: 'center',
59
+ gap: 6,
60
+ fontSize: 12,
61
+ };
62
+
63
+ const TEXT: CSSProperties = { flex: 1 };
@@ -0,0 +1,66 @@
1
+ import type { CSSProperties, ReactElement, ReactNode } from 'react';
2
+
3
+ import { useChromeT } from '../../i18n/ui/useT.ts';
4
+ import type { StructureTransfer } from '../core/structureTransfer.ts';
5
+
6
+ import { StructureDropOverlay } from './StructureDropOverlay.tsx';
7
+ import { useStructureDrop } from './useStructureDrop.ts';
8
+
9
+ /** What {@link StructureDrop} needs. */
10
+ export interface StructureDropProps {
11
+ /** Called with the files or the text dropped. */
12
+ onDrop: (transfer: StructureTransfer) => void;
13
+ /** What the drop lands on. */
14
+ children: ReactNode;
15
+ /**
16
+ * What the overlay says while something is dragged over.
17
+ * @default 'Drop to load the structure', in the page's language
18
+ */
19
+ label?: string;
20
+ /**
21
+ * Take no drop, while a load is already running for instance.
22
+ * @default false
23
+ */
24
+ disabled?: boolean;
25
+ /**
26
+ * Class of the wrapping element.
27
+ * @default undefined
28
+ */
29
+ className?: string;
30
+ /**
31
+ * Extra style for the wrapping element, which is positioned so the overlay
32
+ * covers it.
33
+ * @default undefined
34
+ */
35
+ style?: CSSProperties;
36
+ }
37
+
38
+ /**
39
+ * Make an area take a structure file, or structure text dragged from another
40
+ * page, with the same veil the structure editor raises over its canvas. What
41
+ * was dropped is handed over unread: the page knows which formats it takes.
42
+ * @param props - See {@link StructureDropProps}.
43
+ * @returns The area.
44
+ */
45
+ export function StructureDrop(props: StructureDropProps): ReactElement {
46
+ const { onDrop, children, label, disabled = false } = props;
47
+ const { className, style } = props;
48
+ const t = useChromeT();
49
+ const drop = useStructureDrop(disabled ? undefined : onDrop);
50
+
51
+ return (
52
+ <div
53
+ className={className}
54
+ style={{ position: 'relative', ...style }}
55
+ {...drop.targetProps}
56
+ >
57
+ {children}
58
+ {drop.over ? (
59
+ <StructureDropOverlay
60
+ label={label ?? t('structure.import.drop')}
61
+ handlers={drop.overlayProps}
62
+ />
63
+ ) : null}
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,50 @@
1
+ import { Icon } from '@blueprintjs/core';
2
+ import type { CSSProperties, ReactElement } from 'react';
3
+
4
+ import type { DropHandlers } from './useStructureDrop.ts';
5
+
6
+ /** What {@link StructureDropOverlay} needs. */
7
+ export interface StructureDropOverlayProps {
8
+ /** What the overlay says. */
9
+ label: string;
10
+ /** The overlay handlers of `useStructureDrop`. */
11
+ handlers: DropHandlers;
12
+ }
13
+
14
+ /**
15
+ * The veil a drop target raises while a file or text is dragged over it.
16
+ * @param props - See {@link StructureDropOverlayProps}.
17
+ * @returns The overlay.
18
+ */
19
+ export function StructureDropOverlay(
20
+ props: StructureDropOverlayProps,
21
+ ): ReactElement {
22
+ const { label, handlers } = props;
23
+ return (
24
+ <div data-testid="structure-drop-overlay" style={OVERLAY} {...handlers}>
25
+ {/* Nothing inside takes the pointer, so leaving the overlay is the only
26
+ dragleave it hears. */}
27
+ <Icon icon="import" size={28} style={PASSIVE} />
28
+ <span style={PASSIVE}>{label}</span>
29
+ </div>
30
+ );
31
+ }
32
+
33
+ const OVERLAY: CSSProperties = {
34
+ position: 'absolute',
35
+ inset: 0,
36
+ zIndex: 20,
37
+ display: 'flex',
38
+ flexDirection: 'column',
39
+ alignItems: 'center',
40
+ justifyContent: 'center',
41
+ gap: 8,
42
+ border: '2px dashed var(--accent)',
43
+ borderRadius: 'inherit',
44
+ background: 'color-mix(in oklab, var(--accent) 10%, rgb(255 255 255 / 88%))',
45
+ color: 'var(--accent)',
46
+ fontSize: 14,
47
+ fontWeight: 600,
48
+ };
49
+
50
+ const PASSIVE: CSSProperties = { pointerEvents: 'none' };