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:
megaproxy 2026-05-22 12:31:29 +01:00
commit b352f8f049
36 changed files with 11534 additions and 0 deletions

45
memory.md Normal file
View file

@ -0,0 +1,45 @@
# memory — tiletopia
Durable memory for this project. Read at session start, update before session end. Date format: `YYYY-MM-DD`.
## Decisions & rationale
- **Stack: Tauri 2 + Svelte 5 + TypeScript + Vite + pnpm + xterm.js + `portable-pty`.** Mirrors `claude-usage-widget` so we reuse a known-good Windows-targeting toolchain (MSVC + WebView2 + NSIS installer). No new technology bets stacked on top of the new product bet.
- **Layout model: binary tree of splits, NOT free-form rectangles.** Same as i3 / tmux / Zellij. Each internal node is HSplit/VSplit + ratio; each leaf is a terminal. Dragging a gutter mutates one parent ratio; both sibling subtrees reflow; descendants get `resize`. Adaptive resize falls out automatically with no constraint solver. Preset layouts ("3 columns", "2×2") are pre-built trees.
- **PTY backend: `portable-pty` (same crate WezTerm uses).** Spawns `wsl.exe -d <distro> --cd <path>` on Windows. Manager is a `Mutex<HashMap<PaneId, PaneHandle>>` in Rust; each pane has a background reader thread that emits `pane://{id}/data` events.
- **Wire format: base64-encoded byte chunks via Tauri events.** xterm.js's `onData` emits strings; we UTF-8 encode then base64. Slower than a typed-array payload but trivially correct. Revisit if throughput matters.
- **Source on Windows-native disk (`D:\dev\tiletopia\`), symlinked into WSL.** Same pattern as `rimlike` (`D:\godot\rimlike`) and `tavernkeep`. Forced by pnpm 11.x's `isDriveExFat` crashing on `\\wsl.localhost\...` UNC paths.
- **Don't commit `node_modules`, `src-tauri/target`, or `.pnpm-store`. DO commit `Cargo.lock`** (binary project, reproducible builds).
- **Session awareness without an in-pane agent.** Plan: poll `/proc/<pid>/cwd` of the shell's child + foreground process every ~2s. Sufficient to detect `cd` and whether `claude` is running.
## Open questions / TODOs
- [ ] **HMR distro picker reset.** After a Vite hot reload, the previously-selected distro persists in Svelte 5 `$state`, so the picker doesn't re-default. Workaround in place (clickable distro buttons in titlebar). Fix properly in M3 when workspace state lives in a separate persisted store.
- [ ] **M2 — splits-tree layout component.** Two panes side by side, draggable divider, both panes alive. Save/restore layout as JSON.
- [ ] **M3 — workspace persistence.** Save/restore layouts + per-pane (distro, cwd, label) in `%APPDATA%/tiletopia/workspaces.json`. Preset layouts (3 columns, 2×2 grid). Distro picker UX, pane labels.
- [ ] **M4 — orchestration.** Broadcast input groups, idle/finish notifications, Ctrl+K fuzzy palette.
- [ ] **M5 — Ship.** Replace placeholder icons, NSIS installer, Forgejo release. Copy `claude-usage-widget`'s release scripts.
- [ ] **Native Windows shells (cmd / pwsh)?** `portable-pty` supports them for free; keep the option open. Decide whether to expose in UI at M3.
- [ ] **Persistent scrollback across app restarts.** Would need an out-of-process mux daemon. Big scope creep; explicitly deferred past v1.
- [ ] **Keybinding philosophy.** Copy tmux, copy WezTerm, or invent? Decide at M3.
## Session log
### 2026-05-22
- Graduated from `ideas/wsl-mux/` to project. Renamed working name `wsl-mux` → final name `tiletopia` across Cargo/package/Tauri configs and source.
- Promoted spike contents from `D:\dev\wsl-mux\spike\` to `D:\dev\tiletopia\` (no more spike subdir; the project IS what was the spike).
- Initialized git, created private Forgejo repo `tiletopia`, pushed initial scaffold.
- M1 verified manually on the Windows host: window opens, xterm.js renders, `claude` TUI works inside the pane, resize reflows cleanly, `htop` renders. Distro auto-pick chose `docker-desktop` (Docker Desktop's BusyBox helper distro) on first try — added explicit clickable distro buttons in the titlebar as both a diagnostic and a manual override. Clicking `Ubuntu` works end-to-end.
- Old idea folder archived to `~/claude/archive/ideas/wsl-mux/` (preserves full brainstorm + session log).
## External references
- **Approved plan / roadmap:** `~/.claude/plans/imperative-coalescing-feigenbaum.md` (M0M5 milestones with verification criteria for each)
- **Stack precedent:** `~/claude/projects/claude-usage-widget/` — same Tauri + Svelte + WebView2 toolchain, already ships a Windows installer via Forgejo releases. WSL distro-probing logic copied/adapted into `src-tauri/src/pty.rs`.
- **Archived idea history:** `~/claude/archive/ideas/wsl-mux/plan.md`
- **Forgejo repo:** https://git.rdx4.com/megaproxy/tiletopia
- **xterm.js docs:** https://xtermjs.org/
- **portable-pty crate:** https://crates.io/crates/portable-pty
- **Tauri 2 docs:** https://v2.tauri.app/
- **Prior art for splits-tree layout:** i3, tmux, Zellij, WezTerm