{"id":51509448,"url":"https://github.com/justinstimatze/lucida","last_synced_at":"2026-07-08T04:30:46.996Z","repository":{"id":354807956,"uuid":"1223954377","full_name":"justinstimatze/lucida","owner":"justinstimatze","description":"Real-time visualization dashboard for Claude Code — passively mints Vega charts, Mermaid diagrams, 3D scenes, and animated SVGs from your conversation as you work","archived":false,"fork":false,"pushed_at":"2026-06-09T20:31:02.000Z","size":51772,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T22:11:31.627Z","etag":null,"topics":["anthropic","claude","claude-code","dashboard","fui","llm-tools","mermaid","python","vega","visualization"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/justinstimatze.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-28T20:22:40.000Z","updated_at":"2026-06-09T20:31:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/justinstimatze/lucida","commit_stats":null,"previous_names":["justinstimatze/lucida"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/justinstimatze/lucida","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Flucida","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Flucida/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Flucida/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Flucida/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/justinstimatze","download_url":"https://codeload.github.com/justinstimatze/lucida/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justinstimatze%2Flucida/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35252324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","claude","claude-code","dashboard","fui","llm-tools","mermaid","python","vega","visualization"],"created_at":"2026-07-08T04:30:46.310Z","updated_at":"2026-07-08T04:30:46.982Z","avatar_url":"https://github.com/justinstimatze.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lucida\n\n[![CI](https://github.com/justinstimatze/lucida/actions/workflows/ci.yml/badge.svg)](https://github.com/justinstimatze/lucida/actions/workflows/ci.yml)\n\n*For the monitor where you used to read code.*\n\nYour Claude Code session, rendered live as a mission-control display. Every\ndecision, comparison, flow, and structure becomes a live visual cell — charts,\ndiagrams, 3D scenes, animated SVGs. The display builds up as you work.\n\nIt probably doesn't make you more productive. Das Blinkenlights for AI sessions.\n\nhttps://github.com/user-attachments/assets/d08aa14f-2a12-4f6d-a107-d71d98529dac\n\n---\n\n## Themes\n\nThe dashboard ships a wardrobe of themes — switch via `?theme=` in the URL\nor the `THEME` chip in the HUD.\n\n| Theme       | Feel                                                       |\n| ----------- | ---------------------------------------------------------- |\n| `lab`       | Default dark, cyan accent                                  |\n| `mars-blue` | The Expanse, Rocinante — cobalt tactical radar, bezel gauges |\n| `mars-red`  | The Expanse, classic MCRN — red war-table, Donnager data stack |\n| `earth`     | The Expanse, UN Navy — royal-blue situations plot, institutional |\n| `drift`     | The Expanse, OPA/Belter — amber orbital plot, salvage eclectic |\n| `vigil`     | MCU/Jarvis — cold electric cyan, arc reactor gold          |\n| `ops`       | Star Trek LCARS — full L-frame chrome                      |\n| `circuit`   | Tron Legacy — hard grid, neon data strip                   |\n| `noir`      | Blade Runner 2049 — amber holograms, blue-black            |\n| `terminus`  | Alien/Nostromo — phosphor green, CRT vignette              |\n| `renegade`  | Mass Effect N7 — omnitool orange, diagonal geometry        |\n| `mainframe` | ReBoot (1994) — Energy Sea teal                            |\n| `conclave`  | Eva/NERV — amber scan lines, monospace                     |\n| `minimal`   | Vercel/Linear — clean flat light                           |\n| `gastown`   | Steampunk brass + serif                                    |\n| `hackers`   | Hackers (1995) Gibson canyon — cyan-dominant, rare magenta |\n| `hailmary`  | Project Hail Mary — cyan-white wireframe, monochrome       |\n\nEach theme ships per-theme entrance animations and window-edge chrome\nauthentic to its source material. Themes also declare a preferred layout\nthat activates on switch.\n\nThe four faction themes from *The Expanse* — each with its own live\ntactical furniture, not just a palette swap:\n\n![The four Expanse faction themes: mars-blue, mars-red, earth, drift](assets/expanse-themes-2x2.png)\n\nhttps://github.com/user-attachments/assets/6da05581-f458-48f4-9044-4ad3795d152f\n\n---\n\n## mixed3d — the canyon flythrough\n\nThe `hackers` theme pairs with a `mixed3d` layout that arranges your\ncells onto the faces of a city of glass towers. A swoopy camera tours\nthe canyon, climbing between corridors and pausing on tier-1 cells as\nit passes. The visual lineage is *Hackers* (1995) — Ellingson Mineral\nCompany's Gibson — with a bit of *Tron Legacy* and *Ghost in the\nShell* in the mix.\n\nhttps://github.com/user-attachments/assets/7faee1aa-e2e8-4f6a-b5e9-b938624c055f\n\nTry it:\n\n```\nhttp://localhost:8766/?theme=hackers\u0026layout=mixed3d\n```\n\nCells render in three LOD tiers as the camera approaches: ambient\ndecorative bed (tier 2, shared per-substrate textures) → text/title\nsnap (tier 1 mid) → full graph render (tier 1 close). Mermaid,\nanimated-SVG, treemap, gauge, force-graph, timeline-ribbon, and\ntrajectory substrates all render in-place on tower faces. Click any\ncell to park the camera in front of it; press `R` to resume the tour.\n\n---\n\n## Get running in 5 minutes\n\n**Requirements:** Python 3.11+, Node.js 18+, an Anthropic API key, a\nrunning Claude Code session.\n\n```bash\ngit clone https://github.com/justinstimatze/lucida \u0026\u0026 cd lucida\nuv venv \u0026\u0026 uv pip install -e .\nnpm install                       # pulls mermaid + jsdom + puppeteer (bundles its own Chromium, ~170MB)\ncp .env.example .env\n# fill in your ANTHROPIC_API_KEY in .env\n```\n\nWhy `npm install`: lucida lints mermaid specs at mint time and\npre-renders mermaid diagrams server-side so the browser never blocks\non `mermaid.render()`. Both rely on Node deps. Puppeteer's\n`postinstall` hook downloads a bundled Chromium — no system Chrome\nrequired, and the download only happens once per machine.\n\nStart the renderer — open this on your second monitor and leave it there:\n\n```bash\npython3 serve.py\n# http://localhost:8766/\n```\n\n`serve.py` bundles the static server and the snap receiver (which\npersists Mermaid SVG renders into `cells/` so heavy substrates don't\nre-render every session). `python3 -m http.server 8766` works too but\nskips the cache.\n\nFor the Tron/Hackers (1995) Gibson canyon look on first run, try:\n\n```\nhttp://localhost:8766/?theme=hackers\u0026layout=mixed3d\n```\n\nStart watching your Claude Code session:\n\n```bash\npython watcher.py \\\n  --transcript ~/.claude/projects/.../transcript.jsonl \\\n  --watch 30 --write --generate\n```\n\nThat's it. New cells appear as the conversation progresses.\n\nThe display starts blank — cells mint as new content appears in the\ntranscript. Start a conversation in Claude Code and within a few exchanges\nyou'll see the first cells land. You'll immediately feel cooler.\n\nPrefer one command? `./scripts/start.sh` launches `serve.py` and `watcher.py`\nside-by-side with labeled output. Edit the script to point at your own\ntranscript path.\n\n---\n\n## What it produces\n\nLucida reads each passage in your conversation and picks a reasonable visual\nfor it automatically:\n\n- **Graphs and diagrams** — architecture, flows, entity relationships, state machines\n- **Charts** — comparisons, cost breakdowns, quantitative series\n- **Tables** — structured decisions, callouts, tradeoff matrices\n- **Treemaps** — proportional categorical breakdowns\n- **3D wireframes** — topology, spatial structure (Three.js, FUI-style)\n- **Animated SVGs** — cycles, decay, state transitions\n- **Sparklines** — single-variable trajectories\n- **Timeline ribbons** — chronological events with horizontal flow\n- **Gauges** — single scalars within a stated range (memory, latency, score)\n\nAmbient FUI flair — transient cells, mint-time scrubbers, per-theme ambient\nmotion — appears automatically. No prompts required, no payload, no static\nchrome. Implies \"computer go beep boop.\"\n\nVisuals arrive pre-themed to the active theme. No configuration needed — the\nclassifier chooses the substrate, the specialist generates the spec, and the\nrenderer paints it.\n\n---\n\n## Cost\n\nAbout **$0.02–0.03 per cell** using Sonnet 4.6. A busy hour-long session\nmints 30–80 cells — roughly $0.60–$2.00. Classifier calls are cached.\n\nTurn off `--generate` to run the classifier only (free) and mint manually\nwhen you want a visual.\n\n### Where the money goes\n\nMeasured with `tools/spend_audit.py`, which reconstructs per-stage spend\nfrom the cache counters recorded in your own `cells.json` — run it on your\ndata rather than trusting anyone's averages:\n\n```bash\npython tools/spend_audit.py                 # calibrated (a few free count_tokens calls)\npython tools/spend_audit.py --no-calibrate  # fully offline\n```\n\nOn a long real session the classifier was ~44% of recorded spend (it runs\nonce per segment with an ~11.5K-token cached prefix), with the rest spread\nacross the specialists — none above ~11%. Within the classifier, cost\nsplits roughly half cached-prefix reads, half its own output tokens.\n\n### Why the classifier stays on Sonnet\n\nThe obvious cheap move — flipping the classifier to Haiku 4.5 for the ~3x\nrate cut — was tried and **rejected on quality**: on a 72-snippet\nstratified replay (both models, same prompt), Haiku agreed with Sonnet on\ncell_type only 36% of the time and collapsed half the sample to\nlow-confidence `text`, which the confidence gate then suppresses. The\nsaving would have arrived as a half-empty dashboard. The replay harness is\n`tools/classifier_agreement_check.py` — re-run it before trying another\nclassifier model (every stage's model is overridable via `LUCIDA_*_MODEL`\nenv vars; see `.env.example`).\n\nWhat *does* cut cost without touching judgment: the classifier prompt asks\nfor telegraphic one-sentence reasoning (output tokens are the expensive\nhalf), and every stage sets a prompt-cache breakpoint, so keeping the\nwatcher polling inside the 5-minute cache TTL keeps prefix reads at ~10%\nof list price.\n\n---\n\n## Watcher options\n\n```bash\npython watcher.py \\\n  --transcript \u003cpath\u003e     # Claude Code .jsonl transcript\n  --watch 30              # poll interval in seconds (omit for one-pass)\n  --write                 # persist to cells.json\n  --generate              # call specialists (costs API tokens)\n  --session-id \u003cname\u003e     # tag cells with a session name\n  --max-cells all         # default — keep every cell; pass N to cap at last N\n```\n\n**Cells accumulate by default.** `cells.json` grows with each minted cell.\nPass `--max-cells N` (or `LUCIDA_MAX_CELLS=N`) if you want a rolling cap —\ne.g. `--max-cells 500` keeps only the last 500.\n\nMultiple sessions:\n\n```bash\n# Terminal 1\npython watcher.py --transcript session-a.jsonl --session-id A --watch 30 --write --generate\n\n# Terminal 2  \npython watcher.py --transcript session-b.jsonl --session-id B --watch 30 --write --generate\n\n# View both side-by-side\n# http://localhost:8766/?session=A,B\n```\n\n---\n\n## HUD + URL params\n\nThe HUD at the top of the page is a live status bar. Click it to expand.\nThe `SESSION` chip opens a dropdown listing every session in the corpus.\n\nFull URL param reference:\n\n```\n?theme=\u003cname\u003e               theme (lab / vigil / ops / circuit / noir / terminus /\n                            renegade / mainframe / conclave / minimal / gastown /\n                            hackers / hailmary)\n?layout=\u003cname\u003e              layout (pack / grid / treemap / scatter / tactical /\n                            terminal / mixed3d)\n?session=\u003cid\u003e               scope to one session\n?session=\u003ca\u003e,\u003cb\u003e,\u003cc\u003e        N-column mission-control view\n?nocache=1                  bypass the persistent SVG cache (force fresh mermaid\n                            renders this load)\n?perf=1                     dev: enable per-frame perf logging\n?debug=1                    dev: enable mixed3d debug logging\n```\n\nIn `?layout=mixed3d`, dev keys: `D` toggles the debug overlay (camera path\n+ tower bounds), `Q` dumps a contact sheet of all rendered tier-1 cells\ninto `refs/gibson/live-shots/`.\n\n---\n\n## Related projects\n\n- [agentic-city](https://github.com/mrf/agentic-city) by Mark Ferree —\n  kindred local-only FUI dashboard for AI sessions, but framed from the\n  opposite angle: it renders the codebase as an isometric SimCity with\n  active Claude/Codex/Gemini agents flying overhead as UFOs. Where\n  lucida centers the *transcript content* as visual cells, agentic-city\n  centers the *codebase* as terrain. The companion library\n  [agentwatch](https://github.com/mrf/agentwatch) is a Go transcript-\n  watcher that normalizes Claude/Codex/Gemini session state into one\n  feed — worth a look if you want multi-vendor session ingest.\n\n---\n\n## How it works (for the curious)\n\nThe pipeline behind each cell:\n\n1. `watcher.py` polls the transcript for new prose\n2. `segmenter.py` chops it into discrete snippets\n3. `classifier.py` assigns a substrate type; low-value snippets are suppressed\n4. `specialists.py` produces a snippet-grounded visual spec — a forcing-step\n   audit checks that the specialist didn't invent data not in the source\n5. Cell lands in `cells.json`; the renderer polls and paints it live\n6. Every N mints, `reflect.py` synthesizes the stream into a summary cell\n\nThe `UserPromptSubmit` hook injects recent mints into your next Claude Code\nprompt, so the conversation knows what just landed on the display.\n\nTo wire it up, add this to your `~/.claude/settings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"UserPromptSubmit\": [\n      {\n        \"matcher\": \"\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"/path/to/lucida/hooks/recent_mints.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\nThe hook is silent when nothing has minted recently — it only speaks when\nthere are new cells. Set `LUCIDA_MINT_WINDOW_MIN=30` (default: 60) to\ntune the lookback window.\n\n---\n\n## Adapters\n\nFlatten any AI session log into the format the watcher expects:\n\n```bash\npython -m adapters.cli --source claude-code \u003ctranscript.jsonl\u003e --out /tmp/transcript.txt\npython -m adapters.cli --source aider \u003cchat.md\u003e --out /tmp/transcript.txt\n```\n\n---\n\n## Files\n\n```\nlucida/\n├── index.html             renderer\n├── notebook.css           all theme chrome\n├── themes/                per-theme token JSON\n├── serve.py               static server + snap receiver\n├── orchestrator.py        one-shot entry point\n├── watcher.py             continuous listener\n├── specialists.py         visual spec generators\n├── classifier.py          substrate classifier\n├── reflect.py             synthesis cells\n├── adapters/              transcript adapters\n├── scripts/start.sh       launches serve + watcher together\n└── hooks/recent_mints.sh  Claude Code prompt injection hook\n```\n\nNot committed: `cells.json`, `mint_log.jsonl`\n\n---\n\n## Development\n\n```bash\nuv venv \u0026\u0026 uv pip install -e .[dev]\npre-commit install\n```\n\n**Lint:**\n```bash\nuv run ruff check .\nuv run ruff format .\n```\n\n**Tests:**\n```bash\nuv run pytest tests/\n# integration tests (needs ANTHROPIC_API_KEY):\nuv run pytest tests/integration/\n```\n\nCI runs lint + tests + a bandit security scan on every push.\n\n---\n\n## Known limitations\n\n- **Single-user, local-host by default.** `serve.py` binds 127.0.0.1.\n  No auth layer — if you expose the port externally, anything that can\n  reach it can read your cells and mint log.\n- **State files are POSIX-only.** `cells_lock` and `state_lock` use\n  `fcntl.flock`. Windows users will run without locking — fine for\n  single-process use, race-prone for parallel watchers.\n- **Cells are LLM output rendered with DOMPurify sanitization.** HTML\n  and SVG cells are sanitized before insertion (strips `\u003cscript\u003e`,\n  event handlers, dangerous URLs). A bug in DOMPurify or a future\n  sanitizer-bypass would still be exposure for a session run with an\n  attacker-controlled transcript.\n- **API cost is on the user.** Every transcript turn that lands a mint\n  triggers a classifier + specialist call (plus an occasional\n  reflection). Default models are tuned for cost, but a runaway\n  transcript ingestor will burn through API credit. Set\n  `LUCIDA_RETRIGGER_SCORE_FLOOR` higher to make the mint gate stricter.\n- **Mixed3d is GPU-heavy.** ~250-500MB GPU memory at saturation\n  (~1500 cells in scene, 300-entry snap cache). Integrated GPUs may\n  drop frames; use `?layout=pack` for a pure-2D mode.\n- **No Anthropic-side context window cap.** Long sessions accumulate\n  context in `recent_cells` for the orchestrator. We don't truncate.\n  A multi-day session could hit `200k context` errors.\n- **Cell substrate prompts are tuned for code/dev transcripts.** Other\n  transcript domains (writing, research) work but may classify\n  differently than expected. Override via the `--type` CLI flag.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustinstimatze%2Flucida","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjustinstimatze%2Flucida","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustinstimatze%2Flucida/lists"}