UI == Agency's product UI is a **schema-driven SPA**: Django (and host apps) describe *what* the interface means; **agency-ui** (Vue 3 / Vite) decides *how* it renders. Operators use the **Agency desktop** at ``/`` (after ``/login/``). The desktop is an installable PWA: offline it waits for the server, and a backend restart reloads the shell. Staff admin sites remain under ``/admin/``, ``/system/``, ``/custom/``. **Mission Control** is the Agents / Missions / Executions app on that desktop (:doc:`mission-control`). .. toctree:: :maxdepth: 2 sites schema event-bus frontend python mission-control Three-repo split ---------------- .. list-table:: :header-rows: 1 :widths: 18 82 * - Repo - Role * - **agency-ui** - Vue source — schema types, renderer, desktop + Mission Control components * - **agency-django** - Domain + REST + Channels; ships published ``static/agency/ui/`` * - **agency-server** - Compose, nginx, ``collectstatic`` Publish flow:: agency-ui ──npm run build:django──► agency-django/static/agency/ui/ │ ▼ collectstatic nginx /static/ + Django / Do **not** hand-edit ``agency-django/.../static/agency/ui/``. Core principle -------------- Django sends semantic nodes, never Vuetify tags:: { "type": "progress", "props": { "value": 42, "label": "Indexing" } } The renderer maps ``progress`` → a Vue/Vuetify component. Adding a screen means a new **schema node type** + **registry entry**, not a one-off Django template. Component catalogue: :doc:`frontend`. Envelope and node contracts: :doc:`schema`. Reactive event routing: :doc:`event-bus`. Bootstrap / observe from Python: :doc:`python`. Useful URLs: :doc:`sites`. User preferences & Desktop -------------------------- Authenticated users have ``UserPreferences`` (workspace, UI, operator, system voice), exposed as ``GET/PATCH /api/me/prefs/`` and injected in bootstrap as ``prefs``. * **UI** — light/dark, language (en/es), IANA timezone. Change language in the user window; the page reloads. Timezone is used by ``get_datetime`` when a secretary run is bound. * **RAG** — active corpus workspace (Documentos / Buscar). Explorers have no workspace dropdown. * **Preferences** — active operator agent, **system voice** (grave OS lines and speaking on behalf of the user). Each Agent has its own ``Voice`` (TTS provider + key); agent chat / mission TTS uses that, falling back to the system voice. * **Desktop snapshot** — open windows, ``minimized`` / ``maximized`` flags, geometry ``x/y/w/h``, chrome ``pose``, ``projection`` (``flat`` / ``perspective``), and optional child ``props`` for ephemeral restore. Debounced PATCH after move / resize / open / close. Window addresses ~~~~~~~~~~~~~~~~ Stable ids for dedupe, the **Windows** drawer, and Desktop persistence: * Singleton (schema app): ``agents``, ``documents``, ``system``, ``media``, ``map``, … * Instance: ``kind.ref`` — e.g. ``extraction.``, ``agent.``, ``user.me``, ``execution.``. Dot present ⇒ instance window. ``kind`` is a *window kind* (view), not always a Django model name. Helpers: ``agency-ui/src/desktop/windowAddress.ts`` and ``openAddress.ts``. Legacy ``win-*`` ids normalize to this grammar. Window manager (operator UX) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``RuntimeShell`` drawer: **profile header** (48px avatar + name) then tabs **Menu** · **Windows** · **Inbox**. App bar (right): one **System** icon (channel / two PCs while idle; REST or telemetry glyph during recent activity — click opens System). **Theme** toggle only while windows are floating. On mobile, and when a desktop window is **maximized**, the active window's **minimize** / **close** replace the theme button and the title moves into the app bar (``Agency / {window}``). Desktop maximized also shows **restore**. The app bar is always flat (no elevation). Mobile has no restore — minimize is the way back. * **Profile** — click avatar/name in drawer → ``user.me`` window (**User** · **Options** · **Sites**). **Options** is grouped **UI** / **RAG** / **Preferences**; **Sites** lists external admin/docs links. * **Menu** — open catalog / tool windows (Agents, Missions, System, …). * **Windows** — all open windows (desktop and mobile). Click to focus or restore; close button on each row. Badge shows open count. * **Inbox** — pending HITL questions (agent + text); click opens the reply dialog. Internationalization ~~~~~~~~~~~~~~~~~~~~ Shell strings use **Django gettext** (same catalogs as admin). ``build_bootstrap`` activates the user language and embeds ``locale`` + ``messages`` in ``#agency-bootstrap``. Preference ``UserPreferences.language`` (``me/prefs/``); changing language in the user window PATCHes prefs and reloads the page. Catalog source: ``agency.ui.i18n``; Spanish: ``agency/locale/es/LC_MESSAGES/django.po``. Vue keeps ``agency-ui/src/i18n/*.ts`` only as a **Vite dev fallback** when bootstrap has no ``messages``. Desktop: floating windows over the wallpaper; icons that are not in the drawer Menu (active-agent face, Chat, Tests). Catalog apps open from **Menu**. Mobile (and desktop **maximize**): one full-screen window at a time (same store, no floating chrome); title and min/close live in the app bar. Desktop maximize also exposes restore so the window returns to floating geometry; the app bar drops its shadow while that chrome is up. Window controls: floating title bar has minimize / maximize / close. Mosaic lives in the drawer tools row. Maximize uses the same chrome as mobile. Wheel over maximize scales the window's CSS 3D ``pose.scale`` 5% about the cursor (persisted). Mosaic tiles every open window — 2D shelf / 3D carousel included — in a CCTV grid (2 = split, 4 = 2×2, 5 = 1+4, up to 16); the active window takes the featured pane. Minimize from that chrome returns to the desktop (or the next window). Restore from the app bar (desktop only) does the same without hiding the window. Double-click the floating title bar toggles maximize unless ``noFull`` / ``noMax`` is set. Staff can inspect or reset a user's Desktop snapshot from Django admin **User preferences** (``/admin/`` and ``/system/``). Desktop vs Mission Control -------------------------- ``/`` is the Agency **desktop** (icons, windows, drawer). Other apps on the same shell: Help, Camera, Documents, Displays, Photos, Media, Map, System, … **Mission Control** (Menu → Dispatch) is only Agents, Missions, Executions, and the APIs that drive them. Flows: :doc:`mission-control`. Execution lifecycle: :doc:`../processes/execution`. WebSockets: :doc:`../../framework/connections`. Development & publish --------------------- .. list-table:: :header-rows: 1 :widths: 28 72 * - Task - Where * - Edit Vue source - ``agency-ui`` * - Publish dist - ``npm run build:django`` or umbrella ``./dev ui`` (:doc:`../workspace`) * - Commit dist - ``agency-django/src/agency/static/agency/ui/`` * - Collect static - ``agency-server`` — ``collectstatic`` in Docker See **DEVELOPMENT.md** in each repo. Node ≥ 20.19 for Vite 8.