openpencil/crates/op-host-web/Cargo.toml

108 lines
4.4 KiB
TOML
Raw Normal View History

[package]
name = "op-host-web"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
description = "OpenPencil web host — wasm32-unknown-unknown web bundle entry (Step 1b §1.2, C-hard pipeline)"
[lib]
name = "op_host_web"
path = "src/lib.rs"
crate-type = ["cdylib", "rlib"]
[dependencies]
# Phase 7.3 reorg: openpencil-shell-core dissolved — widget facade /
# theme / layout scene / scene vars / render-backend / gesture types
# all resolve through the op-editor-ui crate.
op-editor-ui = { path = "../op-editor-ui" }
# Reorg: the web host owns an `op_editor_core::EditorState` as its
# single source of truth; `op-pen-loader` bridges it into the
# read-only paint `Document` the shared widgets consume.
#
# Both are `optional` + behind the `skia` feature for the SAME reason
# `skia-safe` is: the host (`widget_host`) is `#[cfg(feature="skia")]`-
# gated, so the skia-free CI stub baseline
# (`--no-default-features --features web`) never needs them. This
# matters because `op-pen-loader` pulls `jian-skia` transitively
# (`pen_document_to_document` runs `SkiaMeasure` for text layout);
# keeping it optional preserves the wasm32-clean stub baseline.
op-editor-core = { path = "../op-editor-core", optional = true }
op-pen-loader = { path = "../op-pen-loader", optional = true }
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
# Step 1b §3.6: handwritten DOM mirror consumes accesskit::TreeUpdate (0.24).
accesskit = "0.24"
console_error_panic_hook = "0.1"
js-sys = "0.3"
feat(shell-web): Phase C1 — pure DOM event mapping modules Lands the four pure W3C → Jian gesture mappers that Phase C2's browser listeners will consume: - `event::keyboard::map_keyboard_parts(key, code, location, repeat, pressed, modifiers, is_composing) -> KeyEvent` — W3C KeyboardEvent.key/code/location string lookups produce KeyValue (Char / Named / Unidentified) + KeyCode enum + KeyLocation enum. Phase C1 covers KeyA-Z, Digit0-9, Enter / Escape / Tab / Space / Backspace / Delete / arrows; Home/End/PageUp/PageDown/F-keys/ modifier physical codes (ShiftLeft etc) extend the table in C2 (codex C1 NIT-2 deferred). - `event::ime::{composition_start, composition_update, composition_end}` + `utf16_selection_to_utf8` helper. The helper walks `text.char_indices()` accumulating `len_utf16()` to remap UTF-16 code-unit offsets (what `CompositionEvent.getTargetRanges()` hands us) to UTF-8 byte offsets (what jian-core's `ImeKind:: CompositionUpdate { selection: Range<usize> }` requires per spec §2.4). Mis-ordered range returns None; out-of-range bounds clamp to text.len(). CJK (你好), surrogate pairs (🙂), mixed-encoding (aé), zero-length selections, and empty text are all covered. - `event::pointer::map_wheel(position, dx, dy, dz, mode, mods, timestamp: Instant) -> WheelEvent` — flips W3C deltaY sign so widget code reads Jian-internal positive-up. Phase C1 intentionally takes `timestamp` as a parameter rather than calling `Instant::now()` internally; `std::time::Instant::now()` panics on wasm32-unknown-unknown ("time not implemented on this platform") and the C2 listener will fill the timestamp from a polyfill (web_time::Instant). Made the mapper pure to keep the panic locus in the listener glue, where the polyfill lives. - `event::focus::map_focus(gained, node_id_hint, related_node_id_hint) -> FocusEvent` — pure pass-through; the W3C target → WidgetId correlation work lives in C2 alongside the DOM mirror id registry (Phase D groundwork). Tests (`tests/dom_event_mapping.rs`, 22 tests, all native): - keyboard: 5 tests + 1 empty-string-key fallback test (codex C1 NIT-3) — key/code/location preservation, named key, location decoding, is_composing propagation, multi-codepoint & empty-string Unidentified fallback - ime: 8 tests — start/update/end shapes, UTF-16→UTF-8 remap for CJK / surrogate pair / mixed encoding / zero-length / no-selection / mis-ordered range / out-of-range clamp / empty text - wheel: 4 tests — Y sign flip, X no-flip, mode decoding, deltaZ passthrough - focus: 2 tests — gained=true with both hints, blur with no related target Plumbing: - shell-web grows a direct path-with-version `jian-core` dep. shell-core re-exports `gesture::*` but not `geometry::*`, and the wheel mapper builds `WheelEvent.position` from `jian_core::geometry::Point::new(...)`. Same path-with-version pattern as shell-core's own jian-core dep. - `pub mod event;` is NOT cfg-gated to skia — the mappers are pure and useful on the wasm32-clean stub baseline too. Plan-vs-implementation deviations (deliberate, all kept narrow): - `map_keyboard_parts` adds `is_composing: bool` parameter (jian-core KeyEvent struct REQUIRES the field per spec §2.4). - `map_focus` adds `related_node_id_hint: Option<u64>` parameter (jian-core FocusEvent struct field; plan body missed it). - `map_wheel` takes `timestamp: Instant` parameter instead of calling `Instant::now()` internally (avoids wasm32-unknown-unknown runtime panic; pure mapper). - `event/pointer.rs` covers ONLY wheel; full PointerEvent mapping (kind / phase / buttons / pressure) lives in C2 alongside listener registration since that's where the W3C PointerEvent surface meets the runtime context. Verification: - `cargo test -p openpencil-shell-web --test dom_event_mapping` — 22/22 passing - `cargo check -p openpencil-shell-web --target wasm32-unknown-unknown --no-default-features --features web` — green (compile guard) - `EMSDK=$HOME/.emsdk bash tools/check-wasm-bundle.sh` — PASS - 0 env.* imports - 615 616 bytes gzip = 58% of 1 MiB ceiling (no growth — event modules dead-code-eliminated when not called) - `bash tools/check-widget-boundary.sh` — PASS (event/ doesn't violate F1-F4) Codex iterate review: 1 round → GO with 1 deferred CONCERN (Instant source for C2 — already documented inline) + 3 NITs (deltaX comment wording, additional KeyCode entries, extra edge-case tests). NIT-1 and NIT-3 fixed in this commit; NIT-2 deferred to C2.
2026-05-09 13:49:00 +00:00
# Phase C1 DOM event mappers build `jian_core::gesture::WheelEvent` etc
# whose position/delta fields use `jian_core::geometry::Point`. The
# `geometry` module is not re-exported by shell-core (which only
# re-exports `gesture::*`), so pull jian-core directly. Same path-with-
# version pattern as shell-core's own jian-core dep.
jian-core = { path = "../../vendor/jian/crates/jian-core", version = "0.0.1" }
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
# Pin to 0.2.117 — last wasm-bindgen-cli release compatible with the
# workspace's Rust 1.85 toolchain. Newer 0.2.120+ requires rustc 1.86.
# When the workspace bumps Rust, raise this bound.
wasm-bindgen = "=0.2.117"
# Step 1b §2.2 (post C-hard.2 lock-in 2026-05-09): wasm32-unknown-unknown
# via the vendor/skia-safe-op fork + crates/wasm-libc-shim. Optional dep
# behind the `skia` feature so `cargo check --target wasm32-unknown-
# unknown --no-default-features --features web` (the kickoff §1.2
# wasm32-clean CI baseline) still compile-checks without pulling skia
# — that path uses the stub mount entry. Phase A and onward opt in via
# `--features skia` to get the real WebBackend + raster paint loop.
[dependencies.skia-safe]
version = "0.97.0"
default-features = false
fix(shell-web): Step 3 stop-hook — actually render canvas text Codex stop-hook flagged: "web Step 3 cannot render the claimed canvas text". Root cause: WebBackend::draw_text was a Phase A no-op stub. CanvasViewport calls draw_text for "Hello OpenPencil" / "Click me" / Layer panel labels / etc — none of those text strings actually rendered in the browser. # Fix `crates/openpencil-shell-web/src/backend/mod.rs::WebBackend`: - Embeds `assets/Roboto-Regular.ttf` (Apache 2.0, 35 KB, copied from rust-skia test resources) via `include_bytes!`. The C-hard wasm32-unknown-unknown skia build uses `skia_enable_fontmgr_custom_empty=yes` (see `vendor/skia-safe-op/skia-bindings/build_support/platform/ wasm_unknown.rs`), so there are no system fonts and we have to bake the bytes in. - New `typeface: Option<Typeface>` field, lazy-init on first draw_text via `FontMgr::custom_empty().and_then(|m| m.new_from_data(ROBOTO_TTF, None))`. Build failure → None silently no-ops subsequent draws (no panic — text just doesn't render). - `draw_text` now iterates `layout.runs()` and calls `Canvas::draw_str` per run with a `Font::new(typeface, font_size)` + `Paint` from the run color. `crates/openpencil-shell-web/Cargo.toml`: - Drops `textlayout` skia-safe feature. We use raw `draw_str` not paragraph builder; textlayout pulled ICU + Harfbuzz + ~400 KB gzip + a swarm of font-lookup imports we don't exercise. # 24 new env.* imports — wasm-libc-shim expansion Even without textlayout, Skia's font path imports 24 libc symbols our prior C-hard.2 shim didn't cover. All resolved: `crates/wasm-libc-shim/src/imp.rs` — Rust extern "C" shims: - string ops (real impls): strncmp, strncpy, strstr, strrchr, strcat, strtol, tolower, qsort (insertion sort, fits Skia's small-array call sites; debug_assert on element size > 256) - file I/O (sentinel error returns, no filesystem on wasm): fopen → null, fread → 0, fclose → 0, fputc → c, fileno → -1, fstat → -1, pread → -1, ftell → -1, fseek → -1 - env: getenv → null - mmap: returns MAP_FAILED ((void*)-1); munmap → -1 - setjmp/longjmp: setjmp returns 0 (treat as initial call); longjmp panics — happy text path through in-memory TTF parse should never trigger it - C++ nothrow new: `_ZnwmRKSt9nothrow_t` forwards to malloc, returns nullptr on OOM (the nothrow contract) `crates/wasm-libc-shim/src/stdio_stub.c`: - fprintf C-side variadic stub (Skia diagnostic path) that routes into the same panic helper as snprintf / vsnprintf / vfprintf — same fail-fast policy. # Verification - `cargo build -p openpencil-shell-web --target wasm32-unknown-unknown --features skia --release` — green - `wasm-bindgen --target web` produces ../pkg/openpencil_shell _web.{js,_bg.wasm} - `bash tools/check-wasm-bundle.sh` — PASS: - 0 env.* imports preserved (24 new ones absorbed by shim) - 906 935 bytes gzip = 86% of 1 MiB ceiling (+286 KB vs pre-text — Skia font/freetype subsystem is substantial. Headroom: 14% of ceiling) - `cargo test -p openpencil-shell-core --lib` — 39 tests - `cargo build -p openpencil-shell-native --example inspector_window` — green (desktop demo unchanged — uses jian-skia textlayout via NativeBackend, not the web font path) - `cargo check -p openpencil-shell-native --target aarch64-apple-ios` — green - `cargo check -p openpencil-shell-native --target aarch64-linux-android` — green # Re-run the demo ``` EMSDK="$HOME/.emsdk" cargo build -p openpencil-shell-web \ --target wasm32-unknown-unknown --features skia --release wasm-bindgen --target web --out-dir crates/openpencil-shell-web/pkg \ target/wasm32-unknown-unknown/release/openpencil_shell_web.wasm cd crates/openpencil-shell-web/smoke python3 -m http.server 8000 # Browser: http://localhost:8000/step-1b.html ``` Now the canvas viewport renders "Hello OpenPencil" + "Click me" text in addition to the rect/stroke geometry.
2026-05-10 04:38:33 +00:00
# Step 3 codex stop-hook fix: text rendering uses
# `Canvas::draw_str` + raw Font + Typeface (NOT skia paragraph
# builder), so the `textlayout` feature is unnecessary on web.
# Dropping it removes ICU + Harfbuzz from the bundle (~400 KB
# gzip) and avoids the env.* import explosion that came with
# textlayout's filesystem font lookups.
features = ["binary-cache"]
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
optional = true
# C-hard.2 libc/libcxx/libm shim — only linked when targeting
# wasm32-unknown-unknown with the `skia` feature on. The crate's lib.rs
# guards every symbol with `cfg(all(target_arch = "wasm32", target_os
# = "unknown"))` so native builds (where this would conflict with the
# host C runtime) link an empty crate.
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
wasm-libc-shim = { path = "../wasm-libc-shim", optional = true }
[dependencies.web-sys]
version = "0.3"
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
features = [
"CanvasRenderingContext2d",
"CompositionEvent",
"console",
"Document",
"Element",
"Event",
"EventTarget",
"FocusEvent",
"HtmlCanvasElement",
"HtmlElement",
"ImageData",
"KeyboardEvent",
"MouseEvent",
"Performance",
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
"PointerEvent",
"WheelEvent",
"Window",
]
[features]
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
# `web` keeps the kickoff §1.2 wasm32-unknown-unknown CI baseline green —
# the crate compile-checks as a wasm32-clean stub without pulling skia.
default = ["web"]
web = []
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
# `skia` opts into the Phase A WebBackend (skia-safe + raster surface).
# When the cargo target is wasm32-unknown-unknown the build also links
# the libc/libcxx/libm shim crate so the bundle is runtime-loadable.
# Native targets resolve the target-conditional shim dep to nothing.
skia = ["dep:skia-safe", "dep:op-editor-core", "dep:op-pen-loader", "wasm-libc-shim"]
feat(shell): switch openpencil-shell-web to wasm32-unknown-unknown C-hard pipeline Lights up the Phase A WebShell on the C-hard pipeline (vendor/skia- safe-op + crates/wasm-libc-shim, wired in the previous two commits) so `cargo build --target wasm32-unknown-unknown --features skia` followed by `wasm-bindgen --target web` produces a browser-loadable ES module with 0 env.* imports. What's added: - WebBackend (src/backend/mod.rs): impl RenderBackend over a skia-safe raster N32_PREMUL surface; presents each frame to the host <canvas> via image_snapshot → read_pixels → ImageData → put_image_data. end_frame surfaces present errors via last_present_error / take_present_error so a stale failure does not leak into a subsequent successful frame - skia_wasm.rs: thin make_raster_surface helper so swapping in a GPU GrContext (Phase A round 2) is a self-contained change - mount(canvas_id) entry: locates the host <canvas>, builds a WebBackend, paints the Phase A red-rect demo synchronously, propagates any present error as a JsValue exception - smoke/step-1b.html: manual smoke harness that mounts the shell and surfaces a structured diagnostic (with regression-mode LinkError messaging + rebuild instructions) if loading fails - extern crate wasm_libc_shim as _; in lib.rs to keep the shim's no_mangle symbols from being dead-code-eliminated - .gitignore for wasm-bindgen pkg/ output Cargo.toml feature wiring: - default = ["web"] keeps the kickoff §1.2 wasm32-clean compile guard CI green (stub mount, no skia) - skia = ["dep:skia-safe", "wasm-libc-shim"] opts into the real WebBackend + raster paint loop; the shim dep is target-gated so only wasm32-unknown-unknown actually pulls it in - wasm-bindgen = "=0.2.117" pinned (last release that compiles on Rust 1.85; bump alongside the toolchain in a future commit) Verified end-to-end: - `cargo build … --features skia --release` green - `wasm-bindgen --target web` produces ../pkg/*.{js,_bg.wasm} - WebAssembly.Module.imports() returns 22 imports, all from ./openpencil_shell_web_bg.js; 0 env.* imports - post `wasm-opt -Oz`: 1542 KiB raw / 599 KiB gzip — within spec §6 ceiling (≤ 1024 KiB gzip) - the kickoff §1.2 wasm32-clean compile guard still passes (`cargo check … --no-default-features --features web`) Browser-side manual smoke (Phase E) is still TODO; the bundle is structurally LinkError-free but a human still needs to confirm the red rect actually paints in Safari / Chrome / Firefox before the sub-phase can be marked complete. Step 1b §3.2 P0.5B Run path, sub-phase C-hard.2.
2026-05-09 13:08:00 +00:00
# Internal feature toggled on by `skia` to flip the cfg-target dep.
wasm-libc-shim = ["dep:wasm-libc-shim"]