Keep editor open across in-site navigation, retargeting to the new page

load() closed the editor on every navigation, and the close raced the
navigation: the shell's close-time loadPlain re-render resolved last,
swapping the old page back in and replaceStating its URL. Navigation now
leaves the panel open and the existing pagerite:switch-editor retarget
fires while editing; the exception is /_a, where closeEditor({ navigating:
true }) closes the panel without the re-swap or title restore.
This commit is contained in:
2026-09-20 00:23:31 +00:00
parent b248241af0
commit c337651020
3 changed files with 25 additions and 11 deletions
+4 -2
View File
@@ -44,11 +44,13 @@ function openShell() {
editorLang.value = session editorLang.value = session
pinPreviewLang() pinPreviewLang()
} }
function unpinPreviewLang() { function unpinPreviewLang(ev) {
if (!pinned) return if (!pinned) return
pinned = false pinned = false
setLangOverride(null) setLangOverride(null)
loadPlain(currentPath.value) // A close caused by navigation (to /_a) must not re-render the page the
// editor was on: the navigation itself is swapping in the target page.
if (!ev?.detail?.navigating) loadPlain(currentPath.value)
} }
watch(editorLang, () => { if (pinned) pinPreviewLang() }) watch(editorLang, () => { if (pinned) pinPreviewLang() })
// The page's primary may be (re)learned while pinned on it (doc accept, // The page's primary may be (re)learned while pinned on it (doc accept,
+7 -2
View File
@@ -125,7 +125,11 @@ function showEditor() {
dispatchEvent(new CustomEvent('pagerite:editor-shown')) dispatchEvent(new CustomEvent('pagerite:editor-shown'))
} }
export function closeEditor() { // navigating: the close is part of a fetch-navigation (to /_a) — the shell
// must not re-swap the page it was previewing back in, and the navigation
// itself sets the new title, so both the unpin re-render and the title
// restore are skipped.
export function closeEditor({ navigating = false } = {}) {
if (!visible) return if (!visible) return
visible = false visible = false
stopTrackingPanel() stopTrackingPanel()
@@ -136,7 +140,8 @@ export function closeEditor() {
host.firstElementChild?.classList.add('closing') host.firstElementChild?.classList.add('closing')
const h = host const h = host
setTimeout(() => { h.style.display = 'none' }, 250) setTimeout(() => { h.style.display = 'none' }, 250)
dispatchEvent(new CustomEvent('pagerite:editor-hidden')) dispatchEvent(new CustomEvent('pagerite:editor-hidden', { detail: { navigating } }))
if (navigating) return
// The editor may have dropped the prefetch cache; warm it again for the // The editor may have dropped the prefetch cache; warm it again for the
// now-final page so navigation stays instant. // now-final page so navigation stays instant.
dispatchEvent(new CustomEvent('pagerite:preload-pages')) dispatchEvent(new CustomEvent('pagerite:preload-pages'))
+14 -7
View File
@@ -843,13 +843,16 @@ import { reconnectPolicy, socketSlot, watchConnecting } from "./reconnect";
// --- Fetch navigation ------------------------------------------------ // --- Fetch navigation ------------------------------------------------
async function load(url, push = true, back = false) { async function load(url, push = true, back = false) {
// Navigating with the editor open closes it; unsaved edits are lost // Navigating with the editor open keeps it open: the shell retargets to
// (the region swap discards the previewed changes anyway). Cache must be // the new page once the swap lands (below). The exception is /_a
// bypassed for this navigation because the editor may have invalidated // (analytics), where the panel does not apply — close it there, with
// the prefetched copies of other pages. // navigating: true so the close does not re-swap/re-title the page it
// was previewing (this navigation is already swapping). The cache is
// bypassed while editing because the editor may have invalidated the
// prefetched copies of other pages.
const editing = document.body.classList.contains("editing"); const editing = document.body.classList.contains("editing");
if (editing) { if (editing && new URL(url, location.href).pathname === "/_a") {
editorModule?.then((m) => m.closeEditor()); await editorModule?.then((m) => m.closeEditor({ navigating: true }));
} }
teardownAnalytics(); teardownAnalytics();
let doc; let doc;
@@ -936,7 +939,11 @@ import { reconnectPolicy, socketSlot, watchConnecting } from "./reconnect";
// own lang="en" dir="ltr", so it is unaffected). // own lang="en" dir="ltr", so it is unaffected).
document.documentElement.lang = doc.documentElement.lang; document.documentElement.lang = doc.documentElement.lang;
document.documentElement.dir = doc.documentElement.dir; document.documentElement.dir = doc.documentElement.dir;
document.title = doc.title; // The editor keeps its own title while open (the retargeted tab
// re-applies it); only inherit the server title when not editing.
if (!document.body.classList.contains("editing")) {
document.title = doc.title;
}
// Banners may contain scripts (canvas etc.), content pages may too. // Banners may contain scripts (canvas etc.), content pages may too.
runScripts(document.getElementById("page-banner")); runScripts(document.getElementById("page-banner"));
runScripts(document.getElementById("main")); runScripts(document.getElementById("main"));