Merge pull request #74640 from NousResearch/bb/undo-close-tab-focus

fix(desktop): focus the tab restored by undo-close (⌘⇧T)
This commit is contained in:
brooklyn! 2026-07-30 02:06:29 -05:00 committed by GitHub
commit 05f5df6bdb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 96 additions and 4 deletions

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { ClientSessionState } from '@/app/types'
import { group, split } from '@/components/pane-shell/tree/model'
import { findGroupOfPane, group, split } from '@/components/pane-shell/tree/model'
import { $layoutTree } from '@/components/pane-shell/tree/store'
import { $selectedStoredSessionId } from '@/store/session'
import type { SessionTile } from '@/store/session-states'
@ -138,3 +138,92 @@ describe('blankDraftTile', () => {
expect(blankDraftTile([], {})).toBeNull()
})
})
// ⌘⇧T used to only restore `$sessionTiles`. Adoption inserts silently
// (activate:false), so the tab came back behind the still-fronted workspace.
// Real path: register, adopt, focus — same as paneMirror + reopen.
describe('reopenLastClosedTile focuses the restored tab', () => {
beforeEach(() => {
window.localStorage.clear()
vi.resetModules()
})
afterEach(() => {
vi.resetModules()
})
async function setup() {
const tree = await import('@/components/pane-shell/tree/store')
const model = await import('@/components/pane-shell/tree/model')
const { registry } = await import('@/contrib/registry')
const session = await import('@/store/session')
const states = await import('@/store/session-states')
registry.register({
area: 'panes',
data: { placement: 'main', uncloseable: true },
id: 'workspace',
render: () => null,
title: 'chat'
})
// panes ← $sessionTiles (paneMirror stub). Adoption is synchronous on
// register, so openSessionTile + focusOpenSession works the same tick.
const registered = new Map<string, () => void>()
const syncTiles = () => {
const wanted = new Set(states.$sessionTiles.get().map(t => t.storedSessionId))
for (const id of wanted) {
if (registered.has(id)) {
continue
}
registered.set(
id,
registry.register({
area: 'panes',
data: { dock: { pane: 'workspace', pos: 'center' }, placement: 'main' },
id: tilePane(id),
render: () => null,
title: id
})
)
}
for (const [id, dispose] of registered) {
if (!wanted.has(id)) {
dispose()
registered.delete(id)
tree.removeTreePane(tilePane(id))
}
}
}
states.$sessionTiles.listen(syncTiles)
tree.watchContributedPanes()
session.$selectedStoredSessionId.set('primary')
tree.declareDefaultTree(model.group(['workspace'], { active: 'workspace', id: 'grp-main' }))
states.openSessionTile('closed', 'center', 'workspace')
states.focusOpenSession('closed')
tree.noteActiveTreeGroup('grp-main')
expect(findGroupOfPane(tree.$layoutTree.get()!, tilePane('closed'))?.active).toBe(tilePane('closed'))
return { states, tree }
}
it('fronts the restored tab after ⌘⇧T', async () => {
const { states, tree } = await setup()
states.closeSessionTile('closed')
expect(states.$sessionTiles.get().some(t => t.storedSessionId === 'closed')).toBe(false)
expect(findGroupOfPane(tree.$layoutTree.get()!, 'workspace')?.active).toBe('workspace')
states.reopenLastClosedTile()
expect(states.$sessionTiles.get().some(t => t.storedSessionId === 'closed')).toBe(true)
expect(findGroupOfPane(tree.$layoutTree.get()!, tilePane('closed'))?.active).toBe(tilePane('closed'))
expect(tree.$activeTreeGroup.get()).toBe('grp-main')
})
})

View file

@ -702,8 +702,10 @@ export function discardSessionTile(storedSessionId: string) {
saveTiles($sessionTiles.get().filter(t => t.storedSessionId !== storedSessionId))
}
/** T reopen the most recently closed tab where it was. Skips ids that are
* live again (reopened, or now the primary). */
/** T reopen the most recently closed tab where it was, then focus it.
* Adoption alone is silent (won't steal the active tab), so restore has to
* front the pane explicitly. Skips ids that are live again (reopened / now
* the primary). */
export function reopenLastClosedTile(): void {
const stack = closedStack()
@ -716,6 +718,7 @@ export function reopenLastClosedTile(): void {
if (!$sessionTiles.get().some(t => t.storedSessionId === storedSessionId)) {
openSessionTile(storedSessionId, tile.dir, tile.anchor, tile.before)
focusOpenSession(storedSessionId)
return
}