@@ -23,6 +23,7 @@ import {
2323 COMMAND_ARGS_HINTS ,
2424 COMMAND_ARGS_OPTIONS ,
2525} from '../../../hooks/ui/useCommandPanel.js' ;
26+ import { isInlineInsertionCommand } from '../../../hooks/input/keyboard/utils/inlineCommandTrigger.js' ;
2627import { useFilePicker } from '../../../hooks/picker/useFilePicker.js' ;
2728import { useHistoryNavigation } from '../../../hooks/input/useHistoryNavigation.js' ;
2829import { useClipboard } from '../../../hooks/input/useClipboard.js' ;
@@ -851,29 +852,38 @@ export default function ChatInput({
851852 return match [ 2 ] && match [ 2 ] . length > 0 ? hint : ` ${ hint } ` ;
852853 } , [ buffer . text ] ) ;
853854
854- // 当输入为以 `/cmd` 开头且 cmd 命中已注册指令时,计算高亮长度(含开头的 `/`)。
855- // 用于在输入框中以主题色高亮“完整指令”片段,方便用户确认命令已被识别。
856- const completedCommandLength = useMemo ( ( ) => {
857- const text = buffer . text ;
858- if ( ! text . startsWith ( '/' ) ) return 0 ;
859- const match = text . match ( / ^ \/ ( [ a - z A - Z 0 - 9 _ - ] + ) (?: \s | $ ) / ) ;
860- if ( ! match ) return 0 ;
861- const cmd = match [ 1 ] ?? '' ;
862- if ( ! cmd ) return 0 ;
855+ // 计算可高亮的指令长度(含开头的 `/`)。
856+ // - 行首位置允许所有已注册指令;
857+ // - 内容中的内联位置只高亮真正支持内联插入的指令,避免把普通 `/help` 误标成可用内联指令。
858+ const getCommandHighlightLength = useMemo ( ( ) => {
863859 const allCommands = getAllCommands ( ) ;
864- // 精确匹配(如 /help、/clear、/branch ...)
865- const exact = allCommands . some ( c => c . name === cmd ) ;
866- if ( exact ) return 1 + cmd . length ;
867- // 前缀型指令(如 agent-、todo-、skills-):cmd 以 `name` 开头且长度更长
868- const prefixHit = allCommands . some (
869- c =>
870- c . name . endsWith ( '-' ) &&
871- cmd . length > c . name . length &&
872- cmd . startsWith ( c . name ) ,
873- ) ;
874- if ( prefixHit ) return 1 + cmd . length ;
875- return 0 ;
876- } , [ buffer . text , getAllCommands ] ) ;
860+
861+ return ( text : string , inlineOnly : boolean ) : number => {
862+ if ( ! text . startsWith ( '/' ) ) return 0 ;
863+
864+ const commandToken = text . slice ( 1 ) . match ( / ^ \S * / ) ?. [ 0 ] ?? '' ;
865+ if ( ! commandToken ) return 0 ;
866+
867+ const availableCommands = inlineOnly
868+ ? allCommands . filter ( isInlineInsertionCommand )
869+ : allCommands ;
870+ const exact = availableCommands . some ( c => c . name === commandToken ) ;
871+ if ( exact ) return 1 + commandToken . length ;
872+
873+ if ( inlineOnly ) return 0 ;
874+
875+ // 前缀型指令(如 agent-、todo-、skills-):cmd 以 `name` 开头且长度更长
876+ const prefixHit = availableCommands . some (
877+ c =>
878+ c . name . endsWith ( '-' ) &&
879+ commandToken . length > c . name . length &&
880+ commandToken . startsWith ( c . name ) ,
881+ ) ;
882+ if ( prefixHit ) return 1 + commandToken . length ;
883+
884+ return 0 ;
885+ } ;
886+ } , [ getAllCommands ] ) ;
877887
878888 const renderContent = ( ) => {
879889 if ( buffer . text . length > 0 ) {
@@ -916,9 +926,10 @@ export default function ChatInput({
916926 }
917927
918928 // 渲染单行内容的辅助函数:同时高亮
919- // 1. 首行已识别的完整指令 `/cmd`
920- // 2. 各类 `[...]` 占位符标签(Paste / image / Skill / GitLine / » running-agent)
921- // 3. `#agent_xxx` 裸文本子代理标签(词边界上)
929+ // 1. 已识别的行首完整指令 `/cmd`
930+ // 2. 内容中支持内联插入的指令(如 `/gitline`、Prompt 类型自定义指令)
931+ // 3. 各类 `[...]` 占位符标签(Paste / image / Skill / GitLine / » running-agent)
932+ // 4. `#agent_xxx` 裸文本子代理标签(词边界上)
922933 const renderLineSegments = ( line : string , isFirstLine : boolean ) => {
923934 type Token = { text : string ; highlight : boolean } ;
924935 const tokens : Token [ ] = [ ] ;
@@ -930,32 +941,40 @@ export default function ChatInput({
930941 }
931942 } ;
932943
933- let i = 0 ;
934-
935- // 1) 首行完整指令高亮
936- if (
937- isFirstLine &&
938- completedCommandLength > 0 &&
939- line . length >= completedCommandLength
940- ) {
941- tokens . push ( {
942- text : line . slice ( 0 , completedCommandLength ) ,
943- highlight : true ,
944- } ) ;
945- i = completedCommandLength ;
946- }
947-
948944 const isPlaceholderTag = ( tag : string ) =>
949945 / ^ \[ P a s t e \d + l i n e s # \d + \] $ / . test ( tag ) ||
950946 / ^ \[ i m a g e # \d + \] $ / . test ( tag ) ||
951947 / ^ \[ S k i l l : [ ^ \] ] + \] $ / . test ( tag ) ||
952948 / ^ \[ G i t L i n e : [ ^ \] ] + \] $ / . test ( tag ) ||
953949 / ^ \[ » [ ^ \] ] * \] $ / . test ( tag ) ;
954950
951+ let i = 0 ;
955952 while ( i < line . length ) {
956953 const ch = line [ i ] ;
957954
958- // 2) [...] 占位符标签
955+ // 1/2) slash 指令高亮:行首允许所有指令,内容中只允许可内联插入指令。
956+ if ( ch === '/' ) {
957+ const prevCh = i === 0 ? '' : line [ i - 1 ] ?? '' ;
958+ const leftBoundary = i === 0 || / \s / . test ( prevCh ) ;
959+ if ( leftBoundary ) {
960+ const isRootCommandPosition = isFirstLine && i === 0 ;
961+ const commandLength = getCommandHighlightLength (
962+ line . slice ( i ) ,
963+ ! isRootCommandPosition ,
964+ ) ;
965+ if ( commandLength > 0 ) {
966+ flushPlain ( ) ;
967+ tokens . push ( {
968+ text : line . slice ( i , i + commandLength ) ,
969+ highlight : true ,
970+ } ) ;
971+ i += commandLength ;
972+ continue ;
973+ }
974+ }
975+ }
976+
977+ // 3) [...] 占位符标签
959978 if ( ch === '[' ) {
960979 const closeIdx = line . indexOf ( ']' , i + 1 ) ;
961980 if ( closeIdx !== - 1 ) {
@@ -970,7 +989,7 @@ export default function ChatInput({
970989 }
971990 }
972991
973- // 3 ) #agent 裸文本标签:需要词边界(前面是行首或空白,后面是行末或空白)
992+ // 4 ) #agent 裸文本标签:需要词边界(前面是行首或空白,后面是行末或空白)
974993 if ( ch === '#' ) {
975994 const prevCh = i === 0 ? '' : line [ i - 1 ] ?? '' ;
976995 const leftBoundary = i === 0 || / \s / . test ( prevCh ) ;
0 commit comments