Status: Proof of Concept (Alpha)
Atlas UI v3 is a browser-based registry explorer driven by two independent momentum streams:
This decoupling means the browser is always in sync with both the LLM's intent and the user's exploration, without either side blocking the other.
(require '[atlas.atlas-ui.server :as ui])
;; Start v3 (file-root keeps hot-reload working without JAR rebuild)
(atlas.atlas-ui.server/start!
{:ui-version :v3
:open-browser? false
:port 8082
:file-root "/path/to/atlas/ui/resources/public-v3"})
;; Navigate from the REPL or any tool
(clojure.data.json/write-str
{:mode "entity-focus" :entity ":fn/validate-token"})
;; → POST /api/v3/state
Or via curl:
# entity-focus
curl -s -X POST http://localhost:8082/api/v3/state \
-H 'Content-Type: application/json' \
-d '{"mode":"entity-focus","entity":":fn/validate-token"}'
# domain-survey
curl -s -X POST http://localhost:8082/api/v3/state \
-H 'Content-Type: application/json' \
-d '{"mode":"domain-survey","aspect":":domain/auth"}'
# dataflow-focus
curl -s -X POST http://localhost:8082/api/v3/state \
-H 'Content-Type: application/json' \
-d '{"mode":"dataflow-focus","data-key":":execution-function/context"}'
┌──────────────────────────────────────────────────────────────┐
│ Claude Code / REPL / curl │
│ │
│ POST /api/v3/state {"mode":…,"entity":…,"aspect":…} │
└───────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ atlas.atlas-ui.server (Clojure/Ring) │
│ │
│ - Stores current view-state atom │
│ - GET /api/v3/sse → streams state changes as SSE events │
│ - GET /api/v3/registry → full registry snapshot (JSON) │
│ - POST /api/v3/state → updates atom, fans out to SSE │
└───────────────────────────┬──────────────────────────────────┘
│ SSE event stream
▼
┌──────────────────────────────────────────────────────────────┐
│ Browser (ClojureScript / Reagent) │
│ │
│ app-state atom: │
│ :view-state — current mode + entity/aspect/data-key │
│ :registry — fetched once on load │
│ :nav-history — [{:view-state … :scroll-top … …}] │
│ :expanded-lists — {:aspects true :home-types false …} │
│ :filter-text — domain-survey filter input │
│ :search-text — header search input │
│ │
│ User clicks → navigate-to! → POST /api/v3/state → SSE loop │
└──────────────────────────────────────────────────────────────┘
The registry is fetched once at startup and cached client-side. View state is the only thing that flows over SSE — registry updates require a server restart and browser reload.
| Mode | Triggered by | Shows |
|---|---|---|
home | default / home button | Registry overview — entity count, type breakdown with bar chart |
entity-focus | clicking an entity link | Entity identity (type chip, aspects), properties with semantic chips, data-flow panel |
blast-radius | blast-radius tab (requires entity) | All entities whose :*/deps include the focused entity, grouped by type |
domain-survey | clicking an aspect or type | All entities carrying a given aspect, grouped by type, with live filter input |
dataflow-focus | clicking a data-key chip | Split panel: producers (green) on left, consumers (blue) on right |
| Color | Meaning | Behavior |
|---|---|---|
Blue #3b82f6 | Entity reference (:dev-id resolves in registry) | Clickable → entity-focus |
Green #22c55e | Aspect (keyword appears in compound IDs) | Clickable → domain-survey |
Amber #f59e0b | Entity type (:atlas/… type keyword) | Clickable → domain-survey filtered to that type |
Purple #a855f7 | Data key (other qualified keyword) | Clickable → dataflow-focus |
Classification is done by classify-kw which checks the live registry:
:atlas/dev-id? → :entity-ref:aspect:data-keyEvery SSE-triggered navigation (i.e., any external tool navigating the browser) pushes the current state onto nav-history before applying the new state. The ← button in the header pops the stack and restores:
scrollTop of #v3-scroll-area):expanded-lists):filter-text)Long lists auto-collapse to a configurable threshold with a … N more toggle. Thresholds:
| Location | Default threshold |
|---|---|
| Home type list | 8 |
| Entity aspects | 6 |
| Domain-survey / blast-radius entity lists | 10 |
| Property keyword collections | 5 |
Each list has a stable key (:aspects, :home-types, (keyword entity-type), property keyword). Expanded state is stored in app-state [:expanded-lists key] so it survives re-renders and is saved/restored on back navigation.
The header search box (top-right) filters the in-memory registry by :atlas/dev-id. Type 2+ characters to show the top 10 matches; click to navigate to entity-focus. Search text resets on any SSE navigation.
The five mode tabs in the header are context-aware:
home — always clickableentity-focus / blast-radius — clickable when an entity is in scopedomain-survey — clickable when an aspect is in scopedataflow-focus — clickable when a data-key is in scopeTabs for unavailable context render in #555 with cursor: default.
When the server's view state includes a :narrative string, it appears as a pre-wrapped text block at the bottom of the screen (max 180px). The LLM or REPL sets this field to annotate the current view with context that wouldn't fit in the UI itself.
GET /api/v3/sse
→ text/event-stream; each event is {"mode":…,"entity":…,"aspect":…,"data-key":…,"narrative":…}
GET /api/v3/state
→ current view-state as JSON
POST /api/v3/state
Body: {"mode":"…","entity":"…","aspect":"…","data-key":"…","narrative":"…"}
→ updates server state, fans out to all SSE subscribers
GET /api/v3/registry
→ full registry as JSON: {"<compound-id>": {":atlas/dev-id":"…", …}, …}
ui/src/atlas_ui_v3/
├── core.cljs — all UI logic (single-file PoC)
└── api.cljs — SSE subscription + registry fetch
ui/resources/public-v3/
└── index.html — shell page; mounts #app
# Compile ClojureScript (one-shot)
cd ui
npx shadow-cljs release atlas-ui-v3
# Watch mode (hot reload)
npx shadow-cljs watch atlas-ui-v3
Start the backend from the REPL:
(atlas.atlas-ui.server/start!
{:ui-version :v3
:open-browser? false
:port 8082
:file-root "/path/to/atlas/ui/resources/public-v3"})
ZipException note: Always pass
:file-rootpointing to the compiled resources directory. Without it, the JVM serves from the JAR — if the JAR is stale or open by another process you'll seeZipFile invalid LOC header. The:file-rootoption bypasses the JAR entirely.
#hash for bookmarks and native browser back/forward/ to focus search, Esc to back, h for home:ns/name patterns in narrative text and render as chipsCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |