Skip to content

Commit f1325fc

Browse files
committed
In process
1 parent 6744a20 commit f1325fc

396 files changed

Lines changed: 9737 additions & 49 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,4 @@ record.txt
125125
# ==========================================================
126126
molsysviewer/_version.py
127127
!*.nbconvert.log
128+
codex_session.sh

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ v.load(pdb_text)
9797
v.add_sphere(center=(12.0, 12.0, 8.0), radius=3.0, color=0x00ff00, alpha=0.4)
9898
```
9999

100+
`new_view(...)` also accepts `load_mode`:
101+
102+
- `load_mode="selection"` (default): the selection subsets the loaded system.
103+
- `load_mode="all"`: the full system loads, the global view is hidden, and a
104+
region tagged `"selection"` is created for the selection.
105+
100106
---
101107

102108
## 📦 Installation

docs/content/developer/architecture_full.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
This page is the canonical architecture reference for MolSysViewer.
44
It complements the short overview in {doc}`architecture`.
55
If you want a longer, implementation-oriented snapshot from the original
6-
devguide, see {doc}`architecture_snapshot_2025_11`.
6+
devguide, see {doc}`architecture_snapshot_2026_01` (latest) or
7+
{doc}`architecture_snapshot_2025_11` (historical).
78

89
## Layers and responsibilities
910

@@ -67,6 +68,10 @@ The widget emits events back to Python via `widget.on_msg`:
6768
5. TS builds Mol* `Topology`, `Coordinates`, `Trajectory` and applies a preset.
6869
6. The controller captures the structure and notifies state/trajectory handlers.
6970

71+
Note: `new_view(..., load_mode="selection")` uses the same flow but subsets the
72+
system before loading, while `new_view(..., load_mode="all")` loads the full
73+
system and creates a `selection` region for the requested selection.
74+
7075
### String/URL loads
7176

7277
String, ID, and URL inputs are handled through MolSysMT in Python.
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Architecture snapshot (2026-01)
2+
_Last updated: 2026-01-30_
3+
4+
This page preserves a detailed, implementation-oriented snapshot of the current
5+
MolSysViewer architecture. It is not the source of truth, but it is useful when
6+
you need a compact description of the data flow and message protocol.
7+
8+
If you want the current high-level view, see {doc}`architecture` and
9+
{doc}`architecture_full`.
10+
11+
## 1. High-level layers
12+
13+
MolSysViewer is split into:
14+
15+
- **Python layer (`molsysviewer/`)**
16+
- `MolSysView` facade and widget wrapper.
17+
- Loaders for MolSysMT/PDB/mmCIF/URL.
18+
- Regions/layers/whole and shapes APIs.
19+
- HTML export helpers (standalone + docs-lite).
20+
- **TypeScript/Mol* layer (`molsysviewer/js/src/`)**
21+
- AnyWidget entry (`index.ts`).
22+
- `MolSysViewerController` and handlers (`loader`, `scene`, `state`, `trajectory`).
23+
- Shape builders under `shapes/`.
24+
- Popup host + popup implementation.
25+
26+
The original design overview is preserved in {doc}`design_overview`.
27+
28+
## 2. Python ↔ JS message protocol
29+
30+
### 2.1 Python → JS
31+
32+
All visual actions are sent as JSON-like dictionaries with:
33+
34+
- `op`: an operation identifier.
35+
- `options` (or additional fields) depending on the operation.
36+
37+
Examples
38+
39+
- Load a MolSys payload:
40+
```python
41+
view._send({
42+
"op": "load_molsys_payload",
43+
"payload": payload,
44+
"label": label,
45+
})
46+
```
47+
- Update visibility:
48+
```python
49+
view._send({
50+
"op": "update_visibility",
51+
"options": {"visible_atom_indices": visible_indices},
52+
})
53+
```
54+
- Shapes:
55+
```python
56+
view._send({
57+
"op": "add_sphere",
58+
"options": {...},
59+
})
60+
```
61+
62+
The TypeScript union type `ViewerMessage` (`molsysviewer/js/src/messages/viewer-messages.ts`)
63+
is the best “contract” view.
64+
65+
### 2.2 JS → Python
66+
67+
The widget emits events back to Python via `widget.on_msg`:
68+
69+
- `ready`: frontend initialized; Python flushes `_pending_messages`.
70+
- `region_ack`: region creation confirmation (`atom_indices`, `selection`).
71+
- `region_deleted`
72+
- `layer_ack`
73+
- `layer_deleted`
74+
- `registry_cleared`: `clear_all` was executed; Python must reset registries.
75+
- `camera_snapshot`: camera state sent from the frontend.
76+
- `js_log`: debug logs when `debug_js=True`.
77+
78+
These events keep the Python mirrors (`view.regions`, `view.layers`) consistent
79+
with the Mol* state tree.
80+
81+
## 3. Data path: MolSysMT → Mol*
82+
83+
1. You call `MolSysView.load(molecular_system, ...)`.
84+
2. Python converts inputs to `molsysmt.MolSys`.
85+
3. `ViewerJSON` is serialized into a stable MolSys payload:
86+
- `atoms`: parallel arrays (ids, names, residue, chain, element, charge).
87+
- `structures`: snapshots with `coordinates` (Å), optional `box` (Å), optional `time`.
88+
- optional `bonds`: `indexA/indexB` (+ optional `order`).
89+
4. Python sends `{"op": "load_molsys_payload", "payload": ...}`.
90+
5. TypeScript builds Mol* objects:
91+
- `Topology`, `Coordinates`, and a `Trajectory`.
92+
- A trajectory node in the Mol* state tree (`InsertMolSysTrajectory`).
93+
- A default representation preset (`default` / `auto`).
94+
6. The controller captures the loaded structure and notifies state/trajectory handlers.
95+
96+
This path avoids intermediate PDB conversions when MolSysMT inputs are available.
97+
98+
### new_view convenience modes
99+
100+
`new_view(...)` is a convenience wrapper around `MolSysView.load(...)` and adds
101+
`load_mode`:
102+
103+
- `load_mode="selection"` (default): the selection subsets the system before loading.
104+
- `load_mode="all"`: the full system loads, the global representation is hidden,
105+
and a region tagged `selection` is created for the requested selection.
106+
107+
## 4. Visibility and masking
108+
109+
### 4.1 Python
110+
111+
- `MolSysView` maintains a boolean `atom_mask` (`n_atoms`).
112+
- `hide(selection)` updates the mask and then calls `_update_visibility_in_frontend()`.
113+
- `show(selection, structure_indices, force=False)` restores parts of the mask and re-applies global visibility intent.
114+
- `visible_atom_indices` is computed as `np.nonzero(atom_mask)[0].tolist()` and sent through `update_visibility`.
115+
116+
### 4.2 TypeScript
117+
118+
- `StateHandlers.updateVisibility`:
119+
- If no structure is loaded yet, stores `pendingVisibility`.
120+
- Otherwise:
121+
- Clears previous transparency.
122+
- Computes the hidden selection as the complement of `visible_atom_indices`.
123+
- Applies transparency/visibility via Mol* helpers to global reps and regions.
124+
125+
The source of truth for visibility is Python.
126+
Mol* is responsible for render-time application.
127+
128+
## 5. Regions and layers
129+
130+
### 5.1 Python API
131+
132+
- `MolSysView.new_region(...)`:
133+
- Accepts a MolSysMT selection or explicit `atom_indices`.
134+
- Supports complements (`complement_of_regions=["tagA", ...]` or `"all"`).
135+
- Registers a `Region` and sends `create_region`.
136+
- `Region.set_representation(...)`:
137+
- Normalizes representation type/preset and user preset rules.
138+
- Sends `set_region_representation`.
139+
- `Region.show/hide/delete`:
140+
- Send `show_region`, `hide_region`, `delete_region`.
141+
- `MolSysView.new_layer(...)`:
142+
- Registers a `Layer` and sends `create_layer`.
143+
- `Layer.show/hide/delete/set_tag`:
144+
- Send `show_layer`, `hide_layer`, `delete_layer`, `set_layer_tag`.
145+
- `Whole.set_representation(...)`:
146+
- Resolves user preset rules in Python and sends `set_global_representation`.
147+
148+
### 5.2 TypeScript state model
149+
150+
`StateHandlers` maintains:
151+
152+
- `regionIndex`: `tag -> { component, representations[], atomIndices[], selection, hidden }`
153+
- `layerMeta`: `tag -> { kind, meta }`
154+
- `tagIndex`: `tag -> Set<StateObjectRef>` (refs for shapes/overlays)
155+
- `globalReprs`: baseline/global representations for the root structure
156+
- pending flags for operations invoked before a structure exists
157+
158+
Operations
159+
160+
- `create_region`: build `StructureSelection` from indices, create `StructureComponent`, add reps, store refs.
161+
- `set_region_representation`: replace region reps (type, preset, or user preset).
162+
- `show/hide_region`: toggle visibility via `setSubtreeVisibility`.
163+
- `create_layer`: store metadata and acknowledge to Python.
164+
- `show/hide_layer`: toggle visibility of all refs under a tag.
165+
- `set_global_representation`: rebuild baseline/global reps and store them in `globalReprs`.
166+
- `show/hide_global`: show/hide baseline/global reps; `target="all"` can include everything.
167+
168+
`clear_all` clears regions, layers, and global reps, removes the loaded structure,
169+
and emits `registry_cleared`.
170+
171+
## 6. Popup (host ↔ popup)
172+
173+
Host (notebook)
174+
175+
- `PopupHostManager` opens a window and injects the runtime:
176+
- Blob + `import` in notebooks, or
177+
- `moduleUrl` in docs-lite exports.
178+
- Keeps a `commandLog` (Python → JS messages) to reconstruct state.
179+
- Uses `postMessage` to:
180+
- send `molsysviewer-initial-sync` (commands + camera snapshot + UI state),
181+
- stream `molsysviewer-sync-op` messages,
182+
- send `molsysviewer-sync-camera` while the user interacts.
183+
184+
Popup
185+
186+
- `bootPopup` creates a new `MolSysViewerController` in the popup DOM.
187+
- Replays the `commandLog` and applies the initial camera snapshot.
188+
- Syncs camera host ↔ popup without “camera fights”.
189+
190+
This design keeps the popup as a live mirror of the host.
191+
192+
## 7. HTML export behavior (current)
193+
194+
- HTML export replays `_message_history` and does not require `show()` to run.
195+
- If the frontend is live, `write_html(...)` requests a fresh camera snapshot
196+
before exporting (best-effort).
197+
- UI-only changes (toggle buttons, manual style changes) do not populate
198+
`_message_history` and are not exported.
199+
200+
## 8. What was still aspirational (at the time)
201+
202+
The design overview mentions additional modules (hbonds, topology, alternative engines) and richer `structure.get_* / show_*` APIs.
203+
At this snapshot:
204+
205+
- Implemented core:
206+
- MolSys payload loading,
207+
- scientific shapes (spheres, pockets, tubes, pharmacophores),
208+
- regions/layers/whole,
209+
- popup,
210+
- trajectory controls,
211+
- automatic display in notebooks via `_repr_mimebundle_`.
212+
- Still future:
213+
- dedicated hbonds module (`get_hbonds/show_hbonds`),
214+
- topology module with higher-level APIs (`get_bonds/show_bonds`),
215+
- extended numeric APIs under `structure.*`.
216+
217+
Revisit this page when a “design” item becomes a shipped feature.

docs/content/developer/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,7 @@ string_style.md
6565
roadmap.md
6666
changes_notes.md
6767
future/index.md
68+
architecture_snapshot_2026_01.md
69+
architecture_snapshot_2025_11.md
6870
appendix/index.md
6971
```

