mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-13 22:34:06 +00:00
fix: close search when starting new chats
maintainer edit: Close the session search dialog when the global new-chat shortcut navigates to the blank chat route, and expose the new shortcut through the sidebar button title so the shortcut is discoverable.
This commit is contained in:
parent
4275678b43
commit
54d8d3010b
@ -813,6 +813,7 @@ function Shell({
|
|||||||
navigate(defaultShellRoute());
|
navigate(defaultShellRoute());
|
||||||
setDraftWorkspaceScope(null);
|
setDraftWorkspaceScope(null);
|
||||||
setWorkspaceError(null);
|
setWorkspaceError(null);
|
||||||
|
setSessionSearchOpen(false);
|
||||||
setMobileSidebarOpen(false);
|
setMobileSidebarOpen(false);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
|
|||||||
@ -124,6 +124,7 @@ export function Sidebar(props: SidebarProps) {
|
|||||||
label={t("sidebar.newChat")}
|
label={t("sidebar.newChat")}
|
||||||
onClick={props.onNewChat}
|
onClick={props.onNewChat}
|
||||||
icon={<SquarePen className="h-4 w-4" />}
|
icon={<SquarePen className="h-4 w-4" />}
|
||||||
|
shortcut="Cmd/Ctrl+Shift+O"
|
||||||
/>
|
/>
|
||||||
<SidebarActionButton
|
<SidebarActionButton
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
@ -213,6 +214,7 @@ function SidebarActionButton({
|
|||||||
onClick,
|
onClick,
|
||||||
active = false,
|
active = false,
|
||||||
className,
|
className,
|
||||||
|
shortcut,
|
||||||
}: {
|
}: {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
@ -220,14 +222,17 @@ function SidebarActionButton({
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
shortcut?: string;
|
||||||
}) {
|
}) {
|
||||||
|
const title = shortcut ? `${label} (${shortcut})` : collapsed ? label : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
aria-current={active ? "page" : undefined}
|
aria-current={active ? "page" : undefined}
|
||||||
title={collapsed ? label : undefined}
|
title={title}
|
||||||
onClick={() => onClick()}
|
onClick={() => onClick()}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group h-8 min-w-0 gap-2 overflow-hidden rounded-full font-medium text-sidebar-foreground/85 hover:bg-sidebar-accent/75 hover:text-sidebar-foreground",
|
"group h-8 min-w-0 gap-2 overflow-hidden rounded-full font-medium text-sidebar-foreground/85 hover:bg-sidebar-accent/75 hover:text-sidebar-foreground",
|
||||||
|
|||||||
@ -1354,7 +1354,10 @@ describe("App layout", () => {
|
|||||||
expect(createChatSpy).not.toHaveBeenCalled();
|
expect(createChatSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("starts a new chat from the keyboard shortcut", async () => {
|
it.each([
|
||||||
|
["Command", { metaKey: true }],
|
||||||
|
["Control", { ctrlKey: true }],
|
||||||
|
])("starts a new chat from the %s keyboard shortcut", async (_label, modifier) => {
|
||||||
mockSessions = [
|
mockSessions = [
|
||||||
{
|
{
|
||||||
key: "websocket:chat-a",
|
key: "websocket:chat-a",
|
||||||
@ -1369,11 +1372,49 @@ describe("App layout", () => {
|
|||||||
render(<App />);
|
render(<App />);
|
||||||
|
|
||||||
await waitFor(() => expect(connectSpy).toHaveBeenCalled());
|
await waitFor(() => expect(connectSpy).toHaveBeenCalled());
|
||||||
fireEvent.keyDown(window, { key: "O", shiftKey: true, metaKey: true });
|
fireEvent.keyDown(window, { key: "O", shiftKey: true, ...modifier });
|
||||||
|
|
||||||
expect(window.location.hash).toBe("#/new");
|
expect(window.location.hash).toBe("#/new");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("closes search when starting 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: "k", metaKey: true });
|
||||||
|
expect(await screen.findByRole("dialog", { name: "Search" })).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.keyDown(window, { key: "O", shiftKey: true, metaKey: true });
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.queryByRole("dialog", { name: "Search" })).not.toBeInTheDocument(),
|
||||||
|
);
|
||||||
|
expect(window.location.hash).toBe("#/new");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exposes the new chat keyboard shortcut in the sidebar title", async () => {
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
await waitFor(() => expect(connectSpy).toHaveBeenCalled());
|
||||||
|
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
|
||||||
|
|
||||||
|
expect(within(sidebar).getByRole("button", { name: "New chat" })).toHaveAttribute(
|
||||||
|
"title",
|
||||||
|
"New chat (Cmd/Ctrl+Shift+O)",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps large sidebars light while search still covers every chat", async () => {
|
it("keeps large sidebars light while search still covers every chat", async () => {
|
||||||
mockSessions = Array.from({ length: 170 }, (_, index) => {
|
mockSessions = Array.from({ length: 170 }, (_, index) => {
|
||||||
const chatId = `chat-${index}`;
|
const chatId = `chat-${index}`;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user