Unverified 提交 3b68fb4e authored 作者: Adeniji Adekunle James's avatar Adeniji Adekunle James 提交者: GitHub

feat: add hotkey to toggle chat modes (#1274) (#995) (#1284)

Adds Ctrl+Shift+A (Cmd+Shift+A on Mac) keyboard shortcut to toggle between Ask and Build chat modes. **Changes:** - Created `useChatModeToggle` hook for shared logic - Added shortcut to ChatInput and HomeChatInput components **Usage:** Press Ctrl+Shift+A (or Cmd+Shift+A on Mac) to quickly switch between chat modes without using the UI. <img width="434" height="123" alt="image" src="https://github.com/user-attachments/assets/a8c167b6-2b54-46f5-8191-5019991fc8e5" /> Closes (#995) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Add a global hotkey to toggle between Ask and Build modes: Ctrl+Shift+A (Cmd+Shift+A on Mac). Makes switching faster without touching the UI and fulfills Linear #995. - **New Features** - Added useChatModeToggle hook to flip selectedChatMode, detect platform, and track PostHog event chat:mode_toggle. - Wired the hotkey into ChatInput and HomeChatInput via useChatModeToggle. - Updated ChatModeSelector tooltip to show the shortcut. <!-- End of auto-generated description by cubic. --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds Cmd/Ctrl + . shortcut to cycle chat modes via a new hook, and updates the mode selector tooltip to show the shortcut. > > - **UX/Hotkey**: > - Introduces `useChatModeToggle` to cycle `settings.selectedChatMode` (captures `chat:mode_toggle`), with platform detection (`detectIsMac`/`useIsMac`) and `useShortcut` binding for `Cmd/Ctrl + .`. > - **Integrations**: > - Wires `useChatModeToggle()` into `components/chat/ChatInput.tsx` and `components/chat/HomeChatInput.tsx` to enable the shortcut in chat inputs. > - **UI**: > - Enhances `components/ChatModeSelector.tsx` tooltip to display the platform-specific shortcut hint (`⌘ + .` or `Ctrl + .`). > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit d8cc3fff43b6eb3227623f1c084410f42392c0b3. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: 's avatarWill Chen <willchen90@gmail.com>
上级 8f318214
......@@ -13,6 +13,7 @@ import {
import { useSettings } from "@/hooks/useSettings";
import type { ChatMode } from "@/lib/schemas";
import { cn } from "@/lib/utils";
import { detectIsMac } from "@/hooks/useChatModeToggle";
export function ChatModeSelector() {
const { settings, updateSettings } = useSettings();
......@@ -35,6 +36,7 @@ export function ChatModeSelector() {
return "Build";
}
};
const isMac = detectIsMac();
return (
<Select value={selectedMode} onValueChange={handleModeChange}>
......@@ -53,7 +55,14 @@ export function ChatModeSelector() {
<SelectValue>{getModeDisplayName(selectedMode)}</SelectValue>
</MiniSelectTrigger>
</TooltipTrigger>
<TooltipContent>Open mode menu</TooltipContent>
<TooltipContent>
<div className="flex flex-col">
<span>Open mode menu</span>
<span className="text-xs text-gray-200 dark:text-gray-500">
{isMac ? "⌘ + ." : "Ctrl + ."} to toggle
</span>
</div>
</TooltipContent>
</Tooltip>
<SelectContent align="start" onCloseAutoFocus={(e) => e.preventDefault()}>
<SelectItem value="build">
......
......@@ -65,6 +65,7 @@ import { selectedComponentPreviewAtom } from "@/atoms/previewAtoms";
import { SelectedComponentDisplay } from "./SelectedComponentDisplay";
import { useCheckProblems } from "@/hooks/useCheckProblems";
import { LexicalChatInput } from "./LexicalChatInput";
import { useChatModeToggle } from "@/hooks/useChatModeToggle";
const showTokenBarAtom = atom(false);
......@@ -107,6 +108,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
refreshProposal,
} = useProposal(chatId);
const { proposal, messageId } = proposalResult ?? {};
useChatModeToggle();
const lastMessage = messages.at(-1);
const disableSendButton =
......
......@@ -12,6 +12,7 @@ import { usePostHog } from "posthog-js/react";
import { HomeSubmitOptions } from "@/pages/home";
import { ChatInputControls } from "../ChatInputControls";
import { LexicalChatInput } from "./LexicalChatInput";
import { useChatModeToggle } from "@/hooks/useChatModeToggle";
export function HomeChatInput({
onSubmit,
}: {
......@@ -23,6 +24,7 @@ export function HomeChatInput({
const { isStreaming } = useStreamChat({
hasChatId: false,
}); // eslint-disable-line @typescript-eslint/no-unused-vars
useChatModeToggle();
// Use the attachments hook
const {
......
import { useCallback, useMemo } from "react";
import { useSettings } from "./useSettings";
import { useShortcut } from "./useShortcut";
import { usePostHog } from "posthog-js/react";
import { ChatModeSchema } from "../lib/schemas";
export function useChatModeToggle() {
const { settings, updateSettings } = useSettings();
const posthog = usePostHog();
// Detect if user is on mac
const isMac = useIsMac();
// Memoize the modifiers object to prevent re-registration
const modifiers = useMemo(
() => ({
ctrl: !isMac,
meta: isMac,
}),
[isMac],
);
// Function to toggle between ask and build chat modes
const toggleChatMode = useCallback(() => {
if (!settings || !settings.selectedChatMode) return;
const currentMode = settings.selectedChatMode;
const modes = ChatModeSchema.options;
const currentIndex = modes.indexOf(settings.selectedChatMode);
const newMode = modes[(currentIndex + 1) % modes.length];
updateSettings({ selectedChatMode: newMode });
posthog.capture("chat:mode_toggle", {
from: currentMode,
to: newMode,
trigger: "keyboard_shortcut",
});
}, [settings, updateSettings, posthog]);
// Add keyboard shortcut with memoized modifiers
useShortcut(
".",
modifiers,
toggleChatMode,
true, // Always enabled since we're not dependent on component selector
);
return { toggleChatMode, isMac };
}
// Add this function at the top
type NavigatorWithUserAgentData = Navigator & {
userAgentData?: {
platform?: string;
};
};
export function detectIsMac(): boolean {
const nav = navigator as NavigatorWithUserAgentData;
// Try modern API first
if ("userAgentData" in nav && nav.userAgentData?.platform) {
return nav.userAgentData.platform.toLowerCase().includes("mac");
}
// Fallback to user agent check
return /Mac|iPhone|iPad|iPod/.test(navigator.userAgent);
}
// Export the utility function and hook for use elsewhere
export function useIsMac(): boolean {
return useMemo(() => detectIsMac(), []);
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论