Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c841836
Promote space-group-database ADR to accepted
AndrewSazonov Jun 2, 2026
8a7fc16
Promote plotting-docs-performance ADR to accepted
AndrewSazonov Jun 2, 2026
07b0bae
Add plotting-docs-performance implementation plan
AndrewSazonov Jun 2, 2026
54d27f5
Add FigureEmbedMode enum and env resolver
AndrewSazonov Jun 2, 2026
06437d1
Add vendored-JS bump script and pixi task
AndrewSazonov Jun 2, 2026
7f75ffa
Vendor plotly-cartesian and refresh three.js
AndrewSazonov Jun 2, 2026
6b5e0dc
Add docs-sync-vendored-js task and wiring
AndrewSazonov Jun 2, 2026
25e1d57
Add shared ed-figures.js lazy figure loader
AndrewSazonov Jun 2, 2026
acfb334
Wire docs runtime assets and page importmap
AndrewSazonov Jun 2, 2026
be0f1bc
Add SHARED embedding mode to Plotly serializer
AndrewSazonov Jun 2, 2026
2015213
Add SHARED embedding mode to Three.js renderer
AndrewSazonov Jun 2, 2026
19c285a
Pass STANDALONE mode from report renderer
AndrewSazonov Jun 2, 2026
54f1f4e
Route docs notebook execution to SHARED mode
AndrewSazonov Jun 2, 2026
0aaf7b7
Reach Phase 1 review gate
AndrewSazonov Jun 2, 2026
f4c4b61
Guard LICENSES.md drift in vendor-check-js
AndrewSazonov Jun 2, 2026
7e9b711
Downcast SHARED figure data to float32
AndrewSazonov Jun 2, 2026
0f3bc26
Finalize Wyckoff letter detection ADR
AndrewSazonov Jun 2, 2026
f94376f
Record and bound float32 display-precision decision
AndrewSazonov Jun 2, 2026
a1eca56
Drop broken cif-numeric-precision ADR links
AndrewSazonov Jun 2, 2026
1765eb7
Apply pixi run fix lint and format fixes
AndrewSazonov Jun 2, 2026
13dc80f
Add Phase 2 tests for embedding mode and float32
AndrewSazonov Jun 2, 2026
407fb51
Resolve RequireJS open question (kept, removal deferred)
AndrewSazonov Jun 2, 2026
b89054e
Add float32 fidelity, report STANDALONE, and bump drift tests
AndrewSazonov Jun 2, 2026
d5dd1ab
Finalize Wyckoff ADR, add CIF precision ADR
AndrewSazonov Jun 2, 2026
1aeef1e
Add match to resolver test pytest.raises (PT011)
AndrewSazonov Jun 2, 2026
48939df
Record Phase 2 verification results in plan
AndrewSazonov Jun 2, 2026
b842011
Add plotting-docs-performance ADR index row
AndrewSazonov Jun 2, 2026
fe1b677
Rewrap documentation paragraphs
AndrewSazonov Jun 2, 2026
9882c50
Document Wyckoff coords_xyz operator-form regression and fix
AndrewSazonov Jun 2, 2026
79345ce
Make axes and scene background opaque, paper transparent
AndrewSazonov Jun 2, 2026
c45b844
Display squared degrees as deg² instead of deg^2
AndrewSazonov Jun 2, 2026
eba0a58
Standardize tutorial section headings
AndrewSazonov Jun 2, 2026
7fdd962
Limit tutorial notebook TOC to two levels
AndrewSazonov Jun 2, 2026
041ab81
Theme modebar icons; clear background outside correlation cells
AndrewSazonov Jun 2, 2026
2683857
Inset Plotly plot legend from the top-right corner
AndrewSazonov Jun 2, 2026
c248711
Use #212121 for dark background inside the axes
AndrewSazonov Jun 2, 2026
00b48a9
Theme modebar icons via native Plotly colors, drop CSS hack
AndrewSazonov Jun 2, 2026
656f7d9
Match Plotly legend top inset to its right inset
AndrewSazonov Jun 2, 2026
4b147ef
Exclude vendored JS licenses from docs nav
AndrewSazonov Jun 2, 2026
4f65d30
Paint structure scene background via WebGL clear color
AndrewSazonov Jun 2, 2026
55a3638
Theme modebar icons via class CSS overriding Plotly inline fill
AndrewSazonov Jun 2, 2026
9407d66
Add themed code-cell border colors to docs extra.css
AndrewSazonov Jun 2, 2026
70fdbed
Convert shared figure-height fallback from units to pixels
AndrewSazonov Jun 2, 2026
c810e13
Sync Plotly tooltip border to axis-frame in docs loader
AndrewSazonov Jun 2, 2026
8f7ff74
Update report style test for new axis-frame color
AndrewSazonov Jun 2, 2026
5048f69
Fall back to baked theme for modebar when host theme hidden
AndrewSazonov Jun 2, 2026
1130227
Refine chart theme background and grid colors
AndrewSazonov Jun 2, 2026
1eb11d3
Blend shared-mode loading skeleton with page background
AndrewSazonov Jun 2, 2026
56b984a
Size posterior distribution plots to single main panel
AndrewSazonov Jun 2, 2026
51bfe05
Size posterior distribution plots to single main panel
AndrewSazonov Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ jobs:
# if: false # Temporarily disabled to speed up the docs build
run: pixi run notebook-exec-ci

