lantern: host-injected wasm streaming->buffer fallback so wasm apps boot over lantern://
WebKitGTK will not stream-compile wasm over the custom lantern:// scheme: WebAssembly.compileStreaming/instantiateStreaming reject with 'Unexpected response MIME type. Expected application/wasm' even though the asset backend serves an explicit Content-Type: application/wasm. Every Sigil-wasm app (Slate included) boot-died blank as a result.
Registering the scheme as secure + cors-enabled via WebKitSecurityManager did NOT fix it (WebKit ignores a custom scheme MIME for streaming compile, confirmed empirically). Kept the 'secure' registration for secure-context hygiene; dropped cors.
Fix: (lantern js) wasm-streaming-shim-js patches WebAssembly.compileStreaming/ instantiateStreaming to clone the response, try streaming, and buffer-compile on failure. app.sgl injects it at document-start before the app boots. This makes the fallback a HOST responsibility (every web build gets it) rather than relying on each build to carry its own. It is deliberately LOUD: one console.warn on first fallback, pointing at the durable fix. See topics/lantern-wasm-streaming-fallback.
Also: run-slate.sh now takes a Slate repo folder (resolves build/web), launches with --transparent, and passes LANTERNSOFTWARERENDER through.
lantern/src/lantern/app.sgl | 5 +++++
lantern/src/lantern/backend/gtk.sgl | 23 +++++++++++++++++++++--
lantern/src/lantern/js.sgl | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
run-slate.sh | 44 ++++++++++++++++++++++++++++++++++----------
4 files changed, 133 insertions(+), 13 deletions(-)lantern/src/lantern/app.sglmodified
(unless is-url (webview-register-scheme wv "lantern" (make-asset-resolver assets))) ;; Inject the wasm streaming->buffer fallback FIRST (before the app boots ;; and calls compileStreaming): WebKitGTK will not stream-compile wasm over ;; the lantern:// scheme, so this host-injected shim patches WebAssembly to ;; buffer-compile on failure. It is loud (warns once) — see (lantern js). (webview-inject-script wv win wasm-streaming-shim-js) ;; Inject the bridge glue at document-start, then any plugin glue. (webview-inject-script wv win lantern-js) (for-each (lambda (p)lantern/src/lantern/backend/gtk.sglmodified
(define gdk-display-get-default #f) (define webkit-web-context-get-default #f) (define webkit-web-context-get-security-manager #f) (define webkit-security-manager-register-scheme-secure #f) (define webkit-web-context-register-uri-scheme #f) (define webkit-uri-scheme-request-get-uri #f) (define webkit-uri-scheme-request-get-path #f) (set! gdk-display-get-default (c-function libgtk "gdk_display_get_default" (list) ffi/pointer)) (set! webkit-web-context-get-default (c-function libwebkit "webkit_web_context_get_default" (list) ffi/pointer)) (set! webkit-web-context-get-security-manager (c-function libwebkit "webkit_web_context_get_security_manager" (list ffi/pointer) ffi/pointer)) (set! webkit-security-manager-register-scheme-secure (c-function libwebkit "webkit_security_manager_register_uri_scheme_as_secure" (list ffi/pointer ffi/string) ffi/void)) (set! webkit-web-context-register-uri-scheme (c-function libwebkit "webkit_web_context_register_uri_scheme" (list ffi/pointer ffi/string ffi/pointer ffi/pointer ffi/pointer) ffi/void)) (define (register-scheme scheme resolver) (set! *scheme-resolver* resolver) (webkit-web-context-register-uri-scheme (webkit-web-context-get-default) scheme scheme-cb NULL NULL)) (let ((ctx (webkit-web-context-get-default))) ;; Register the app scheme as a SECURE context (like https) before the ;; first load: a custom scheme is not a secure context by default, which ;; gates web features an app may rely on (SubtleCrypto, service workers, ;; etc.). Good hygiene for the app origin. ;; ;; NB: secure + cors-enabled was ALSO tried as a fix for wasm streaming ;; compile over lantern:// and does NOT work — WebKitGTK still ignores a ;; custom scheme MIME for WebAssembly.compileStreaming (empirically ;; confirmed 2026-07-16). That is handled instead by the injected ;; streaming->buffer shim (see (lantern js) wasm-streaming-shim-js and ;; topics/lantern-wasm-streaming-fallback). CORS was dropped as useless. (webkit-security-manager-register-scheme-secure (webkit-web-context-get-security-manager ctx) scheme) (webkit-web-context-register-uri-scheme ctx scheme scheme-cb NULL NULL))) (define (register-messages window name on-message) (set! *msg-handler* on-message)lantern/src/lantern/js.sglmodified
(define-library (lantern js) (import (sigil core)) (export lantern-js) (export lantern-js wasm-streaming-shim-js) (begin ;; WebAssembly streaming-compile fallback (injected at document-start, BEFORE ;; the app boots so its compileStreaming call is already patched). ;; ;; WHY THIS EXISTS — READ BEFORE DELETING: WebKitGTK will NOT stream-compile ;; wasm over Lantern's custom lantern:// scheme. `WebAssembly.compileStreaming` ;; /`instantiateStreaming` reject with "Unexpected response MIME type. Expected ;; application/wasm" even though the backend serves an explicit ;; `Content-Type: application/wasm` (via finish_with_response) AND the scheme is ;; registered secure + cors-enabled — WebKit simply does not consult a custom ;; scheme response MIME for streaming compile (confirmed 2026-07-16). Without a ;; fallback, EVERY Sigil-wasm app (Slate included) boot-dies blank in Lantern. ;; ;; This shim makes the fallback a HOST responsibility (Lantern injects it into ;; every page), not a per-web-build one — so no app has to remember the ;; workaround. It is a WORKAROUND, not a fix: it is deliberately LOUD (a single ;; console.warn the first time it fires) so we do not forget. The durable fix is ;; to serve assets over a loopback http origin where wasm streams natively; see ;; topics/lantern-wasm-streaming-fallback. Remove this shim if/when that lands ;; (or if a future WebKitGTK honors custom-scheme MIME for streaming). ;; ;; String constraints (same as lantern-js): NO double quotes, NO backslashes, ;; NO apostrophes inside the single-quoted JS strings — it embeds in a Sigil ;; double-quoted string literal. (define wasm-streaming-shim-js "(function () { if (!(window.WebAssembly)) return; var WA = window.WebAssembly; if (WA.__lanternWasmShim) return; WA.__lanternWasmShim = true; var warned = false; function warnOnce(kind) { if (warned) return; warned = true; if (!(window.console) || !console.warn) return; console.warn( 'lantern: WebAssembly.' + kind + ' was rejected over this origin ' + '(WebKitGTK does not honor a custom lantern:// scheme MIME for streaming ' + 'wasm compile). lantern is buffer-compiling as a HOST-SIDE FALLBACK. This ' + 'is a workaround, NOT a fix. Durable fix: serve assets over a loopback http ' + 'origin so wasm streams natively. See topics/lantern-wasm-streaming-fallback.'); } function patch(name, onFail) { var orig = WA[name]; if (typeof orig !== 'function') return; WA[name] = function (src, importObject) { return Promise.resolve(src).then(function (resp) { var canClone = resp && typeof resp.clone === 'function'; var attempt = canClone ? resp.clone() : resp; return Promise.resolve(orig.call(WA, attempt, importObject)) .catch(function (e) { warnOnce(name); return onFail(resp, importObject); }); }); }; } patch('compileStreaming', function (resp) { return resp.arrayBuffer().then(function (buf) { return WA.compile(buf); }); }); patch('instantiateStreaming', function (resp, importObject) { return resp.arrayBuffer().then(function (buf) { return WA.compile(buf).then(function (mod) { return WA.instantiate(mod, importObject).then(function (inst) { return { module: mod, instance: inst }; }); }); }); });})();") (define lantern-js "(function () { if (window.lantern) return;run-slate.shmodified
#!/usr/bin/env bash# Launch Slate in a native Lantern window with REAL local fs + pty access.# Launch Slate in a native Lantern window with TRANSPARENCY on + REAL local# fs + pty access.## ./run-slate.sh [slate-web-build-dir]# ./run-slate.sh [slate-repo-dir] [extra lantern args...]## Defaults to the sibling slate worktree's web build. Wraps the invocation in an# ephemeral `guix shell` (the GTK4 + WebKitGTK + Mesa graphics stack, from# gl-manifest.scm) — no persistent profile. If the window never maps, re-run with# LANTERN_SOFTWARE_RENDER=1 (the Mesa GL loader-path gotcha).# Give it a path to a Slate REPO folder (the one holding build/web/index.html# after `sigil build`). Defaults to the sibling `slate` checkout. It resolves the# web build, wraps the run in an ephemeral `guix shell` (the GTK4 + WebKitGTK +# Mesa graphics stack from gl-manifest.scm — no persistent profile), and launches# with --transparent so the window is transparency-CAPABLE.## Transparency is opt-in-safe: with Slate's own translucency OFF (the default) the# window looks completely normal; toggle Slate translucency ON (command palette:# "Transparency: on") and the desktop shows through the frosted content.## If the window never maps, re-run with LANTERN_SOFTWARE_RENDER=1 (the Mesa GL# loader-path gotcha) — it is passed through to the child if set in your env.set -euo pipefailHERE="$(cd "$(dirname "$0")" && pwd)"SLATE_BUILD="${1:-$HERE/../task-lantern-system-slate/build/web}"SLATE_REPO="${1:-$HERE/../slate}"shift || true # remaining args pass through to `lantern run`LANTERN_BIN="$HERE/build/native/bin/lantern"[ -x "$LANTERN_BIN" ] || { echo "missing $LANTERN_BIN — run: sigil build lantern-cli --config native" >&2; exit 1; }[ -f "$SLATE_BUILD/index.html" ] || { echo "no web build at $SLATE_BUILD" >&2; exit 1; }# Accept either a repo folder (…/build/web/index.html) or a web build dir directly.if [ -f "$SLATE_REPO/build/web/index.html" ]; then SLATE_BUILD="$SLATE_REPO/build/web"elif [ -f "$SLATE_REPO/index.html" ]; then SLATE_BUILD="$SLATE_REPO"else echo "no Slate web build found under: $SLATE_REPO" >&2 echo " expected $SLATE_REPO/build/web/index.html (build Slate first: sigil build)" >&2 exit 1fi# Normalize to an absolute path so the guix-shell child resolves it correctly.SLATE_BUILD="$(cd "$SLATE_BUILD" && pwd)"echo "==> Slate repo: $SLATE_REPO"echo "==> Slate build: $SLATE_BUILD"echo "==> lantern: $LANTERN_BIN"echo "==> transparency: ON (toggle Slate translucency from its command palette)"exec guix shell -m "$HERE/gl-manifest.scm" -- bash -c ' export LD_LIBRARY_PATH="$GUIX_ENVIRONMENT/lib" export LANTERN_LIB_DIR="$GUIX_ENVIRONMENT/lib" exec "$0" run "$1" --title Slate' "$LANTERN_BIN" "$SLATE_BUILD" bin="$1"; build="$2"; shift 2 exec "$bin" run "$build" --title Slate --transparent "$@"' _ "$LANTERN_BIN" "$SLATE_BUILD" "$@"