Shared editor language selection; paced WebSocket reconnects

- LangSelect.vue: one language selector for the page and structure tabs
  (small flag, clean dropdown), v-modeled on the shell-wide editorLang —
  switching in either panel switches both AND the page preview. While the
  panel is open the selection overrides the normal language preferences
  (swapdoc.setLangOverride: loadPlain and pagerite.js fetches/prefetches
  pin ?lang=, the primary language by its own code); closing restores.
- reconnect.js: every socket (page/banner editors, analytics view, the
  activity channel) now connects through a staggered slot — simultaneous
  attempts at page load (Vite's HMR socket plus ours, repeated on every
  refresh) trip the browser's WebSocket throttling, leaving all sockets
  to the host "pending" for minutes, which was the recurring empty
  editor. A watchdog closes sockets stuck CONNECTING so they reschedule
  through the policy (jittered exponential backoff, reset only by a
  healthy connection) instead of hanging forever; a reconnected editor
  re-opens its document when the previous open died with its socket.
This commit is contained in:
2026-09-03 01:47:24 +00:00
parent 43793ef9a4
commit 2a430247e0
13 changed files with 411 additions and 223 deletions
+4
View File
@@ -15,6 +15,10 @@ Media uploads everywhere use the image icon buttons (pasting into the editor wor
In-place page re-rendering shared by the banner/site/structure tabs lives in `swapdoc.js` (`runScripts`/`loadPlain`: fetch a page, swap the dynamic regions, replaceState). It also exports `dropPageCache`, which the editor tabs call after any save that can alter the rendered HTML of other pages (theme, headings, structure, banners, site brand/CSS, favicon). Dropping the cache while editing avoids re-fetching every page immediately; the public runtime re-preloads visible links once the editor panel closes.
The page and structure tabs share one language selector: `LangSelect.vue` (small flag + dropdown) v-modeled on the shell-wide selection in `editorLang.js` (`''` = primary). While the panel is open that selection overrides the page's normal language preferences: EditorShell calls `swapdoc.setLangOverride`, which pins every `loadPlain` fetch (`?lang=`, the primary by its own code) and pagerite.js's own fetches/prefetches (`pagerite:session-lang`), until the panel closes and the override clears.
All WebSockets (page/banner editors, analytics view, the pagerite.js activity channel) pace their connections through `reconnect.js`: new sockets are created a staggered slot apart (a page load opens Vite's HMR socket plus several of ours at the same moment, and such bursts — like rapid retries — trip the browser's WebSocket throttling, leaving every socket to the host "pending" for minutes), a watchdog closes sockets stuck CONNECTING so they reschedule instead of hanging forever, and retries follow an exponential backoff with jitter that only a healthy connection resets.
## Saving behavior
Everything saves immediately as you edit (brand/title/CSS debounced, slug on commit since it renames the path), theme change swaps the stylesheet in place, tree rows navigate in place without transitions when focused, and the front page is a root-only row whose empty slug is editable like any other. Saves that can affect other pages drop the prefetch cache; the cache is rebuilt when the editor panel closes so navigation stays instant.