# Sync the canonical Three.js snapshot into the docs assets so
# MkDocs can serve it (belt-and-braces; docs-build also depends on
# this task).
- name: Sync vendored JS into docs assets
run: pixi run docs-sync-vendored-js

# Build the static files for the documentation site for local inspection
# Input: docs/ directory containing the Markdown files
# Output: site/ directory containing the generated HTML files
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ build/
# MkDocs
docs/site/

# Generated docs-serving copy of the canonical Three.js (synced from
# src/ by `pixi run docs-sync-vendored-js`; the source of truth is src/)
docs/docs/assets/javascripts/vendor/threejs/

# Jupyter Notebooks
.ipynb_checkpoints

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ADR: Plotting & Docs Performance for Interactive Figures

**Status:** Proposed **Date:** 2026-06-02
**Status:** Accepted **Date:** 2026-06-02

## Group

Expand All @@ -9,8 +9,8 @@ Documentation.
> This ADR follows [`AGENTS.md`](../../../../AGENTS.md). It spans the
> documentation build (MkDocs) and the display serialization contract,
> so it also relates to the User-facing API ADRs
> [`display-ux.md`](../accepted/display-ux.md) and
> [`crysview-structure-visualization.md`](../accepted/crysview-structure-visualization.md).
> [`display-ux.md`](display-ux.md) and
> [`crysview-structure-visualization.md`](crysview-structure-visualization.md).
> No public Python API change is intended; the change is in how figure
> HTML and its JavaScript runtime are delivered.

Expand All @@ -29,8 +29,8 @@ appear progressively.

1. Tutorial sources are `docs/docs/tutorials/ed-*.py`; notebooks are
generated artifacts (per
[`notebook-generation.md`](../accepted/notebook-generation.md)) and
are committed with **outputs stripped** (`notebook-strip`).
[`notebook-generation.md`](notebook-generation.md)) and are committed
with **outputs stripped** (`notebook-strip`).
2. The docs CI
([`.github/workflows/docs.yml`](../../../../.github/workflows/docs.yml))
runs `notebook-exec-ci` to **execute** every notebook, baking the
Expand Down Expand Up @@ -76,11 +76,11 @@ appear progressively.
The same serialization paths feed three contexts with **conflicting**
runtime needs, which is the crux of any robust fix:

| Target | Who | Runtime requirement |
| --------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Live notebook** | `_show_figure` in Jupyter | Runtime must be reachable from the running kernel/browser (today: Plotly via CDN; Three.js inlined). |
| **MkDocs site** | executed-notebook HTML embedded by `mkdocs-jupyter` | Wants the runtime loaded **once per page** and figures rendered **lazily**. |
| **Standalone report** | `report/html_renderer.py` → `PlotlyPlotter.serialize_html` / Three.js `render(offline=...)` | Delivery set by the existing `offline` flag — embedded/self-contained when `offline=True`, CDN when `offline=False` (default). Authoritative per [`project-summary-rendering.md`](../accepted/project-summary-rendering.md). |
| Target | Who | Runtime requirement |
| --------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Live notebook** | `_show_figure` in Jupyter | Runtime must be reachable from the running kernel/browser (today: Plotly via CDN; Three.js inlined). |
| **MkDocs site** | executed-notebook HTML embedded by `mkdocs-jupyter` | Wants the runtime loaded **once per page** and figures rendered **lazily**. |
| **Standalone report** | `report/html_renderer.py` → `PlotlyPlotter.serialize_html` / Three.js `render(offline=...)` | Delivery set by the existing `offline` flag — embedded/self-contained when `offline=True`, CDN when `offline=False` (default). Authoritative per [`project-summary-rendering.md`](project-summary-rendering.md). |

A useful precedent already lives in the report renderer
([`src/easydiffraction/report/html_renderer.py`](../../../../src/easydiffraction/report/html_renderer.py)):
Expand Down Expand Up @@ -157,7 +157,7 @@ delivered together** in one change. Concretely:
`include_requirejs` if verification confirms it is no longer needed.

