From 7e1fbcabf09ee00184005d13ce0909cf1b9eb33b Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Sun, 10 May 2026 13:14:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(shell-web):=20Step=203=20stop-hook=20?= =?UTF-8?q?=E2=80=94=20read=20canvas=20size=20from=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex Step 3 stop-hook flagged: "web repaint ignores actual canvas size". The prior fix `9cf0f865` hardcoded `960.0` to match the smoke HTML's ``, but that's brittle — any host that mounts onto a differently-sized canvas (responsive HTML, programmatic mount, future smoke fixture changes) gets the wrong layout viewport. Fix: WebBackend gains `canvas_width(&self) -> u32` + `canvas_height(&self) -> u32` accessors. The `width` / `height` fields are already refreshed at construction (`canvas.width()`) and on `RenderBackend::resize`, so reading them per-paint reflects whatever the host's `` width attribute currently is. `paint_inspector` now: - Reads `viewport_w` + `viewport_h` from the backend at the start of each frame. - Uses them for the white-background clear AND for the WidgetHost::paint viewport_width arg. This also resolves the prior "web smoke paints only the toolbar" issue since the smoke canvas is 960×640 — the first frame still receives 960 as viewport_width, but now via the backend instead of a hardcode. Verification: - `cargo build -p openpencil-shell-web --target wasm32-unknown-unknown --features skia --release` — green - `wasm-bindgen --target web` — produces ../pkg/* - `bash tools/check-wasm-bundle.sh` — PASS, 0 env.*, 907 031 bytes gzip = 86% of 1 MiB ceiling (negligible delta — two small accessor methods). --- .../openpencil-shell-web/src/backend/mod.rs | 15 +++++++++++ crates/openpencil-shell-web/src/lib.rs | 27 ++++++++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/crates/openpencil-shell-web/src/backend/mod.rs b/crates/openpencil-shell-web/src/backend/mod.rs index f3f3b0bbb..b22f526eb 100644 --- a/crates/openpencil-shell-web/src/backend/mod.rs +++ b/crates/openpencil-shell-web/src/backend/mod.rs @@ -85,6 +85,21 @@ impl WebBackend { self.last_present_error.take() } + /// Current physical width of the host `` element, refreshed + /// at construction + on every `resize` call. Hosts use this so a + /// repaint after the canvas DOM attribute changes pulls the new + /// width through the WidgetHost layout (codex Step 3 stop-hook + /// "web repaint ignores actual canvas size"). + pub fn canvas_width(&self) -> u32 { + self.width + } + + /// Current physical height of the host `` element. Same + /// refresh contract as `canvas_width`. + pub fn canvas_height(&self) -> u32 { + self.height + } + /// Snapshot the raster surface and `put_image_data` it onto the host /// ``'s 2D context. Spec §5.3 raster-fallback contract: /// N32_PREMUL surface, RGBA8888 + Unpremul read, full-frame copy each diff --git a/crates/openpencil-shell-web/src/lib.rs b/crates/openpencil-shell-web/src/lib.rs index 76abc24bf..889773c9b 100644 --- a/crates/openpencil-shell-web/src/lib.rs +++ b/crates/openpencil-shell-web/src/lib.rs @@ -109,26 +109,29 @@ impl Inner { fn repaint(&mut self) -> Result<(), JsValue> { use openpencil_shell_core::{Color, Point2D, Rect, RenderBackend}; + // Pull the actual canvas dimensions from the backend + // every frame so a host that swaps the `` width + // attribute (responsive layouts, devtool resizing, + // future programmatic mount with a different size) + // gets a layout that matches reality. Codex Step 3 + // stop-hook "web repaint ignores actual canvas size" + // caught the prior hardcoded `960.0` — this swap also + // resolves the "web smoke paints only the toolbar" + // regression since the smoke canvas is 960×640 and the + // first frame still gets the full width. + let viewport_w = self.backend.canvas_width() as f32; + let viewport_h = self.backend.canvas_height() as f32; + self.backend.begin_frame(); // Clear to white so widget paints sit on a clean background. self.backend.fill_rect( Rect { origin: Point2D::new(0.0, 0.0), - size: Point2D::new(960.0, 640.0), + size: Point2D::new(viewport_w, viewport_h), }, Color::WHITE, ); - // Step 3: pass the FULL canvas width (matches the smoke - // HTML's ``). The host's - // `paint(&self, backend, viewport_width)` lays out - // Toolbar-top + LayerPanel-left + CanvasViewport-center - // + PropertyPanel-right; viewport_width below the - // MIN_RAIL_WIDTH cutoff would silently skip everything - // except the toolbar — codex stop-hook - // "web smoke paints only the toolbar" caught this when - // the leftover Step 1b 280-px hardcode was still in - // place. - self.host.paint(&mut self.backend, 960.0); + self.host.paint(&mut self.backend, viewport_w); self.backend.end_frame(); if let Some(err) = self.backend.take_present_error() { return Err(err);