docs/content/developer/public_api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ You treat these as public:
1515

1616
If you rename, remove, or change behavior here, you update docs and add tests.
1717

18+
`new_view(...)` includes a convenience argument `load_mode`:
19+
20+
- `"selection"` (default): `selection` subsets the loaded system.
21+
- `"all"`: the full system loads, the global representation is hidden, and a
22+
region tagged `"selection"` is created for the selection.
23+
1824
## Internal Python APIs
1925

2026
You can change these without a stability guarantee:

docs/content/developer/repo_structure.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Key modules
3636
- AnyWidget wrapper, exports `viewer.js` as `_esm` and synced traits.
3737
- `molsysviewer/new_view.py`
3838
- Convenience `new_view(...)` that creates or reuses a `MolSysView`.
39+
- Includes `load_mode` to choose between subsetting the system or loading all
40+
and creating a `selection` region.
3941
- `molsysviewer/loaders/`
4042
- `load_molsysmt.py`: MolSysMT conversion and payload serialization.
4143
- `molsysviewer/regions.py`, `molsysviewer/layers.py`, `molsysviewer/whole.py`

docs/content/user/molecular_system/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ MolSysViewer builds on MolSysMT, so the way you load, query, and modify what you
66

77
If you are new to these ideas, start with the first section, an overview on what a molecular system is for MolSysViewer, and then continue in order:
88