2. **Introduce a figure _embedding mode_** (a `(str, Enum)` per
[`enum-backed-closed-values.md`](../accepted/enum-backed-closed-values.md))
[`enum-backed-closed-values.md`](enum-backed-closed-values.md))
threaded through `serialize_html` and the Three.js `render`:
- `INLINE` — live Jupyter: render eagerly with the runtime reachable
as today. **Default.**
Expand Down Expand Up @@ -196,7 +196,7 @@ delivered together** in one change. Concretely:

4. **Reports keep their existing `offline` contract, authoritative and
unchanged.** Per
[`project-summary-rendering.md`](../accepted/project-summary-rendering.md),
[`project-summary-rendering.md`](project-summary-rendering.md),
`render_html_report(offline=...)` already decides runtime delivery:
`offline=True` embeds a self-contained runtime; `offline=False` (the
default) links the CDN, embedding Plotly in the first figure and
Expand Down Expand Up @@ -261,6 +261,31 @@ delivered together** in one change. Concretely:
scene consumes it (the tiny JSON is inert), keeping the override
simple.

7. **SHARED figures downcast bulk float64 arrays to float32 (a bounded,
display-only precision decision).** In `SHARED` mode only, the
serializer transcodes the figure spec's float64 typed arrays to
float32 (~7 significant figures) before embedding, roughly halving
the payload (measured: ed-6 5.5 MB → 3.4 MB; 2.2 → 1.4 MB gzipped).
This is an explicit **display** decision, not a change to stored
data, and it is bounded:
- It operates on a **copy** of the serialized figure
(`fig.to_plotly_json()`), never the source parameters, CIF, or any
computation.
- It applies **only** to docs `SHARED` figures. Live notebooks
(`INLINE`), reports (`STANDALONE`), and every CIF/state file keep
full float64.
- It is **visually lossless**: screens resolve ~3 significant
figures, and the tutorials' hover templates format to a few
decimals (e.g. `:,.2f`), so float32 changes no displayed or
hover-visible value at the precision actually shown.

Storage-side numeric precision is a separate, deliberate decision,
proposed in a `cif-numeric-precision` ADR suggestion (out of scope
for this change, not committed on this branch). Phase 2 adds coverage
for the `f8`→`f4` transcode (shape preserved, round-trips through
Plotly) and a representative hover/range-sensitive figure whose
formatted values are unchanged.

This pays the network bill once per page from the same origin, removes
the per-figure JS duplication, and turns first paint from "render every
figure" into "render nothing until seen" — addressing both bottlenecks
Expand Down Expand Up @@ -366,7 +391,7 @@ inject one shared runtime, and add the lazy loader globally.
the resolver with a unit test asserting both the default and the
docs-build override.
- **Report `offline` contract.** Keep
[`project-summary-rendering.md`](../accepted/project-summary-rendering.md)
[`project-summary-rendering.md`](project-summary-rendering.md)
authoritative (Decision 4); the existing `offline=True` /
`offline=False` report tests must stay green and gain no `SHARED`
behavior.
Expand Down Expand Up @@ -414,17 +439,24 @@ Settled in discussion on 2026-06-02:

- Static-image-first placeholders (kaleido) if skeletons prove
insufficient on the heaviest pages.
- Lazy on-scroll download of the Three.js runtime. The docs `SHARED`
three.js view self-hosts (page-level import map) and shows a loading
skeleton but renders **eagerly** — P1.8 was scoped to this low-risk
path. Deferring the runtime download behind an `IntersectionObserver`
(dynamic `import('three')`) is a follow-up; Plotly already loads
lazily and structure views are rare, so the win is small relative to
the module-rewrite risk.
- Trace downsampling for very dense series in the docs view (smaller
payload + faster draw) — a separate, data-side optimization.
- A docs CI budget check (page weight / figure count) to catch
regressions, aligning with
[`documentation-ci-build.md`](suggestions/documentation-ci-build.md).
[`documentation-ci-build.md`](../suggestions/documentation-ci-build.md).
- Hoist a single importmap into the **report** template `<head>` for
standalone reports that render multiple Three.js scenes (the same
per-scene-importmap bug as docs, but governed by
[`project-summary-rendering.md`](../accepted/project-summary-rendering.md)).
Out of scope here since it touches the report contract; flagged so it
is not lost.
[`project-summary-rendering.md`](project-summary-rendering.md)). Out
of scope here since it touches the report contract; flagged so it is
not lost.

## Alternatives considered

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# ADR: Complete Space-Group Reference Database

**Status:** Proposed **Date:** 2026-06-01
**Status:** Accepted **Date:** 2026-06-01

## Group

Structure model.

