fix(app): keep the agent pane mounted when opening the first split
Splitting a pane right from the default layout tore down the existing pane and rebuilt it, losing the composer, terminal, and scroll state in it. Both split reducers derived the parent group from `parentPath.length`, but `targetPath.slice(0, -1)` is empty for two different situations: the pane is the whole tree, and the pane is a direct child of the root group. The second case was read as parentless, so the same-direction fast path never ran at root level and every split wrapped the pane in a fresh group. That reparents the pane, and the renderer reconciles panes positionally, so React unmounted the subtree. The workspace already starts as a horizontal group holding the main pane and the hidden explorer pane — the group was there all along, ready to take another child. `findParentGroup` now asks `targetPath`, so a same-direction split appends a sibling into the group that is already running in that axis. Perpendicular splits still nest, because changing axis needs a group. The old test asserted the nesting as intended behaviour, which is how this survived. It now asserts flat insertion, alongside the invariant that matters: a same-direction split never changes an existing pane's render path. Node id sequences shifted in three tests because the first split no longer spends a group id, and four user splits now fit inside the depth cap where the fourth used to be refused.
This commit is contained in:
@@ -468,6 +468,21 @@ function findGroupPathById(
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The group holding the node at `targetPath`, or null when that node is the whole tree.
|
||||
*
|
||||
* Ask `targetPath`, never the parent path: `targetPath.slice(0, -1)` is empty both for "this node
|
||||
* is the root" and for "this node is a direct child of the root group". Treating the second case as
|
||||
* parentless wraps root-level panes in a redundant group on every split, which reparents the
|
||||
* existing pane and remounts its whole subtree.
|
||||
*/
|
||||
function findParentGroup(root: SplitNodeInternal, targetPath: number[]): SplitNodeInternal | null {
|
||||
if (targetPath.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return getNodeAtPath(root, targetPath.slice(0, -1));
|
||||
}
|
||||
|
||||
function getNodeAtPath(node: SplitNodeInternal, path: number[]): SplitNodeInternal {
|
||||
let current = node;
|
||||
for (const index of path) {
|
||||
@@ -869,7 +884,7 @@ function insertSplitInternal(input: InsertSplitInternalInput): InsertSplitIntern
|
||||
|
||||
const parentPath = targetPath.slice(0, -1);
|
||||
const targetIndex = targetPath[targetPath.length - 1] ?? 0;
|
||||
const parentNode = parentPath.length > 0 ? getNodeAtPath(detached.root, parentPath) : null;
|
||||
const parentNode = findParentGroup(detached.root, targetPath);
|
||||
|
||||
if (parentNode?.kind === "group" && parentNode.group.direction === direction) {
|
||||
const targetSize = parentNode.group.sizes[targetIndex] ?? 0;
|
||||
@@ -1448,7 +1463,7 @@ export function splitPaneEmptyInLayout(
|
||||
|
||||
const parentPath = targetPath.slice(0, -1);
|
||||
const targetIndex = targetPath[targetPath.length - 1] ?? 0;
|
||||
const parentNode = parentPath.length > 0 ? getNodeAtPath(layout.root, parentPath) : null;
|
||||
const parentNode = findParentGroup(layout.root, targetPath);
|
||||
|
||||
let nextRoot: SplitNodeInternal;
|
||||
if (parentNode?.kind === "group" && parentNode.group.direction === direction) {
|
||||
|
||||
Reference in New Issue
Block a user