fix(desktop): Hide tabs works on the sessions sidebar

hideOnly chrome pinned the Sessions/Bots strip on at any tab count, so
never was a silent no-op. The panes stay; ⌘⌥T brings the strip back.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
brooklyn!
2026-09-08 15:20:07 -05:00
committed by brooklyn!
parent 9b0d75ce44
commit 960dee7fe5
4 changed files with 15 additions and 30 deletions

View File

@@ -104,14 +104,14 @@ describe('hide-only strip tabs', () => {
expect(hideOnlyZoneTabs('g-main')).toEqual([])
})
it('refuses to hide the strip that is the only handle for hide-only tabs', () => {
it('honors never on the sessions/Bots strip', () => {
sessionsBotsTree()
setTreeGroupTabStrip('g-side', 'never')
const side = $layoutTree.get()
const group = side && side.type === 'split' ? side.children[0] : side
expect(group && group.type === 'group' ? tabStripVisibleForGroup(group) : false).toBe(true)
expect(group && group.type === 'group' ? tabStripVisibleForGroup(group) : true).toBe(false)
})
it('excludes hide-only tabs from every close verb', () => {

View File

@@ -135,15 +135,13 @@ describe('Sessions/Bots strip — #91223', () => {
expect(tabEl('hermes-bots:pane')).toBeTruthy()
})
it('an explicit never still paints the strip — hide-only chrome has no other handle', () => {
it('an explicit never hides the sessions/Bots strip', () => {
setTreeGroupTabStrip('g-side', 'never')
expect(tabStripVisibleForGroup(zoneAt(0))).toBe(true)
expect(tabStripVisibleForGroup(zoneAt(0))).toBe(false)
render(<LiveTreeGroup parentAxis="row" />)
expect(tablist()).toBeTruthy()
expect(tabEl('sessions')).toBeTruthy()
expect(tabEl('hermes-bots:pane')).toBeTruthy()
expect(tablist()).toBeNull()
})
})

View File

@@ -9,7 +9,6 @@ const tile = (): StripPane => ({ collapsePane: false, placement: 'main' })
const workspace = (): StripPane => ({ collapsePane: false, placement: 'main', uncloseable: true })
const toolPanel = (): StripPane => ({ collapsePane: true, placement: 'bottom' })
const sideChrome = (): StripPane => ({ collapsePane: false, placement: 'right' })
const hideOnlyChrome = (): StripPane => ({ collapsePane: false, hideOnly: true, placement: 'left' })
describe('auto (no stored choice)', () => {
it('gives a lone workspace no strip and a stack of two a strip', () => {
@@ -45,18 +44,14 @@ describe('no dead zone', () => {
expect(resolveTabStripVisible({ mode: 'never', shown: [toolPanel()] })).toBe(true)
})
it('keeps the strip for hide-only chrome even when the zone says never', () => {
// Sessions / Bots: the Show/Hide rows and the chips themselves live on
// the strip. Hiding it is the #91223 trap — nothing left to click.
expect(resolveTabStripVisible({ mode: 'never', shown: [hideOnlyChrome()] })).toBe(true)
expect(resolveTabStripVisible({ mode: 'never', shown: [hideOnlyChrome(), hideOnlyChrome()] })).toBe(true)
})
it('still hides a zone that cannot strand anything', () => {
// The workspace is uncloseable, and a stack is reachable by tab cycling —
// the invariant protects handles, it does not veto hiding as such.
// The workspace is uncloseable, a stack is reachable by tab cycling, and
// hide-only chrome (sessions / Bots) keeps its panes + ⌘⌥T — the invariant
// protects handles, it does not veto hiding as such.
expect(resolveTabStripVisible({ mode: 'never', shown: [workspace()] })).toBe(false)
expect(resolveTabStripVisible({ mode: 'never', shown: [toolPanel(), toolPanel()] })).toBe(false)
expect(resolveTabStripVisible({ mode: 'never', shown: [sideChrome()] })).toBe(false)
expect(resolveTabStripVisible({ mode: 'never', shown: [sideChrome(), sideChrome()] })).toBe(false)
})
})

View File

@@ -19,9 +19,6 @@ import { paneChrome } from './track-model'
export interface StripPane {
/** A tool panel (terminal / logs) that collapses rather than closes. */
collapsePane: boolean
/** Standing chrome (sessions / Bots) whose only handle is the strip:
* show/hide replaces Close, and the Show/Hide rows live on the strip. */
hideOnly?: boolean
/** Contribution placement — `'main'` marks a docked tile (session, page,
* preview) as opposed to standing side chrome. */
placement?: string
@@ -42,9 +39,11 @@ export interface StripZone {
/**
* A pane is STRANDED without a strip when the strip is the only thing carrying
* its handle: a lone closeable tile needs its ✕, a lone tool panel needs a chip
* to grab, hide-only chrome (sessions / Bots) needs the chip that show/hide
* lives on. The uncloseable workspace is not strandable — it cannot be closed
* or lost, so a lone chat is free to be chromeless.
* to grab. The uncloseable workspace is not strandable — it cannot be closed
* or lost, so a lone chat is free to be chromeless. Hide-only chrome (sessions
* / Bots) is the same: the panes stay, Show/Hide is a separate verb, and a
* hidden strip comes back via ⌘⌥T. Treating it as stranded at any count made
* Hide tabs a silent no-op on the sessions sidebar.
*
* This outranks an explicit `never` on purpose. "Hide the strip" is a request
* about chrome, never a request to make a surface unreachable, and a zone that
@@ -59,12 +58,6 @@ export interface StripZone {
* both the menu row and ⌘⌥T became silent no-ops.
*/
function stranded(shown: readonly StripPane[]): boolean {
// Hide-only chrome is stranded at ANY count: it has no close verb at all, and
// both the chips and the Show/Hide rows that replace one live on the strip.
if (shown.some(pane => pane.hideOnly)) {
return true
}
if (shown.length !== 1) {
return false
}
@@ -123,7 +116,6 @@ export function tabStripVisibleForZone(zone: {
return {
collapsePane: zone.isCollapsePane(id),
hideOnly: chrome.hideOnly,
placement: chrome.placement,
uncloseable: chrome.uncloseable
}