9+
Why this matters: `new_view` defaults to `load_mode="selection"`, so a selection subsets the system you load. Use `load_mode="all"` when you want the full system and a visible region tagged `selection`.
10+
911
```{toctree}
1012
:maxdepth: 1
1113

docs/content/user/molecular_system/loading_and_inspect.ipynb

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -398,18 +398,7 @@
398398
"cell_type": "markdown",
399399
"id": "7064e5ee",
400400
"metadata": {},
401-
"source": [
402-
"## Loading your own molecular system\n",
403-
"\n",
404-
"When you move from demos to your own work, you will typically load a molecular system from a local file or from an object you already have in memory (for example, an `mdtraj.Trajectory`, an OpenMM topology + coordinates, or a MolSysMT object).\n",
405-
"\n",
406-
"There are two common patterns:\n",
407-
"\n",
408-
"1. Create a new view from a molecular-system-like input with {func}`molsysviewer.new_view`.\n",
409-
"2. Create a view first, then call `view.load(...)`.\n",
410-
"\n",
411-
"Both are valid; pick the one that matches your style.\n"
412-
]
401+
"source": "## Loading your own molecular system\n\nWhen you move from demos to your own work, you will typically load a molecular system from a local file or from an object you already have in memory (for example, an `mdtraj.Trajectory`, an OpenMM topology + coordinates, or a MolSysMT object).\n\nThere are two common patterns:\n\n1. Create a new view from a molecular-system-like input with {func}`molsysviewer.new_view`.\n2. Create a view first, then call `view.load(...)`.\n\nBoth are valid; pick the one that matches your style.\n\nWhy this matters: `new_view` can either subset the system or keep it intact. Use `load_mode=\"selection\"` (default) to load only the selection. Use `load_mode=\"all\"` to load everything and create a region tagged `selection`.\n"
413402
},
414403
{
415404
"cell_type": "code",
@@ -677,4 +666,4 @@
677666
},
678667
"nbformat": 4,
679668
"nbformat_minor": 5
680-
}
669+
}

