react-cheminfo 0.39.1 → 0.40.1

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 (100) hide show
  1. package/bin/check-deploy.mjs +4 -2
  2. package/lib/deploy/core/checkDeploy.d.ts +3 -1
  3. package/lib/deploy/core/checkDeploy.d.ts.map +1 -1
  4. package/lib/deploy/core/checkDeploy.js +8 -2
  5. package/lib/deploy/core/checkDeploy.js.map +1 -1
  6. package/lib/deploy/core/index.d.ts +1 -0
  7. package/lib/deploy/core/index.d.ts.map +1 -1
  8. package/lib/deploy/core/index.js +1 -0
  9. package/lib/deploy/core/index.js.map +1 -1
  10. package/lib/deploy/core/publishedImage.d.ts +30 -0
  11. package/lib/deploy/core/publishedImage.d.ts.map +1 -0
  12. package/lib/deploy/core/publishedImage.js +96 -0
  13. package/lib/deploy/core/publishedImage.js.map +1 -0
  14. package/lib/deploy/core/types.d.ts +1 -1
  15. package/lib/deploy/core/types.d.ts.map +1 -1
  16. package/lib/deploy/core/types.js.map +1 -1
  17. package/lib/molecule3d/ui/Molecule3DToolbar.d.ts +9 -7
  18. package/lib/molecule3d/ui/Molecule3DToolbar.d.ts.map +1 -1
  19. package/lib/molecule3d/ui/Molecule3DToolbar.js +13 -8
  20. package/lib/molecule3d/ui/Molecule3DToolbar.js.map +1 -1
  21. package/lib/molecule3d/ui/MoleculeCanvas3D.js +1 -1
  22. package/lib/molecule3d/ui/MoleculeCanvas3D.js.map +1 -1
  23. package/lib/molecule3d/ui/camera.d.ts +0 -11
  24. package/lib/molecule3d/ui/camera.d.ts.map +1 -1
  25. package/lib/molecule3d/ui/camera.js +6 -80
  26. package/lib/molecule3d/ui/camera.js.map +1 -1
  27. package/lib/molecule3d/ui/exportMoleculeImage.d.ts +6 -2
  28. package/lib/molecule3d/ui/exportMoleculeImage.d.ts.map +1 -1
  29. package/lib/molecule3d/ui/exportMoleculeImage.js.map +1 -1
  30. package/lib/molecule3d/ui/index.d.ts +9 -0
  31. package/lib/molecule3d/ui/index.d.ts.map +1 -1
  32. package/lib/molecule3d/ui/index.js +5 -0
  33. package/lib/molecule3d/ui/index.js.map +1 -1
  34. package/lib/molecule3d/ui/useImageExport.d.ts +2 -2
  35. package/lib/molecule3d/ui/useImageExport.d.ts.map +1 -1
  36. package/lib/molecule3d/ui/useImageExport.js.map +1 -1
  37. package/lib/molecule3d/ui/viewer.d.ts.map +1 -1
  38. package/lib/molecule3d/ui/viewer.js +18 -53
  39. package/lib/molecule3d/ui/viewer.js.map +1 -1
  40. package/lib/molstar/core/camera.d.ts +37 -0
  41. package/lib/molstar/core/camera.d.ts.map +1 -0
  42. package/lib/molstar/core/camera.js +49 -0
  43. package/lib/molstar/core/camera.js.map +1 -0
  44. package/lib/molstar/core/cameraPose.d.ts +54 -0
  45. package/lib/molstar/core/cameraPose.d.ts.map +1 -0
  46. package/lib/molstar/core/cameraPose.js +78 -0
  47. package/lib/molstar/core/cameraPose.js.map +1 -0
  48. package/lib/{molecule3d/ui → molstar/core}/captureScene.d.ts +1 -1
  49. package/lib/molstar/core/captureScene.d.ts.map +1 -0
  50. package/lib/molstar/core/captureScene.js.map +1 -0
  51. package/lib/molstar/core/hover.d.ts +49 -0
  52. package/lib/molstar/core/hover.d.ts.map +1 -0
  53. package/lib/molstar/core/hover.js +92 -0
  54. package/lib/molstar/core/hover.js.map +1 -0
  55. package/lib/molstar/core/index.d.ts +26 -0
  56. package/lib/molstar/core/index.d.ts.map +1 -0
  57. package/lib/molstar/core/index.js +23 -0
  58. package/lib/molstar/core/index.js.map +1 -0
  59. package/lib/{molecule3d/ui → molstar/core}/measurements.d.ts +23 -3
  60. package/lib/molstar/core/measurements.d.ts.map +1 -0
  61. package/lib/{molecule3d/ui → molstar/core}/measurements.js +31 -7
  62. package/lib/molstar/core/measurements.js.map +1 -0
  63. package/lib/molstar/core/plugin.d.ts +83 -0
  64. package/lib/molstar/core/plugin.d.ts.map +1 -0
  65. package/lib/molstar/core/plugin.js +128 -0
  66. package/lib/molstar/core/plugin.js.map +1 -0
  67. package/lib/structure/ui/ConformerTable.d.ts +8 -0
  68. package/lib/structure/ui/ConformerTable.d.ts.map +1 -1
  69. package/lib/structure/ui/ConformerTable.js +24 -4
  70. package/lib/structure/ui/ConformerTable.js.map +1 -1
  71. package/package.json +2 -1
  72. package/src/deploy/core/checkDeploy.ts +14 -2
  73. package/src/deploy/core/index.ts +5 -0
  74. package/src/deploy/core/publishedImage.ts +110 -0
  75. package/src/deploy/core/types.ts +3 -1
  76. package/src/molecule3d/ui/Molecule3DToolbar.tsx +23 -16
  77. package/src/molecule3d/ui/MoleculeCanvas3D.tsx +1 -1
  78. package/src/molecule3d/ui/camera.ts +18 -93
  79. package/src/molecule3d/ui/exportMoleculeImage.ts +6 -2
  80. package/src/molecule3d/ui/index.ts +12 -0
  81. package/src/molecule3d/ui/useImageExport.ts +2 -2
  82. package/src/molecule3d/ui/viewer.ts +36 -58
  83. package/src/molstar/core/camera.ts +61 -0
  84. package/src/molstar/core/cameraPose.ts +126 -0
  85. package/src/{molecule3d/ui → molstar/core}/captureScene.ts +1 -1
  86. package/src/molstar/core/hover.ts +110 -0
  87. package/src/molstar/core/index.ts +38 -0
  88. package/src/{molecule3d/ui → molstar/core}/measurements.ts +48 -8
  89. package/src/molstar/core/plugin.ts +162 -0
  90. package/src/structure/ui/ConformerTable.tsx +38 -4
  91. package/lib/molecule3d/ui/captureScene.d.ts.map +0 -1
  92. package/lib/molecule3d/ui/captureScene.js.map +0 -1
  93. package/lib/molecule3d/ui/measurements.d.ts.map +0 -1
  94. package/lib/molecule3d/ui/measurements.js.map +0 -1
  95. package/lib/molecule3d/ui/viewerSpec.d.ts +0 -17
  96. package/lib/molecule3d/ui/viewerSpec.d.ts.map +0 -1
  97. package/lib/molecule3d/ui/viewerSpec.js +0 -34
  98. package/lib/molecule3d/ui/viewerSpec.js.map +0 -1
  99. package/src/molecule3d/ui/viewerSpec.ts +0 -38
  100. /package/lib/{molecule3d/ui → molstar/core}/captureScene.js +0 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * What the pointer rests on, as a line of text.
