From 47d498bab07b795d05ee33a3551715e46eaf2105 Mon Sep 17 00:00:00 2001
From: chengyongru <2755839590@qq.com>
Date: Fri, 10 Jul 2026 23:00:59 +0800
Subject: [PATCH] fix(webui): disarm queued prompt for voice shortcut
---
.../src/components/thread/ThreadComposer.tsx | 1 +
webui/src/tests/thread-composer.test.tsx | 46 +++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/webui/src/components/thread/ThreadComposer.tsx b/webui/src/components/thread/ThreadComposer.tsx
index 7571e517..973cbdf2 100644
--- a/webui/src/components/thread/ThreadComposer.tsx
+++ b/webui/src/components/thread/ThreadComposer.tsx
@@ -1304,6 +1304,7 @@ export function ThreadComposer({
function onKeyDown(event: KeyboardEvent): void {
if (!isVoiceShortcutDown(event) || event.repeat || voiceShortcutDownRef.current) return;
event.preventDefault();
+ secondEnterPromptIdRef.current = null;
voiceShortcutDownRef.current = true;
voiceRecorder.beginShortcutHold();
}
diff --git a/webui/src/tests/thread-composer.test.tsx b/webui/src/tests/thread-composer.test.tsx
index 239ccdbb..5baf3063 100644
--- a/webui/src/tests/thread-composer.test.tsx
+++ b/webui/src/tests/thread-composer.test.tsx
@@ -1581,9 +1581,41 @@ describe("ThreadComposer", () => {
fireEvent.keyDown(input, { key: "Enter" });
expect(onSend).toHaveBeenCalledWith("send this guidance now");
+ expect(onSend).toHaveBeenCalledTimes(1);
expect(screen.queryByText("send this guidance now")).not.toBeInTheDocument();
});
+ it("disarms the second Enter shortcut when keyboard voice recording starts", async () => {
+ mockVoiceRecorder();
+ const onSend = vi.fn();
+ const onTranscribeAudio = vi.fn(async () => "voice guidance");
+ render(
+ ,
+ );
+
+ const input = screen.getByLabelText("Message input");
+ fireEvent.change(input, { target: { value: "keep this queued" } });
+ fireEvent.keyDown(input, { key: "Enter" });
+ fireEvent.keyDown(window, { code: "KeyD", ctrlKey: true, key: "D", shiftKey: true });
+
+ expect(await screen.findByLabelText("Recording 0:00")).toBeInTheDocument();
+ expect(input).toHaveFocus();
+ fireEvent.keyDown(input, { key: "Enter" });
+
+ expect(onSend).not.toHaveBeenCalled();
+ expect(screen.getByText("keep this queued")).toBeInTheDocument();
+
+ await waitForVoiceCapture();
+ fireEvent.keyUp(window, { code: "KeyD", ctrlKey: true, key: "D", shiftKey: true });
+ await waitFor(() => expect(onTranscribeAudio).toHaveBeenCalled());
+ });
+
it("disarms the second Enter shortcut after stopping the active response", () => {
const onSend = vi.fn();
const onStop = vi.fn();
@@ -1669,6 +1701,7 @@ describe("ThreadComposer", () => {
fireEvent.keyDown(input, { key: "Enter" });
expect(onSend).toHaveBeenCalledWith("guide this one now");
+ expect(onSend).toHaveBeenCalledTimes(1);
expect(screen.getByText("older guidance")).toBeInTheDocument();
expect(screen.queryByText("guide this one now")).not.toBeInTheDocument();
});
@@ -2021,6 +2054,19 @@ describe("ThreadComposer", () => {
expect(screen.queryByText("remember this edited follow-up")).not.toBeInTheDocument();
});
+ rerender(
+ ,
+ );
+ expect(await screen.findByText("remember this edited follow-up")).toBeInTheDocument();
+ fireEvent.keyDown(screen.getByLabelText("Message input"), { key: "Enter" });
+ expect(onSend).not.toHaveBeenCalled();
+
unmount();
const remount = render(