fix: align bypass permission path with active tab

The bypass-permission confirmation was resolving its displayed workspace from the session list's activeSessionId, while the chat composer sends runtime updates to the currently active tab. When those two UI states diverged after tab restores or project switches, the confirmation could show a parent or stale workspace path even though the chat was operating in another project.

Constraint: Desktop tabs and session-list selection can diverge during restore and multi-tab workflows.
Rejected: Thread workDir through every chat-composer call site | the selector already has the active tab context needed for uncontrolled usage.
Confidence: high
Scope-risk: narrow
Directive: Keep uncontrolled permission-mode actions keyed to the same active tab used for set_permission_mode.
Tested: cd desktop && bun run test -- src/components/controls/PermissionModeSelector.test.tsx --run
Tested: cd desktop && bun run lint
Not-tested: Live Windows desktop packaging smoke.
This commit is contained in:
程序员阿江(Relakkes) 2026-05-24 15:48:39 +08:00
parent 2a937c6cc7
commit 89f66a1c25
2 changed files with 46 additions and 3 deletions

View File

@ -47,7 +47,7 @@ import { useSettingsStore } from '../../stores/settingsStore'
import { useSessionStore } from '../../stores/sessionStore'
import { useTabStore } from '../../stores/tabStore'
describe('PermissionModeSelector mobile access', () => {
describe('PermissionModeSelector', () => {
beforeEach(() => {
viewportMocks.isMobile = false
useSettingsStore.setState({ permissionMode: 'default' })
@ -73,4 +73,46 @@ describe('PermissionModeSelector mobile access', () => {
expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument()
expect(screen.getByRole('menuitem', { name: /Auto accept edits/ })).toBeInTheDocument()
})
it('uses the active tab workspace when showing the bypass confirmation path', () => {
useSessionStore.setState({
activeSessionId: 'previous-session',
sessions: [
{
id: 'previous-session',
title: 'Previous',
createdAt: '2026-05-24T00:00:00.000Z',
modifiedAt: '2026-05-24T00:00:00.000Z',
messageCount: 1,
projectPath: 'C:\\Users\\LinTan',
projectRoot: 'C:\\Users\\LinTan',
workDir: 'C:\\Users\\LinTan',
workDirExists: true,
},
{
id: 'current-tab',
title: 'Current',
createdAt: '2026-05-24T00:00:00.000Z',
modifiedAt: '2026-05-24T00:00:00.000Z',
messageCount: 1,
projectPath: 'C:\\Users\\LinTan\\MyScript\\test5',
projectRoot: 'C:\\Users\\LinTan\\MyScript\\test5',
workDir: 'C:\\Users\\LinTan\\MyScript\\test5',
workDirExists: true,
},
],
})
useTabStore.setState({
activeTabId: 'current-tab',
tabs: [{ sessionId: 'current-tab', title: 'Current', type: 'session', status: 'idle' }],
})
render(<PermissionModeSelector compact />)
fireEvent.click(screen.getByRole('button', { name: 'Ask permissions' }))
fireEvent.click(screen.getByRole('menuitem', { name: /Bypass permissions/ }))
expect(screen.getByText('C:\\Users\\LinTan\\MyScript\\test5')).toBeInTheDocument()
expect(screen.queryByText('C:\\Users\\LinTan')).not.toBeInTheDocument()
})
})

View File

@ -35,7 +35,6 @@ export function PermissionModeSelector({ workDir: workDirProp, compact = false,
const setSessionPermissionMode = useChatStore((s) => s.setSessionPermissionMode)
const activeTabId = useTabStore((s) => s.activeTabId)
const sessions = useSessionStore((s) => s.sessions)
const activeSessionId = useSessionStore((s) => s.activeSessionId)
const [open, setOpen] = useState(false)
const [confirmDialog, setConfirmDialog] = useState(false)
const ref = useRef<HTMLDivElement>(null)
@ -87,7 +86,9 @@ export function PermissionModeSelector({ workDir: workDirProp, compact = false,
dontAsk: t('permMode.label.dontAsk'),
}
const activeSession = sessions.find((s) => s.id === activeSessionId)
const activeSession = activeTabId
? sessions.find((s) => s.id === activeTabId)
: null
const workDir = workDirProp || activeSession?.workDir || '~'
const compactButtonClass = compact
? isMobile