openpencil/crates
Kayshen-X d951942613 fix(variables): themed writes never clobber the default entry
Second codex stop-gate on `set_color_hex` themed-write routing.
The previous fix (042a62c8) made the write follow resolve's subset
match, but its fallback path still mutated the `theme: None`
default entry when no themed subset matched. That's wrong under a
non-empty active theme: the default is the resolve fallback for
EVERY axis the user hasn't explicitly authored, so mutating it
during a dark-mode edit silently rewrites light + sepia + every
unmapped axis too.

New write rules:

  Subset match:                   → write through that entry.
  No subset match + active EMPTY: → write through default
                                    (creating it if absent).
  No subset match + active SET:   → push a new entry keyed to
                                    active_theme at index 0, so
                                    resolve's first-match walk
                                    picks the new entry under the
                                    active axes. The default and
                                    other-axis entries stay
                                    untouched.

Inserting at the front (not pushing at the end) matters because
resolve uses subset matching: a pre-existing entry whose theme is
a superset of the new one's axes would otherwise win the resolve
walk for active = (its theme + extra axes). Front-insert pins the
new entry's precedence under the active theme it was created
for.

Tests rewired (2 swapped — 288 → 289 shell-core total):

  - `set_color_hex_under_active_theme_does_not_clobber_default`
    replaces the previous "writes the default" test. Setup:
    light entry + None default + active=dark; write under dark
    must scope to dark only. Asserts three theme states:
      active=dark   → reads NEW value
      active=sepia  → reads ORIGINAL default (untouched)
      active=light  → reads ORIGINAL light entry (untouched)
    Pre-fix this test would have failed on the sepia assertion
    because the default would carry the new dark value.

  - `set_color_hex_empty_active_theme_targets_default_entry`
    confirms the inverse: with active_theme empty, the write
    still routes through the default (resolve's fallback in that
    state). This is the explicit "edit the universal value" path.

Existing themed-axis test passes unchanged because that case
already had a subset match and never reached the fallback path.
2026-05-14 18:39:29 +08:00
..
anthropic-agent-sdk chore(crates): move vendored SDKs into workspace as forkable crates 2026-05-14 16:49:17 +08:00
copilot-sdk chore(crates): move vendored SDKs into workspace as forkable crates 2026-05-14 16:49:17 +08:00
openpencil-app feat(openpencil-app): skeleton crate (Stage F entry placeholder) 2026-05-03 22:05:00 +08:00
openpencil-desktop refactor(desktop): extract rotate-cursor builder into cursor_icon.rs 2026-05-14 17:38:16 +08:00
openpencil-shell-core fix(variables): themed writes never clobber the default entry 2026-05-14 18:39:29 +08:00
openpencil-shell-native fix(geometry): canvas_region shrinks for VariablesPanel too (codex BLOCK) 2026-05-14 17:57:55 +08:00
openpencil-shell-web feat(shell): export dialog + multi-format raster (PNG/JPEG/WEBP) + Property-panel preview 2026-05-14 14:02:52 +08:00
pen-codegen chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-core chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-engine chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-figma chore(workspace): cargo-deny 0.18 activation (Phase 1 Task 1.8 Step 6) 2026-05-03 23:05:00 +08:00
pen-types feat(pen-types): skeleton crate (bucket A) 2026-05-03 22:35:00 +08:00
wasm-libc-shim style(shell): cargo fmt --all (rustfmt-clean) 2026-05-10 18:47:33 +08:00
CLAUDE.md feat(shell): canonical .op loader + jian-core layout + visual fidelity pass 2026-05-14 09:26:08 +08:00