mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-26 17:38:36 +00:00
fix(desktop): mark kept-alive panes so document lookups can skip them
Inactive tabs stay mounted and hidden with `visibility`, which preserves their layout box — so a background tab answers a document-wide selector with a rect identical to the visible tab's. Tag hidden layers and route those lookups through visible-scoped query helpers.
This commit is contained in:
parent
ca35663013
commit
9285de4a41
2 changed files with 32 additions and 1 deletions
27
apps/desktop/src/components/pane-shell/pane-visibility.ts
Normal file
27
apps/desktop/src/components/pane-shell/pane-visibility.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Keep-alive visibility — the one policy every document-wide lookup must obey.
|
||||
*
|
||||
* A tab group keeps each ever-active pane MOUNTED and hides the inactive ones
|
||||
* with `visibility: hidden` (see `tree/renderer/tree-group.tsx`), deliberately
|
||||
* preserving their layout box so scroll positions survive a tab round-trip. The
|
||||
* cost is that an inactive tab's rect is IDENTICAL to the visible tab's, so
|
||||
* neither selector order nor a rect hit-test can tell them apart. A lookup that
|
||||
* resolves "the chat surface / composer / viewport" from the document therefore
|
||||
* has to skip hidden panes, or it silently answers with the wrong tab.
|
||||
*/
|
||||
|
||||
/** Marks a mounted-but-hidden pane layer (an inactive tab in a stack). */
|
||||
export const PANE_HIDDEN_ATTR = 'data-pane-hidden'
|
||||
|
||||
const HIDDEN_PANE = `[${PANE_HIDDEN_ATTR}]`
|
||||
|
||||
/** Spread onto a kept pane layer so the lookups below can skip it. */
|
||||
export const hiddenPaneProps = (hidden: boolean): Record<string, string> => (hidden ? { [PANE_HIDDEN_ATTR]: '' } : {})
|
||||
|
||||
/** `querySelectorAll` minus anything inside an inactive tab. */
|
||||
export const queryAllVisible = <T extends HTMLElement>(selector: string, root: ParentNode = document): T[] =>
|
||||
[...root.querySelectorAll<T>(selector)].filter(el => !el.closest(HIDDEN_PANE))
|
||||
|
||||
/** `querySelector` minus anything inside an inactive tab. */
|
||||
export const queryVisible = <T extends HTMLElement>(selector: string, root: ParentNode = document): null | T =>
|
||||
queryAllVisible<T>(selector, root)[0] ?? null
|
||||
|
|
@ -30,6 +30,7 @@ import { cn } from '@/lib/utils'
|
|||
|
||||
import { $layoutEditMode } from '../../edit-mode'
|
||||
import { useWindowControlsOverlap } from '../../geometry'
|
||||
import { hiddenPaneProps } from '../../pane-visibility'
|
||||
import type { DropPosition, GroupNode, RootEdge } from '../model'
|
||||
import { adjacentGroup } from '../model'
|
||||
import {
|
||||
|
|
@ -520,7 +521,9 @@ export function TreeGroup({
|
|||
{/* Body: the zone's pane content — every kept (ever-active) pane stays
|
||||
mounted in an absolute layer; only the active one is visible.
|
||||
`visibility` (not display) keeps the hidden pane's layout box, so
|
||||
scroll positions and measurements survive the round-trip. */}
|
||||
scroll positions and measurements survive the round-trip — which also
|
||||
makes a hidden layer's rect identical to the visible one's, hence the
|
||||
marker document-wide lookups filter on (see pane-visibility.ts). */}
|
||||
{!node.minimized && (
|
||||
<div className="relative min-h-0 min-w-0 flex-1 overflow-hidden">
|
||||
{isEmpty ? (
|
||||
|
|
@ -538,6 +541,7 @@ export function TreeGroup({
|
|||
aria-hidden={!isActive || undefined}
|
||||
className={cn('absolute inset-0 overflow-auto', !isActive && 'pointer-events-none invisible')}
|
||||
key={paneId}
|
||||
{...hiddenPaneProps(!isActive)}
|
||||
>
|
||||
{pane?.render ? (
|
||||
<ContribBoundary id={pane.id}>{pane.render()}</ContribBoundary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue