# typst-leaf 🍂 A completely self-hostable, high-performance editor for [Typst](https://typst.app/), built on the philosophy of "Git as the Database." **Features:** * **Instant Preview:** Incremental compilation using `tinymist`. * **Live diagnostics & autocomplete** from the Typst language server. * **Git Native:** No hidden databases. Your projects are just folders with `.git`. * **Vim Mode:** Toggleable via toolbar. * **SyncTeX:** Click in the preview to jump to source in the editor. * **Hybrid:** Run it in a browser, or as a native desktop app via Tauri. ## 📂 Repository Structure ```text typst-leaf/ ├── typst-leaf-backend/ # Node.js/TS server (spawns tinymist, REST + WS) ├── typst-leaf-frontend/ # React + Monaco frontend (includes Tauri) ├── VISION.md # Project philosophy and architecture └── AGENTS.md # Guidelines for AI coding assistants ``` ## Prerequisites * **Node.js 20+** * **pnpm** (10+) * **Rust** (only required for the Tauri desktop wrapper) * **`tinymist`** on your `PATH` — the Typst language server & preview engine: ```bash cargo install --git https://github.com/Myriad-Dreamin/tinymist --bin tinymist --locked tinymist-cli ``` ## 🚀 Quick Start (Development) ```bash # 1. Install workspace dependencies pnpm install # 2. Start the backend (port 4000) pnpm --filter typst-leaf-backend dev # 3. Start the web frontend (port 1420 — mandated by Tauri) pnpm --filter typst-leaf-frontend dev # 4. (Optional) Launch the Tauri desktop wrapper pnpm --filter typst-leaf-frontend tauri dev ``` Then open in your browser. A `hello` project is auto-seeded on first run. ### Environment variables | Var | Default | Description | | --- | --- | --- | | `PROJECTS_ROOT` | `/projects` | Directory under which project folders live. The backend refuses to read or write outside this root. | | `TINYMIST_PREVIEW_HOST` | `127.0.0.1` | Host the tinymist preview server binds to. | | `TINYMIST_PREVIEW_PORT` | `23635` | Port the tinymist preview server binds to. | | `PORT` (backend) | `4000` | Backend HTTP/WS port. | | `FRONTEND_ORIGIN` (backend) | `http://localhost:1420` | CORS origin allowed to call the REST API. | ## 🏗️ Architecture * **Editor:** Monaco Editor (bundled locally) * **LSP:** Raw JSON-RPC client over WebSocket → backend thin proxy → `tinymist` stdio * **Preview:** Frontend calls `tinymist.startDefaultPreview` via LSP, discovers random data-plane port, relays to backend via `POST /api/preview/sink`; backend proxies the preview webview and its WebSocket. * **Frontend LSP:** Custom ~300-line `RawLspClient` class (no `monaco-languageclient`). * **Git:** `simple-git` wrapper, per-project `.git`, commit identity via `-c` flags (no mutation of repo config). * **Styling:** Tailwind v4 via `@tailwindcss/vite` plugin. Light theme only. * **Data plane:** * `ws://localhost:4000/lsp` → `tinymist lsp` (single active connection, takeover on reconnect) * `ws://localhost:4000/preview` → tinymist preview server (SVG stream) * `GET/POST/DELETE/PATCH /api/projects/:project/file?path=…` * `POST /api/projects/:project/git/init`, `GET …/status`, `GET …/log`, `POST …/commit`, `POST …/push`, `POST …/pull`, `PUT …/remote`