From 8f53d3b74b8b203565a9a6262e3ad4319bc99015 Mon Sep 17 00:00:00 2001 From: Guilherme Vieira Date: Mon, 4 May 2026 23:58:20 +0100 Subject: [PATCH] fix(web): handle macOS Home and End in composer --- .../src/components/ComposerPromptEditor.tsx | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/ComposerPromptEditor.tsx b/apps/web/src/components/ComposerPromptEditor.tsx index c9696b0c737..4d788dee57d 100644 --- a/apps/web/src/components/ComposerPromptEditor.tsx +++ b/apps/web/src/components/ComposerPromptEditor.tsx @@ -8,6 +8,7 @@ import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin"; import { type ServerProviderSkill } from "@t3tools/contracts"; import { $applyNodeReplacement, + $createRangeSelectionFromDom, $createRangeSelection, $getSelection, $setSelection, @@ -22,6 +23,7 @@ import { KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, + KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, COMMAND_PRIORITY_HIGH, @@ -65,7 +67,7 @@ import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER, type TerminalContextDraft, } from "~/lib/terminalContext"; -import { cn } from "~/lib/utils"; +import { cn, isMacPlatform } from "~/lib/utils"; import { basenameOfPath, getVscodeIconUrlForEntry, inferEntryKindFromPath } from "~/vscode-icons"; import { COMPOSER_INLINE_CHIP_CLASS_NAME, @@ -1030,6 +1032,53 @@ function ComposerInlineTokenArrowPlugin() { return null; } +function ComposerHomeEndKeyPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + return editor.registerCommand( + KEY_DOWN_COMMAND, + (event) => { + if (!isMacPlatform(navigator.platform)) { + return false; + } + if (event.key !== "Home" && event.key !== "End") { + return false; + } + if (event.altKey || event.metaKey || event.ctrlKey || event.isComposing) { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + + const rootElement = editor.getRootElement(); + const selection = window.getSelection(); + const anchorNode = selection?.anchorNode; + if (!rootElement || !selection || !anchorNode || !rootElement.contains(anchorNode)) { + return true; + } + if (selection.rangeCount === 0 || typeof selection.modify !== "function") { + return true; + } + + selection.modify( + event.shiftKey ? "extend" : "move", + event.key === "Home" ? "backward" : "forward", + "lineboundary", + ); + editor.update(() => { + $setSelection($createRangeSelectionFromDom(selection, editor)); + }); + return true; + }, + COMMAND_PRIORITY_HIGH, + ); + }, [editor]); + + return null; +} + function ComposerInlineTokenSelectionNormalizePlugin() { const [editor] = useLexicalComposerContext(); @@ -1642,6 +1691,7 @@ function ComposerPromptEditorInner({ +