Add Windows WebView2 backend, port jsaddle-webkitgtk to GTK4/WebKitGTK 6.0 - #166
Merged
Conversation
Rewrite the backend against gi-gtk4 / gi-webkit 6.x / gi-javascriptcore6 (webkit2gtk 4.0 has been removed from nixpkgs; 6.0 is the current API): GLib main loop instead of gtk_main, windowSetChild/scrolledWindowSetChild, webViewEvaluateJavascript, and the WebKit 6 script-message + script-dialog (JSaddleSync prompt) bridge. Add a demo executable with a --smoke mode that asserts a full bridge round trip (async eval + a JS→Haskell callback through the synchronous prompt path) and exits 0/1, suitable for headless CI under xvfb-run.
New jsaddle backend hosting the app in a native Win32 window with a
Microsoft Edge WebView2 control, mirroring jsaddle-wkwebview's design:
async batches via chrome.webview.postMessage, synchronous results via a
window.prompt("JSaddleSync", …) intercepted in ScriptDialogOpening.
The C shim (cbits/WebView2Shim.c) does plain-C COM against the official
WebView2.h; only the header is needed at compile time —
WebView2Loader.dll is loaded dynamically at run time and the runtime
ships with Windows 11. All ICoreWebView2 calls are marshalled to the
UI thread via PostMessage (WebView2 is single-threaded/STA).
nix/hix.nix fetches the header from the WebView2 NuGet package and
passes it via extra-include-dirs for windows cross builds
(hydraJobs.x86_64-linux.x86_64-w64-mingw32).
cabal.project 'if os(...)' conditionals do NOT apply to 'packages:' fields (they are silently ignored), so list every package unconditionally and gate platforms in each .cabal with buildable: False covering every component; the solver ignores dependencies of non-buildable components, so this solves on all platforms. - jsaddle-wkwebview / jsaddle-webkit2gtk: accurate platform guards (osx/ios and linux respectively, ghcjs stubs allowed). - jsaddle-terminal: unix-only, and the demo exe moves behind a manual 'demo' flag (default False) — reflex-dom-core caps base below new GHCs, which otherwise makes the whole project unsolvable. - jsaddle-webkit2gtk stays out of the project: it needs pkg-config webkit2gtk-4.0, which no longer exists in nixpkgs, and a missing pkg-config package fails the solve regardless of buildable: guards. jsaddle-webkitgtk (GTK4 / WebKitGTK 6.0) replaces it. Verified: hydraJobs plans for x86_64-linux, x86_64-w64-mingw32 cross, javascript-unknown-ghcjs cross, and aarch64-darwin all solve and contain exactly the intended components.
haskell.nix master now carries the WebKitGTK 6.0 pkg-config map entries (webkitgtk-6.0 / javascriptcoregtk-6.0), so gi-webkit solves with plain upstream — no override-input needed. checks.<linux>.webkitgtk-smoke (also in hydraJobs.checks) runs the jsaddle-webkitgtk demo's --smoke mode under Xvfb in the build sandbox: dbus pointed at its store session.conf, WebKit's bubblewrap sandbox off (cannot nest in the nix sandbox), and software EGL via llvmpipe with glvnd/dri/gbm loader paths set to the store (no /run/opengl-driver). Passes iff the jsaddle bridge round-trips (SMOKE-OK). Also ignore nix result symlinks.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new Windows-native JSaddle backend via Microsoft Edge WebView2, modernizes the Linux WebKitGTK backend to GTK4/WebKitGTK 6.0, and restructures packaging/Nix so the repo can solve and check consistently across platforms.
Changes:
- Introduces
jsaddle-webview2(Win32 + WebView2) with a C shim and Haskell transport mirroringjsaddle-wkwebview. - Ports
jsaddle-webkitgtkto GTK4 + WebKitGTK 6.0 and adds a headless--smokedemo mode plus a Nix/flake check to run it under Xvfb. - Makes multi-platform solving work from a flat
cabal.projectby pushing platform gating into per-packagebuildable: Falseguards.
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| nix/webkitgtk-smoke.nix | Adds a headless WebKitGTK runtime smoke test derivation (Xvfb + dbus-run-session + llvmpipe). |
| nix/hix.nix | Bumps compiler, adds WebView2 header fetch for cross builds, and expands cross platforms. |
| jsaddle-wkwebview/jsaddle-wkwebview.cabal | Tightens native buildability to macOS/iOS (with ghcjs/javascript stubs elsewhere). |
| jsaddle-webview2/src/Language/Javascript/JSaddle/WebView2/Internal.hs | Implements the JSaddle transport wiring for WebView2 (async + prompt-based sync bridge). |
| jsaddle-webview2/src/Language/Javascript/JSaddle/WebView2.hs | Public API for running JSaddle apps in a WebView2-backed native window. |
| jsaddle-webview2/README.md | Documents how WebView2 backend works and how to build/run it. |
| jsaddle-webview2/LICENSE | Adds MIT license file for the new package. |
| jsaddle-webview2/jsaddle-webview2.cabal | Defines the new Windows-only package + demo component. |
| jsaddle-webview2/demo/Main.hs | Adds a minimal demo app (counter + ticker). |
| jsaddle-webview2/cbits/WebView2Shim.c | Provides the Win32/WebView2 hosting shim, message-loop marshalling, and JS bridge integration. |
| jsaddle-webkitgtk/src/Language/Javascript/JSaddle/WebKitGTK.hs | Ports backend to GTK4/WebKitGTK 6.0 and updates the bridge mechanism. |
| jsaddle-webkitgtk/jsaddle-webkitgtk.cabal | Updates dependencies and platform gating; adds a Linux-only demo executable. |
| jsaddle-webkitgtk/demo/Main.hs | Adds demo with --smoke mode for CI headless runtime verification. |
| jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal | Adds explicit Linux-only native buildability guard (plus ghcjs/javascript stubs). |
| jsaddle-terminal/jsaddle-terminal.cabal | Adds demo flag gating and marks Windows as non-buildable for POSIX raw-mode terminal code. |
| flake.nix | Wires the new webkitgtk-smoke check into checks and hydraJobs on Linux. |
| flake.lock | Updates flake inputs (notably haskell.nix / nixpkgs pins) to support newer GTK/WebKit mapping. |
| cabal.project | Flattens unconditional package list and documents why per-package buildable guards are used. |
| .gitignore | Ignores Nix result* outputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a Windows backend, modernises the Linux backend, and makes the whole repo solve on every platform from one flat
cabal.project.jsaddle-webview2— Windows backend hosting the app in a native Win32 window with a Microsoft Edge WebView2 control.jsaddle-webkitgtkported to GTK4 + WebKitGTK 6.0 — replaces the WebKit1/GTK3 code (and supersedesjsaddle-webkit2gtk, whose webkit2gtk-4.0 dependency no longer exists in nixpkgs).cabal.projectrestructured — flat unconditionalpackages:list with per-packagebuildable: Falseplatform guards.jsaddle-webview2
Mirrors jsaddle-wkwebview's design: async batches are evaluated as
runJSaddleBatch(...)and results come back viachrome.webview.postMessage; synchronous results usewindow.prompt("JSaddleSync", …)intercepted natively inScriptDialogOpening.The C shim (
cbits/WebView2Shim.c) is plain-C COM against the officialWebView2.h— only the header is needed at compile time (fetched from the WebView2 NuGet package innix/hix.nixfor cross builds);WebView2Loader.dllis loaded dynamically at run time and the WebView2 runtime ships with Windows 11. WebView2 is single-threaded (STA), so allICoreWebView2*calls are marshalled to the UI thread viaPostMessage.Cross-compiles from nix via
hydraJobs.<system>.x86_64-w64-mingw32(lib + demo exe verified). Not yet run on real Windows hardware — the prompt-bridge length limits and first-navigation timing are the known risks to check there.jsaddle-webkitgtk (GTK4 / WebKitGTK 6.0)
Rewritten against
gi-gtk4/gi-webkit6.x /gi-javascriptcore6: GLib main loop instead ofgtk_main,windowSetChild/scrolledWindowSetChild,webViewEvaluateJavascript, and the WebKit 6 script-message + script-dialog (JSaddleSync prompt) bridge. Requires haskell.nix master (the WebKitGTK 6.0 pkg-config map entries —webkitgtk-6.0,javascriptcoregtk-6.0— landed there recently); the flake input is bumped accordingly.A new demo executable has a
--smokemode that asserts a full bridge round trip (async eval, plus a JS→Haskell callback through the synchronous prompt path) and exits 0/1.Per-platform packaging
cabal.projectif os(...)conditionals do not apply topackages:fields (silently ignored), so platform gating now lives in each.cabalfile asbuildable: Falseguards covering every component; the solver ignores dependencies of non-buildable components. Notes:jsaddle-terminal's demo moved behind a manualdemoflag (default off): reflex-dom-core capsbasebelow new GHCs and otherwise makes the whole project unsolvable.jsaddle-webkit2gtkstays out of the project: a missing pkg-config package (webkit2gtk-4.0) fails the solve regardless ofbuildable:guards.CI / testing
Plans verified for x86_64-linux, x86_64-w64-mingw32 cross, javascript-unknown-ghcjs cross, and aarch64-darwin — each contains exactly the intended components.
New flake check
checks.<linux>.webkitgtk-smoke(also inhydraJobs.checks) runs the WebKitGTK demo's--smokemode headlessly under Xvfb inside the nix sandbox (dbus config from the store, WebKit's bubblewrap sandbox off, llvmpipe software EGL) and passes iff the bridge round-trips: