# WebUI JavaScript DOX ## Purpose - Own shared frontend JavaScript modules, client-side infrastructure, API helpers, WebSocket clients, stores, extension loaders, and UI utilities. - Keep frontend contracts stable for core UI and plugin extensions. ## Ownership - `AlpineStore.js` owns store creation and persistence helpers. - `api.js` owns CSRF-aware HTTP helpers. - `websocket.js` owns browser WebSocket client behavior. - `extensions.js` owns frontend extension loading. - `components.js` owns `` loading, component caching, module injection, nested component processing, and `globalThis.xAttrs`. - `modals.js` owns the stacked modal shell, `openModal`, `closeModal`, `scrollModal`, footer relocation, backdrop, and modal z-index behavior. - `surfaces.js` owns shared surface registration, right-canvas/modal mode routing, surface modal action rails, and reusable draggable/focus modal chrome. - `initFw.js` owns Alpine bootstrap and custom lifecycle directives such as `x-create`, `x-destroy`, and periodic `x-every-*` hooks. - Other modules own focused UI utilities such as modals, messages, safe markdown, shortcuts, TTS/STT, surfaces, and initialization. ## Local Contracts - Use ES modules and browser-compatible JavaScript. - Route JSON and fetch calls through `api.js` unless a caller has a specific nonstandard transport contract. - `callJsonApi()` is for JSON request/response flows and must preserve CSRF/auth behavior. - `fetchApi()` must continue adding CSRF headers, retrying 403 CSRF refresh paths, and redirecting to `/login` when required. - `createStore(name, model)` must continue working before and after Alpine boots by proxying to the raw model first and Alpine store later. - `saveState()` and `loadState()` must not persist functions and should support include/exclude filtering for transient fields. - `openModal(path)` returns a promise that resolves when that modal DOM node is removed; invalid paths show an in-modal error instead of rejecting. - Opening the same modal path multiple times must continue creating multiple stack entries; no dedupe is assumed. - `closeModal()` with no path closes the top modal; `closeModal(path)` closes that path wherever it is in the stack; missing paths are no-ops. - Modal stack semantics are top-modal-first for Escape, close buttons, z-index, and backdrop placement. - Restorable modal state is session-scoped and opt-in; surface modals may set `data-modal-restore="surface"` and `modals.js` restores only those path-based surface windows after reload navigation. Browser hard-refresh is still reported to app code as reload, so do not treat this as durable cross-session UI state. - The modal shell structure is `.modal` > `.modal-inner` > `.modal-header`, `.modal-scroll` containing `.modal-bd`, and `.modal-footer-slot`. - `data-modal-footer` content is relocated from modal body into `.modal-footer-slot`. - Click-outside close requires both `mousedown` and `mouseup` on the outer `.modal` container. - `scrollModal(id)` scrolls inside the top modal's `.modal-scroll`. - Keep extension loader cache keys and extension point names stable for plugins. - HTML extension loading turns discovered HTML files into `` tags; JavaScript extensions must export a default function. - `` loading must process component `style`, `script`, and stylesheet-link assets only once, even when a component keeps its scoped `