Backend shell: route repair lives in TreePane, so bad URLs strand the editor #35
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#35
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #33 / PR #34, which shipped the TYPO3-style backend shell. Three defects found while landing that PR were accepted as known and deferred to here. They share one root cause: route repair is implemented as an effect inside
TreePane— a conditionally-mounted component — instead of in the shell, and it navigates with a push instead of a replace.1. Nonexistent-page repair only runs while the tree column is mounted
web/src/components/TreePane.tsx:184(prune effect),web/src/App.tsx:352(<Show when={showTree()}>).The only code that repairs a URL naming a page the tree does not have is TreePane's prune effect, and TreePane renders only when
treeAvailable() && !treeCollapsed(). Collapse is persisted indeckle:tree-collapsed.Repro: collapse the page tree (or set
sessionStorage["deckle:tree-collapsed"]="1"), then load/page/en/ghost.md.selectedPage()is non-null, PagePane fetches, the server 404s, and thepage-erroralert (PagePane.tsx:954) renders permanently — the URL never repairs and the empty state never appears.#33 requires the opposite: "A URL naming a nonexistent page falls back to the empty state (no error page)." This is the everyday case of a stale bookmark or a link to a page a colleague deleted.
The same hole opens for any future module declaring
tree: false, which the registry is explicitly designed to allow. Even with the tree visible, the error pane flashes for one frame before the tree arrives and prunes.2. The repair pushes instead of replacing — the back button is trapped
web/src/selection.ts:111—selectPagecallsdeps.navigate(...)with no{replace: true}, and the prune goes through it.Repro: load
/page/en/ghost.md(tree visible). It prunes to/page/enandhistory.lengthgrows by one. Press Back: you return to/page/en/ghost.md, the prune fires again and pushes/page/enagain. The editor can never navigate back past that point.Second manifestation:
handleRenamed/handleMoved(TreePane.tsx:296)await props.refetch()first, which makes the prune fire synchronously (the old path is gone) and push/page/en, then push the new path. So every rename, move and bundle conversion leaves a spurious empty-state entry in history.A route repair must
replace, neverpush.3. An unknown language is never repaired
web/src/App.tsx:106— the route-repair effect fires only whenactiveModule() === undefined, i.e. for an unknown module id. An unknownlanggets nothing.Repro: open
/page/de/about.mdon a single-language (en) site. The module resolves, so nothing redirects.GET /api/page?lang=dereturns 400unknown lang(internal/server/server.go:413) and the error pane shows; the tree then prunes and rebuilds the route withroute.lang, leaving the editor parked on/page/dewith a permanently empty page module while every tree click silently rewrites the URL back to/page/en/.... With the tree collapsed (defect 1) it is a permanent error page.A lang the tree does not report should be repaired exactly as an unknown module id is.
Suggested fix
Lift route repair out of
TreePaneand into the shell, where it belongs as a route-level rule: it should run regardless of whether any tree is mounted, cover module / lang / page alike, and navigate withreplace. That addresses all three defects together. Rename/move handlers should navigate to the new path without the intermediate prune.Not blocking, found in the same review
web/src/guard.ts:133—saveAndContinueis re-entrant: the dialog's "Save & continue" button has no busy state and the guard keeps no in-flight flag, so a double-click issues two concurrentPOST /api/page/savecalls for the same file — precisely the read-modify-write race the sequential-save loop is documented to prevent.web/src/App.tsx:146— a pending preview-bridge selection is cleared only when its exact page loads. Navigating elsewhere first leaves it stashed, and it can hijack the element selection when that page is next opened.