Expand description
Phase E.4.B Phase 4 — pure layout-tree helpers (Rust port of layoutTree.ts). Pure layout-tree helper functions for the srv E.4.B reducer.
Phase 4 of docs/specs/srv-phase-e4b-formal-spec-2026-05-03.md.
Ports the 11 pure-ish action handlers from
frontend/layout/lib/layoutTree.ts to Rust. These functions take
&mut LayoutNode (the tree root) and operation params; they have no
I/O and no side effects. The reducer arms (Phase 5) will call these.
Test oracle: the 30 tests in frontend/layout/tests/layoutTree.test.ts
(shipped in PR #686) — the Rust implementations must produce identical
state transitions for identical inputs.
Structs§
- Insert
Candidate 🔒 - Candidate for insertion location, collected during tree traversal.
Enums§
Constants§
- DEFAULT_
MAX_ CHILDREN - Maximum children before
insert_node’s heuristic descends. Mirrorsfrontend/layout/lib/layoutTree.ts::DEFAULT_MAX_CHILDREN. - DEFAULT_
NODE_ SIZE - Default flex size for newly-inserted nodes.
Mirrors
frontend/layout/lib/types.ts::DefaultNodeSize.
Functions§
- clear_
tree_ node - Clear the tree root. Callers set the
Option<LayoutNode>toNone. This function exists only to be referenced in the test helper pattern; actual clearing in the reducer just sets root = None. - collect_
insert_ 🔒candidates - delete_
node - Remove the node with the given id from the tree.
Returns
Ok(())on success, orErr(NodeNotFound)ifnode_idis not in the tree. Callers are responsible for clearingfocused_node_idif needed (spec §7.1). Collapses single-child parents. - delete_
recursive 🔒 - Recursive delete. Returns
truewhen the node was found and removed,falsewhen not found. - ensure_
group_ 🔒node - If
nodeis a leaf (hasdata), promote it to a group by wrapping its data in an intermediate child. Mirrors TypeScriptaddIntermediateNodeinfrontend/layout/lib/layoutTree.ts. - find_
next_ 🔒insert_ location - Find the best insertion location using the TypeScript scoring heuristic
(
Math.pow(depth, index + maxChildren)). Collects all candidates, then sorts by ascending score. MirrorsfindNextInsertLocationinfrontend/layout/lib/layoutNode.ts. - find_
node_ by_ id - Find a node by id (immutable).
- find_
node_ by_ id_ mut - Find a node by id (mutable).
- find_
parent_ 🔒id - insert_
node - Insert
nodeusing the TypeScript scoring heuristic (up to DEFAULT_MAX_CHILDREN per node before descending). If the target location is a leaf, it is promoted to a group first viaensure_group_node. MirrorsinsertNode/addChildAtinfrontend/layout/lib/layoutTree.ts. - insert_
node_ at_ index - Insert
nodeat the location identified byindex_arr— an ordered sequence of child indices (e.g.[0, 2]= root.children[0].children at index 3). The node is inserted AFTER the position identified by the last index. - move_
node - Move the node identified by
node_idto be the child atindexunder the node identified bynew_parent_id. Resets the moved node’s size to DEFAULT_NODE_SIZE unless it stays under the same parent. - remove_
node_ 🔒from_ parent - replace_
node - Replace the node at
target_idwithnew_node, preserving the target’ssize. If target is root, replaces root-in-place (caller’sOption<LayoutNode>doesn’t change reference — the fields are updated on the root node). - resize_
nodes - Apply resize operations. Validates all sizes first (0.0–100.0 range); if any is invalid, returns an error WITHOUT applying any ops (atomically rejected — matches the frontend’s early-return semantic from PR #686).
- reverse_
flex_ 🔒direction - Reverse a flex direction (Row ↔ Column).
Mirrors
frontend/layout/lib/layoutNode.ts::reverseFlexDirection. - split_
horizontal - Horizontal split: insert
new_nodebefore/aftertarget_id. - split_
impl 🔒 - split_
vertical - Vertical split: insert
new_nodebefore/aftertarget_id. - swap_
nodes - Swap two nodes (by id). Their sizes travel with them (swap positions but preserve the size each node had). Neither can be the root.