Closes the missing wire between the new `VariableTable::set_color_hex`
write path (round 3 fix in febe7a76) and the existing ColorPicker
floating UI. The host can now route a VariablesPanel row click into
the picker keyed to a variable; HSV drag flows back through the
variable's storage, paint reflects the change on the next frame.
`crates/openpencil-shell-core/src/document.rs`:
- `ColorPickerState` grew two parallel fields:
`variable: Option<String>` — name of the Color variable
being edited; `None` ⇒ node-target
mode (existing Fill/Stroke path).
`variable_pre_color: Option<Color>` — resolved colour at open
time, used by `close_color_picker`
to detect "did anything change?"
without requiring DocumentSnapshot
to carry var_table.
- `ColorTarget` stays Copy + Fill/Stroke-only. Adding a
`Variable(String)` variant would have forced touching every
pattern-match site; the parallel field keeps the existing 18+
`ColorTarget` callers untouched.
`crates/openpencil-shell-core/src/document/color_picker.rs`:
- `Document::open_color_picker_for_variable(name, anchor_y)`:
new entry point. Seeds HSV from `var_table.resolve_color(name)`,
captures the resolved colour into `variable_pre_color`. Returns
`false` when the name is unknown or the variable isn't
Color-kind — so a host-side dispatcher can `if !doc.open_...
return;` without inspecting state.
- `Document::color_picker_set_hsv` branches on `variable.is_some()`:
routes through `format_color_hex_for_var(rgb)` →
`var_table.set_color_hex(name, hex)`. The set_color_hex
correctness saga (3 commits, no shadowing, no cross-axis
clobber) carries this commit transparently.
- `Document::close_color_picker` for the variable path compares
`state.variable_pre_color` vs `var_table.resolve_color(name)` to
decide whether to push the pre-edit snapshot onto undo.
Single-bool changed-check matches the node-target branch.
- New `format_color_hex_for_var(rgba) -> String` helper emits
`#rrggbb` for the var-write commit path.
Tests (4 added, 294 shell-core total):
- `open_color_picker_for_variable_seeds_hsv_from_resolved_color`
— picker opens against `#ff8800`; HSV anchor reads as orange-hue
(20°–45° range), near-max sat + val.
- `open_color_picker_for_variable_fails_when_var_missing_or_wrong_kind`
— unknown name returns false; Number-kind variable returns false;
picker state stays None in both cases.
- `color_picker_set_hsv_writes_through_variable_path` — end-to-end:
open on `#ff8800`, push HSV (0°, 1, 1) = pure red, assert
`resolve_color` returns red.
- `close_color_picker_after_variable_edit_pushes_history_only_when_changed`
— open + close with no HSV change → no history push. Open +
drag + close → history depth grows by 1.
The full variable edit UX now has a working host integration point:
when VariablesPanel emits `VariablesPanelHit::Row(idx)` for a color
variable, the host calls `open_color_picker_for_variable(name)` and
the existing picker chrome takes over.