Replaces the Phase A red-rect demo with the Step 1b inspector
composition: WebShell now owns a `WidgetHost` and `mount()` paints
the four shell-core widgets (Tree / PropertyRow / Dropdown /
TextInput) into a 280-px column on a white-cleared canvas.
What's added:
- `crates/openpencil-shell-web/src/widget_host.rs` — the only file
in shell-web that calls into `openpencil_shell_core::widgets::*`,
per spec §1.4 boundary. Module doc anchors the invariant; the
paint signature carries the `// glue:` marker that the Phase B4
boundary check script (`tools/check-widget-boundary.sh`) will
grep for.
- `WidgetHost { tree, width, dropdown, text_input }` owns one of
each kind; `WidgetHost::new()` populates them from the B2 sample
/ new constructors. `Default` forwards to `new()`.
- `WidgetHost::paint(&self, backend, available_width)` builds a
`LayoutCx`, iterates a `[&dyn Widget; 4]` array, places each at
x=16 with 12-px vertical gaps. Reborrows backend each iteration
(`&mut *backend`) so subsequent iterations don't fail
borrow-check on the moved `&mut WebBackend`.
shell-web/src/lib.rs:
- Adds `mod widget_host;` cfg-gated to the `skia` feature.
- WebShell gains `host: WidgetHost`.
- Renames `paint_phase_a` → `paint_inspector`. Body clears the
canvas to white, dispatches via `self.host.paint(...)`, then
surfaces `take_present_error()` as JsValue exception. Same
panic-safe + canvas-type-check + present-error-propagation
pattern as Phase A C-hard.2 (codex Phase A gate review approved).
- The stub mount entry (no skia feature) is unchanged — the
kickoff §1.2 wasm32-clean compile guard CI still uses it.
Plan-vs-implementation deviations (deliberate):
- Plan B3 step 2 simplifies `mount()` in a way that drops the
panic hook + canvas-type-check + present-error propagation.
Preserved all three because the codex Phase A gate review
explicitly approved them as "panic-safe mount". Plan body's
`mount` block is treated as historical sketch.
- Plan body's `let mut cx = PaintCx { backend };` would move the
reference and fail borrow-check on the second iteration.
Changed to explicit reborrow `&mut *backend`. This is the
plan's intent, just with the borrow-checker subtlety made
explicit.
Verification:
- `cargo build -p openpencil-shell-web --target
wasm32-unknown-unknown --features skia --release` — green
- `cargo check -p openpencil-shell-web --target
wasm32-unknown-unknown --no-default-features --features web` —
green (compile guard)
- `bash tools/check-wasm-bundle.sh` — PASS
- 0 env.* imports (bundle still LinkError-free)
- 615 764 bytes gzip = 58% of 1 MiB ceiling
- +2 KiB vs Phase A C-hard.2 (~613 KiB) — widget code is small
- `cargo check -p openpencil-shell-native` — green (no regression)
Codex iterate review: 1 round → GO with 2 NITs (script name
singular vs plural, stale "Phase A red-rect" phrase) — both
fixed in this commit.