From 4875b64293906e053d5c8dd60be3657cd752343b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 29 Aug 2026 02:36:26 -0700 Subject: [PATCH] Split pane tree equalization logic --- config/max-lines-baseline.txt | 1 - .../pane-manager/pane-tree-equalization.ts | 59 ++++++++++++++++++ .../src/lib/pane-manager/pane-tree-ops.ts | 62 +------------------ 3 files changed, 60 insertions(+), 62 deletions(-) create mode 100644 src/renderer/src/lib/pane-manager/pane-tree-equalization.ts diff --git a/config/max-lines-baseline.txt b/config/max-lines-baseline.txt index 740626e9bae..db6c77afbd8 100644 --- a/config/max-lines-baseline.txt +++ b/config/max-lines-baseline.txt @@ -61,7 +61,6 @@ inline src/renderer/src/components/terminal-pane/remote-runtime-pty-transport.ts inline src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts inline src/renderer/src/hooks/useAutomationDispatchEvents.ts inline src/renderer/src/hooks/useSettingsNavigationMetadata.ts -inline src/renderer/src/lib/pane-manager/pane-tree-ops.ts inline src/renderer/src/runtime/remote-runtime-terminal-multiplexer.ts inline src/renderer/src/runtime/runtime-file-client.ts inline src/renderer/src/runtime/sync-runtime-graph.ts diff --git a/src/renderer/src/lib/pane-manager/pane-tree-equalization.ts b/src/renderer/src/lib/pane-manager/pane-tree-equalization.ts new file mode 100644 index 00000000000..7e36064d9bc --- /dev/null +++ b/src/renderer/src/lib/pane-manager/pane-tree-equalization.ts @@ -0,0 +1,59 @@ +/** Find non-divider children (panes and splits) of an element. */ +export function findPaneChildren(parent: HTMLElement): HTMLElement[] { + return Array.from(parent.children).filter( + (child): child is HTMLElement => + child instanceof HTMLElement && + (child.classList.contains('pane') || child.classList.contains('pane-split')) + ) +} + +function getSplitDirection(split: HTMLElement): 'vertical' | 'horizontal' { + return split.classList.contains('is-horizontal') ? 'horizontal' : 'vertical' +} + +function getEqualizeWeight(el: HTMLElement, direction: 'vertical' | 'horizontal'): number { + if (!el.classList.contains('pane-split') || getSplitDirection(el) !== direction) { + return 1 + } + + const children = findPaneChildren(el) + return Math.max( + 1, + children.reduce((sum, child) => sum + getEqualizeWeight(child, direction), 0) + ) +} + +export function equalizePaneSplitSizes(root: HTMLElement | null): boolean { + if (!root) { + return false + } + + let changed = false + const visit = (el: HTMLElement): void => { + if (!el.classList.contains('pane-split')) { + return + } + + const direction = getSplitDirection(el) + const children = findPaneChildren(el) + if (children.length >= 2) { + for (const child of children) { + // Why: same-axis nested splits need pane-count weighting so three + // side-by-side panes become thirds, not 50/25/25. + const weight = getEqualizeWeight(child, direction) + const nextFlex = `${weight} 1 0%` + if (child.style.flex !== nextFlex) { + child.style.flex = nextFlex + changed = true + } + } + } + + for (const child of children) { + visit(child) + } + } + + visit(root) + return changed +} diff --git a/src/renderer/src/lib/pane-manager/pane-tree-ops.ts b/src/renderer/src/lib/pane-manager/pane-tree-ops.ts index 833eb1ff822..3549860183b 100644 --- a/src/renderer/src/lib/pane-manager/pane-tree-ops.ts +++ b/src/renderer/src/lib/pane-manager/pane-tree-ops.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-lines -- Why: split-tree DOM reparent, promote, and equalize rules need one consistent owner. */ import type { DropZone, ManagedPane, @@ -16,6 +15,7 @@ export { type SafeFitContinuationHandle } from './pane-fit' export { captureScrollState, restoreScrollState } from './pane-scroll' +export { equalizePaneSplitSizes, findPaneChildren } from './pane-tree-equalization' // --------------------------------------------------------------------------- // Split-tree manipulation: detach, insert, promote sibling @@ -242,66 +242,6 @@ export function removeDividers(parent: HTMLElement): void { } } -/** Find non-divider children (panes and splits) of an element. */ -export function findPaneChildren(parent: HTMLElement): HTMLElement[] { - return Array.from(parent.children).filter( - (child): child is HTMLElement => - child instanceof HTMLElement && - (child.classList.contains('pane') || child.classList.contains('pane-split')) - ) -} - -function getSplitDirection(split: HTMLElement): 'vertical' | 'horizontal' { - return split.classList.contains('is-horizontal') ? 'horizontal' : 'vertical' -} - -function getEqualizeWeight(el: HTMLElement, direction: 'vertical' | 'horizontal'): number { - if (!el.classList.contains('pane-split') || getSplitDirection(el) !== direction) { - return 1 - } - - const children = findPaneChildren(el) - return Math.max( - 1, - children.reduce((sum, child) => sum + getEqualizeWeight(child, direction), 0) - ) -} - -export function equalizePaneSplitSizes(root: HTMLElement | null): boolean { - if (!root) { - return false - } - - let changed = false - const visit = (el: HTMLElement): void => { - if (!el.classList.contains('pane-split')) { - return - } - - const direction = getSplitDirection(el) - const children = findPaneChildren(el) - if (children.length >= 2) { - for (const child of children) { - // Why: same-axis nested splits need pane-count weighting so three - // side-by-side panes become thirds, not 50/25/25. - const weight = getEqualizeWeight(child, direction) - const nextFlex = `${weight} 1 0%` - if (child.style.flex !== nextFlex) { - child.style.flex = nextFlex - changed = true - } - } - } - - for (const child of children) { - visit(child) - } - } - - visit(root) - return changed -} - /** * Create a flex split wrapper that replaces `existingContainer` in the DOM, * then places [existing] [divider] [new] inside it.