@@ -63,6 +63,14 @@ import { cn } from "@/common/lib/utils";
6363import { CreationControls } from "./CreationControls" ;
6464import { useCreationWorkspace } from "./useCreationWorkspace" ;
6565
66+ const LEADING_COMMAND_NOISE = / ^ (?: \s | \u200B | \u200C | \u200D | \u200E | \u200F | \uFEFF ) + / ;
67+
68+ function normalizeSlashCommandInput ( value : string ) : string {
69+ if ( ! value ) {
70+ return value ;
71+ }
72+ return value . replace ( LEADING_COMMAND_NOISE , "" ) ;
73+ }
6674type TokenCountReader = ( ) => number ;
6775
6876function createTokenCountResource ( promise : Promise < number > ) : TokenCountReader {
@@ -301,9 +309,10 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
301309
302310 // Watch input for slash commands
303311 useEffect ( ( ) => {
304- const suggestions = getSlashCommandSuggestions ( input , { providerNames } ) ;
312+ const normalizedSlashSource = normalizeSlashCommandInput ( input ) ;
313+ const suggestions = getSlashCommandSuggestions ( normalizedSlashSource , { providerNames } ) ;
305314 setCommandSuggestions ( suggestions ) ;
306- setShowCommandSuggestions ( suggestions . length > 0 ) ;
315+ setShowCommandSuggestions ( normalizedSlashSource . startsWith ( "/" ) && suggestions . length > 0 ) ;
307316 } , [ input , providerNames ] ) ;
308317
309318 // Load provider names for suggestions
@@ -463,8 +472,11 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
463472 return ;
464473 }
465474
466- const messageText = input . trim ( ) ;
467- const parsed = parseCommand ( messageText ) ;
475+ const rawInputValue = input ;
476+ const messageText = rawInputValue . trim ( ) ;
477+ const normalizedCommandInput = normalizeSlashCommandInput ( messageText ) ;
478+ const isSlashCommand = normalizedCommandInput . startsWith ( "/" ) ;
479+ const parsed = isSlashCommand ? parseCommand ( normalizedCommandInput ) : null ;
468480
469481 if ( parsed ) {
470482 const context : SlashCommandContext = {
@@ -491,11 +503,20 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
491503 const result = await processSlashCommand ( parsed , context ) ;
492504
493505 if ( ! result . clearInput ) {
494- setInput ( messageText ) ; // Restore input on failure
506+ setInput ( rawInputValue ) ; // Restore exact input on failure
495507 }
496508 return ;
497509 }
498510
511+ if ( isSlashCommand ) {
512+ setToast ( {
513+ id : Date . now ( ) . toString ( ) ,
514+ type : "error" ,
515+ message : `Unknown command: ${ normalizedCommandInput . split ( / \s + / ) [ 0 ] ?? "" } ` ,
516+ } ) ;
517+ return ;
518+ }
519+
499520 // Handle standard message sending based on variant
500521 if ( variant === "creation" ) {
501522 setIsSending ( true ) ;
@@ -511,7 +532,6 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
511532 }
512533
513534 // Workspace variant: regular message send
514- if ( variant !== "workspace" ) return ;
515535
516536 try {
517537 // Regular message - send directly via API
@@ -555,18 +575,18 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
555575 let muxMetadata : MuxFrontendMetadata | undefined ;
556576 let compactionOptions = { } ;
557577
558- if ( editingMessage && messageText . startsWith ( "/" ) ) {
559- const parsed = parseCommand ( messageText ) ;
560- if ( parsed ?. type === "compact" ) {
578+ if ( editingMessage && normalizedCommandInput . startsWith ( "/" ) ) {
579+ const parsedEditingCommand = parseCommand ( normalizedCommandInput ) ;
580+ if ( parsedEditingCommand ?. type === "compact" ) {
561581 const {
562582 messageText : regeneratedText ,
563583 metadata,
564584 sendOptions,
565585 } = prepareCompactionMessage ( {
566586 workspaceId : props . workspaceId ,
567- maxOutputTokens : parsed . maxOutputTokens ,
568- continueMessage : parsed . continueMessage ,
569- model : parsed . model ,
587+ maxOutputTokens : parsedEditingCommand . maxOutputTokens ,
588+ continueMessage : parsedEditingCommand . continueMessage ,
589+ model : parsedEditingCommand . model ,
570590 sendMessageOptions,
571591 } ) ;
572592 actualMessageText = regeneratedText ;
@@ -602,7 +622,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
602622 // Show error using enhanced toast
603623 setToast ( createErrorToast ( result . error ) ) ;
604624 // Restore input and images on error so user can try again
605- setInput ( messageText ) ;
625+ setInput ( rawInputValue ) ;
606626 setImageAttachments ( previousImageAttachments ) ;
607627 } else {
608628 // Track telemetry for successful message send
@@ -623,7 +643,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
623643 raw : error instanceof Error ? error . message : "Failed to send message" ,
624644 } )
625645 ) ;
626- setInput ( messageText ) ;
646+ setInput ( rawInputValue ) ;
627647 setImageAttachments ( previousImageAttachments ) ;
628648 } finally {
629649 setIsSending ( false ) ;
@@ -749,18 +769,16 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
749769 data-component = "ChatInputSection"
750770 >
751771 < div className = "mx-auto w-full max-w-4xl" >
752- { /* Creation toast */ }
753- { variant === "creation" && (
754- < ChatInputToast
755- toast = { creationState . toast }
756- onDismiss = { ( ) => creationState . setToast ( null ) }
757- />
758- ) }
759-
760- { /* Workspace toast */ }
761- { variant === "workspace" && (
762- < ChatInputToast toast = { toast } onDismiss = { handleToastDismiss } />
763- ) }
772+ { /* Toast - show shared toast (slash commands) or variant-specific toast */ }
773+ < ChatInputToast
774+ toast = { toast ?? ( variant === "creation" ? creationState . toast : null ) }
775+ onDismiss = { ( ) => {
776+ handleToastDismiss ( ) ;
777+ if ( variant === "creation" ) {
778+ creationState . setToast ( null ) ;
779+ }
780+ } }
781+ />
764782
765783 { /* Command suggestions - available in both variants */ }
766784 < CommandSuggestions
0 commit comments