Add M3: APPDATA persistence + presets + per-pane distro/label
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>
This commit is contained in:
parent
1869d08181
commit
64b90ebddb
10 changed files with 434 additions and 74 deletions
|
|
@ -108,6 +108,66 @@ export function findLeaf(root: TreeNode, leafId: NodeId): LeafNode | null {
|
|||
return findLeaf(root.a, leafId) ?? findLeaf(root.b, leafId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the distro on a leaf. The leaf gets a **new id** so the rendering
|
||||
* layer's `{#key node.id}` block remounts XtermPane — the old PTY is killed
|
||||
* and a fresh one spawns with the new distro.
|
||||
*/
|
||||
export function changeDistro(
|
||||
root: TreeNode,
|
||||
leafId: NodeId,
|
||||
distro: string,
|
||||
): TreeNode {
|
||||
return replaceById(root, leafId, (node) => {
|
||||
if (node.kind !== "leaf") return node;
|
||||
return { ...node, id: newId(), distro };
|
||||
});
|
||||
}
|
||||
|
||||
/** Set or clear a leaf's label. Does NOT remount (label is metadata only). */
|
||||
export function changeLabel(
|
||||
root: TreeNode,
|
||||
leafId: NodeId,
|
||||
label: string | undefined,
|
||||
): TreeNode {
|
||||
return replaceById(root, leafId, (node) => {
|
||||
if (node.kind !== "leaf") return node;
|
||||
const trimmed = label?.trim();
|
||||
return { ...node, label: trimmed ? trimmed : undefined };
|
||||
});
|
||||
}
|
||||
|
||||
// ---- preset layouts --------------------------------------------------------
|
||||
|
||||
type LeafDefaults = Partial<Omit<LeafNode, "kind" | "id">>;
|
||||
|
||||
export function presetSingle(d: LeafDefaults = {}): TreeNode {
|
||||
return newLeaf(d);
|
||||
}
|
||||
|
||||
export function presetTwoColumns(d: LeafDefaults = {}): TreeNode {
|
||||
return newSplit("h", newLeaf(d), newLeaf(d));
|
||||
}
|
||||
|
||||
export function presetThreeColumns(d: LeafDefaults = {}): TreeNode {
|
||||
// Even thirds: outer split at 1/3, inner split at 1/2.
|
||||
return newSplit(
|
||||
"h",
|
||||
newLeaf(d),
|
||||
newSplit("h", newLeaf(d), newLeaf(d), 0.5),
|
||||
1 / 3,
|
||||
);
|
||||
}
|
||||
|
||||
export function presetTwoRows(d: LeafDefaults = {}): TreeNode {
|
||||
return newSplit("v", newLeaf(d), newLeaf(d));
|
||||
}
|
||||
|
||||
export function presetTwoByTwo(d: LeafDefaults = {}): TreeNode {
|
||||
const row = () => newSplit("h", newLeaf(d), newLeaf(d));
|
||||
return newSplit("v", row(), row());
|
||||
}
|
||||
|
||||
/** Number of leaves in the tree. */
|
||||
export function leafCount(root: TreeNode): number {
|
||||
if (root.kind === "leaf") return 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue