Fix back-to-cancel behavior of editor

This commit is contained in:
2026-07-24 10:55:29 +02:00
parent a849474b26
commit da30bb8fc3
3 changed files with 32 additions and 22 deletions
+13 -8
View File
@@ -75,14 +75,19 @@ Prefer separate, human-readable `.html` files over inlined HTML strings in Go. E
- Do not inline JS in templates or merge unrelated features into one file - Do not inline JS in templates or merge unrelated features into one file
- `ALT+SHIFT` is the modifier for all keyboard shortcuts — do not introduce others - `ALT+SHIFT` is the modifier for all keyboard shortcuts — do not introduce others
- Editor toolbar buttons use `data-action` + `data-key`; adding `data-key` auto-registers the shortcut - Editor toolbar buttons use `data-action` + `data-key`; adding `data-key` auto-registers the shortcut
- The editor is a *mode* of a page, not a destination. `history-nav.js` turns any - The editor is a *mode* of a page. Opening it pushes a normal history entry so
same-path `?edit` link (and the editor's CANCEL link back out) into the browser/Android Back button cancels the edit and returns to the page — this
`location.replace`, and SAVE POSTs via fetch and then rewrites the entry with "back to cancel" gesture takes priority. `history-nav.js` only rewrites the way
the saved page. Net effect: an edit session never occupies a history entry of *out*: leaving the editor via the same-page CANCEL link uses `location.replace`
its own. Links to a *different* page's editor (new page / new child) still push. so the editor entry is collapsed rather than sandwiched between two page entries
The save POST answers `204` + `X-Target` when the request carries (which would let Back walk back into the editor). SAVE does the equivalent — it
`X-Save-Mode: replace`, because the target may hold a `#section` anchor only POSTs via fetch and then `replaceState`s the editor entry with the saved page,
the server can compute and fetch drops fragments from followed redirects. so the editor never lingers in history once you leave it (the pre-save page
snapshot one step back is the accepted cost). Links to a *different* page's
editor (new page / new child) push normally. The save POST answers `204` +
`X-Target` when the request carries `X-Save-Mode: replace`, because the target
may hold a `#section` anchor only the server can compute and fetch drops
fragments from followed redirects.
- For mutating modals (anything that POSTs and then navigates), call `closeModal()` and then `postReplace(action, body, target)` from `page/actions.js`. Do NOT use `<form>.submit()`. Two reasons: - For mutating modals (anything that POSTs and then navigates), call `closeModal()` and then `postReplace(action, body, target)` from `page/actions.js`. Do NOT use `<form>.submit()`. Two reasons:
1. The modal must be removed from the DOM before navigation, or the browser's bfcache snapshots it open and back-nav restores the modal. 1. The modal must be removed from the DOM before navigation, or the browser's bfcache snapshots it open and back-nav restores the modal.
2. `postReplace` uses `window.location.replace` so the action + result occupy a single history entry. A naive POST → 303 → GET creates two entries, and back-nav lands on a stale pre-mutation snapshot of the same page. 2. `postReplace` uses `window.location.replace` so the action + result occupy a single history entry. A naive POST → 303 → GET creates two entries, and back-nav lands on a stale pre-mutation snapshot of the same page.
+3 -2
View File
@@ -4,8 +4,9 @@
switch (e.key) { switch (e.key) {
case 'E': case 'E':
e.preventDefault(); e.preventDefault();
// replace, not assign — same reasoning as history-nav.js. // assign, not replace — opening the editor pushes a history
window.location.replace(window.location.pathname + '?edit'); // entry so Back cancels the edit (see history-nav.js).
window.location.href = window.location.pathname + '?edit';
break; break;
case 'N': case 'N':
e.preventDefault(); e.preventDefault();
+16 -12
View File
@@ -1,14 +1,14 @@
// Keeps the page editor out of the browser history. // Keeps the editor from lingering in history once you leave it — while still
// letting the browser/Android Back button CANCEL an edit session.
// //
// Opening the editor for the page you are already on is a mode switch, not a // Opening the editor pushes a normal history entry, so Back exits the editor
// new destination, so it replaces the current history entry instead of pushing // and returns to the page (the primary "back to cancel" gesture on mobile).
// one; CANCEL replaces it right back, and SAVE does the same via postSave in
// editor/main.js. Without this, page -> edit -> save leaves
// [prev, page, editor, page'] behind and Back walks through the editor and a
// stale pre-save snapshot of the page before reaching prev.
// //
// Links to a *different* page's editor (new page, new child) still push — the // Leaving the editor by CANCEL is the one transition we rewrite: the CANCEL
// page you started from has to stay in history. // link points back at the same page, so we replace the editor entry instead of
// pushing a second page entry on top of it. Without this, page -> edit -> CANCEL
// would leave [page, editor, page] and Back would walk straight back into the
// editor. SAVE does the equivalent from editor/main.js (replaceState + reload).
(function () { (function () {
function isEdit(loc) { function isEdit(loc) {
return new URLSearchParams(loc.search).has('edit'); return new URLSearchParams(loc.search).has('edit');
@@ -20,12 +20,16 @@
var a = e.target.closest ? e.target.closest('a[href]') : null; var a = e.target.closest ? e.target.closest('a[href]') : null;
if (!a || a.target || a.hasAttribute('download')) return; if (!a || a.target || a.hasAttribute('download')) return;
// Only act while inside the editor. Entering the editor stays a normal
// push so Back can cancel it.
if (!isEdit(window.location)) return;
var url = new URL(a.href, window.location.href); var url = new URL(a.href, window.location.href);
if (url.origin !== window.location.origin) return; if (url.origin !== window.location.origin) return;
if (url.pathname !== window.location.pathname) return; if (url.pathname !== window.location.pathname) return;
// Same page: only editor entry/exit is a mode switch. Plain anchor // Leaving the editor to another page (e.g. a wikilink) keeps its normal
// links share the pathname too and must keep their normal behaviour. // push; only the same-page exit (CANCEL) is collapsed.
if (!isEdit(url) && !isEdit(window.location)) return; if (isEdit(url)) return;
e.preventDefault(); e.preventDefault();
window.location.replace(url.href); window.location.replace(url.href);