3
+ *
4
+ * Every drawing our sites make carries a label already — `C1 sp³ σ to H5 (+)`,
5
+ * `C3 along [111]` — but molstar's own UI is not mounted, so nothing shows
6
+ * them. This turns the plugin's hover behaviour into that one string, which the
7
+ * canvas renders itself.
8
+ */
9
+ import { isEmptyLoci, isEveryLoci } from 'molstar/lib/mol-model/loci.js';
10
+ import { Bond, StructureElement, StructureProperties, } from 'molstar/lib/mol-model/structure.js';
11
+ import { lociLabel } from 'molstar/lib/mol-theme/label.js';
12
+ /**
13
+ * Report whatever the pointer rests on.
14
+ * @param plugin - The molstar context.
15
+ * @param listener - Called with the loci, or `null` when the pointer is over
16
+ * nothing. The pointer over the background reports an empty loci, whose label
17
+ * is the word "Nothing" — a sentence, not an absence — so it arrives as `null`.
18
+ * @returns The unsubscribe function.
19
+ */
20
+ export function subscribeHover(plugin, listener) {
21
+ const subscription = plugin.behaviors.interaction.hover.subscribe((event) => {
22
+ const { loci } = event.current;
23
+ listener(isEmptyLoci(loci) || isEveryLoci(loci) ? null : loci);
24
+ });
25
+ return () => {
26
+ subscription.unsubscribe();
27
+ };
28
+ }
29
+ /**
30
+ * The one line a loci is worth, in molstar's own words.
31
+ * @param loci - What the pointer is over.
32
+ * @returns The label, stripped of the markup molstar writes into it, or `null`
33
+ * when there is nothing to say.
34
+ */
35
+ export function lociText(loci) {
36
+ if (isEmptyLoci(loci) || isEveryLoci(loci))
37
+ return null;
38
+ const label = stripMarkup(lociLabel(loci, { granularity: 'element' }));
39
+ return label === '' ? null : label;
40
+ }
41
+ /**
42
+ * The atoms a loci holds, in words a reader wants.
43
+ *
44
+ * molstar names one after the row it parsed — `xyz | Model 0 | Instance 1_555 |
45
+ * A | MOL 1 | O [idx 1]` — which is the address of a line in a file we wrote to
46
+ * hand it the atoms, and says nothing a student wants. The element and which
47
+ * atom of the scene it is do: the two hydrogens of water can then be told apart.
48
+ * @param loci - What the pointer rests on: an atom, or the two ends of a bond.
49
+ * @returns `O 1`, `Si 4 — O 8`, or `null` when the loci holds no atom.
50
+ */
51
+ export function atomText(loci) {
52
+ const atoms = StructureElement.Loci.is(loci)
53
+ ? loci
54
+ : Bond.isLoci(loci)
55
+ ? Bond.toStructureElementLoci(loci)
56
+ : null;
57
+ if (atoms === null)
58
+ return null;
59
+ const names = [];
60
+ StructureElement.Loci.forEachLocation(atoms, (location) => {
61
+ names.push(atomName(String(StructureProperties.atom.type_symbol(location)), StructureProperties.atom.sourceIndex(location)));
62
+ });
63
+ return names.length === 0 ? null : names.join(' — ');
64
+ }
65
+ /**
66
+ * What a site calls one atom of its scene.
67
+ *
68
+ * molstar upper-cases an element symbol on its way in, so the atom it hands
69
+ * back from an `Si` it was given is an `SI`, which is not how anybody writes
70
+ * silicon. The number is which atom of the scene it is, counting from one.
71
+ * @param element - Its element symbol, in any case.
72
+ * @param index - Its place in the file the viewer was handed, from zero.
73
+ * @returns `O 1`, `Si 4`.
74
+ */
75
+ export function atomName(element, index) {
76
+ const symbol = element.charAt(0).toUpperCase() + element.slice(1).toLowerCase();
77
+ return `${symbol} ${index + 1}`;
78
+ }
79
+ /**
80
+ * Molstar's label providers return HTML, and a readout is plain text.
81
+ * @param label - The provider's label.
82
+ * @returns The same label with its tags and entities resolved.
83
+ */
84
+ function stripMarkup(label) {
85
+ return label
86
+ .replaceAll(/<[^>]*>/g, ' ')
87
+ .replaceAll('&nbsp;', ' ')
88
+ .replaceAll('&amp;', '&')
89
+ .replaceAll(/\s+/g, ' ')
90
+ .trim();
91
+ }
92
+ //# sourceMappingURL=hover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hover.js","sourceRoot":"","sources":["../../../src/molstar/core/hover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The molstar layer under the 3D views: the plugin lifecycle, the camera, the
3
+ * hover labels, the measurements and the picture of a scene.
4
+ *
5
+ * Three of our sites mount a headless molstar canvas — the molecule viewer
6
+ * here, lcao's orbitals and symmetry's crystals — and each of them had its own
7
+ * copy of the lifecycle, the spec and the spin. This is that one copy; what
8
+ * stays in a site is the scene it draws.
9
+ *
10
+ * **Importing this pulls molstar in statically**, which is why it is reached
11
+ * through the `./molstar/*` pattern rather than a plain subpath: molstar is an
12
+ * optional peer, so a plain subpath would be one the package test imports —
13
+ * in a project that installs no optional peer — and it cannot succeed there.
14
+ * A site that only wants `<MoleculeViewer3D>` imports `react-cheminfo/molecule3d`,
15
+ * whose canvas is lazy and whose barrel stays molstar-free.
16
+ */
17
+ export type { CameraPose, CameraReference, PoseOptions } from './cameraPose.ts';
18
+ export { cameraToPose, poseToCamera } from './cameraPose.ts';
19
+ export { DEFAULT_CAMERA_DURATION, DEFAULT_SPIN_SPEED, FRAMING_MARGIN, MINIMUM_FRAMING_RADIUS, framedRadius, setSpin, } from './camera.ts';
20
+ export { atomName, atomText, lociText, subscribeHover } from './hover.ts';
21
+ export type { MolstarPluginOptions } from './plugin.ts';
22
+ export { MolstarPlugin } from './plugin.ts';
23
+ export { captureScene } from './captureScene.ts';
24
+ export type { MoleculeStructureSource } from './measurements.ts';
25
+ export { MeasurementPicker, atomReferenceOf, clearMeasurements, renderMeasurements, } from './measurements.ts';
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/molstar/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,sBAAsB,EACtB,YAAY,EACZ,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1E,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The molstar layer under the 3D views: the plugin lifecycle, the camera, the
3
+ * hover labels, the measurements and the picture of a scene.
4
+ *
5
+ * Three of our sites mount a headless molstar canvas — the molecule viewer
6
+ * here, lcao's orbitals and symmetry's crystals — and each of them had its own
7
+ * copy of the lifecycle, the spec and the spin. This is that one copy; what
8
+ * stays in a site is the scene it draws.
9
+ *
10
+ * **Importing this pulls molstar in statically**, which is why it is reached
11
+ * through the `./molstar/*` pattern rather than a plain subpath: molstar is an
12
+ * optional peer, so a plain subpath would be one the package test imports —
13
+ * in a project that installs no optional peer — and it cannot succeed there.
14
+ * A site that only wants `<MoleculeViewer3D>` imports `react-cheminfo/molecule3d`,
15
+ * whose canvas is lazy and whose barrel stays molstar-free.
16
+ */
17
+ export { cameraToPose, poseToCamera } from "./cameraPose.js";
18
+ export { DEFAULT_CAMERA_DURATION, DEFAULT_SPIN_SPEED, FRAMING_MARGIN, MINIMUM_FRAMING_RADIUS, framedRadius, setSpin, } from "./camera.js";
19
+ export { atomName, atomText, lociText, subscribeHover } from "./hover.js";
20
+ export { MolstarPlugin } from "./plugin.js";
21
+ export { captureScene } from "./captureScene.js";
22
+ export { MeasurementPicker, atomReferenceOf, clearMeasurements, renderMeasurements, } from "./measurements.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/molstar/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,sBAAsB,EACtB,YAAY,EACZ,OAAO,GACR,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
@@ -9,16 +9,25 @@
9
9
  * instead of moving the camera, and once enough atoms are in it they become a
