Initial scaffold from M1 spike (tiletopia)
Tauri 2 + Svelte 5 + xterm.js + portable-pty. Single full-window WSL terminal pane with clickable distro picker. M1 verified manually on Windows: window opens, xterm.js renders, claude TUI works, resize reflows cleanly. Graduated from ~/claude/ideas/wsl-mux/ per the approved plan at ~/.claude/plans/imperative-coalescing-feigenbaum.md. See memory.md for decisions, open TODOs, and the M2-M5 roadmap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
b352f8f049
36 changed files with 11534 additions and 0 deletions
75
README.md
Normal file
75
README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# tiletopia
|
||||
|
||||
A Windows desktop app for running and arranging many WSL terminals at once. Built primarily to manage multiple `claude` sessions across projects in parallel, but works for any multi-shell workflow.
|
||||
|
||||
Status: **early — single-pane M1 works**. Tiling layout (M2), workspace persistence (M3), and cross-pane orchestration (M4) are the next milestones. See `memory.md`.
|
||||
|
||||
## Stack
|
||||
|
||||
- **Tauri 2** (Rust backend, WebView2 frontend) — small bundle, native Windows installer via NSIS.
|
||||
- **Svelte 5** + TypeScript + Vite + pnpm.
|
||||
- **xterm.js** + `@xterm/addon-fit` for terminal rendering.
|
||||
- **`portable-pty`** (Rust crate) spawning `wsl.exe -d <distro>` PTYs.
|
||||
|
||||
## Run
|
||||
|
||||
This project targets Windows. Dev requires:
|
||||
|
||||
- Windows 10/11 + [WebView2 Runtime](https://developer.microsoft.com/microsoft-edge/webview2/) (preinstalled on Win11).
|
||||
- [MSVC toolchain](https://v2.tauri.app/start/prerequisites/#windows) (VS Build Tools, "C++ build tools" workload).
|
||||
- [Rust](https://rustup.rs/) on the Windows host.
|
||||
- Node 20+ and pnpm (`corepack use pnpm@11.2.2`).
|
||||
- WSL with at least one distro installed.
|
||||
|
||||
**Location matters.** The source must live on a Windows-native drive (here: `D:\dev\tiletopia\`). Don't run pnpm against the `\\wsl.localhost\...` UNC path — pnpm 11.x crashes inside `isDriveExFat` and the actual error gets swallowed by the crashing error-hint formatter.
|
||||
|
||||
From a Windows shell:
|
||||
|
||||
```powershell
|
||||
cd D:\dev\tiletopia
|
||||
pnpm install
|
||||
pnpm tauri dev # iterate
|
||||
pnpm tauri build # NSIS installer at src-tauri\target\release\bundle\nsis\
|
||||
```
|
||||
|
||||
The WSL-side symlink at `~/claude/projects/tiletopia` points here for in-WSL editing.
|
||||
|
||||
## How it works (current state)
|
||||
|
||||
- **Backend (`src-tauri/src/pty.rs`):** a `PtyManager` holding a `Mutex<HashMap<PaneId, PaneHandle>>` of `portable-pty` children. Each spawned pane gets a background reader thread that emits `pane://{id}/data` events to the frontend (base64-encoded byte chunks). Counterparts: `write_to_pane`, `resize_pane`, `kill_pane`. Distro enumeration parses `wsl.exe -l -q` (UTF-16LE).
|
||||
- **Frontend (`src/components/XtermPane.svelte`):** xterm.js + FitAddon mounted into a div. On mount, calls `spawn_pane`, subscribes to the pane's event stream, wires `term.onData` → `write_to_pane`, and uses a `ResizeObserver` to forward dimension changes to the PTY.
|
||||
- **App (`src/App.svelte`):** titlebar with clickable distro buttons (auto-picks first non-docker-desktop distro; user can override). One XtermPane wrapped in `{#key selected}` so changing distro destroys + respawns the pane.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
tiletopia/
|
||||
├── CLAUDE.md, memory.md, README.md
|
||||
├── .gitignore, pnpm-workspace.yaml
|
||||
├── package.json, vite.config.ts, svelte.config.js, tsconfig.json, tsconfig.node.json
|
||||
├── index.html
|
||||
├── src/
|
||||
│ ├── main.ts # mounts App, imports xterm.css
|
||||
│ ├── App.svelte # titlebar + one XtermPane (M1)
|
||||
│ ├── styles.css
|
||||
│ ├── ipc.ts # typed Tauri command wrappers
|
||||
│ └── components/
|
||||
│ └── XtermPane.svelte
|
||||
└── src-tauri/
|
||||
├── Cargo.toml, build.rs, tauri.conf.json
|
||||
├── capabilities/default.json
|
||||
├── icons/ # placeholder, copied from claude-usage-widget
|
||||
└── src/
|
||||
├── main.rs
|
||||
├── lib.rs # tauri builder, registers commands, manages PtyManager
|
||||
├── pty.rs # PtyManager + list_wsl_distros
|
||||
└── commands.rs # #[tauri::command] surface
|
||||
```
|
||||
|
||||
## Known gotchas (today)
|
||||
|
||||
- **Don't `pnpm install` from a UNC path** (`\\wsl.localhost\...`). pnpm 11.x crashes in its `isDriveExFat` probe; the underlying error gets swallowed.
|
||||
- **Console flash on `wsl.exe -l -q`:** suppressed via the `CREATE_NO_WINDOW` flag in `pty.rs`. The PTY itself doesn't allocate a console (portable-pty uses ConPTY directly).
|
||||
- **base64 wire format:** xterm.js emits `string` from `onData`; we UTF-8 encode then base64. Not the fastest; switch to typed-array event payloads later if throughput is an issue.
|
||||
- **No icons of our own:** copied from `claude-usage-widget`. Replace before any release.
|
||||
- **Cargo build only works on Windows host** — Rust toolchain isn't installed in WSL. `pnpm check` runs in WSL and validates the Svelte/TS side.
|
||||
Loading…
Add table
Add a link
Reference in a new issue