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
- `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
- The editor is a *mode* of a page, not a destination. `history-nav.js` turns any
same-path `?edit` link (and the editor's CANCEL link back out) into
`location.replace`, and SAVE POSTs via fetch and then rewrites the entry with
the saved page. Net effect: an edit session never occupies a history entry of
its own. Links to a *different* page's editor (new page / new child) still push.
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.
- The editor is a *mode* of a page. Opening it pushes a normal history entry so
the browser/Android Back button cancels the edit and returns to the page — this
"back to cancel" gesture takes priority. `history-nav.js` only rewrites the way
*out*: leaving the editor via the same-page CANCEL link uses `location.replace`
so the editor entry is collapsed rather than sandwiched between two page entries
(which would let Back walk back into the editor). SAVE does the equivalent — it
POSTs via fetch and then `replaceState`s the editor entry with the saved page,
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:
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.
+3 -2
View File
@@ -4,8 +4,9 @@
switch (e.key) {
case 'E':
e.preventDefault();
// replace, not assign — same reasoning as history-nav.js.
window.location.replace(window.location.pathname + '?edit');
// assign, not replace — opening the editor pushes a history
// entry so Back cancels the edit (see history-nav.js).
window.location.href = window.location.pathname + '?edit';
break;
case 'N':
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
// new destination, so it replaces the current history entry instead of pushing
// 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.
// Opening the editor pushes a normal history entry, so Back exits the editor
// and returns to the page (the primary "back to cancel" gesture on mobile).
//
// Links to a *different* page's editor (new page, new child) still push — the
// page you started from has to stay in history.
// Leaving the editor by CANCEL is the one transition we rewrite: the CANCEL
// 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 isEdit(loc) {
return new URLSearchParams(loc.search).has('edit');
@@ -20,12 +20,16 @@
var a = e.target.closest ? e.target.closest('a[href]') : null;
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);
if (url.origin !== window.location.origin) return;
if (url.pathname !== window.location.pathname) return;
// Same page: only editor entry/exit is a mode switch. Plain anchor
// links share the pathname too and must keep their normal behaviour.
if (!isEdit(url) && !isEdit(window.location)) return;
// Leaving the editor to another page (e.g. a wikilink) keeps its normal
// push; only the same-page exit (CANCEL) is collapsed.
if (isEdit(url)) return;
e.preventDefault();
window.location.replace(url.href);