@@ -44,6 +44,10 @@ interface FloatingPromptInputProps {
4444 className ?: string ;
4545}
4646
47+ export interface FloatingPromptInputRef {
48+ addImage : ( imagePath : string ) => void ;
49+ }
50+
4751type Model = {
4852 id : "sonnet" | "opus" ;
4953 name : string ;
@@ -70,19 +74,21 @@ const MODELS: Model[] = [
7074 * FloatingPromptInput component - Fixed position prompt input with model picker
7175 *
7276 * @example
77+ * const promptRef = useRef<FloatingPromptInputRef>(null);
7378 * <FloatingPromptInput
79+ * ref={promptRef}
7480 * onSend={(prompt, model) => console.log('Send:', prompt, model)}
7581 * isLoading={false}
7682 * />
7783 */
78- export const FloatingPromptInput : React . FC < FloatingPromptInputProps > = ( {
84+ export const FloatingPromptInput = React . forwardRef < FloatingPromptInputRef , FloatingPromptInputProps > ( ( {
7985 onSend,
8086 isLoading = false ,
8187 disabled = false ,
8288 defaultModel = "sonnet" ,
8389 projectPath,
8490 className,
85- } ) => {
91+ } , ref ) => {
8692 const [ prompt , setPrompt ] = useState ( "" ) ;
8793 const [ selectedModel , setSelectedModel ] = useState < "sonnet" | "opus" > ( defaultModel ) ;
8894 const [ isExpanded , setIsExpanded ] = useState ( false ) ;
@@ -96,6 +102,34 @@ export const FloatingPromptInput: React.FC<FloatingPromptInputProps> = ({
96102 const textareaRef = useRef < HTMLTextAreaElement > ( null ) ;
97103 const expandedTextareaRef = useRef < HTMLTextAreaElement > ( null ) ;
98104 const unlistenDragDropRef = useRef < ( ( ) => void ) | null > ( null ) ;
105+
106+ // Expose a method to add images programmatically
107+ React . useImperativeHandle (
108+ ref ,
109+ ( ) => ( {
110+ addImage : ( imagePath : string ) => {
111+ setPrompt ( currentPrompt => {
112+ const existingPaths = extractImagePaths ( currentPrompt ) ;
113+ if ( existingPaths . includes ( imagePath ) ) {
114+ return currentPrompt ; // Image already added
115+ }
116+
117+ const mention = `@${ imagePath } ` ;
118+ const newPrompt = currentPrompt + ( currentPrompt . endsWith ( ' ' ) || currentPrompt === '' ? '' : ' ' ) + mention + ' ' ;
119+
120+ // Focus the textarea
121+ setTimeout ( ( ) => {
122+ const target = isExpanded ? expandedTextareaRef . current : textareaRef . current ;
123+ target ?. focus ( ) ;
124+ target ?. setSelectionRange ( newPrompt . length , newPrompt . length ) ;
125+ } , 0 ) ;
126+
127+ return newPrompt ;
128+ } ) ;
129+ }
130+ } ) ,
131+ [ isExpanded ]
132+ ) ;
99133
100134 // Helper function to check if a file is an image
101135 const isImageFile = ( path : string ) : boolean => {
@@ -410,21 +444,21 @@ export const FloatingPromptInput: React.FC<FloatingPromptInputProps> = ({
410444 </ Button >
411445 </ div >
412446
413- < Button
414- onClick = { handleSend }
415- disabled = { ! prompt . trim ( ) || isLoading || disabled }
416- size = "sm"
417- className = "min-w-[80px]"
418- >
419- { isLoading ? (
420- < div className = "rotating-symbol text-primary-foreground" > </ div >
421- ) : (
422- < >
423- < Send className = "mr-2 h-4 w-4" / >
424- Send
425- </ >
426- ) }
427- </ Button >
447+ { isLoading ? (
448+ < div className = "flex items-center justify-center min-w-[60px] h-10" >
449+ < div className = "rotating-symbol text-primary text-2xl" > </ div >
450+ </ div >
451+ ) : (
452+ < Button
453+ onClick = { handleSend }
454+ disabled = { ! prompt . trim ( ) || disabled }
455+ size = "sm"
456+ className = "min-w-[80px]"
457+ >
458+ < Send className = "mr-2 h-4 w-4" />
459+ Send
460+ </ Button >
461+ ) }
428462 </ div >
429463 </ motion . div >
430464 </ motion . div >
@@ -541,18 +575,20 @@ export const FloatingPromptInput: React.FC<FloatingPromptInputProps> = ({
541575 </ div >
542576
543577 { /* Send Button */ }
544- < Button
545- onClick = { handleSend }
546- disabled = { ! prompt . trim ( ) || isLoading || disabled }
547- size = "default"
548- className = "min-w-[60px]"
549- >
550- { isLoading ? (
551- < div className = "rotating-symbol text-primary-foreground" > </ div >
552- ) : (
578+ { isLoading ? (
579+ < div className = "flex items-center justify-center min-w-[60px] h-10" >
580+ < div className = "rotating-symbol text-primary text-2xl" > </ div >
581+ </ div >
582+ ) : (
583+ < Button
584+ onClick = { handleSend }
585+ disabled = { ! prompt . trim ( ) || disabled }
586+ size = "default"
587+ className = "min-w-[60px]"
588+ >
553589 < Send className = "h-4 w-4" />
554- ) }
555- </ Button >
590+ </ Button >
591+ ) }
556592 </ div >
557593
558594 < div className = "mt-2 text-xs text-muted-foreground" >
@@ -563,4 +599,6 @@ export const FloatingPromptInput: React.FC<FloatingPromptInputProps> = ({
563599 </ div >
564600 </ >
565601 ) ;
566- } ;
602+ } ) ;
603+
604+ FloatingPromptInput . displayName = 'FloatingPromptInput' ;
0 commit comments