10
10
  * measurement.
11
11
  */
12
+ import type { ElementIndex, Structure } from 'molstar/lib/mol-model/structure.js';
12
13
  import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
13
- import type { Measurement, MeasurementKind } from '../core/measurement.ts';
14
+ import type { AtomReference, Measurement, MeasurementKind } from '../../molecule3d/core/measurement.ts';
15
+ /**
16
+ * Where the atoms a measurement addresses are found in the plugin's state.
17
+ *
18
+ * Every site builds its scene under refs of its own, so it says here which
19
+ * structure the clicks and the labels are read against.
20
+ */
21
+ export type MoleculeStructureSource = (plugin: PluginContext) => Structure | undefined;
14
22
  /**
15
23
  * Draw exactly `measurements` over the current molecule. An atom the structure
16
24
  * does not have skips its measurement rather than failing the scene.
17
25
  * @param plugin - The molstar context.
18
26
  * @param measurements - Everything to show, replacing what is shown.
27
+ * @param structureOf - See {@link MoleculeStructureSource}.
19
28
  * @returns Nothing; resolves once every label is on screen.
20
29
  */
21
- export declare function renderMeasurements(plugin: PluginContext, measurements: readonly Measurement[]): Promise<void>;
30
+ export declare function renderMeasurements(plugin: PluginContext, measurements: readonly Measurement[], structureOf: MoleculeStructureSource): Promise<void>;
22
31
  /**
23
32
  * Remove every measurement from the scene.
24
33
  * @param plugin - The molstar context.
@@ -32,8 +41,9 @@ export declare class MeasurementPicker {
32
41
  * Start listening to the selection history of `plugin`.
33
42
  * @param plugin - The molstar context.
34
43
  * @param onMeasure - Called with each measurement once its atoms are picked.
44
+ * @param structureOf - See {@link MoleculeStructureSource}.
35
45
  */
