From 63596512479c9e371d54478f8815ed24ba6df628 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Sat, 4 Apr 2026 13:24:26 +0800 Subject: [PATCH] feat(chat): add slash command dropdown button in message input toolbar Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/chat/message-input.tsx | 65 +++++++++++++++++-- .../chat/session-config-selector.tsx | 7 +- src/i18n/messages/ar.json | 3 +- src/i18n/messages/de.json | 3 +- src/i18n/messages/en.json | 3 +- src/i18n/messages/es.json | 3 +- src/i18n/messages/fr.json | 3 +- src/i18n/messages/ja.json | 3 +- src/i18n/messages/ko.json | 3 +- src/i18n/messages/pt.json | 3 +- src/i18n/messages/zh-CN.json | 3 +- src/i18n/messages/zh-TW.json | 3 +- 12 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/components/chat/message-input.tsx b/src/components/chat/message-input.tsx index 35b8f55..72e7314 100644 --- a/src/components/chat/message-input.tsx +++ b/src/components/chat/message-input.tsx @@ -20,6 +20,7 @@ import { ListPlus, Plus, Send, + Command, Square, X, } from "lucide-react" @@ -54,6 +55,7 @@ import { ModeSelector } from "@/components/chat/mode-selector" import { SessionConfigSelector } from "@/components/chat/session-config-selector" import { SlashCommandMenu } from "@/components/chat/slash-command-menu" import { FileMentionMenu } from "@/components/chat/file-mention-menu" +import { DropdownRadioItemContent } from "@/components/chat/dropdown-radio-item-content" import { useFileTree } from "@/hooks/use-file-tree" import { joinFsPath } from "@/lib/path-utils" import { @@ -314,6 +316,7 @@ export function MessageInput({ const textareaRef = useRef(null) const lastDomDropAtRef = useRef(0) const composingRef = useRef(false) + const cursorPosRef = useRef(null) const textRef = useRef(text) const disabledRef = useRef(disabled) const isPromptingRef = useRef(isPrompting) @@ -778,6 +781,24 @@ export function MessageInput({ setSlashMenuOpen(false) }, []) + const handleSlashPopoverSelect = useCallback((cmd: AvailableCommandInfo) => { + const pos = cursorPosRef.current ?? textRef.current.length + const before = textRef.current.slice(0, pos) + const after = textRef.current.slice(pos) + const needsSpace = pos > 0 && !/\s$/.test(before) + const insertion = `${needsSpace ? " " : ""}/${cmd.name} ` + const newText = before + insertion + after + setText(newText) + requestAnimationFrame(() => { + const ta = textareaRef.current + if (ta) { + ta.focus() + const newPos = pos + insertion.length + ta.setSelectionRange(newPos, newPos) + } + }) + }, []) + const atTriggerPosRef = useRef(atTriggerPos) useEffect(() => { atTriggerPosRef.current = atTriggerPos @@ -1476,26 +1497,60 @@ export function MessageInput({ autoFocus={autoFocus} />
-
+
+ + + + + + {slashCommands.map((cmd) => ( + handleSlashPopoverSelect(cmd)} + > + + + ))} + + {/* 宽屏内联显示,窄屏(<300px)通过"更多"气泡显示 */}
{selectorItems}
{hasAnySelector && ( diff --git a/src/components/chat/session-config-selector.tsx b/src/components/chat/session-config-selector.tsx index 781088f..c8a9402 100644 --- a/src/components/chat/session-config-selector.tsx +++ b/src/components/chat/session-config-selector.tsx @@ -37,9 +37,12 @@ export function SessionConfigSelector({