- 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>
6.5 KiB
6.5 KiB
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. Mirrorsclaude-usage-widgetso 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). Spawnswsl.exe -d <distro> --cd <path>on Windows. Manager is aMutex<HashMap<PaneId, PaneHandle>>in Rust; each pane has a background reader thread that emitspane://{id}/dataevents. - Wire format: base64-encoded byte chunks via Tauri events. xterm.js's
onDataemits 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 asrimlike(D:\godot\rimlike) andtavernkeep. Forced by pnpm 11.x'sisDriveExFatcrashing on\\wsl.localhost\...UNC paths. - Don't commit
node_modules,src-tauri/target, or.pnpm-store. DO commitCargo.lock(binary project, reproducible builds). - Session awareness without an in-pane agent. Plan: poll
/proc/<pid>/cwdof the shell's child + foreground process every ~2s. Sufficient to detectcdand whetherclaudeis running. - State propagation in the layout tree: hybrid mutable + replace. The root tree is
$state(...)at App level. Direct mutation (e.g.node.ratio = Xduring gutter drag) is reactive via Svelte 5's deep proxy. Structural changes (split/close) go through pure helpers intree.tsthat return a new root, which App reassigns. Drag stays fast (no tree walk); structural changes stay simple.{#key leaf.id}aroundLeafPaneensures swapping a leaf in/out cleanly unmounts XtermPane (which kills the PTY on destroy). - Layout persistence: localStorage at App level, key
tiletopia.tree.v1. Saves on every$effecttick (deep reactivity catches all mutations). Migrating to%APPDATA%/tiletopia/is M3.
Open questions / TODOs
M2 — splits-tree layout component. Two panes side by side, draggable divider, both panes alive. Save/restore layout as JSON.Done 2026-05-22.- HMR distro picker reset. Less acute now that distro selection is per-leaf (auto-applied at split time from app-level default). The titlebar
default:buttons let the user re-set the default at any time. Revisit when adding per-pane distro switching in M3. - Auto-save debouncing. Currently every tree mutation writes to localStorage. Dragging a gutter fires many writes per second. Cheap (localStorage is fast, JSON is small) but worth debouncing in M3 when persistence moves to disk.
- M3 — workspace persistence + preset layouts. Migrate from localStorage to
%APPDATA%/tiletopia/workspaces.json(via Tauri'splugin-storeor direct FS). Add preset layouts (3 columns, 2×2 grid). Multi-workspace tabs. Per-pane distro override and 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-ptysupports 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 — M2 splits-tree layout
- Added
src/lib/layout/:tree.ts(pure helpers: types, newLeaf, splitLeaf, closeLeaf, replaceById, serialize/deserialize with shape-checking),SplitNode.svelte(flex container + draggable gutter with pointer-capture),LeafPane.svelte(toolbar with split-right/split-down/close + XtermPane underneath),Pane.svelte(recursive dispatcher). - Rewrote
App.svelteto hold the tree as$stateand wire split/close callbacks through. Auto-saves to localStorage on every$effecttick. - Distro UX: titlebar shows clickable distro buttons that set the default for new panes. Existing panes keep their distro. Per-pane override is M3.
- Passes
pnpm checkcleanly (108 files, 0 errors, 0 warnings). - Validated manually on Windows: open app, click
⇥to split right and⇣to split down, both panes alive simultaneously, drag dividers reflows xterm. (Filled in after test.)
2026-05-22
- Graduated from
ideas/wsl-mux/to project. Renamed working namewsl-mux→ final nametiletopiaacross Cargo/package/Tauri configs and source. - Promoted spike contents from
D:\dev\wsl-mux\spike\toD:\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,
claudeTUI works inside the pane, resize reflows cleanly,htoprenders. Distro auto-pick chosedocker-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. ClickingUbuntuworks 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(M0–M5 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 intosrc-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