36
- constructor(plugin: PluginContext, onMeasure: (measurement: Measurement) => void);
46
+ constructor(plugin: PluginContext, onMeasure: (measurement: Measurement) => void, structureOf: MoleculeStructureSource);
37
47
  /**
38
48
  * Choose what the next clicks measure. Atoms already picked are dropped.
39
49
  * @param kind - The measurement to build, or `null` to give clicks back to
@@ -43,4 +53,14 @@ export declare class MeasurementPicker {
43
53
  /** Stop listening to the plugin. */
44
54
  dispose(): void;
45
55
  }
56
+ /**
57
+ * Address one atom of a structure the way a {@link Measurement} does.
58
+ *
59
+ * A site that builds its own scene numbers atoms by their position in the file
60
+ * it drew; this is how that number becomes a reference a measurement keeps.
61
+ * @param structure - The structure on screen.
62
+ * @param element - The atom's index in the model, i.e. its position in the file.
63
+ * @returns The reference, or `undefined` when the structure has no such atom.
64
+ */
65
+ export declare function atomReferenceOf(structure: Structure, element: ElementIndex): AtomReference | undefined;
46
66
  //# sourceMappingURL=measurements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measurements.d.ts","sourceRoot":"","sources":["../../../src/molstar/core/measurements.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACV,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAIvE,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,eAAe,EAChB,MAAM,sCAAsC,CAAC;AAM9C;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,CACpC,MAAM,EAAE,aAAa,KAClB,SAAS,GAAG,SAAS,CAAC;AAE3B;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,SAAS,WAAW,EAAE,EACpC,WAAW,EAAE,uBAAuB,GACnC,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5E;AAED,2EAA2E;AAC3E,qBAAa,iBAAiB;;IAO5B;;;;;OAKG;gBAED,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,EAC7C,WAAW,EAAE,uBAAuB;IAmBtC;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IAO3C,oCAAoC;IACpC,OAAO,IAAI,IAAI;CAqBhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,YAAY,GACpB,aAAa,GAAG,SAAS,CAS3B"}
