Files
hermes-agent/apps
Austin Pickett 5c6aff1430 fix(desktop): keep the chat in front of the terminal in Focus layout (#81019)
* fix(desktop): hide the terminal overlay when its pane is inactive

One xterm is CSS-overlayed onto whichever `<TerminalSlot />` is active,
positioned with `position: fixed` from the slot's bounding rect. Keep-alive
tab layers stay MOUNTED when inactive — hidden with `visibility: hidden` +
`data-pane-hidden`, deliberately preserving their layout box so scroll state
and xterm survive a tab round-trip.

So an inactive terminal slot still reports a full-size rect identical to the
front tab's, and `rect.width > 0 && rect.height > 0` cannot tell the two
apart. The overlay stayed painted at z-4 over whatever tab the user switched
to, swallowing its clicks.

Sample the hidden state alongside the geometry: `Rect` carries `hidden` from
`isElementInHiddenPane(slot)`, `sameRect` compares it so a tab switch wakes
the tracker, the ancestor MutationObserver watches `PANE_HIDDEN_ATTR`, and
the overlay gates on `!rect.hidden`. `TerminalWorkspace` stays mounted
throughout — PTYs are never torn down, only the surface stops painting.

`opacity: 0` rides alongside `visibility: hidden` because Electron can keep
xterm's WebGL canvas composited after an ancestor goes hidden.

Refs #71407

* fix(desktop): collapse an active tool pane onto the workspace, not a neighbour

`setPaneCollapsed` on the ACTIVE pane of a shared zone that holds the
uncloseable workspace handed the active slot to `group.panes[at - 1]` — the
tab to its left, whichever that happened to be.

The workspace can't minimize (it would strand the app), so tab-switching to a
sibling is the right shape; picking a positional neighbour is not. In the
Focus preset the terminal is a tab in the workspace's own group:

    [workspace, files, review, terminal]

Collapsing the active terminal therefore selected `review`. The user asked for
the terminal to go away and landed on a diff pane they never opened — and with
the overlay still painting (before the previous commit), it read as "the
terminal came back".

Hand the slot to the uncloseable pane itself. That pane is the zone's anchor:
it's the one member guaranteed to be a real destination rather than another
tool the user was not asking for. The positional fallback stays for the
defensive case of collapsing the uncloseable pane itself.

This is deliberately broader than one entry point — every route into
`setPaneCollapsed` for a shared zone gets it: the rail, the tab toggle, and
⌃`. Pure tool-only zones are untouched and still fold as a unit.

* fix(desktop): front the workspace when a fresh chat starts

`startFreshSessionDraft` resets the whole view — messages, usage, timers,
route intent, cwd — but left `$terminalTakeover` set. That atom is not a
cosmetic flag: `controller.tsx` binds it as the terminal's toggle store via
`bindToolPaneCollapse`, so while it stays true the terminal keeps the pane
fronted and ⌘N appeared to bounce straight back into the shell.

Clear it, then `revealTreePane('workspace')`. The reveal is not redundant
with the clear: takeover can already be false while the terminal is simply
the active tab (the flag stays true behind a stacked sibling, and tile flows
never touch it), so the state the user sees and the state the flag describes
drift apart. Clearing homes the common case; revealing states the intent
outright — a new chat shows the chat.

The terminal is not torn down. Tool panels collapse to a rail and keep their
PTYs; re-opening finds the same shell.

The `+` / ⌘T tile path needs no takeover clear — it fronts its new tile
through `revealTreePane` and relies on the hidden-pane-aware overlay.

* fix(desktop): reveal the workspace without closing the terminal

The fresh-session commit cleared `$terminalTakeover` on the way to fronting
the workspace. That atom is not a Focus-only fronting flag — it is the
terminal's open/closed state in every layout, and clearing it is wrong twice
over.

Only the Focus preset stacks the terminal with the workspace. Default,
Terminal deck, and Quad each give it a zone of its own, where it sits beside
the chat and obscures nothing — and there ⌘N minimized a terminal the user
had deliberately open.

The flag is also persisted, so the damage outlived the session. On the next
boot the Focus terminal tab is still in the strip and its zone is not
minimized, so clicking it only calls `activateTreePane`; `PersistentTerminal`
mounts its workspace solely while takeover is true, so the tab fronted empty.

`revealTreePane('workspace')` already carries the whole intent. Behind another
tab the terminal is HIDDEN, not closed: it keeps its PTYs, and the overlay
stops painting on the pane-hidden marker from the first commit in this branch
— which is what was actually covering the chat. Removing the clear costs
nothing and keeps the toggle store truthful.

Two regression tests, both verified to fail when the clear is reinstated: a
terminal in its own zone stays open and visible across a fresh chat, and a
Focus terminal tab still mounts after a restart.

Reported by Copilot review on #81019.

---------

Co-authored-by: izumi0uu <izumi0uu@gmail.com>
Co-authored-by: Ritesh Patel <60716910+DECRUX9812@users.noreply.github.com>
2026-08-07 09:23:03 -04:00
..