- src/lib/layout/tree.ts: pure helpers + types (newLeaf, splitLeaf, closeLeaf, replaceById, serialize/deserialize with shape-checking). - SplitNode.svelte: flex container with pointer-captured gutter drag. - LeafPane.svelte: per-pane toolbar (split-right ⇥, split-down ⇣, close ×) over the existing XtermPane. - Pane.svelte: recursive dispatcher between SplitNode and LeafPane, keyed on leaf.id so swaps unmount XtermPane cleanly (kills PTY). - App.svelte: tree-as-state with split/close handlers, auto-save to localStorage on every \$effect tick. Titlebar shows clickable distro buttons setting the default for new panes; existing panes keep theirs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| src | ||
| src-tauri | ||
| .gitignore | ||
| CLAUDE.md | ||
| index.html | ||
| memory.md | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
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-fitfor terminal rendering. portable-pty(Rust crate) spawningwsl.exe -d <distro>PTYs.
Run
This project targets Windows. Dev requires:
- Windows 10/11 + WebView2 Runtime (preinstalled on Win11).
- MSVC toolchain (VS Build Tools, "C++ build tools" workload).
- Rust 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:
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): aPtyManagerholding aMutex<HashMap<PaneId, PaneHandle>>ofportable-ptychildren. Each spawned pane gets a background reader thread that emitspane://{id}/dataevents to the frontend (base64-encoded byte chunks). Counterparts:write_to_pane,resize_pane,kill_pane. Distro enumeration parseswsl.exe -l -q(UTF-16LE). - Frontend (
src/components/XtermPane.svelte): xterm.js + FitAddon mounted into a div. On mount, callsspawn_pane, subscribes to the pane's event stream, wiresterm.onData→write_to_pane, and uses aResizeObserverto 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 installfrom a UNC path (\\wsl.localhost\...). pnpm 11.x crashes in itsisDriveExFatprobe; the underlying error gets swallowed. - Console flash on
wsl.exe -l -q: suppressed via theCREATE_NO_WINDOWflag inpty.rs. The PTY itself doesn't allocate a console (portable-pty uses ConPTY directly). - base64 wire format: xterm.js emits
stringfromonData; 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 checkruns in WSL and validates the Svelte/TS side.