diff --git a/apps/web/components/keyboard-shortcut-tooltip.tsx b/apps/web/components/keyboard-shortcut-tooltip.tsx new file mode 100644 index 0000000000..5637b4835e --- /dev/null +++ b/apps/web/components/keyboard-shortcut-tooltip.tsx @@ -0,0 +1,96 @@ +/** + * Reusable tooltip component for displaying keyboard shortcuts + */ + +import { Tooltip, TooltipContent, TooltipTrigger } from '@kandev/ui/tooltip'; +import { Kbd, KbdGroup } from '@kandev/ui/kbd'; +import type { KeyboardShortcut } from '@/lib/keyboard/constants'; +import { formatShortcut, detectPlatform } from '@/lib/keyboard/utils'; + +type KeyboardShortcutTooltipProps = { + shortcut: KeyboardShortcut; + children: React.ReactNode; + /** Additional description text to show alongside the shortcut */ + description?: string; + /** Whether to show the tooltip (default: true) */ + enabled?: boolean; +}; + +/** + * Tooltip that displays a keyboard shortcut hint + * + * @example + * ```tsx + * + * + * + * ``` + */ +export function KeyboardShortcutTooltip({ + shortcut, + children, + description, + enabled = true, +}: KeyboardShortcutTooltipProps) { + if (!enabled) { + return <>{children}; + } + + const platform = detectPlatform(); + const formattedShortcut = formatShortcut(shortcut, platform); + const parts = formattedShortcut.split('+'); + + return ( + + {children} + +
+ {description && {description}} + + {parts.map((part, index) => ( + {part} + ))} + +
+
+
+ ); +} + +/** + * Simple text-only version that just shows the formatted shortcut + * Useful for inline display without tooltip + * + * @example + * ```tsx + * + * // Renders: Cmd+Enter (on Mac) or Ctrl+Enter (on Windows/Linux) + * ``` + */ +export function KeyboardShortcutText({ shortcut }: { shortcut: KeyboardShortcut }) { + const platform = detectPlatform(); + return {formatShortcut(shortcut, platform)}; +} + +/** + * Kbd group version for displaying shortcuts with proper styling + * + * @example + * ```tsx + * + * ``` + */ +export function KeyboardShortcutKbd({ shortcut }: { shortcut: KeyboardShortcut }) { + const platform = detectPlatform(); + const formattedShortcut = formatShortcut(shortcut, platform); + const parts = formattedShortcut.split('+'); + + return ( + + {parts.map((part, index) => ( + {part} + ))} + + ); +} + diff --git a/apps/web/components/task-create-dialog.tsx b/apps/web/components/task-create-dialog.tsx index 2c94bbe4c9..623b31d914 100644 --- a/apps/web/components/task-create-dialog.tsx +++ b/apps/web/components/task-create-dialog.tsx @@ -40,6 +40,9 @@ import { useAppStore } from '@/components/state-provider'; import { useRepositories } from '@/hooks/use-repositories'; import { useRepositoryBranches } from '@/hooks/use-repository-branches'; import { useSettingsData } from '@/hooks/use-settings-data'; +import { SHORTCUTS } from '@/lib/keyboard/constants'; +import { useKeyboardShortcutHandler } from '@/hooks/use-keyboard-shortcut'; +import { KeyboardShortcutTooltip } from '@/components/keyboard-shortcut-tooltip'; interface TaskCreateDialogProps { open: boolean; @@ -184,6 +187,11 @@ export function TaskCreateDialog({ textarea.style.height = `${textarea.scrollHeight}px`; }, [description]); + // Use keyboard shortcut hook for Cmd+Enter / Ctrl+Enter + const handleKeyDown = useKeyboardShortcutHandler(SHORTCUTS.SUBMIT, (event) => { + handleSubmit(event as unknown as FormEvent); + }); + const handleSubmit = async (e: FormEvent) => { e.preventDefault(); const trimmedTitle = title.trim(); @@ -283,6 +291,7 @@ export function TaskCreateDialog({ placeholder="Write a prompt for the agent..." value={description} onChange={(e) => setDescription(e.target.value)} + onKeyDown={handleKeyDown} rows={2} className="min-h-[96px] max-h-[240px] resize-y overflow-auto" /> @@ -498,9 +507,11 @@ export function TaskCreateDialog({ Cancel - + + + diff --git a/apps/web/components/task/task-chat-panel.tsx b/apps/web/components/task/task-chat-panel.tsx index 381b4c33cf..f9c3386dc2 100644 --- a/apps/web/components/task/task-chat-panel.tsx +++ b/apps/web/components/task/task-chat-panel.tsx @@ -33,6 +33,9 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@kandev/ui/tooltip'; import { cn } from '@/lib/utils'; import { useAppStore } from '@/components/state-provider'; import type { Comment } from '@/lib/types/http'; +import { SHORTCUTS } from '@/lib/keyboard/constants'; +import { useKeyboardShortcutHandler } from '@/hooks/use-keyboard-shortcut'; +import { KeyboardShortcutTooltip } from '@/components/keyboard-shortcut-tooltip'; type AgentOption = { id: string; @@ -266,6 +269,11 @@ export function TaskChatPanel({ agents, onSend, isLoading, isAgentWorking, taskD } }; + // Use keyboard shortcut hook for Cmd+Enter / Ctrl+Enter + const handleKeyDown = useKeyboardShortcutHandler(SHORTCUTS.SUBMIT, (event) => { + handleSubmit(event as unknown as FormEvent); + }); + const formatContent = (comment: Comment) => { // For content type comments, the content might be streamed chunks // For message type, show as-is @@ -344,6 +352,7 @@ export function TaskChatPanel({ agents, onSend, isLoading, isAgentWorking, taskD