From 9e0342951c1e0d61876965a62f2dae707cd39bc5 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Sun, 10 May 2026 22:36:51 +0200 Subject: [PATCH] Improve page swap fallback --- slhx-js/runtime/slhx.js | 22 +++++++++++++++------- slhx-js/tests/runtime.rs | 2 ++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/slhx-js/runtime/slhx.js b/slhx-js/runtime/slhx.js index a9584f2..9cc3b2f 100644 --- a/slhx-js/runtime/slhx.js +++ b/slhx-js/runtime/slhx.js @@ -144,7 +144,10 @@ async function navigateUrl(href, root, mode = "replace") { const response = await fetch(href, { headers: { "X-SLHX-Partial": "1", "Accept": "text/html" } }); - await applyResponse(response, root); + if (!await applyResponse(response, root) && mode !== "none") { + location.href = href; + return; + } if (mode === "push") history.pushState({ slhx: true }, "", href); else if (mode === "replace") history.replaceState({ slhx: true }, "", href); } @@ -152,15 +155,19 @@ async function applyResponse(response, root) { if (response.redirected) { location.href = response.url; - return; + return false; } if (!compatibleFingerprint(response, root)) { location.reload(); - return; + return false; } const type = response.headers.get("content-type") || ""; - if (type.includes("text/html")) applyHtml(await response.text(), root, response.headers.get("x-slhx-title")); - else if (type.includes("application/slhx")) applyBatch(await response.arrayBuffer(), root); + if (type.includes("text/html")) return applyHtml(await response.text(), root, response.headers.get("x-slhx-title")); + if (type.includes("application/slhx")) { + applyBatch(await response.arrayBuffer(), root); + return true; + } + return false; } function compatibleFingerprint(response, root) { @@ -335,12 +342,13 @@ const doc = new DOMParser().parseFromString(html, "text/html"); const template = doc.querySelector("template[data-slhx]"); if (!replaceSlot(scope, doc, "content", template ? template.innerHTML : html)) { - location.reload(); - return; + emit(scope, "slhx:missing-content-slot", null); + return false; } replaceSlot(scope, doc, "nav"); const nextTitle = title || (doc.querySelector("title") && doc.querySelector("title").textContent); if (nextTitle) document.title = nextTitle; + return true; } function replaceSlot(scope, doc, name, fallback) { diff --git a/slhx-js/tests/runtime.rs b/slhx-js/tests/runtime.rs index cc39756..80db231 100644 --- a/slhx-js/tests/runtime.rs +++ b/slhx-js/tests/runtime.rs @@ -36,6 +36,8 @@ fn runtime_exposes_page_swap_hooks() { assert!(source.contains("popstate")); assert!(source.contains("x-slhx-title")); assert!(source.contains("x-slhx-fingerprint")); + assert!(source.contains("slhx:missing-content-slot")); + assert!(source.contains("location.href = href")); } #[test]