Repository navigation
feat: add lean Rust desktop SDK with typed IPC - #367
Conversation
Add the desktop sidecar, native shell backends, app-root packaging, and Contact Book desktop runner. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Populate the new component asset roots field for desktop build option initializers after rebasing on main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a cross-platform DesktopFrame and DesktopFrameBackend contract, route platform launch through PlatformFrameBackend, and update the Contact Book runner to use the generic API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reuse already-loaded bundle manifests and serve packaged assets through a manifest-backed index to avoid duplicate manifest parsing and per-request asset metadata reads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-runtime # Conflicts: # Cargo.lock # crates/webui-cli/src/main.rs # deny.toml # docs/.webui-press/config.json # docs/ai.md # docs/guide/integrations/index.md # examples/app/contact-book-manager/README.md # examples/app/contact-book-manager/package.json
Merges origin/main (133 commits ahead) and adapts webui-desktop, webui-desktop-cli, and the contact-book-manager desktop example to the webui-handler API refactor from #390 (bundler-neutral state projection): - Replace WebUIProtocol/CompiledRouteCache/WebUIHandler::handle with the new Protocol wrapper and WebUIHandler::render across runtime.rs and bundle.rs. - Build test fixtures with --plugin=webui so navigation state projection actually populates per-component keys. - Always ship the desktop runtime's full computed per-request state in partial responses instead of trusting the generic component-navigation projection, since desktop route providers inject fields (route params, basePath, tokens) no single component's template declares. - Add the four new BuildOptions fields (css_bundle, metafile, theme, projection_manifests) and drop the removed ResolvedTokens::warnings field across webui-desktop-cli and the example app. - Bump stale 0.0.17 path-dependency pins to the current workspace version (0.0.29). - Add syn/system-deps to deny.toml's bans skip list (unavoidable gtk4-rs transitive duplicates) and drop skip entries that cargo deny now reports as unnecessary after the Cargo.lock refresh. cargo xtask check passes end to end. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- Collapse the desktop titlebar to zero height/padding with var(x, 0px) fallbacks so it renders nothing in a plain browser, where the desktop runtime never defines --webui-titlebar-*. A style() container query could detect the vars directly but is too new to be load-bearing, so the conservative fallback approach is used instead. - Replace the titlebar app name link with a plain, aria-hidden label (it duplicated <title> and duplicated the skip link's target). - Style .skip-link with the standard visually-hidden-until-focused pattern instead of rendering as a stray visible link. - Move the skip-link target off <route id="main-content">, which silently drops unknown attributes and never emits that id, onto a real <main id="main-content" tabindex="-1"> landmark wrapping the route so the skip link actually moves focus. - Reorder markup so the skip link is the first focusable element. Updates the five affected Playwright visual snapshots; the diffs are the removal of the previously mis-collapsed titlebar's stray height.
Fix defects in the desktop window contract and the example that adopts it. Drag regions resolved hit-testing with e.target.closest(), but WebUI renders through Web Components and a document-level listener sees shadow DOM events retargeted to the host element. A titlebar declared inside any component's shadow root would never have been draggable. Walk composedPath() instead, innermost first, so the nearest webui-drag/webui-no-drag ancestor wins. Also ignore non-primary buttons, so right-clicking a titlebar no longer starts a window drag, and skip non-element path entries. Report a poisoned command-queue mutex as a distinct Unavailable error rather than QueueFull, whose help text told callers to wait for a drain that will never happen. Replace the magic queue bound with MAX_QUEUED_WINDOW_COMMANDS and compare with >= so the cap cannot be overrun. Emit the CustomEvent name directly instead of formatting it through Rust's Debug, and document the remaining public event, command, and response items. In the example, correct --color-neutral-900, which is not a defined token (the scale skips 800 to 950), and subtract the titlebar height from cb-app's 100vh so the titlebar and app together are exactly one window tall instead of overflowing by the titlebar height. Both resolve to the previous values in a browser, where the custom properties are undefined. Restore the CRLF endings of README.md, package.json, and index.html, which had been rewritten to LF and were producing whole-file diffs. Revert the five contact-book visual baselines. They were regenerated on macOS, but CI baselines them on ubuntu-latest; the differences are font metrics and emoji fallback, the same environmental variance that makes the untouched commerce suite fail locally. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Desktop apps previously exposed five window fields and no way to observe or control the window after launch. DesktopShellConfig was serialized into the manifest and documented, but no backend read it and platform_capabilities() returned all-false everywhere, so it was a documented no-op. Define the window, event, and window-state contract in webui-desktop and implement it in all three backends: - WindowOptions gains min/max size, resizable, maximized, fullscreen, always_on_top, center, background, titlebar, effect, and remember_state. - TitlebarStyle supports native, hidden-inset, overlay, and none. For non-native styles the runtime injects --webui-titlebar-inset-start/-end, --webui-titlebar-height, and --webui-window-background so content can lay out around native controls without hardcoding per-OS offsets. - Drag regions use webui-drag/webui-no-drag attributes rather than Chromium's -webkit-app-region, which WKWebView and WebKitGTK do not implement. - Eighteen lifecycle events dispatch to Rust handlers and mirror to web content as webui:* CustomEvents. Close and navigation requests are cancelable. - WindowHandle queues UI-thread commands from any thread, bounded by MAX_QUEUED_WINDOW_COMMANDS. - remember_state persists geometry and rejects stale or off-screen state. Wire platform_capabilities() to advertise only what each backend implements, so an unsupported request fails at startup with an actionable message instead of silently doing nothing. macOS adds app menu and tray; Windows adds Mica and Acrylic; Linux advertises no effects (GTK4 has no portable blur) and no tray (GTK4 removed GtkStatusIcon). Consolidate the navigation allowlist into one tested function. The three backends had drifted apart: macOS accepted the entire about: scheme, Windows only about:blank, and Linux had no policy at all. The shared policy is deny-by-default and rejects origin lookalikes such as webui://app.evil. Split the Windows and Linux backends into submodules alongside macOS. Verified on macOS: the example launches with a hidden-inset titlebar, and cargo xtask check passes. Windows and Linux are verified against vendored sources but have never been compiled locally; CI is their first real check. Visual confirmation of the rendered window is still outstanding. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Address an independent review of the desktop window work plus defects found during macOS runtime verification and Windows cross-compilation. Correctness: - Linux registered a WebKit script-message handler but never defined the `window.webuiHostPostMessage` global the injected drag script calls, so every drag, minimize, maximize, and close message was silently dropped. Rename the handler to `webuiHost` to match macOS and inject an explicit bridge alias. - Linux attached a `GestureDrag` to the whole webview, starting a native window move on any pointer drag in content. This broke text selection, scrolling, and sliders, and ignored the `webui-drag` contract. Remove it so dragging flows only through the `StartDrag` host message. - Linux `read_body` returned a truncated body as a valid request on stream error; return `400` instead. - Report `WindowResized`/`WindowMoved` in logical pixels on Windows, which alone reported physical pixels. Native sizing and hit-testing are unchanged. - Fix `pack_point` using a nonexistent `isize: From<u16>` impl, and add the missing `window_dpi` helper. - Update the Linux and Windows dev-serve `WindowOptions` initializer, which had not grown with the struct. Contract: - Add `WindowId::PRIMARY` so every backend reports one primary-window identity; macOS previously reported `0` and the others `1`. - Replace `window_effects: bool` with `supported_effects`. No backend implements every `WindowEffect`, so a single boolean could not be honest. Windows now maps `vibrancy` onto the acrylic backdrop it advertises and rejects `tabbed`, which has no Windows analogue. Events: emit `WindowMaximized`/`WindowUnmaximized` and `NavigationCompleted` on macOS, and window-state transitions, `Ready`, and `Exiting` on Windows and Linux. All 18 events are now emitted on every backend except `WindowMoved` on Linux, which GTK4 and Wayland make unavailable. Titlebar: suppress text selection in `[webui-drag]` regions at the framework level. Double-clicking a titlebar selected its text before the drag script converted the gesture to a maximize toggle, which no native titlebar does. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Linux desktop backend in microsoft-webui-desktop-cli depends on gtk4 and webkit6, and that crate is a workspace member, so `cargo clippy --workspace`, `cargo test --workspace`, and the workspace build all now need GTK's development packages on Linux runners. No workflow installed them, so Lint failed in glib-sys's build script before any Rust compiled and gated every downstream job. Add libgtk-4-dev and libwebkitgtk-6.0-dev to the shared build action, folded into the existing protoc step so Linux jobs still run one apt update. Both pr.yml and docs.yml use this action, and the only raw workspace cargo commands live in jobs that consume it, so this covers every affected job. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CI installed GTK for the first time, which compiled the Linux backend and surfaced seven errors that no local toolchain could reach. The substantive one is the command wakeup. `WindowHandle::set_wakeup` requires a `Send + Sync` callback, but the closure captured `ApplicationWindow` and `WebView`, which are neither, so queueing a window command from a background thread could never have compiled. The UI-thread handles now live in thread-local storage and the wakeup callback captures nothing, hopping to the default main context to reach them. This mirrors the Windows backend, which posts a message instead of capturing its HWND. Also fixed: - `display_bounds` called `window.display()`, ambiguous between `RootExt` and `WidgetExt`. Both return `gdk::Display`, not an `Option`, so the `let Some(..) else` binding was wrong as well. - `connect_webview_events` cloned the event registry into the load-changed closure and then reused the moved binding for the policy closure. - Dropped scheme and protocol imports that `linux/protocol.rs` owns. Docs: replace the short IPC note in the desktop guide with a full message passing section covering `invokeDesktop`, the `webui:*` lifecycle events with their exact payload fields, and the window control channel. Event shapes were captured from running code rather than inferred. Re-export `DEFAULT_MAX_IPC_PAYLOAD_BYTES` so the documented limit is reachable. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
`state_store` has no use after `install_events`, so the clone only created a value that was immediately dropped. Caught by `clippy::redundant-clone` on Linux, which could not run until GTK was available in CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
`EventRegistry::dispatch` held the handler mutex while invoking user callbacks. `std::sync::Mutex` is not reentrant, so a handler that registered another handler or dispatched a nested event deadlocked the UI thread with no way to recover. The handler list is now copy-on-write behind `Arc<[_]>`: dispatch clones the `Arc` and drops the guard before invoking anything, so re-entry is safe. Cloning is a refcount bump rather than a per-dispatch allocation, which matters because resize events fire continuously during a window drag. Dispatch now also runs every handler instead of short-circuiting on the first cancellation, so whether a handler observes an event no longer depends on registration order. `IpcRegistry::dispatch_frame` applied the size limit to the inbound frame and request payload but never to the response, so a small request could return an unbounded allocation. Handler payloads are checked before encoding, and the finished frame is checked after, since protobuf framing and long handler error messages both add bytes. Both paths return the existing structured `payload-too-large` error. Regression tests cover all three. The deadlock test dispatches on a worker thread with a bounded wait so a regression fails the suite instead of hanging it forever. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Packaged desktop apps linked the build-time template compiler they never call. `from_source` and the bundle/package surface now sit behind a default-on `source` feature, so a packaged app can build with `--no-default-features` and let LTO drop the parser. Measured on contact-book-desktop (stripped release): default 2,494,672 bytes lean 1,331,008 bytes saved 1,163,664 bytes (46.65%) The default build is byte-identical to before, so dev mode is unchanged. The `webui` CLI also execs a separate `webui-desktop` sidecar without checking it matches, so a stale binary on PATH silently won. `webui-desktop` now answers `--webui-version` and the shim verifies it before dispatch, failing with an actionable diagnostic on skew or on a sidecar too old to answer. Also fix a race in the webui-press E2E suite: the injected live-reload client could call location.reload() while the test drove its own cross-document navigation, interrupting an in-flight page.goto(). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The `-cli` suffix hid that this crate is the platform backend library every packaged desktop app links, not a developer-only CLI. Its library was already named `webui_desktop_runner`, and the scaffolder in init.rs already aliased the dependency as `webui-desktop-runner`, so the package name was the odd one out. Rename the package and its directory together so both match the library name. The `webui-desktop` binary name is unchanged, and no `use` statements move. Both desktop crates are new in this PR and unpublished, so this costs nothing now and would be a breaking change later. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
mddinbox
left a comment
There was a problem hiding this comment.
Reviewed the current head c291ddf and rechecked both prior findings after the author fixes. Event dispatch now releases the registry lock before callbacks, and IPC response payloads and encoded frames are bounded with structured errors. The focused desktop suite passes all 58 tests and CI is green. No remaining issues found.
Expose one desktop SDK with native, source, and CLI features instead of requiring applications to coordinate runtime and runner crates. Keep headless and production graphs free of compiler and CLI dependencies. Unify source and bundle app construction, preserve shell and app identity, and generate standalone app workspaces with explicit source opt-in. Package optimized runtime-only executables by default while allowing explicit application features and debug builds. Own subscriptions and command queues by the frame lifetime, bound retained title storage, avoid reentrant wakeup locks, and reject work after shutdown. Retain native menu targets, isolate persisted geometry by app identity, and write bounded window state atomically. Reject native message objects before formatting, decode untrusted NSString UTF-16 safely, and attach Windows wakeups only after receiver installation. Extend native CI and the feature matrix with regression coverage. Measured Contact Book release packaging: executable: 2,494,672 -> 1,314,368 bytes bundle: 2,746,433 -> 1,566,129 bytes production dependency graph: 95 -> 42 packages Document the remaining application IPC gap explicitly: current generic messaging is JS-to-Rust byte-oriented request/reply, not a generated bidirectional RPC and notification contract. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Navigation API only grants interception to documents with an HTTP-family origin. Desktop shells serve applications from a custom scheme, where WebKit refuses to treat a navigation as same-document and reports canIntercept: false on every NavigateEvent even though window.navigation exists. The router bailed out of its navigate handler and resolved each route through navigateDocument, so every route change became a full document load and the web engine retained one document per navigation. Gate the Navigation API path on the origin scheme and intercept capture-phase link clicks plus popstate otherwise, driving the same handleNavigation path through history.pushState. Browsers without the Navigation API take the same fallback. The scheme is the gate rather than a runtime probe because probing with history.replaceState is misleading: a same-document state change is interceptable on a custom scheme while an actual navigation is not. Measured on a packaged macOS app over 120 route changes: before 164 document loads, WebContent 190.9 MB and still climbing after 1 document load, WebContent 72.6 MB The baseline never finished the workload because each navigation reloaded the document and restarted it. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The desktop generator still compiles application protobuf codecs with ts-proto. Keep the fixed-layout framework envelope independent while retaining the application codec toolchain required by release CI and generator tests.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace private feed tarball metadata with the corresponding public npm integrity records so locked installs satisfy the repository supply-chain policy.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
mddinbox
left a comment
There was a problem hiding this comment.
The generator now emits hello.wireVersion: 3, but the checked-in crates/webui-desktop/src/generated/ipc/native-bootstrap.js still rejects every handshake whose version is not 2. The release-consumer acceptance step consequently fails on Linux, macOS, and Windows with Generated IPC asset differs: native-bootstrap.js; if shipped, the stale asset would reject the v3 handshake as invalid-payload. Please regenerate and commit the checked-in bootstrap with the repository's explicit sync-bootstrap.mjs --write flow, then rerun the release-consumer checks.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
mddinbox
left a comment
There was a problem hiding this comment.
Reviewed the latest head 46dd9cf, including the one-commit delta that regenerated the checked-in desktop runtime asset to match the v3 wire contract. The previously reported bootstrap/version mismatch is fixed, all prior authored threads are resolved, and the latest PR Checks run is green across the desktop build and native IPC validation. No remaining confirmed issues.
Move generated desktop IPC payload encoding and decoding to WebUI-owned Rust and TypeScript codecs, keeping Rust in charge of registry policy and the TypeScript layer as a thin native messaging facade. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…otocol Measures DesktopRuntime::from_bundle_config_and_manifest (protocol compiled ahead of time at build) against DesktopRuntime::from_source (protocol compiled from templates on every launch): ~452us vs ~2.63ms, a ~5.8x win for the precompiled path, confirming the packaged/bundle desktop launch path should be preferred over the source feature outside development. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…op frame The webuiHost message handler used for drag/minimize/maximize/close was injected with UserContentInjectedFrames::AllFrames, letting an embedded iframe on Linux invoke native window commands through the convenience window.webuiHostPostMessage alias. macOS already restricts this bridge to the main frame via WKScriptMessage.frameInfo().isMainFrame(), and Windows only subscribes to top-level WebMessageReceived. Restrict injection to UserContentInjectedFrames::TopFrame to keep the alias out of subframes. This is a partial mitigation: WebKitGTK 6's register_script_message_handler has no forMainFrameOnly equivalent and script-message-received reports no sending-frame identity, so a subframe can still reach window.webkit.messageHandlers.webuiHost directly. Document this platform gap in the message handler and in DESIGN.md so it isn't mistaken for a complete fix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
mddinbox
left a comment
There was a problem hiding this comment.
The newly added locked fetch fails on the current head in all three platform jobs (PR Checks run 35823332602): Cargo reports that it cannot update crates/webui-desktop/tests/fixtures/native-ipc/Cargo.lock because --locked was passed. This means the checked-in fixture lockfile does not match the manifest/resolved feature graph, so the job exits before the native IPC fixture runs and the PR cannot pass its merge gate. Regenerate the fixture lockfile from this manifest (including the native platform feature graph) and commit the resulting Cargo.lock, then keep --locked to preserve reproducible CI.
Regenerate the fixture lockfile after removing the desktop SDK's direct prost dependency. Keep all package versions and the locked CI fetch unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Drive shared browser and desktop layouts from explicit mode state. Add accessible caption controls, drag exclusions, contained desktop scrolling, and responsive header and navigation layouts. Preserve macOS frameless window capabilities and close vetoes. Add source/bundle rendering, browser interaction and native close-veto regressions without enabling application IPC. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Prepare one serializable projected response for desktop and web while preserving the owned and raw JSON string APIs. Remove the desktop full-state bypass and pass client projection metadata through desktop builds and packaging. Exclude projection inputs from bundle assets and protect them during output cleanup, including symlink-parent paths. Cover source/package/web parity, reserved-state filtering, unknown-surface fallback and manifest handling. In the local 1000-contact unused-state benchmark, desktop payload falls from 1,385,467 to 187,669 bytes and P50 from 925 to 475 microseconds. Owned-web allocations are unchanged; its measured latency cost is 2-4 percent. Keep the representative benchmark and parity guards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Build source and packaged test frames from per-test templates, state, assets, theme and valid projection metadata instead of requiring a prior frontend build. Preserve production validation and add a regression for missing projection metadata. Verify static assets are served in both modes and retain the route-mutation and token assertions. The source-enabled tests pass in a tracked-only checkout with no generated dist or installed frontend packages. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Integrate the WebUI Press npm package relocation while preserving desktop runtime, projection and clean-checkout tests. The relocated Press suite and full cargo xtask check pass on the merged tree. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use FileRenameInfoEx with replacement and POSIX semantics directly so existing readers retain their snapshots and new opens see the replacement. Preserve strict concurrent-read assertions, report unsupported storage and native save failures, and add held-reader, descriptor and read-only regressions. Keep Windows native IPC registration within the workspace argument limit by accepting the existing frame context. Windows-target Clippy and executable linking, portable persistence tests and cargo xtask check pass; Windows execution is left to CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
## Release Bumps WebUI to `0.0.30` across Rust, npm, Python, and .NET package metadata and updates desktop crate version examples. Native desktop and Press support is distributed as opt-in, version-matched platform packages; the former standalone `webui-press` npm command is replaced by `webui press` without a Node command wrapper. The six new desktop platform packages include READMEs. The native IPC fixture shares the workspace lockfile after #568. Previous release tag: `v0.0.29` ## Changes since `v0.0.29` Features: - WebUI Press ships native npm artifacts for macOS, Windows, and Linux and runs through the opt-in `webui press` command (Add native npm distribution for WebUI Press #555 by @copilot-swe-agent, fix: distribute native desktop and Press sidecars #570 by @mohamedmansour). - A lean Rust desktop SDK adds system webviews, app scaffolding, and typed application IPC (feat: add lean Rust desktop SDK with typed IPC #367 by @mohamedmansour). - Opt-in native desktop npm packages make `webui desktop` available without adding desktop webview dependencies to the base installation (fix: distribute native desktop and Press sidecars #570 by @mohamedmansour). - Windows desktop apps gain native App SDK caption controls (feat: add native Windows App SDK caption controls #560 by @mohamedmansour). - Desktop apps can be configured without package metadata (feat: configure desktop apps without package metadata #569 by @mohamedmansour). Fixes: - Duplicate outlets at one route level produce an actionable build warning instead of silently dropping later outlets (Warn on duplicate outlets at the same route level #551 by @copilot-swe-agent). - Press builds keep temporary inputs and outputs on the same filesystem volume (fix: keep Press builds on one filesystem volume #552 by @mohamedmansour). - Streaming terminal markup stays inside the HTML body (fix: keep streaming terminal inside body #558 by @mohamedmansour). - The WebUI reference loader selects the correct source or installed-package reference for the target project (fix: select WebUI reference by target project #559 by @mohamedmansour). - The Contact Book desktop icon displays correctly and header double-click behavior is restored (fix: show Contact Book desktop icon and restore header double-click #562 by @mohamedmansour). - Linux desktop IPC preserves navigation and shutdown reasons when retiring native requests (fix: preserve Linux IPC retirement reasons before cancellation #566 by @mohamedmansour). - Reactive callbacks wait until components are ready (fix: defer reactive callbacks until components are ready #564 by @mohamedmansour). - Windows desktop state reads tolerate transient file-open contention while retaining atomic state replacement (fix: handle Windows state contention and synchronize IPC tests #571 by @mohamedmansour). Docs: - Routing, project-specific reference installation, and interactivity guidance were updated (Warn on duplicate outlets at the same route level #551 by @copilot-swe-agent, fix: select WebUI reference by target project #559 by @mohamedmansour, fix: defer reactive callbacks until components are ready #564 by @mohamedmansour). - Press installation and desktop SDK, caption control, icon, configuration, and opt-in native sidecar documentation were expanded (Add native npm distribution for WebUI Press #555 by @copilot-swe-agent, feat: add lean Rust desktop SDK with typed IPC #367 by @mohamedmansour, feat: add native Windows App SDK caption controls #560 by @mohamedmansour, fix: show Contact Book desktop icon and restore header double-click #562 by @mohamedmansour, feat: configure desktop apps without package metadata #569 by @mohamedmansour, fix: distribute native desktop and Press sidecars #570 by @mohamedmansour). - Architecture and subsystem references were clarified, with the todo tutorial kept aligned (chore: keep DESIGN.md architectural and preserve internal contracts #563 by @mohamedmansour). Maintenance: - The WebUI Press npm package moved into the shared `packages/` workspace (chore: move webui-press npm package #556 by @mohamedmansour). - Desktop acceptance and native IPC test orchestration moved to Rust xtask and the workspace Cargo lockfile (chore: migrate desktop acceptance tests to Rust xtask #568 by @mohamedmansour). - Desktop IPC capacity assertions now wait for the admitted handshake worker to release its permit (test(desktop): wait for handshake capacity before checking retired work #572 by @mohamedmansour). - GitHub Actions dependencies were updated (chore(deps): bump the github-actions group with 2 updates #565 by Dependabot). ## Validation - `cargo xtask desktop-acceptance` and the full `cargo xtask check` passed on the merged 0.0.30 tree. - `cargo xtask publish-stage --target aarch64-apple-darwin --native-only` staged core, Press, and desktop binaries. Seven local 0.0.30 tarballs were packed and installed offline in a fresh npm consumer; installed binaries matched the staged release artifacts by SHA-256. - Installed `webui build` and `inspect` succeeded; `webui serve` returned HTTP 200. Installed `webui press build` rendered a one-page full site after installing its expected `esbuild`, TypeScript, and framework peers. Installed `webui desktop init`, `build`, `package`, and `run` succeeded. Both `desktop run` and the packaged macOS `.app` loaded an image and executed page JavaScript that sent the expected heading to a local probe. Core and desktop builds also passed with Node absent from `PATH`. - `pnpm --dir docs build` passed after refreshing the staged workspace CLI. `cargo xtask native-ipc --timeout 60` passed source and packaged macOS native IPC, including two BFCache restores in each mode, plus the no-IPC fixture. - PR Checks run 36299856666 finished green: Build (Linux) passed unchanged on a targeted rerun after an intermittent packaged history-navigation assertion; Build (macOS) passed on a targeted rerun after GitHub reported its first hosted runner lost communication; Build (Windows) and all six native npm platform jobs passed. All 41 current PR checks are green. The deterministic acceptance-fixture correction for the Linux race is separately green in #573 but is not yet part of this release branch. Local npm smoke covers macOS arm64, not the full release matrix. --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Summary
microsoft-webui-desktophas no default features.nativeenables the system webview;sourceenables development compilation;clienables separate tooling. App-root packaging defaults to optimized runtime-only builds.DesktopAppbuilder for source and immutable bundles, preserving window configuration, shell settings, application identity, route providers, and handlers before startup rendering.Typed application IPC
Replace the unmerged string-method API with generated Rust and TypeScript interfaces from one proto3 contract:
webui desktop ipc generate, the build-timemicrosoft-webui-desktop-buildcrate, and browser-only@microsoft/webui-desktop. Shared bindings preserve bigint/64-bit values, binary bytes, native maps, optional fields, and oneofs.--check.Window customization and ownership
Cross-platform options cover native/custom titlebars, caption-control insets, background, effects, geometry, and native controls. Unsupported features fail before launch rather than silently becoming no-ops. Frames own lifecycle subscriptions, bounded command queues, and IPC state; weak handles cannot keep a closed frame accepting work. Wakeups are coalesced, callbacks run outside registry locks, and persisted geometry uses bounded reads and atomic replacement.
Release resource measurements
Measured SDK source:
b72f4e91, unchanged by the subsequent CI metadata repair. Apple M4 Pro, macOS arm64, optimized runtime-only builds, one warmup per mode and five rotated triples. This is a minimal empty-schema harness, not the Contact Book application. Registered-unused injects the bootstrap; admitted-idle additionally imports the real browser runtime and retains an authenticated connection without application traffic.Admission versus registered-unused adds a paired median 0.781 MiB of WebContent footprint (range +0.641 to +0.984 MiB). The paired host delta, +0.062 MiB, is within observed variability; GPU variation prevents a strong total-memory conclusion. Both executable variants have 49 normal-edge dependency entries excluding the harness, without compiler/parser, Tokio, Rayon, prost-build or prost-reflect. Disabled builds still include shared SDK IPC code, so the 96-byte executable difference is not the total size of the IPC implementation.
Hidden process-launch-to-connection-ready p50/p95 was 254.34/358.45 ms. Admission after DOM-ready was 2.95/52.52 ms, retaining both slower samples. These warm-cache, hidden-window observations are not foreground startup or first-paint measurements, and no startup improvement is claimed. Product byte totals exclude observer/container metadata and signing; resource observations include constant measurement scaffolding. All 72 owned host/helper PIDs exited.
Earlier, before typed IPC, SDK consolidation reduced the measured macOS Contact Book executable from 2,494,672 to 1,314,368 bytes and its logical app bundle from 2,746,433 to 1,566,129 bytes. Those remain historical consolidation measurements, not the current Contact Book artifact size.
Verification and remaining platform acceptance
cargo xtask checkpasses with all new files tracked, including formatting, linting, dependency audit, workspace tests, generated-artifact checks, native/source/CLI coverage, builds, WASM, examples, benchmarks, and docs. Strict desktop native library/test Clippy also passes.The previous macOS Contact Book UI/screenshot verification remains applicable to the unchanged visual shell; the new IPC fixture is non-visual protocol verification.