feat: add ⌘L shortcut to focus message input with hint

Show a muted "⌘L to focus" hint in the top-right of the message input
when unfocused and empty on web. Registers Cmd+L (Mac) / Ctrl+L
(non-Mac) bindings for the existing message-input.focus action.
This commit is contained in:
Mohamed Boudra
2026-04-15 09:57:05 +07:00
parent 8f04e65cf1
commit d40e4b6f0d
2 changed files with 45 additions and 0 deletions
@@ -42,6 +42,8 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
import { Shortcut } from "@/components/ui/shortcut";
import { useWebElementScrollbar } from "@/components/use-web-scrollbar";
import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
import { formatShortcut } from "@/utils/format-shortcut";
import { getShortcutOs } from "@/utils/shortcut-platform";
import type { MessageInputKeyboardActionKind } from "@/keyboard/actions";
import {
markScrollInvestigationEvent,
@@ -233,7 +235,9 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const voiceMuteToggleKeys = useShortcutKeys("voice-mute-toggle");
const dictationToggleKeys = useShortcutKeys("dictation-toggle");
const queueKeys = useShortcutKeys("message-input-queue");
const focusInputKeys = useShortcutKeys("focus-message-input");
const [inputHeight, setInputHeight] = useState(MIN_INPUT_HEIGHT);
const [isInputFocused, setIsInputFocused] = useState(false);
const rootRef = useRef<View | null>(null);
const inputWrapperRef = useRef<View | null>(null);
const textInputRef = useRef<TextInput | (TextInput & { getNativeRef?: () => unknown }) | null>(
@@ -997,10 +1001,12 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
accessibilityLabel="Message agent..."
onFocus={() => {
isInputFocusedRef.current = true;
setIsInputFocused(true);
onFocusChange?.(true);
}}
onBlur={() => {
isInputFocusedRef.current = false;
setIsInputFocused(false);
onFocusChange?.(false);
}}
style={[
@@ -1025,6 +1031,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
autoFocus={isWeb && autoFocus}
/>
{inputScrollbar}
{isWeb && !isInputFocused && !value && focusInputKeys ? (
<Text style={styles.focusHintText} pointerEvents="none">
{formatShortcut(focusInputKeys[0], getShortcutOs())} to focus
</Text>
) : null}
</View>
{/* Button row */}
@@ -1270,6 +1281,14 @@ const styles = StyleSheet.create(((theme: any) => ({
textInputScrollWrapper: {
position: "relative",
},
focusHintText: {
position: "absolute",
top: 0,
right: 0,
fontSize: theme.fontSize.xs,
color: theme.colors.foregroundMuted,
opacity: 0.5,
},
textInput: {
width: "100%",
color: theme.colors.foreground,
@@ -791,6 +791,32 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
},
// --- Message input ---
{
id: "message-input-focus-cmd-l-mac",
action: "message-input.action",
combo: "Cmd+L",
when: { mac: true, commandCenter: false },
payload: { type: "message-input", kind: "focus" },
help: {
id: "focus-message-input",
section: "agent-input",
label: "Focus message input",
keys: ["mod", "L"],
},
},
{
id: "message-input-focus-ctrl-l-non-mac",
action: "message-input.action",
combo: "Ctrl+L",
when: { mac: false, commandCenter: false, terminal: false },
payload: { type: "message-input", kind: "focus" },
help: {
id: "focus-message-input",
section: "agent-input",
label: "Focus message input",
keys: ["mod", "L"],
},
},
{
id: "message-input-voice-toggle-cmd-shift-d-mac",
action: "message-input.action",