refactor(activity): prevent sidebar effects on notification threads

Add revealInSidebar and clearSidebarFilters options to worktree activation
functions, allowing notification threads to activate workspaces silently
without revealing or filtering the sidebar.
This commit is contained in:
Jinjing
2026-09-05 20:40:46 -07:00
parent d6273d38bf
commit ccfea81295
4 changed files with 48 additions and 12 deletions
@@ -98,7 +98,9 @@ describe('activity thread host routing', () => {
// Bare setActiveWorktree skips setActiveView('terminal'), initial-terminal seeding and
// sleeping-session resume — the workspace dispatcher is the only path that runs them.
expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith(thread.worktree.id, {
executionHostId: REMOTE_HOST
executionHostId: REMOTE_HOST,
revealInSidebar: false,
clearSidebarFilters: false
})
expect(setActiveWorktree).not.toHaveBeenCalled()
expect(mocks.activateTabAndFocusPane).toHaveBeenCalledWith(
@@ -121,7 +123,9 @@ describe('activity thread host routing', () => {
expect(setSelectedPaneKey).toHaveBeenCalledWith(thread.paneKey)
expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith(thread.worktree.id, {
executionHostId: REMOTE_HOST
executionHostId: REMOTE_HOST,
revealInSidebar: false,
clearSidebarFilters: false
})
expect(mocks.activateTabAndFocusPane).toHaveBeenCalledWith(
thread.tab.id,
@@ -136,7 +140,9 @@ describe('activity thread host routing', () => {
makeActions().selectThread(thread)
expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith(thread.worktree.id, {
executionHostId: REMOTE_HOST
executionHostId: REMOTE_HOST,
revealInSidebar: false,
clearSidebarFilters: false
})
expect(mocks.activateTabAndFocusPane).not.toHaveBeenCalled()
})
@@ -83,7 +83,13 @@ export function createActivityThreadActions({
// state of an SSH session that was never revived — has no resident tab until
// resumeSleepingAgentSessionsForWorktree/ensureWorktreeHasInitialTerminal run inside here.
// Probing tab residency first is what made a remote row click a silent no-op (#16731).
if (activateAndRevealWorkspace(thread.worktree.id, { executionHostId }) === false) {
if (
activateAndRevealWorkspace(thread.worktree.id, {
executionHostId,
revealInSidebar: false,
clearSidebarFilters: false
}) === false
) {
return
}
if (
@@ -1,6 +1,10 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { useAppStore } from '@/store'
import { activateAndRevealFolderWorkspace, activateAndRevealWorktree } from './worktree-activation'
import {
activateAndRevealFolderWorkspace,
activateAndRevealWorkspace,
activateAndRevealWorktree
} from './worktree-activation'
import { ensureWorktreeHasInitialTerminal } from './worktree-initial-terminal-seeding'
import { folderWorkspaceKey } from '../../../shared/workspace-scope'
import { toSshExecutionHostId } from '../../../shared/execution-host'
@@ -202,6 +206,25 @@ function seedEmptiedFolderWorkspaceOnTwoHosts(): void {
}
describe('activating a folder workspace whose last terminal was closed', () => {
it.each(['local', SSH_HOST_ID] as const)(
'opens a notification on %s without revealing the folder',
(executionHostId) => {
seedEmptiedFolderWorkspaceOnTwoHosts()
useAppStore.setState({ sidebarBody: 'agents' })
const result = activateAndRevealWorkspace(FOLDER_KEY, {
executionHostId,
revealInSidebar: false,
clearSidebarFilters: false
})
expect(result).not.toBe(false)
expect(useAppStore.getState().activeWorktreeId).toBe(FOLDER_KEY)
expect(useAppStore.getState().sidebarBody).toBe('agents')
expect(useAppStore.getState().revealWorktreeInSidebar).not.toHaveBeenCalled()
}
)
it('re-seeds a terminal when the workspace is opened', () => {
seedEmptiedFolderWorkspaceOnTwoHosts()
+8 -7
View File
@@ -79,6 +79,7 @@ export function activateAndRevealFolderWorkspace(
folderWorkspaceId: string,
opts?: {
sidebarRevealBehavior?: PendingSidebarWorktreeReveal['behavior']
revealInSidebar?: boolean
startup?: WorktreeStartupPayload
runtimeEnvironmentId?: string | null
executionHostId?: ExecutionHostId
@@ -163,10 +164,11 @@ export function activateAndRevealFolderWorkspace(
opts?.providesInitialSurface
)
if (opts?.sidebarRevealBehavior) {
state.revealWorktreeInSidebar(workspaceKey, { behavior: opts.sidebarRevealBehavior })
} else {
state.revealWorktreeInSidebar(workspaceKey)
if (opts?.revealInSidebar !== false) {
state.revealWorktreeInSidebar(
workspaceKey,
opts?.sidebarRevealBehavior ? { behavior: opts.sidebarRevealBehavior } : undefined
)
}
return { primaryTabId }
@@ -346,8 +348,8 @@ export function activateAndRevealWorkspace(
opts?: {
executionHostId?: ExecutionHostId
providesInitialSurface?: boolean
/** Worktree-only: folder workspaces are never filter-hidden, so these are dropped there. */
revealInSidebar?: boolean
/** Worktree-only: folder workspaces are never filter-hidden. */
clearSidebarFilters?: boolean
}
): ActivateAndRevealResult | false {
@@ -355,8 +357,7 @@ export function activateAndRevealWorkspace(
if (workspaceScope?.type !== 'folder') {
return activateAndRevealWorktree(workspaceId, opts)
}
const { revealInSidebar: _reveal, clearSidebarFilters: _clear, ...folderOpts } = opts ?? {}
return activateAndRevealFolderWorkspace(workspaceScope.folderWorkspaceId, folderOpts)
return activateAndRevealFolderWorkspace(workspaceScope.folderWorkspaceId, opts)
}
// Why: break the import cycle — nav-history slice (under @/store) can't import activation directly, so register the activator here.