@@ -9,12 +9,11 @@
9
9
  * instead of moving the camera, and once enough atoms are in it they become a
10
10
  * measurement.
11
11
  */
12
- import { OrderedSet } from 'molstar/lib/mol-data/int.js';
12
+ import { OrderedSet, SortedArray } from 'molstar/lib/mol-data/int.js';
13
13
  import { StructureElement } from 'molstar/lib/mol-model/structure.js';
14
14
  import { MeasurementGroupTag } from 'molstar/lib/mol-plugin-state/manager/structure/measurement.js';
15
15
  import { StateSelection, StateTransform } from 'molstar/lib/mol-state/index.js';
16
- import { MEASUREMENT_ATOM_COUNTS } from "../core/measurement.js";
17
- import { moleculeStructure } from "./renderMolecule.js";
16
+ import { MEASUREMENT_ATOM_COUNTS } from "../../molecule3d/core/measurement.js";
18
17
  // Lowercased: it is a factory, not a constructor.
19
18
  const elementLoci = StructureElement.Loci;
20
19
  /**
@@ -22,11 +21,12 @@ const elementLoci = StructureElement.Loci;
22
21
  * does not have skips its measurement rather than failing the scene.
23
22
  * @param plugin - The molstar context.
24
23
  * @param measurements - Everything to show, replacing what is shown.
24
+ * @param structureOf - See {@link MoleculeStructureSource}.
25
25
  * @returns Nothing; resolves once every label is on screen.
26
26
  */
