From 73e3e0860ed17062a88779d9f62cb4cfc492cfb1 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Tue, 28 Jul 2026 02:49:46 -0500 Subject: [PATCH] feat(desktop): expose a pane's layout zone to its content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PaneGroupContext, alongside PaneVisibleContext: the zone hands each pane the group id it's rendered in, so state that belongs to a stack of tabs rather than to a pane can key off it — and follows a pane dragged between zones, since the provider is whichever zone renders it. --- .../src/components/pane-shell/pane-visibility.ts | 14 ++++++++++++++ .../pane-shell/tree/renderer/tree-group.tsx | 14 +++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/components/pane-shell/pane-visibility.ts b/apps/desktop/src/components/pane-shell/pane-visibility.ts index 986b9ee3bde2..16f9d402ad81 100644 --- a/apps/desktop/src/components/pane-shell/pane-visibility.ts +++ b/apps/desktop/src/components/pane-shell/pane-visibility.ts @@ -28,6 +28,20 @@ export const PaneVisibleContext = createContext(true) export const usePaneVisible = (): boolean => useContext(PaneVisibleContext) +/** Fallback group key for a surface rendered outside the layout tree (secondary + * windows, plain routes) — one bucket, since there are no sibling zones there + * to tell apart. */ +export const NO_PANE_GROUP = 'window' + +/** The layout-tree GROUP (zone) a pane is rendered in — the identity of "this + * set of tabs". Panes stacked as tabs share one group; each split zone is its + * own. State that should be per-zone rather than per-window or per-tab keys off + * this (see the composer pop-out). Follows a pane dragged between zones, + * because the provider is the zone that renders it. */ +export const PaneGroupContext = createContext(NO_PANE_GROUP) + +export const usePaneGroup = (): string => useContext(PaneGroupContext) + /** `querySelectorAll` minus anything inside an inactive tab. */ export const queryAllVisible = (selector: string, root: ParentNode = document): T[] => [...root.querySelectorAll(selector)].filter(el => !el.closest(HIDDEN_PANE)) diff --git a/apps/desktop/src/components/pane-shell/tree/renderer/tree-group.tsx b/apps/desktop/src/components/pane-shell/tree/renderer/tree-group.tsx index 8ed3ca944049..31b5646813ac 100644 --- a/apps/desktop/src/components/pane-shell/tree/renderer/tree-group.tsx +++ b/apps/desktop/src/components/pane-shell/tree/renderer/tree-group.tsx @@ -24,7 +24,7 @@ import { cn } from '@/lib/utils' import { $layoutEditMode } from '../../edit-mode' import { useWindowControlsOverlap } from '../../geometry' -import { hiddenPaneProps, PaneVisibleContext } from '../../pane-visibility' +import { hiddenPaneProps, PaneGroupContext, PaneVisibleContext } from '../../pane-visibility' import type { DropPosition, GroupNode, RootEdge } from '../model' import { adjacentGroup } from '../model' import { @@ -567,10 +567,14 @@ export function TreeGroup({ > {pane?.render ? ( // Visibility flows to the pane so a kept-alive chat surface - // can gate its hot (per-token) subscriptions while hidden. - - {pane.render()} - + // can gate its hot (per-token) subscriptions while hidden; + // the group id identifies the ZONE it lives in, for state + // that is per-zone rather than per-tab (composer pop-out). + + + {pane.render()} + + ) : ( isActive && (