openpencil/crates/openpencil-shell-core/src/widgets
Kayshen-X af66f3d849 fix(shell-core): Phase C stop-hook #2 — skip dropdown keys during IME
Codex stop-hook #2: with the hidden IME textarea now focused (R1
fix in fe994c4b), every keystroke routes through it — including
arrow keys the user is pressing to navigate the IME's candidate
picker. The window-level keydown listener still fires on these
keystrokes, and `DropdownState::apply_key` was mutating selection
+ opening the menu behind the IME panel. Surfaced as: "focused
IME textarea lets composing keys mutate dropdown state."

Spec §2.4 says: "Widgets that consume keys directly should
usually skip dispatch when is_composing == true and let the
ImeEvent path handle the composition instead." The plumbing for
`is_composing` already runs through Phase C2.2 (W3C
`KeyboardEvent.isComposing` → C1 `map_keyboard_parts(...,
is_composing)` → `KeyEvent.is_composing` → C2.1
`DropdownState::apply_key`); we just weren't honoring the bit on
the consuming side.

Fix: add `event.is_composing` to the early-return condition in
`DropdownState::apply_key`. ArrowDown/Up/Enter/Escape during a
composition no-op now; the IME's candidate picker keeps the
keystroke and the dropdown stays put.

`TextInputState::apply_ime` is unaffected — it already only
processes ImeEvent, never KeyEvent, so composing-key bleed-
through was never a concern there.

Test: `dropdown_apply_key_ignores_keys_during_ime_composition`
asserts both ArrowDown (would advance + open) and Enter (would
close) are no-ops when `is_composing` is true. Test count
20 → 21.

Verification:
- `cargo test -p openpencil-shell-core --test widgets_static` —
  21/21 passing
- `cargo check -p openpencil-shell-core --target
  wasm32-unknown-unknown` — green
- `cargo build -p openpencil-shell-web --target
  wasm32-unknown-unknown --features skia --release` — green
- `bash tools/check-wasm-bundle.sh` — PASS:
  - 0 env.* imports
  - 622 156 bytes gzip = 59% of 1 MiB ceiling

Phase D may extend this guard pattern to other widgets that gain
key handling (Tree typeahead, etc.); the spec §2.4 is_composing
contract becomes a per-widget invariant.
2026-05-09 21:54:00 +08:00
..
dropdown.rs fix(shell-core): Phase C stop-hook #2 — skip dropdown keys during IME 2026-05-09 21:54:00 +08:00
mod.rs feat(shell-core): Phase B2 — four static inspector widgets 2026-05-09 21:46:00 +08:00
prop_row.rs feat(shell-core): Phase B2 — four static inspector widgets 2026-05-09 21:46:00 +08:00
text_input.rs feat(shell-core): Phase C2.1 — widget event handlers + WidgetHost forwarding 2026-05-09 21:50:00 +08:00
tree.rs feat(shell-core): Phase B2 — four static inspector widgets 2026-05-09 21:46:00 +08:00