diff --git a/src/web-ui/src/flow_chat/components/ChatInput.tsx b/src/web-ui/src/flow_chat/components/ChatInput.tsx index 300886164..2703b242f 100644 --- a/src/web-ui/src/flow_chat/components/ChatInput.tsx +++ b/src/web-ui/src/flow_chat/components/ChatInput.tsx @@ -68,6 +68,28 @@ import { deriveDeepReviewSessionConcurrencyGuard } from '../utils/deepReviewCapa import { agentAPI } from '@/infrastructure/api/service-api/AgentAPI'; import './ChatInput.scss'; +// Module-level popup state – used by ModernFlowChatContainer to conditionally +// disable the Escape shortcut so that slash-command and @-mention popups can be +// closed with Escape. +let _chatPopupActive = false; +const _chatPopupListeners = new Set<() => void>(); + +export function isChatPopupActive(): boolean { + return _chatPopupActive; +} + +export function subscribeChatPopupChange(listener: () => void): () => void { + _chatPopupListeners.add(listener); + return () => { _chatPopupListeners.delete(listener); }; +} + +function setChatPopupActive(active: boolean) { + if (_chatPopupActive !== active) { + _chatPopupActive = active; + _chatPopupListeners.forEach(fn => fn()); + } +} + const log = createLogger('ChatInput'); export interface ChatInputProps { @@ -819,6 +841,12 @@ export const ChatInput: React.FC = ({ selectedIndex: 0, }); + // Keep the module-level popup-active flag in sync so ModernFlowChatContainer + // can disable the global Escape shortcut while popups are open. + useEffect(() => { + setChatPopupActive(slashCommandState.isActive || mentionState.isActive); + }, [slashCommandState.isActive, mentionState.isActive]); + const clearPendingLargePastes = useCallback(() => { pendingLargePastesRef.current = {}; }, []); diff --git a/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx b/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx index 5dea6aec6..3b3b0d4b3 100644 --- a/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx +++ b/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx @@ -23,6 +23,7 @@ import { useFlowChatSearch } from './useFlowChatSearch'; import { useVirtualItems, useActiveSession, useVisibleTurnInfo, type VisibleTurnInfo } from '../../store/modernFlowChatStore'; import type { FlowChatConfig, FlowToolItem, Session, DialogTurn } from '../../types/flow-chat'; import type { LineRange } from '@/component-library'; +import { isChatPopupActive, subscribeChatPopupChange } from '../ChatInput'; import { useWorkspaceContext } from '@/infrastructure/contexts/WorkspaceContext'; import { parsePullRequestUrl } from '@/shared/utils/pullRequestLinks'; import { createReviewPlatformPullRequestDetailTab } from '@/shared/utils/tabUtils'; @@ -171,6 +172,16 @@ export const ModernFlowChatContainer: React.FC = ( const visibleTurnInfo = useVisibleTurnInfo(); const [pendingHeaderTurnId, setPendingHeaderTurnId] = useState(null); const [searchOpenRequest, setSearchOpenRequest] = useState(0); + // Track whether a slash-command or @-mention popup is open in ChatInput. + // When a popup is active, the global Escape shortcut is disabled so the + // popup can be closed with Escape instead of cancelling the current task. + const [chatPopupActive, setChatPopupActive] = useState(() => isChatPopupActive()); + + useEffect(() => { + return subscribeChatPopupChange(() => { + setChatPopupActive(isChatPopupActive()); + }); + }, []); const [backgroundSubagents, setBackgroundSubagents] = useState([]); const autoPinnedSessionIdRef = useRef(null); const virtualListRef = useRef(null); @@ -474,7 +485,7 @@ export const ModernFlowChatContainer: React.FC = ( () => { void FlowChatManager.getInstance().cancelCurrentTask(); }, - { priority: 20, description: 'keyboard.shortcuts.chat.stopGeneration' } + { priority: 20, enabled: !chatPopupActive, description: 'keyboard.shortcuts.chat.stopGeneration' } ); useShortcut( diff --git a/src/web-ui/src/tools/mermaid-editor/theme/mermaidTheme.ts b/src/web-ui/src/tools/mermaid-editor/theme/mermaidTheme.ts index 7bd12c5df..7f51360ca 100644 --- a/src/web-ui/src/tools/mermaid-editor/theme/mermaidTheme.ts +++ b/src/web-ui/src/tools/mermaid-editor/theme/mermaidTheme.ts @@ -26,6 +26,7 @@ export function getThemeType(): 'dark' | 'light' { if (dataTheme?.includes('light')) return 'light'; if (dataTheme?.includes('dark')) return 'dark'; if (document.documentElement.classList.contains('light')) return 'light'; + if (typeof window.matchMedia !== 'function') return 'dark'; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; }