diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 767453a..e16cdd5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -18,6 +18,55 @@ Every ZDDC tool compiles to a single self-contained `.html` file — no servers, --- +## ADR: Browse-as-shell with preview-pane plugins (target architecture) + +**Status:** accepted; migrating incrementally (2026-06). + +**Context.** The seven tools have started converging on browse: it already hosts classifier (grid iframe), tables (the table-leaf iframe), forms, and the md / yaml / `.zddc`-form editors in its preview pane, and the header chrome (profile menu + elevation) is shared across every tool. Rather than maintain seven parallel apps, the target is **one shell with a plugin content pane**. + +**Decision.** Browse is the shell — header + tree + preview pane, one top-level document. Content tools render into the preview pane as **plugins**. Server-only behaviour (the account menu, permission-gated affordances) is **progressive enhancement**: it activates when zddc-server serves the page and `/.profile/access` answers, and is simply absent on `file://`. We do **not** iframe browse inside a server-rendered header — browse owns its header and the server enhances it in place. (So "browse opened locally is missing the server header" resolves to "the same header with its server-only items dormant," not a separate page.) + +- **Server mode** is the security boundary: browse fetches ACL-gated listings + per-entry verbs; plugins act through a capability object and can't exceed what the server grants. +- **Local mode** (`file://`) is unrestricted: a picked FS-Access directory handle, no server, no account menu — by design. + +**Plugin contract.** A plugin is a module on `window.app.modules`; the shell dispatches to the first whose `handles` returns true: + +``` +handles(node, ctx) -> bool // claim this node / selection? +render(node, container, ctx) // mount into the preview pane (or a host element) +dispose?() // tear down (called before switching away) +isDirty?() / currentNode?() // optional: unsaved-edit guard + re-render hooks +``` + +`ctx` is the capability object the shell supplies — the ONLY thing that differs between server and local mode, so a plugin is written once: + +``` +ctx = { + mode: 'server' | 'fs', + getArrayBuffer(node), getContentWithVersion(node), // read (etag/lastmod → optimistic concurrency) + saveFile(node, bytes, contentType, opts), // write: ACL-enforced (server) / FS-Access (local) + cap.has(node, verb), // 'rwcda' subset; '' or unknown offline + // server-only (undefined offline): access(path), elevation, history(node) +} +``` + +The md / yaml / `.zddc`-form editors already follow this shape (`handles` / `render` / `isDirty` / `currentNode` + a ctx with `getArrayBuffer` / `getContentWithVersion`); table-leaf and classifier-grid are the same idea via an iframe bridge. Formalising `ctx` makes the contract explicit and lets the heavy tools migrate from iframe to in-pane module — preferred, for shared selection / theme / permission state with no `postMessage`. + +**Migration (incremental; standalone tools keep working throughout).** +1. ✓ Editors are in-pane modules; classifier / tables / forms embed in the pane; the shell header carries the profile menu + progressive-enhancement elevation. +2. Fold `archive` into the tree + a listing plugin. +3. Make `landing` the shell's root ("no project selected") view. +4. Move `transmittal` into a workflow plugin. +5. Flip `default_tool` routing to "browse + plugin X"; retire each standalone `.html` only once its plugin lands. + +**Consequences / tradeoffs.** +- Preserves the single-file + offline value: the shell still builds to one `browse.html` that runs from `file://`. Heavy plugins should lazy-load in server mode to keep the bundle reasonable. +- The server stays the only security boundary; local is unrestricted by definition. +- Seven lockstep release artifacts collapse toward one shell (plus optionally-separate plugins). +- Not every tool is a clean pane plugin — `transmittal` is workflow-heavy, `landing` is really the root view — called out above. + +--- + ## Repository Structure Every HTML tool follows the same directory layout: