- 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.
- New editor-shell tab (lang): translatable languages as clickable flag
tiles (shared langs.js mapping of the 28 Seed-X languages to flags),
saved via the settings round-trip; translator service URL(s) shown.
- Bootstrap seeds translate_langs with Spanish and Chinese (the original
language is never a target).
- GET /_api/settings also returns primary_lang.
- PageEditor's flag/name helpers move to the shared langs.js.
- Image insert always adds an empty "" caption with the cursor inside.
- Fenced blocks (``` code, ::: aside) share one toggle: clicked inside
one it is removed and the content selected; otherwise the selection
(expanded to whole lines) is wrapped, cursor left on the opener line.
- Code button: inline wrap toggles (selection preserved, backtick runs),
line-spanning selections make fenced blocks.
- Link button toggles: clicked inside [label](url) it unwraps.
- Block classes via two pickers (placement, AA size) with current-class
indication and a "normal" reset; placement replaces ::: container
names, fences get their own attribute line.
- New .small/.large/.huge text-size classes (0.7/1.5/3em); base body is
exactly 1rem so the scale is uniform.
- .left/.right floats generalized from figures to any block.
- Toolbar polish: non-emoji glyphs, scaled-up symbols, borderless
hover/active states, reordered (B/i and size last).
- Editor panel no longer closes on Escape.
- CodeMirror editors capture Tab/Shift-Tab for indent/dedent.
- One mounted shell with four kept-alive tabs; pens open/switch tabs,
closing only hides the shell so unsaved page-editor state survives
until a real reload; admin panels never reload the page, regions are
refreshed in place via the shared swapdoc.js helper
- SiteEditor split: site settings vs. the new StructureEditor tree tab;
BannerEditor split out of the old site editor
- Article editor format bar (bold/italic/code/link/table/image) with a
table size picker and Ctrl/Cmd-B/I/S bindings; fenced code block on an
empty line; icon-only save button disabled (saturate(0)) when clean
- Media uploads use icon buttons; favicon upload by clicking the preview
tile; placeholders reserved for actual defaults in effect
- Banner/site pens and auth buttons grouped in an .editor-pens container;
page pen no longer shown to anonymous visitors
- While editing, window scroll is locked, the panel exactly fills the
available window height, and only #main scrolls; editor scroll drives
the article
- Banner design inherit label names the design and its true source;
backend banner_design_source excludes the node's own setting and the
doc payload carries banner_design_inherited