mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(agent-dashboard): keep the drawer's dismissal Escape out of the focused terminal (#34037)
Closing the Agent Dashboard drawer or workspace board with Escape still forwarded the key to a focused xterm, because xterm ignores defaultPrevented. Stop propagation only when the target is the xterm helper textarea so other fields keep their own Escape handling.
This commit is contained in:
@@ -72,6 +72,7 @@ beforeEach(() => {
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
document.body.replaceChildren()
|
||||
useAppStore.setState(initialState, true)
|
||||
})
|
||||
|
||||
@@ -101,6 +102,28 @@ describe('AgentDashboardDrawer', () => {
|
||||
expect(useAppStore.getState().agentDashboardDrawerOpen).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps the dismissal Escape from a focused terminal but not from other fields', () => {
|
||||
const terminalInput = document.createElement('textarea')
|
||||
terminalInput.className = 'xterm-helper-textarea'
|
||||
const field = document.createElement('input')
|
||||
document.body.append(terminalInput, field)
|
||||
const terminalKeyDown = vi.fn()
|
||||
const fieldKeyDown = vi.fn()
|
||||
terminalInput.addEventListener('keydown', terminalKeyDown)
|
||||
field.addEventListener('keydown', fieldKeyDown)
|
||||
render(<AgentDashboardDrawer statusBarVisible />)
|
||||
|
||||
act(() => useAppStore.setState({ agentDashboardDrawerOpen: true }))
|
||||
fireEvent.keyDown(terminalInput, { key: 'Escape' })
|
||||
expect(useAppStore.getState().agentDashboardDrawerOpen).toBe(false)
|
||||
expect(terminalKeyDown).not.toHaveBeenCalled()
|
||||
|
||||
act(() => useAppStore.setState({ agentDashboardDrawerOpen: true }))
|
||||
fireEvent.keyDown(field, { key: 'Escape' })
|
||||
expect(useAppStore.getState().agentDashboardDrawerOpen).toBe(false)
|
||||
expect(fieldKeyDown).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
type RevealAgent = (args: {
|
||||
repoId: string
|
||||
worktreeId: string
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { AgentSubjectReadIntent } from '@/attention/agent-subject-read-acti
|
||||
import { Sheet, SheetContent, SheetTitle } from '@/components/ui/sheet'
|
||||
import { revealDashboardAgent } from './reveal-dashboard-agent'
|
||||
import { AgentKanbanBoard } from '../dashboard-popout/AgentKanbanBoard'
|
||||
import { isXtermHelperTextarea } from '../terminal-pane/regular-terminal-focus-ownership'
|
||||
import type { AgentRevealArgs } from '../dashboard-popout/AgentTerminalDialog'
|
||||
import {
|
||||
isWorkspaceBoardKeepOpenTarget,
|
||||
@@ -132,6 +133,10 @@ export function AgentDashboardDrawer({
|
||||
return
|
||||
}
|
||||
event.preventDefault()
|
||||
// xterm ignores defaultPrevented; stop only its Escape so other targets keep theirs (rename/drag cancel).
|
||||
if (isXtermHelperTextarea(event.target)) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
close()
|
||||
}
|
||||
// The non-modal drawer must hear Escape even when focus is outside it.
|
||||
|
||||
@@ -223,11 +223,29 @@ describe('useWorkspaceBoardPanel', () => {
|
||||
it('still closes the board on Escape from a text field outside it', async () => {
|
||||
await renderHookProbe()
|
||||
const field = appendInput('app')
|
||||
const fieldKeyDown = vi.fn()
|
||||
field.addEventListener('keydown', fieldKeyDown)
|
||||
|
||||
await updatePanel((state) => state.openWorkspaceBoard())
|
||||
await pressEscape(field)
|
||||
|
||||
expect(panelState().workspaceBoardOpen).toBe(false)
|
||||
expect(fieldKeyDown).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('keeps the dismissal Escape from reaching a focused terminal', async () => {
|
||||
await renderHookProbe()
|
||||
const terminalInput = document.createElement('textarea')
|
||||
terminalInput.className = 'xterm-helper-textarea'
|
||||
document.body.appendChild(terminalInput)
|
||||
const terminalKeyDown = vi.fn()
|
||||
terminalInput.addEventListener('keydown', terminalKeyDown)
|
||||
|
||||
await updatePanel((state) => state.openWorkspaceBoard())
|
||||
await pressEscape(terminalInput)
|
||||
|
||||
expect(panelState().workspaceBoardOpen).toBe(false)
|
||||
expect(terminalKeyDown).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps the board open on Escape while a nested dialog is open', async () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { isEditableTarget } from '@/lib/editable-target'
|
||||
import { useAppStore } from '@/store'
|
||||
import { isXtermHelperTextarea } from '../terminal-pane/regular-terminal-focus-ownership'
|
||||
|
||||
const WORKSPACE_BOARD_SHEET_SELECTOR = '[data-workspace-board-sheet]'
|
||||
|
||||
@@ -143,6 +144,10 @@ export function useWorkspaceBoardPanel(): WorkspaceBoardPanelState {
|
||||
return
|
||||
}
|
||||
event.preventDefault()
|
||||
// xterm ignores defaultPrevented; stop only its Escape so other targets keep theirs (rename/drag cancel).
|
||||
if (isXtermHelperTextarea(event.target)) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
closeWorkspaceBoard()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user