27
- export async function renderMeasurements(plugin, measurements) {
27
+ export async function renderMeasurements(plugin, measurements, structureOf) {
28
28
  await clearMeasurements(plugin);
29
- const structure = moleculeStructure(plugin);
29
+ const structure = structureOf(plugin);
30
30
  if (structure === undefined)
31
31
  return;
32
32
  const manager = plugin.managers.structure.measurement;
@@ -61,15 +61,18 @@ export class MeasurementPicker {
61
61
  #plugin;
62
62
  #onMeasure;
63
63
  #unsubscribe;
64
+ #structureOf;
64
65
  #kind = null;
65
66
  /**
66
67
  * Start listening to the selection history of `plugin`.
67
68
  * @param plugin - The molstar context.
68
69
  * @param onMeasure - Called with each measurement once its atoms are picked.
70
+ * @param structureOf - See {@link MoleculeStructureSource}.
69
71
  */
70
- constructor(plugin, onMeasure) {
72
+ constructor(plugin, onMeasure, structureOf) {
71
73
  this.#plugin = plugin;
72
74
  this.#onMeasure = onMeasure;
75
+ this.#structureOf = structureOf;
73
76
  // A molfile is one residue: at molstar's default granularity a click
74
77
  // would pick the whole molecule.
75
78
  plugin.managers.interactivity.setProps({ granularity: 'element' });
@@ -98,7 +101,7 @@ export class MeasurementPicker {
98
101
  }
99
102
  #collect() {
100
103
  const kind = this.#kind;
101
- const structure = moleculeStructure(this.#plugin);
104
+ const structure = this.#structureOf(this.#plugin);
102
105
  if (kind === null || structure === undefined)
103
106
  return;
104
107
  const needed = MEASUREMENT_ATOM_COUNTS[kind];
@@ -117,6 +120,27 @@ export class MeasurementPicker {
117
120
  this.#onMeasure({ kind, atoms: atoms.toReversed() });
118
121
  }
119
122
  }
123
+ /**
124
+ * Address one atom of a structure the way a {@link Measurement} does.
125
+ *
126
+ * A site that builds its own scene numbers atoms by their position in the file
127
+ * it drew; this is how that number becomes a reference a measurement keeps.
128
+ * @param structure - The structure on screen.
129
+ * @param element - The atom's index in the model, i.e. its position in the file.
130
+ * @returns The reference, or `undefined` when the structure has no such atom.
131
+ */
132
+ export function atomReferenceOf(structure, element) {
133
+ const units = structure.units;
134
+ for (let unit = 0; unit < units.length; unit++) {
135
+ const elements = units[unit]?.elements;
136
+ if (elements === undefined)
137
+ continue;
138
+ const index = SortedArray.indexOf(elements, element);
139
+ if (index !== -1)
140
+ return { unit, element: index };
141
+ }
142
+ return undefined;
143
+ }
120
144
  function atomReference(structure, loci) {
121
145
  if (!StructureElement.Loci.is(loci))
122
146
  return undefined;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measurements.js","sourceRoot":"","sources":["../../../src/molstar/core/measurements.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Lifecycle of one headless molstar canvas: created now, disposed whenever,
3
+ * every call queued behind initialisation.
4
+ *
5
+ * The constructor is **synchronous** on purpose. React 19 runs an effect, its
6
+ * cleanup and the effect again on every mount in development, so an `await`ed
7
+ * constructor hands the cleanup nothing to dispose and leaks a WebGL context
8
+ * per mount — browsers drop the oldest after about sixteen, and the viewer
9
+ * silently goes blank. Returning the handle immediately means `dispose()` can
10
+ * always be called, even before initialisation has finished; the work is queued
11
+ * behind `ready`.
12
+ *
13
+ * molstar's own UI is never mounted: every control on our sites is ours.
14
+ */
15
+ import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
16
+ import type { PluginSpec } from 'molstar/lib/mol-plugin/spec.js';
17
+ /** Settings fixed for the life of a plugin. */
18
+ export interface MolstarPluginOptions {
19
+ /**
20
+ * Scene background, as `#rrggbb`. A WebGL clear colour, so it cannot be a CSS
21
+ * custom property.
22
+ * @default '#ffffff'
23
+ */
24
+ background?: string;
25
+ /**
26
+ * How long molstar animates a reframe it decided on itself; 0 jumps.
27
+ *
28
+ * Replacing a scene commits several times — the old drawing is deleted, the
29
+ * new one added — and molstar glides the camera on each, so the model appears
30
+ * to drift into place. Reframing is right; animating it between two unrelated
31
+ * scenes is not.
32
+ * @default molstar's own 250 ms
33
+ */
34
+ cameraResetDurationMilliseconds?: number;
35
+ /**
36
+ * The last word on the spec, applied over everything above: a site that needs
37
+ * a behaviour dropped, a marking colour or a renderer setting of its own
38
+ * passes it here rather than mounting its own view model.
39
+ */
40
+ spec?: (spec: PluginSpec) => PluginSpec;
41
+ }
42
+ /** One molstar canvas, and the queue everything drawn on it goes through. */
43
+ export declare class MolstarPlugin {
44
+ #private;
45
+ /** Resolves once the canvas exists; every call awaits it internally. */
46
+ readonly ready: Promise<void>;
47
+ /**
48
+ * Mount a canvas in `container` and start initialising it.
49
+ * @param container - An element with `position: relative`; molstar inserts
50
+ * its own canvas into it.
51
+ * @param options - See {@link MolstarPluginOptions}.
52
+ */
53
+ constructor(container: HTMLElement, options?: MolstarPluginOptions);
54
+ /**
55
+ * Whether {@link dispose} has been called.
56
+ * @returns True once the canvas has been torn down.
57
+ */
58
+ get disposed(): boolean;
59
+ /**
60
+ * Wait for initialisation, then run `action` on the plugin.
61
+ * @param action - What to do with the molstar context.
62
+ * @returns What `action` returned, or `undefined` once the plugin has been
63
+ * disposed — before the call or while `action` was still running. An
64
+ * initialisation failure, and anything `action` throws while the plugin is
65
+ * alive, still reach the caller.
66
+ */
67
+ run<Result>(action: (plugin: PluginContext) => Result | Promise<Result>): Promise<Result | undefined>;
68
+ /**
69
+ * Start a subscription once the plugin exists.
70
+ * @param start - Subscribes, and returns its own unsubscribe function.
71
+ * @returns A function that stops the subscription, or stops it from ever
72
+ * starting; safe to call at any time.
73
+ */
74
+ subscribe(start: (plugin: PluginContext) => () => void): () => void;
75
+ /** Re-read the container's size. Call from a `ResizeObserver`. */
76
+ handleResize(): void;
77
+ /**
78
+ * Tear the canvas down and release its WebGL context. Idempotent, and safe to
79
+ * call before initialisation has finished.
80
+ */
81
+ dispose(): void;
82
+ }
83
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/molstar/core/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAKjE,+CAA+C;AAC/C,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC;;;;OAIG;IACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,UAAU,CAAC;CACzC;AAED,6EAA6E;AAC7E,qBAAa,aAAa;;IAIxB,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B;;;;;OAKG;gBACS,SAAS,EAAE,WAAW,EAAE,OAAO,GAAE,oBAAyB;IA0BtE;;;OAGG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;;;;OAOG;IACG,GAAG,CAAC,MAAM,EACd,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAC1D,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAY9B;;;;;OAKG;IACH,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,IAAI,GAAG,MAAM,IAAI;IAcnE,kEAAkE;IAClE,YAAY,IAAI,IAAI;IAKpB;;;OAGG;IACH,OAAO,IAAI,IAAI;CAahB"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Lifecycle of one headless molstar canvas: created now, disposed whenever,
3
+ * every call queued behind initialisation.
4
+ *
5
+ * The constructor is **synchronous** on purpose. React 19 runs an effect, its
6
+ * cleanup and the effect again on every mount in development, so an `await`ed
7
+ * constructor hands the cleanup nothing to dispose and leaks a WebGL context
8
+ * per mount — browsers drop the oldest after about sixteen, and the viewer
9
+ * silently goes blank. Returning the handle immediately means `dispose()` can
10
+ * always be called, even before initialisation has finished; the work is queued
11
+ * behind `ready`.
12
+ *
13
+ * molstar's own UI is never mounted: every control on our sites is ours.
14
+ */
15
+ import { PluginViewModel } from 'molstar/lib/extensions/plugin/view-model.js';
16
+ // Lowercased on import: it is a factory, not a constructor.
17
+ import { DefaultPluginSpec as defaultPluginSpec } from 'molstar/lib/mol-plugin/spec.js';
18
+ import { Color } from 'molstar/lib/mol-util/color/color.js';
19
+ /** One molstar canvas, and the queue everything drawn on it goes through. */
20
+ export class MolstarPlugin {
21
+ #model;
22
+ #disposed = false;
23
+ /** Resolves once the canvas exists; every call awaits it internally. */
24
+ ready;
25
+ /**
26
+ * Mount a canvas in `container` and start initialising it.
27
+ * @param container - An element with `position: relative`; molstar inserts
28
+ * its own canvas into it.
29
+ * @param options - See {@link MolstarPluginOptions}.
30
+ */
31
+ constructor(container, options = {}) {
32
+ const { background = '#ffffff', // tokens-ok: a WebGL clear colour
33
+ cameraResetDurationMilliseconds, spec: refine, } = options;
34
+ const base = defaultPluginSpec();
35
+ const spec = {
36
+ ...base,
37
+ canvas3d: {
38
+ ...base.canvas3d,
39
+ renderer: { backgroundColor: Color.fromHexStyle(background) },
40
+ // A scene is read from its own shape, never from the world axes.
41
+ camera: { helper: { axes: { name: 'off', params: {} } } },
42
+ ...(cameraResetDurationMilliseconds === undefined
43
+ ? {}
44
+ : { cameraResetDurationMs: cameraResetDurationMilliseconds }),
45
+ },
46
+ };
47
+ this.#model = new PluginViewModel({
48
+ spec: refine === undefined ? spec : refine(spec),
49
+ });
50
+ this.#model.mount(container);
51
+ this.ready = this.#model.initialized;
52
+ }
53
+ /**
54
+ * Whether {@link dispose} has been called.
55
+ * @returns True once the canvas has been torn down.
56
+ */
57
+ get disposed() {
58
+ return this.#disposed;
59
+ }
60
+ /**
61
+ * Wait for initialisation, then run `action` on the plugin.
62
+ * @param action - What to do with the molstar context.
63
+ * @returns What `action` returned, or `undefined` once the plugin has been
64
+ * disposed — before the call or while `action` was still running. An
65
+ * initialisation failure, and anything `action` throws while the plugin is
66
+ * alive, still reach the caller.
67
+ */
68
+ async run(action) {
69
+ if (this.#disposed)
70
+ return undefined;
71
+ await this.ready;
72
+ if (this.#disposed)
73
+ return undefined;
74
+ try {
75
+ return await action(this.#model.plugin);
76
+ }
77
+ catch (error) {
78
+ if (this.#disposed)
79
+ return undefined;
80
+ throw error;
81
+ }
82
+ }
83
+ /**
84
+ * Start a subscription once the plugin exists.
85
+ * @param start - Subscribes, and returns its own unsubscribe function.
86
+ * @returns A function that stops the subscription, or stops it from ever
87
+ * starting; safe to call at any time.
88
+ */
89
+ subscribe(start) {
90
+ let stop = null;
91
+ let cancelled = false;
92
+ void this.run((plugin) => {
93
+ if (cancelled)
94
+ return;
95
+ stop = start(plugin);
96
+ });
97
+ return () => {
98
+ cancelled = true;
99
+ stop?.();
100
+ stop = null;
101
+ };
102
+ }
103
+ /** Re-read the container's size. Call from a `ResizeObserver`. */
104
+ handleResize() {
105
+ if (this.#disposed)
106
+ return;
107
+ this.#model.plugin.handleResize();
108
+ }
109
+ /**
110
+ * Tear the canvas down and release its WebGL context. Idempotent, and safe to
111
+ * call before initialisation has finished.
112
+ */
113
+ dispose() {
114
+ if (this.#disposed)
115
+ return;
116
+ this.#disposed = true;
117
+ // `mount` creates the canvas synchronously, so the context exists even when
118
+ // initialisation went on to fail; releasing it is what stops the browser
119
+ // dropping an older viewer's. A rejected `ready` must not escape here
120
+ // either — nobody is left to handle it.
121
+ void this.ready
122
+ .catch(() => undefined)
123
+ .then(() => {
124
+ this.#model.plugin.dispose();
125
+ });
126
+ }
127
+ }
128
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/molstar/core/plugin.ts"],"names":[],"mappings":"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"}
@@ -59,6 +59,14 @@ export interface ConformerTableProps {
59
59
  * @default undefined — every column keeps its own
60
60
  */
61
61
  columnLabels?: Partial<Record<ConformerColumn, string>>;
62
+ /**
63
+ * Whether the energies on screen are expected to be replaced — a force
64
+ * field's numbers while a better method is still working on the set. They
65
+ * are then drawn muted, so a reader can tell at a glance which of the two
66
+ * rankings they are looking at.
67
+ * @default false
68
+ */
69
+ provisional?: boolean;
62
70
  }
63
71
  /**
64
72
  * The conformers of a set, as a selectable table.
@@ -1 +1 @@
1
- {"version":3,"file":"ConformerTable.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/ConformerTable.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAgC,YAAY,EAAE,MAAM,OAAO,CAAC;AAUxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,uCAAuC;AACvC,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,GACzB,YAAY,GAAG,IAAI,CA4IrB"}
1
+ {"version":3,"file":"ConformerTable.d.ts","sourceRoot":"","sources":["../../../src/structure/ui/ConformerTable.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAgC,YAAY,EAAE,MAAM,OAAO,CAAC;AAUxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,uCAAuC;AACvC,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,GACzB,YAAY,GAAG,IAAI,CA6IrB"}
@@ -32,7 +32,7 @@ import { CONFORMER_COLUMN_LABELS, NUMERIC_CONFORMER_COLUMNS, PICKER_COLUMNS, } f
32
32
  * @returns The table, or `null` when there is nothing to show.
33
33
  */
34
34
  export function ConformerTable(props) {
35
- const { conformers, columns = PICKER_COLUMNS, ranking = 'force-field', selectedId = null, onSelect, temperature = ROOM_TEMPERATURE, label = 'Conformers, most stable first', rowName = String, columnLabels, } = props;
35
+ const { conformers, columns = PICKER_COLUMNS, ranking = 'force-field', selectedId = null, onSelect, temperature = ROOM_TEMPERATURE, label = 'Conformers, most stable first', rowName = String, columnLabels, provisional = false, } = props;
36
36
  const bodyRef = useRef(null);
37
37
  const shares = useMemo(() => {
38
38
  if (!columns.includes('population'))
@@ -93,7 +93,7 @@ export function ConformerTable(props) {
93
93
  return copyableHere &&
94
94
  COPY_LABELS[column] !== undefined &&
95
95
  typeof content === 'string' &&
96
- content !== '—' ? (_jsx(ClickToCopy, { as: "td", style: cellStyle(column), value: content, label: COPY_LABELS[column], testId: CELL_TEST_IDS[column], children: content }, column)) : (_jsx("td", { style: cellStyle(column), "data-testid": CELL_TEST_IDS[column], children: content }, column));
96
+ content !== '—' ? (_jsx(ClickToCopy, { as: "td", style: cellStyle(column, provisional), value: content, label: COPY_LABELS[column], testId: CELL_TEST_IDS[column], children: content }, column)) : (_jsx("td", { style: cellStyle(column, provisional), "data-testid": CELL_TEST_IDS[column], children: content }, column));
97
97
  }) }, conformer.id));
98
98
  }) })] }));
99
99
  }
