fix(cli): /clear dismisses active /btw side-question dialog (#3431)

The /clear command cleared the history log but left an active /btw
side-question dialog visible in the fixed bottom area, because /btw
stores state in dedicated btwItem state (via setBtwItem) rather than
in history items. The ui.clear callback only called clearItems() and
clearScreen(), never cancelBtw(), so the pending-btw dialog survived.

Call cancelBtw() from ui.clear so /clear (and /reset, /new) abort any
in-flight btw request and null out the btwItem state.

Fixes #3334

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Sharvil Saxena 2026-04-19 02:59:20 -04:00 committed by GitHub
parent f7ebc372f1
commit 6ebe28453d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 51 additions and 0 deletions

View file

@ -14,6 +14,7 @@ import type {
} from '../commands/types.js'; } from '../commands/types.js';
import { CommandKind } from '../commands/types.js'; import { CommandKind } from '../commands/types.js';
import type { LoadedSettings } from '../../config/settings.js'; import type { LoadedSettings } from '../../config/settings.js';
import type { HistoryItemBtw } from '../types.js';
import { MessageType } from '../types.js'; import { MessageType } from '../types.js';
import { BuiltinCommandLoader } from '../../services/BuiltinCommandLoader.js'; import { BuiltinCommandLoader } from '../../services/BuiltinCommandLoader.js';
import { FileCommandLoader } from '../../services/FileCommandLoader.js'; import { FileCommandLoader } from '../../services/FileCommandLoader.js';
@ -1124,4 +1125,53 @@ describe('useSlashCommandProcessor', () => {
expect(logSlashCommand).not.toHaveBeenCalled(); expect(logSlashCommand).not.toHaveBeenCalled();
}); });
}); });
describe('ui.clear and /btw dialog', () => {
it('should dismiss an active btw dialog when ui.clear is called', async () => {
const result = setupProcessorHook();
await waitFor(() => expect(result.current.commandContext).toBeDefined());
const btwItem: HistoryItemBtw = {
type: MessageType.BTW,
btw: { question: 'why?', answer: '', isPending: true },
};
act(() => {
result.current.commandContext.ui.setBtwItem(btwItem);
});
await waitFor(() => {
expect(result.current.commandContext.ui.btwItem).toEqual(btwItem);
});
act(() => {
result.current.commandContext.ui.clear();
});
await waitFor(() => {
expect(result.current.commandContext.ui.btwItem).toBeNull();
});
});
it('should abort the in-flight btw request when ui.clear is called', async () => {
const result = setupProcessorHook();
await waitFor(() => expect(result.current.commandContext).toBeDefined());
const abortController = new AbortController();
const abortSpy = vi.spyOn(abortController, 'abort');
act(() => {
result.current.commandContext.ui.btwAbortControllerRef.current =
abortController;
});
act(() => {
result.current.commandContext.ui.clear();
});
expect(abortSpy).toHaveBeenCalledTimes(1);
expect(
result.current.commandContext.ui.btwAbortControllerRef.current,
).toBeNull();
});
});
}); });

View file

@ -259,6 +259,7 @@ export const useSlashCommandProcessor = (
ui: { ui: {
addItem, addItem,
clear: () => { clear: () => {
cancelBtw();
clearItems(); clearItems();
clearScreen(); clearScreen();
refreshStatic(); refreshStatic();