diff --git a/src/screens/REPL.tsx b/src/screens/REPL.tsx index b091990b..1ee59943 100644 --- a/src/screens/REPL.tsx +++ b/src/screens/REPL.tsx @@ -237,6 +237,7 @@ import { useOfficialMarketplaceNotification } from 'src/hooks/useOfficialMarketp import { usePromptsFromClaudeInChrome } from 'src/hooks/usePromptsFromClaudeInChrome.js'; import { getTipToShowOnSpinner, recordShownTip } from 'src/services/tips/tipScheduler.js'; import type { Theme } from 'src/utils/theme.js'; +import { isPromptTypingSuppressionActive } from './replInputSuppression.js'; import { checkAndDisableBypassPermissionsIfNeeded, checkAndDisableAutoModeIfNeeded, useKickOffCheckAndDisableBypassPermissionsIfNeeded, useKickOffCheckAndDisableAutoModeIfNeeded } from 'src/utils/permissions/bypassPermissionsKillswitch.js'; import { SandboxManager } from 'src/utils/sandbox/sandbox-adapter.js'; import { SANDBOX_NETWORK_ACCESS_TOOL_NAME } from 'src/cli/structuredIO.js'; @@ -1336,6 +1337,7 @@ export function REPL({ const [inputValue, setInputValueRaw] = useState(() => consumeEarlyInput()); const inputValueRef = useRef(inputValue); inputValueRef.current = inputValue; + const promptTypingSuppressionActive = isPromptTypingSuppressionActive(isPromptInputActive, inputValue); const insertTextRef = useRef<{ insert: (text: string) => void; setInputWithCursor: (value: string, cursor: number) => void; @@ -2028,7 +2030,7 @@ export function REPL({ if (isMessageSelectorVisible) return 'message-selector'; // Suppress interrupt dialogs while user is actively typing - if (isPromptInputActive) return undefined; + if (promptTypingSuppressionActive) return undefined; if (sandboxPermissionRequestQueue[0]) return 'sandbox-permission'; // Permission/interactive dialogs (show unless blocked by toolJSX) @@ -2071,7 +2073,7 @@ export function REPL({ const focusedInputDialog = getFocusedInputDialog(); // True when permission prompts exist but are hidden because the user is typing - const hasSuppressedDialogs = isPromptInputActive && (sandboxPermissionRequestQueue[0] || toolUseConfirmQueue[0] || promptQueue[0] || workerSandboxPermissions.queue[0] || elicitation.queue[0] || showingCostDialog); + const hasSuppressedDialogs = promptTypingSuppressionActive && (sandboxPermissionRequestQueue[0] || toolUseConfirmQueue[0] || promptQueue[0] || workerSandboxPermissions.queue[0] || elicitation.queue[0] || showingCostDialog); // Keep ref in sync so timer callbacks can read the current value focusedInputDialogRef.current = focusedInputDialog; diff --git a/src/screens/replInputSuppression.test.ts b/src/screens/replInputSuppression.test.ts new file mode 100644 index 00000000..a7ac2a55 --- /dev/null +++ b/src/screens/replInputSuppression.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'bun:test' + +import { isPromptTypingSuppressionActive } from './replInputSuppression.js' + +describe('isPromptTypingSuppressionActive', () => { + it('suppresses dialogs when early input already exists', () => { + expect(isPromptTypingSuppressionActive(false, 'hello')).toBe(true) + }) + + it('does not suppress dialogs for empty or whitespace-only input', () => { + expect(isPromptTypingSuppressionActive(false, '')).toBe(false) + expect(isPromptTypingSuppressionActive(false, ' ')).toBe(false) + }) + + it('keeps suppression active while the typing flag is set', () => { + expect(isPromptTypingSuppressionActive(true, '')).toBe(true) + }) +}) diff --git a/src/screens/replInputSuppression.ts b/src/screens/replInputSuppression.ts new file mode 100644 index 00000000..be904ad9 --- /dev/null +++ b/src/screens/replInputSuppression.ts @@ -0,0 +1,6 @@ +export function isPromptTypingSuppressionActive( + isPromptInputActive: boolean, + inputValue: string, +): boolean { + return isPromptInputActive || inputValue.trim().length > 0 +}