openpencil/crates/openpencil-shell-core
Kayshen-X c299b3990c fix(shell-core): Step 3 stop-hook — clip-isolate canvas viewport paint
Codex stop-hook flagged: "canvas viewport is not paint-isolated".
Root cause: `CanvasViewport::paint` walked the document tree and
issued draw calls translated by `viewport_origin = rect.origin`,
but never clipped to the widget's `rect`. A document node whose
bounds extend past the canvas-widget rect (e.g. a frame at
document (40, 40)–(960, 640) painted into a 300-px-wide canvas
band) would spill onto the LayerPanel / PropertyPanel area
sitting in adjacent rects.

Fix: wrap the entire viewport paint in
save → clip_rect(rect) → bg fill → recursive node paints →
restore. The host-level clip stack catches whatever the
recursive paint emits, and the recursive code stays unaware of
the bounds (no per-node clipping needed).

Defensive guard up front: `if rect.size.x <= 0.0 || rect.size.y
<= 0.0 { return; }` so a zero-size canvas (host clamped below
MIN_RAIL_WIDTH) doesn't even open the save scope.

Test additions (41 lib tests, was 39):
- `paint_is_clip_isolated_save_clip_then_restore` extends the
  RecordingBackend to capture op order via a new `Op` enum;
  asserts the first three ops are Save → Clip → Fill (canvas
  bg), the last op is Restore, and save/restore counts balance.
- `paint_with_zero_size_rect_skips_entirely` confirms the
  defensive zero-size early return — backend.ops stays empty
  when rect has zero width or height.

Verification:
- `cargo test -p openpencil-shell-core --lib` — 41 tests passing
- `cargo build -p openpencil-shell-native --example
  inspector_window` — green
- `cargo build -p openpencil-shell-web --target
  wasm32-unknown-unknown --features skia --release` — green
- `bash tools/check-wasm-bundle.sh` — PASS, 0 env.*, 907 141
  bytes gzip = 86% of 1 MiB ceiling
2026-05-10 13:29:45 +08:00
..
src fix(shell-core): Step 3 stop-hook — clip-isolate canvas viewport paint 2026-05-10 13:29:45 +08:00
tests fix(shell-core): Phase C stop-hook #2 — skip dropdown keys during IME 2026-05-09 21:54:00 +08:00
Cargo.toml feat(shell-core): Phase B1 — Widget trait + recording test harness 2026-05-09 21:45:00 +08:00