Codex stop-gate caught a real correctness gap: the previous commit
(3c2e7711) wired the ColorPicker's variable-mode commit through
`set_color_hex` and pushed `pre_snap` onto undo when the colour
changed. But `DocumentSnapshot` only covered `pages /
active_page_index / selected / selected_set` — `var_table` wasn't
in the snapshot, so `undo()` would restore pages + selection
without touching the variable. Variable edits were effectively
unrecoverable.
`crates/openpencil-shell-core/src/document.rs`:
- `DocumentSnapshot` grew `var_table: VariableTable`. Captures
every Variable definition + themed entries + active_theme +
fill_refs / stroke_refs at snapshot time, so undo can restore
the complete colour-token graph.
`crates/openpencil-shell-core/src/document/mutators.rs`:
- `snapshot()` clones `var_table` into the captured snapshot.
- `restore()` writes the snapshot's var_table back to the
document. The single literal site for DocumentSnapshot stays
consistent across all callers (`snapshot_for_history()`,
`commit_history()`, `undo()`, `redo()` — all flow through
`snapshot()` + `restore()`).
`crates/openpencil-shell-core/src/document/color_picker.rs`:
- Dropped the now-redundant `variable_pre_color: Option<Color>`
parallel field from `ColorPickerState`. It was a workaround
for snapshot not carrying var_table; now `close_color_picker`
reads `snap.var_table.resolve_color(name)` directly to detect
the pre-edit colour. Cleaner single source of truth.
- The change-detection in `close_color_picker`'s variable branch
now compares `snap.var_table.resolve_color(name)` against the
live resolution. Same result as before, fewer parallel fields.
Tests (1 added — the regression repro, 295 shell-core total):
- `undo_after_variable_edit_restores_pre_edit_color` builds the
exact codex BLOCK case: variable starts at `#ff8800`, picker
opens, HSV drags to pure red, picker closes (history push).
`doc.undo()` runs; the variable must resolve back to the
original orange. Pre-fix the snapshot's pages + selection
would restore but the variable would still read red — the
undo entry was effectively broken.