Expand HTML/CSS import fidelity, preserve layered fills and accurate fonts across HTML, Figma, and OP files, and make missing-font resolution selectable and case-insensitive. Harden code generation recovery and preview rendering, make model selection immediate, and keep image decode/compositing consistent across native and web hosts. Add mobile dependency and widget-boundary guards so all supported targets retain the intended feature surface.
The CLI standard-mode intent router moves to op_web_daemon::chat_intent; its 23
headless tests (which reach chat_intent's #[cfg(test)] internals) move with it as the
#[path] sibling. The 1 host-coupled test (cli_new_design_clears_agent_frame_indicators_after_done
— drives the GUI design-session pumps via WidgetHostNative) is extracted to
op-host-desktop/src/chat_intent_host_tests.rs against the pub run_cli_turn/CliTurnPlan API +
crate::design_session pumps. chat_session/chat_session_launch/web_chat_standard refs repointed.
op-web-daemon 276 + host test 1 green; no dep/lock change.
Codex review of Task 1.2 found four dangling references to the deleted
stub crates (pen-types/core/engine/codegen/figma + openpencil-app):
- package.json: drop the five -p <crate> args from cargo:wasm-check
- rust-release.yml: restore the build job, re-pointed at the real
openpencil-desktop crate so release-draft has artifacts to publish
- check-jian-boundaries.sh: 4 -> 3 invariants in the success message
- README.md: remove the deleted crate rows from the crate-list table
Per 2026-05-10 user directive ("extend, jian 最后也会需要 ios 和
android"): lift the desktop-only cfg gate so the widget render
stack (skia-safe + jian-skia + NativeBackend + widget_host)
compiles for iOS (`aarch64-apple-ios`) AND Android
(`aarch64-linux-android`) cargo check too. Mobile shells now have
a real widget-rendering surface to target in Step 1f, and the
"shell-core widgets are platform-agnostic" claim from spec §1.4
is now compile-verified across desktop trio + mobile pair + wasm.
Cargo.toml restructure (`crates/openpencil-shell-native/Cargo.toml`):
- New `[target.'cfg(any(macos, linux, windows, ios, android))']`
block for the cross-platform widget stack: `skia-safe = "0.97"`
(default-features = false; binary-cache + textlayout) and
`jian-skia` (textlayout). Both pull on every desktop trio +
mobile pair target.
- Existing desktop-only block kept for the GUI host stack: adds
`gl` to skia-safe's features (iOS deprecated GL — Metal goes
in Step 1f; Android GL/Vulkan via the platform provider not
via skia-safe's bundled bindings here), plus glutin / glutin-
winit / winit / scopeguard / jian-host-desktop. Cargo
deduplicates: skia-safe resolves to one crate-version with
feature-union (binary-cache + textlayout from the wider block
+ gl from the desktop block on desktop-only).
src/lib.rs gate lift:
- `pub mod backend;` and `pub mod widget_host;` cfg now includes
`target_os = "ios"` and `target_os = "android"`. `pub use`
re-exports follow.
- `canvas_view_stub` stays desktop-only (uses glow GL-isolation
probe with no mobile equivalent).
- Comment block at the cfg site cites the user directive +
Step 1f handoff (real EaglProvider / AndroidEglProvider impls
+ Metal / Vulkan / event integration).
Boundary script revision (`tools/check-jian-boundaries.sh`):
- Invariant 2 was: mobile targets must NOT pull jian-host-desktop
OR jian-skia. Per the user directive, jian-skia is now ALLOWED
on mobile (the widget render stack uses it). jian-host-desktop
remains forbidden — it carries winit / glutin / desktop
GLContextProvider impls that have no mobile equivalent.
- Header comment block + active grep narrowed accordingly. The
Step 1f path through EaglProvider / AndroidEglProvider is the
spec-blessed mobile host plugin point (no IPC / CLI needed).
Verification:
- `cargo check -p openpencil-shell-native --target
aarch64-apple-ios` — green (skia-bindings + jian-skia +
shell-native all compile)
- `cargo check -p openpencil-shell-native --target
aarch64-linux-android` — green (same)
- `cargo check -p openpencil-shell-native` — green (no desktop
regression)
- `cargo check -p openpencil-shell-core --target
wasm32-unknown-unknown` — green (shell-core stays wasm32-clean)
- `cargo test -p openpencil-shell-core --test widgets_static` —
21/21 passing (widget logic untouched)
- `bash tools/check-wasm-bundle.sh` — PASS:
- 0 env.* imports
- 622 156 bytes gzip = 59% of 1 MiB ceiling (no web regression)
- `bash tools/check-widget-boundary.sh` — PASS
- `bash tools/check-jian-boundaries.sh` — 4/4 invariants pass
(Invariant 2 revised to allow jian-skia on mobile)
What's still mobile-pending (Step 1f scope):
- `EaglProvider` (iOS) — Metal-backed `GlContextProvider` impl
(skia-safe `metal` feature when iOS host actually runs)
- `AndroidEglProvider` (Android) — GL/Vulkan-backed impl
- Mobile host runners (UIKit AppDelegate / Activity wrappers)
- Mobile event translation (jian-host-ios / jian-host-android —
siblings of jian-host-desktop)
- `inspector_window` example is desktop-only by design (winit +
SharedSkiaContext::new_desktop); mobile shells will land their
own UIKit / Activity runners that consume the SAME
`WidgetHostNative::paint(&mut frame, width)` surface
The widget glue itself (NativeFrameBackend + WidgetHostNative)
is platform-agnostic in shape — no winit / glutin / EGL types
leak in. Step 1f mobile work plugs in providers, not widgets.
Drives the three-OS CI matrix verification of the skia-safe + glutin +
glow + winit dep stack per Step 1a spec §7.
- examples/p0_probe.rs: stencil_visibility + readback chain runner (must
own a real OS main thread because winit on macOS rejects
EventLoop::new() from cargo test worker threads).
- tests/p0_probe.rs: subprocess-invoke wrapper, gated
#[ignore = "P0_PROBE_GATE"] so default cargo test stays untouched.
- Cargo.toml: add transient [target.'cfg(not(target_arch = "wasm32"))'.
dev-dependencies] block (skia-safe 0.97 + glutin 0.32.3 + glutin-winit
0.5.0 + glow 0.17.0 + raw-window-handle 0.6.2 + scopeguard 1.2.0 +
winit defaults). Pinned to versions resolved in /tmp/skia-glow-probe.
- .github/workflows/rust-check.yml: install Linux GL prereqs (xvfb,
mesa, libxkbcommon, libwayland) and add a P0-probe-gate step running
cargo test --ignored on each OS (Linux through xvfb-run; Windows
early-returns per spec §8.2 WINDOWS_GPU_DEFERRED_NO_RUNNER).
All three artefacts are TRANSIENT — reverted in a follow-up cleanup
commit after CI is green and the loader-compat notes commit lands.
Task 1 owns the permanent integration.