Plan: Milestone 3 - Databases and Tools.
Done (2026-09-18)
All six steps are implemented and verified on the web build with Playwright; desktop compiles and shares every code path except the three spawners (in-process PTY, stdio language server, in-process Typst), which are covered by native tests. Terminal (xterm.js view, PTY on desktop, websocket relay on web, Ctrl+click on
path:line), Typst preview (live SVG pages next to a.typeditor), LSP (rust-analyzer: diagnostics in the gutter, hover, F12; status bar item), and LadybugDB (embedded Cypher database as a source: schema in the Explorer, Cypher in the table editor, Show in Graph draws the result). Four new E2E suites (terminal,typst,lsp,ladybug) plus the M1/M2 suites pass; 7 new native tests; clippy/fmt clean on every target.
Steps as executed
| # | step | outcome | notes |
|---|---|---|---|
| 1 | moonkale-terminal + moonkale-terminal-pty + js/xterm bundle + editors/terminal panel; desktop PTY | ✅ pty test | terminal.md, terminal-pty.md, editor-terminal.md |
| 2 | /api/terminal websocket + RemoteTerminal; “new terminal here” | ✅ E2E terminal.mjs | typed Websocket<TerminalMessage, TerminalMessage> from dioxus::fullstack |
| 3 | moonkale-typst World + compile_to_svg; compile_typst server fn; preview panel | ✅ 2 tests + E2E typst.mjs | typst.md |
| 4 | moonkale-lsp session + moonkale-lsp-local stdio/discovery; CodeMirror lint/hover/F12; /api/lsp relay; status bar | ✅ 2 tests (one against real rust-analyzer) + E2E lsp.mjs | lsp.md, lsp-local.md, editor-code.md |
| 5 | sources-graph::ladybug (lbug 0.20); Explorer/open_any detection; Cypher in the table editor; Graph panel source picker + Show in Graph | ✅ integration test + E2E ladybug.mjs | sources-graph.md, graph.md |
| 6 | verify, log, vault | ✅ | this note |
What the user sees
- Terminal tile at the bottom (View → New Terminal, Ctrl+
,+, or>_on a folder in the Explorer). Ctrl+click onsrc/main.rs:12:4` in the output opens the file. - Open a
.typfile → a Typst preview tab appears next to it and follows every keystroke. - Open a
.rsfile in a cargo project → the status bar showsrust: starting…→rust-analyzer: ready; errors get a red gutter marker with the message on hover; hovering a symbol shows its signature; F12 jumps to the definition. Without rust-analyzer:rust: no language server for rust (install: rustup component add rust-analyzer). - A
people.lbugin the folder shows in database colour; clicking it adds a PEOPLE.LBUG source with its node and rel tables; a table opens the query box withMATCH (n:Person) RETURN n LIMIT 200; a path query shows Show in Graph, and the Graph panel’s new source picker switches to the database and draws the vertices and relations (schema graph when the query box is unticked).
Deviations from the plan
- Ladybug needed no cmake.
lbug0.20 downloaded its prebuilt static library on first build (~15 s); the binary grows by ~40 MB. Theladybugfeature is on for desktop and the server; the crate still compiles without it. - The desktop app never opened
.sqlitefiles — Milestone 2 only wired that on the server. Nowopen_databaseindesktop/src/main.rshandles SQLite and Ladybug alike. - Hover is plain text. The tooltip is a
<div>; markdown fences from rust-analyzer are stripped inlsp::hover_text. Rendering markdown in tooltips waits for the Milkdown work. - Go-to-definition across files opens the file but does not position the cursor —
open_relative_pathhas no “then move the cursor” hook yet. - Language servers are keyed per (language, folder root), not per file, and
initializeis sent with that root; rust-analyzer needs aCargo.tomlunder it. - Terminal
Ctrl+clickneeds the neighbour rows: xterm wraps long paths, so the panel asks for the row above and below and joins them before link detection. - The
/api/lspwebsocket carries aFrame(String)newtype rather thanString— the server-function macro cannot handle a bareStringtype parameter (P-057). - Typst packages (
@preview/...) are unsupported — no download path inMoonkaleWorldyet. - Graph panel: index filters hide when a database is picked (files/symbols/folders/unresolved are index kinds); database drawings show every kind.
Problems hit (→ Problem Log)
- P-054 Typst preview recompiled itself forever: the effect read
busy()(reactive) and set it, so each compile scheduled another;busy.peek()fixed it. - P-055 Terminal link clicks: (a) the row under the mouse must be computed from
.xterm-rowsgeometry, not the outer element; (b) xterm and headless Firefox swallow a real Ctrl+click, so the E2E dispatches a syntheticclickwithctrlKey. - P-056
dx serverestart cadence — still ~2–3 min per non-rsx change; three restarts this milestone. - P-057
Websocket<String, String>does not compile in a server function — the macro turns the parameter intostr; wrap the payload in a newtype. - P-058 rust-analyzer progress messages are file paths —
$/progressmessages like13/15: /home/…/coreoverflowed the status bar; cut at the first:. - P-060 Database internals invisible in the graph view (found by Daniel on desktop): the picker defaulted to index and Show in Graph didn’t raise the Graph tab. Opening a graph database now selects it;
Command::ShowPanelbrings the tab forward. - P-061 WebKitWebProcess crashes in NVIDIA EGL (core dumps reported by Daniel): reproduced — the web process dies inside the driver while tearing down a live WebGL context, i.e. on every exit and every
dx serverebuild kill. Not a startup crash and not the DMA-BUF renderer. Since the process is exiting anyway, the launcher now setsRLIMIT_CORE=0on NVIDIA machines (opt-outMOONKALE_COREDUMPS=1); verified no dump after a kill, and Daniel confirmed the KDE crash popup is gone underdx serve. - P-062 Only the schema was drawn for a database: added Data/Schema/Query modes with per-label colours and a legend;
Query::All { kinds: [Vertex] }is the source-side contract for “the data graph”. - P-059 E2E fixtures drift: the graph suite saves a wiki-link to
AnotherintoHome.md, which then breaks the links suite’s counts. Suites must be run on a fresh fixture (or restoreHome.mdbetween runs) — noted in the E2E README.
Decisions worth keeping
- Typed websockets for tools (
dioxus::fullstack::{WebSocketOptions, Websocket}): serveroptions.on_upgrade(|mut socket| …), client.send/.recvon a sharedRc— the same shape for the terminal and the LSP relay;Framenewtype for strings. SpawnTerminal/SpawnLsp/CompileTypstare plainfnpointers inWorkspaceConfig, likeopen_folder;Noneon mobile. Platforms decide where the process runs; the panels never know.- Query results carry nodes/edges and a table.
QueryResultalready had both; Ladybug fills both from one Cypher run so the table editor and the graph panel share a result without a second query. Workspace::graph_requestis the one-way channel from any panel to the Graph panel (“draw this”). A checkbox in the panel lets the user go back to the schema.- Security stays explicit:
MOONKALE_ROOTjails cwd/root for terminal, LSP and databases on the server; nothing else is hardened (R-20).
Verified
- Web (Firefox, Playwright):
terminal.mjs,typst.mjs,lsp.mjs,ladybug.mjs, plus M2’sgraph.mjs,links-sqlite.mjs, M1’ssession.mjs,menubar.mjs— all PASS againstMOONKALE_ROOT=<fixture>with rust-analyzer installed. - Native:
cargo testforterminal,terminal-pty(real PTY),typst,lsp,lsp-local(real rust-analyzer),sources-graph --features ladybug(real database). - Desktop:
cargo build -p desktop --features desktoplinks (withlbugstatic lib). Not exercised by hand this session (no display in the agent’s shell) — see below.
What to look at on desktop
- View → New Terminal: a shell in the bottom tile;
lsoutput; Ctrl+click apath:line. - Open a
.typfile: preview tab appears and updates while typing. - Open
src/*.rsin a cargo project: status barrust-analyzer: readyafter indexing; a deliberatelet x: String = 1;gets a gutter marker; hover; F12. - File → Open Folder… on a
.lbugfile or directory (or click one inside a folder): tables appear; Cypher runs; Show in Graph.
Deferred to Milestone 4
Milkdown WYSIWYG (R-11), Postgres/Turso/DuckDB, Falkor/TypeDB/Helix, cross-source edges (R-16), cursor positioning after cross-file go-to-definition, Typst packages, LSP completion/rename, terminal/LSP auth for a real server, wasm-side Typst.