docs/content/user/molecular_system/molecular_system.ipynb

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -911,27 +911,7 @@
911911
},
912912
"tags": []
913913
},
914-
"source": [
915-
"Try running the following code in your own notebook:\n",
916-
"\n",
917-
"```python\n",
918-
"import molsysviewer as viewer\n",
919-
"\n",
920-
"view = viewer.new_view(\n",
921-
" \"181L\",\n",
922-
" selection='molecule_type not in [\"water\", \"ion\"]',\n",
923-
")\n",
924-
"view.show()\n",
925-
"```\n",
926-
"\n",
927-
"You should see the molecular system from the Protein Data Bank entry **181L**, with crystallographic waters and ions removed.\n",
928-
"\n",
929-
"This is a good example of what “conversion” means in practice:\n",
930-
"\n",
931-
"- MolSysViewer does not implement a special-case “PDB id loader”.\n",
932-
"- MolSysMT is the layer that understands inputs like a PDB id string, applies the selection, and converts the result into a viewer-friendly payload.\n",
933-
"- Mol\\* is the rendering engine that draws the scene in the browser."
934-
]
914+
"source": "Try running the following code in your own notebook:\n\n```python\nimport molsysviewer as viewer\n\nview = viewer.new_view(\n \"181L\",\n selection='molecule_type not in [\"water\", \"ion\"]',\n)\nview.show()\n```\n\nYou should see the molecular system from the Protein Data Bank entry **181L**, with crystallographic waters and ions removed.\n\nThis is a good example of what “conversion” means in practice:\n\n- MolSysViewer does not implement a special-case “PDB id loader”.\n- MolSysMT is the layer that understands inputs like a PDB id string, applies the selection, and converts the result into a viewer-friendly payload.\n- Mol\\* is the rendering engine that draws the scene in the browser.\nNote: `new_view` defaults to `load_mode=\"selection\"`, so the selection subsets the system. If you need the full system plus a selection region, pass `load_mode=\"all\"`.\n"
935915
},
936916
{
937917
"cell_type": "markdown",
@@ -977,4 +957,4 @@
977957
},
978958
"nbformat": 4,
979959
"nbformat_minor": 5
980-
}
960+
}

0 commit comments

Comments
 (0)