fix(desktop): closing main in a side-by-side layout promotes the neighboring session instead of spawning a fresh draft (#88924)

nextSessionTileForWorkspace() only walked tabs stacked WITH the workspace
tab. In a side-by-side layout (session tiles in their own zones — db's
three-pane report), the walk found nothing, closeWorkspaceTab() skipped
promotion, and requestFreshSession() dropped main to a new draft: closing
a pane read as 'it gave me a new session'. A tile in any zone now promotes
(its zone collapses via the tile close), so Close is also the path from N
panes back to 1. Ghost panes whose tile is gone still never promote.
This commit is contained in:
Teknium
2026-08-18 01:17:42 -07:00
parent a75d1b5c89
commit 2d511f55aa
2 changed files with 53 additions and 0 deletions
@@ -11,6 +11,7 @@ import {
blankDraftTile,
focusedSessionNeedsRoute,
markSelectionRestore,
nextSessionTileForWorkspace,
orderTilesByTree,
releaseSessionTranscript,
resetTileRuntimeBindings,
@@ -111,6 +112,47 @@ describe('selectionHomesToWorkspace', () => {
})
})
describe('nextSessionTileForWorkspace (⌘W promotion source)', () => {
afterEach(() => {
$layoutTree.set(null)
$sessionTiles.set([])
})
it('prefers a tile stacked WITH the workspace tab (nearest-out)', () => {
$layoutTree.set(group(['workspace', tilePane('a'), tilePane('b')], { active: 'workspace', id: 'main' }))
$sessionTiles.set([tile('a'), tile('b')])
expect(nextSessionTileForWorkspace()).toBe('a')
})
it('side-by-side layout: a tile in ANOTHER zone still promotes instead of dropping main to a fresh draft (#88924)', () => {
// main zone holds only the workspace; the session tile lives in its own
// zone beside it — db's three-pane report shape.
$layoutTree.set(
split('row', [
group(['workspace'], { active: 'workspace', id: 'main' }),
group([tilePane('side')], { active: tilePane('side'), id: 'right' })
])
)
$sessionTiles.set([tile('side')])
expect(nextSessionTileForWorkspace()).toBe('side')
})
it('returns null when no live tile exists anywhere in the tree', () => {
$layoutTree.set(
split('row', [
group(['workspace'], { active: 'workspace', id: 'main' }),
group([tilePane('stale')], { active: tilePane('stale'), id: 'right' })
])
)
// Pane persisted in the tree but its tile is gone — must not promote a ghost.
$sessionTiles.set([])
expect(nextSessionTileForWorkspace()).toBe(null)
})
})
describe('boot-restore selection homing (⌘R tab persistence)', () => {
const mainGroup = () => group(['workspace', tilePane('t')], { active: tilePane('t'), id: 'main' })
+11
View File
@@ -818,6 +818,17 @@ export function nextSessionTileForWorkspace(): null | string {
}
}
// Nothing stacked WITH main — but a session tile in another zone can still
// shift in. Without this, closing main in a side-by-side layout skipped
// promotion entirely and dropped to a fresh "New session" draft, which read
// as "closing a pane gave me a new session" (#88924). Promoting the tile
// also collapses its zone, so Close is how a multi-pane layout shrinks.
for (const tile of tiles) {
if (tree && findGroupOfPane(tree, `${TILE_PANE_PREFIX}${tile.storedSessionId}`)) {
return tile.storedSessionId
}
}
return null
}