> This ADR follows [`AGENTS.md`](../../../../AGENTS.md). It is a
> This ADR follows [`AGENTS.md`](../../../../AGENTS.md). It was a
> prerequisite for
> [`wyckoff-letter-detection.md`](wyckoff-letter-detection.md): Wyckoff
> detection can only resolve letters for space groups present in the
> bundled table, and that table is currently incomplete.
> [`wyckoff-letter-detection.md`](../suggestions/wyckoff-letter-detection.md):
> Wyckoff detection can only resolve letters for space groups present in
> the bundled table, which this ADR's implementation completed for all
> 230 groups.

## Context

Expand Down Expand Up @@ -102,7 +103,8 @@ Coordinates and operators stay **strings** (e.g. `'(x,1/2,0)'`,
`sympify`) in `crystallography.py` and to keep the file JSON-native
(§2). Triclinic no-setting groups keep the `None` coordinate code, as
today (see the `''`→`None` normalisation in
[`wyckoff-letter-detection.md`](wyckoff-letter-detection.md) §2).
[`wyckoff-letter-detection.md`](../suggestions/wyckoff-letter-detection.md)
§2).

**Query surface preserved.** On disk the JSON is a list of setting
records, each carrying the canonical `IT_number` and
Expand Down Expand Up @@ -225,7 +227,7 @@ containing:
The maintainer inspects the report and **selects** the authoritative
value per case. Selections are recorded in a checked-in **YAML overrides
file**,
`docs/dev/adrs/suggestions/space-group-database/space_groups_overrides.yaml`
`docs/dev/adrs/accepted/space-group-database/space_groups_overrides.yaml`
while the ADR is proposed. If this ADR is accepted, move that companion
file with the ADR to the accepted ADR area. YAML lets each selection
carry an inline comment recording its rationale. The generator consumes
Expand Down Expand Up @@ -284,9 +286,10 @@ coordinate-system code": EasyDiffraction's `SpaceGroup` category uses
the empty string `''`, while the table key uses `None`. The database
keeps `(1, None)` and `(2, None)`; callers normalise `''` to `None` at
lookup boundaries, as specified in
[`wyckoff-letter-detection.md`](wyckoff-letter-detection.md). This is
the least surprising solution because it keeps "no setting" distinct
from any real coordinate-code string without inventing a sentinel value.
[`wyckoff-letter-detection.md`](../suggestions/wyckoff-letter-detection.md).
This is the least surprising solution because it keeps "no setting"
distinct from any real coordinate-code string without inventing a
sentinel value.

### 8. The database file is generated, not hand-edited

Expand Down Expand Up @@ -343,9 +346,9 @@ and the documented decisions in sync.
early when `coord_code is None` and `_get_general_position_ops()`
indexes the raw key, so they need the `''`→`None` normalisation
defined in
[`wyckoff-letter-detection.md`](wyckoff-letter-detection.md) §2 (which
also updates these call sites). This ADR delivers the data; that ADR
delivers the `None`-code consumer handling.
[`wyckoff-letter-detection.md`](../suggestions/wyckoff-letter-detection.md)
§2 (which also updates these call sites). This ADR delivers the data;
that ADR delivers the `None`-code consumer handling.

## Alternatives Considered

Expand Down Expand Up @@ -424,7 +427,7 @@ Generated and curation artifacts:
`30f0051c669712ab34d991e60223c5e29264fc033b2ab03392cc01465ceba926`
- `tmp/space-groups/helper-tools/generate_space_groups.py`:
`bf10dcfbcf9e60485037ddabc65425e61f746ad9649cd3ccc67376dd6aae241a`
- `docs/dev/adrs/suggestions/space-group-database/space_groups_overrides.yaml`:
- `docs/dev/adrs/accepted/space-group-database/space_groups_overrides.yaml`:
`7077eec25d0f3b852dd7096a24dc7ac438467f9cb594f91a65ce10cda0e0722a`
- `tmp/space-groups/extracted-comparison/disagreements.md`:
`dda940fbf75862516411685c9b9bdf7170fa4a116f90eeeff93bd068b8acda4c`
Expand Down Expand Up @@ -508,8 +511,8 @@ respectively.

## Related ADRs

- [`wyckoff-letter-detection.md`](wyckoff-letter-detection.md) — the
dependent feature; its `''`→`None` coordinate-code normalisation and
its "unsupported group" handling both build on this database.
- [`wyckoff-letter-detection.md`](../suggestions/wyckoff-letter-detection.md)
— the dependent feature; its `''`→`None` coordinate-code normalisation
and its "unsupported group" handling both build on this database.
- [`iucr-cif-tag-alignment.md`](../accepted/iucr-cif-tag-alignment.md) —
consumes space-group and Wyckoff data on export.
Loading
Loading