Symptoms in v0.1.0 install: 📡 broadcast button didn't change color
on toggle, × close button didn't remove the pane, blue active
border stuck on the first pane. All three were UI-not-rerendering-
on-state-change manifestations of the same prop-reactivity quirk
that drilling activeLeafId tried (and apparently failed) to fix.
Refactor to the Svelte 5 canonical pattern for shared reactive
state:
- New src/lib/layout/orchestration.svelte.ts with an Orchestration
class. Reactive fields (activeLeafId, notifications, distros) are
class-field $state declarations; methods mutate them directly.
Provided via context (provideOrchestration / useOrchestration);
no prop drilling.
- App.svelte: provideOrchestration(treeOps). Tree mutations remain
closures over the App-level tree $state; the class delegates to
them. Pane only takes `node` now.
- Pane.svelte / SplitNode.svelte: stop drilling ops + activeLeafId.
Pure pass-through of node.
- LeafPane.svelte: useOrchestration(); `active = $derived(
orch.activeLeafId === leaf.id)` reads the class field directly so
Svelte 5 tracks it per-property.
- Notifications.svelte: receives notifications + onDismiss from App
(which gets them from orch).
- Deleted src/lib/layout/ops.ts (TreeOps moved into orchestration).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drill activeLeafId as a separate prop through Pane -> SplitNode ->
LeafPane instead of bundling it into the \$derived ops object.
Passing activeLeafId via ops caused subsequent focus changes to
not propagate to children (LeafPane's active = \$derived(...) wasn't
re-evaluating when ops's identity changed). Drilling sidesteps any
prop-as-derived-object reactivity quirks.
- Ctrl+K listener now uses capture phase so it wins over xterm.js's
keydown handler inside the focused terminal.
- Bump active/broadcasting borders to 2px and brighter colors so the
visual change is unmissable.
- Add a 🔔 test-toast button in the titlebar to verify the
notification pipeline independently of idle detection.
- Sprinkle console.log diagnostics through the active/broadcast/
idle/notify flows so we can pinpoint any remaining issues from
devtools next time something looks off.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- save_workspace / load_workspace Tauri commands writing to
%APPDATA%\com.megaproxy.tiletopia\workspace.json with atomic
tmp+rename. Path from app.path().app_config_dir() (no dirs crate).
Layout helpers:
- tree.ts: changeDistro (with id swap to force XtermPane remount via
{#key}), changeLabel, presetSingle / TwoColumns / ThreeColumns /
TwoRows / TwoByTwo.
- New ops.ts with PaneOps interface bundling split / close /
setDistro / setLabel / distros, drilled through Pane chain
instead of individual callbacks.
UI:
- LeafPane: in-toolbar editable label (click to rename, Enter
saves, Esc cancels) and distro chip popover. Picking a different
distro respawns the pane.
- App.svelte: migrated from localStorage to APPDATA via the new
Tauri commands, debounced 500ms. One-time localStorage migration
on boot. Split inherits parent's distro+cwd. Titlebar preset
buttons with confirm when replacing >1 pane.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 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>
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>