feat(webui): add new chat keyboard shortcut

Add Cmd/Ctrl+Shift+O shortcut to start a new chat, matching the
convention used by ChatGPT, Claude.ai, and Gemini.

Addresses #4178

Signed-off-by: axelray-dev <110029405+axelray-dev@users.noreply.github.com>
This commit is contained in:
axelray-dev 2026-06-04 06:47:50 +08:00 committed by Xubin Ren
parent d0eba7cd9d
commit 4275678b43
2 changed files with 28 additions and 1 deletions

View File

@ -1007,6 +1007,13 @@ function Shell({
useEffect(() => {
const handleKeyDown = (event: globalThis.KeyboardEvent) => {
if (event.defaultPrevented) return;
const commandShiftO =
(event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
if (commandShiftO && event.key.toLowerCase() === "o") {
event.preventDefault();
onNewChat();
return;
}
const plainCommandK =
(event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey;
if (!plainCommandK) return;
@ -1017,7 +1024,7 @@ function Shell({
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [onOpenSessionSearch]);
}, [onNewChat, onOpenSessionSearch]);
const onSelectSearchResult = useCallback(
(key: string) => {

View File

@ -1354,6 +1354,26 @@ describe("App layout", () => {
expect(createChatSpy).not.toHaveBeenCalled();
});
it("starts a new chat from the keyboard shortcut", async () => {
mockSessions = [
{
key: "websocket:chat-a",
channel: "websocket",
chatId: "chat-a",
createdAt: "2026-04-16T10:00:00Z",
updatedAt: "2026-04-16T10:00:00Z",
preview: "Existing chat",
},
];
render(<App />);
await waitFor(() => expect(connectSpy).toHaveBeenCalled());
fireEvent.keyDown(window, { key: "O", shiftKey: true, metaKey: true });
expect(window.location.hash).toBe("#/new");
});
it("keeps large sidebars light while search still covers every chat", async () => {
mockSessions = Array.from({ length: 170 }, (_, index) => {
const chatId = `chat-${index}`;