@@ -200,14 +200,34 @@ const ENERGY_RESOLUTION = 0.005;
200
200
  function headerStyle(column) {
201
201
  return NUMERIC_CONFORMER_COLUMNS.has(column) ? numericStyle : undefined;
202
202
  }
203
- function cellStyle(column) {
204
- return NUMERIC_CONFORMER_COLUMNS.has(column) ? numericStyle : undefined;
203
+ /**
204
+ * A cell's own style: numbers are right-aligned and tabular, and every cell a
205
+ * better method will restate is muted while it works. The rank is not one of
206
+ * them — it names the row rather than measuring it, and it is what a reader
207
+ * follows as the order changes under them.
208
+ * @param column - The column the cell is in.
209
+ * @param provisional - Whether the energies are still expected to change.
210
+ * @returns The cell's inline style, or `undefined` when it needs none.
211
+ */
212
+ function cellStyle(column, provisional) {
213
+ const numeric = NUMERIC_CONFORMER_COLUMNS.has(column);
214
+ if (!provisional || column === 'id') {
215
+ return numeric ? numericStyle : undefined;
216
+ }
217
+ return numeric ? provisionalNumericStyle : provisionalStyle;
205
218
  }
206
219
  const tableStyle = { width: '100%' };
207
220
  const numericStyle = {
208
221
  textAlign: 'right',
209
222
  fontVariantNumeric: 'tabular-nums',
210
223
  };
224
+ const provisionalStyle = {
225
+ color: 'var(--text-muted)',
226
+ };
227
+ const provisionalNumericStyle = {
228
+ ...numericStyle,
229
+ ...provisionalStyle,
230
+ };
211
231
  /**
212
232
  * The selected row is tinted with the site's own accent, so the table belongs
213
233
  * to whichever site draws it; a row is a pointer only where a click does