@@ -70,7 +70,7 @@ export function AgentChat() {
7070 const [ showHistoryPicker , setShowHistoryPicker ] = useState ( false ) ;
7171 const [ historyPickerIdx , setHistoryPickerIdx ] = useState ( 0 ) ;
7272 const [ historyRestoreTarget , setHistoryRestoreTarget ] = useState < number | null > ( null ) ;
73- const [ attachedImages , setAttachedImages ] = useState < Array < { name : string ; path : string } > > ( [ ] ) ;
73+ const [ attachedFiles , setAttachedFiles ] = useState < File [ ] > ( [ ] ) ;
7474 const [ uploadingCount , setUploadingCount ] = useState ( 0 ) ;
7575 const fileInputRef = useRef < HTMLInputElement > ( null ) ;
7676
@@ -553,57 +553,57 @@ export function AgentChat() {
553553 }
554554 } ;
555555
556- const handleFileUpload = async ( files : File [ ] ) => {
557- if ( files . length === 0 ) return ;
558- setUploadingCount ( prev => prev + files . length ) ;
559- for ( const file of files ) {
560- try {
561- const result = await api . uploadFile ( file ) ;
562- setAttachedImages ( prev => [ ...prev , { name : file . name , path : result . path } ] ) ;
563- } catch ( err ) {
564- addLocalMessage ( `Failed to upload ${ file . name } : ${ err instanceof Error ? err . message : String ( err ) } ` ) ;
565- } finally {
566- setUploadingCount ( prev => prev - 1 ) ;
567- }
568- }
569- } ;
570-
571556 const handlePaste = ( e : React . ClipboardEvent ) => {
572557 const items = e . clipboardData ?. items ;
573558 if ( ! items ) return ;
574559
575560 const pasteFiles : File [ ] = [ ] ;
576561 for ( let i = 0 ; i < items . length ; i ++ ) {
577562 const item = items [ i ] ;
578- // Accept any file type from clipboard (images, PDFs, etc.)
579563 if ( item . kind === 'file' ) {
580564 const file = item . getAsFile ( ) ;
581565 if ( file ) pasteFiles . push ( file ) ;
582566 }
583567 }
584568 if ( pasteFiles . length > 0 ) {
585569 e . preventDefault ( ) ;
586- handleFileUpload ( pasteFiles ) ;
570+ setAttachedFiles ( prev => [ ... prev , ... pasteFiles ] ) ;
587571 }
588572 } ;
589573
590- const removeAttachedImage = ( index : number ) => {
591- setAttachedImages ( prev => prev . filter ( ( _ , i ) => i !== index ) ) ;
574+ const removeAttachedFile = ( index : number ) => {
575+ setAttachedFiles ( prev => prev . filter ( ( _ , i ) => i !== index ) ) ;
592576 } ;
593577
594- const handleSend = ( ) => {
595- if ( ( ! input . trim ( ) && attachedImages . length === 0 ) || ! id ) return ;
578+ const handleSend = async ( ) => {
579+ if ( ( ! input . trim ( ) && attachedFiles . length === 0 ) || ! id ) return ;
596580
597581 // Save to input history
598582 const trimmed = input . trim ( ) ;
599583 const hist = inputHistoryRef . current ;
600- if ( hist [ 0 ] !== trimmed ) {
584+ if ( trimmed && hist [ 0 ] !== trimmed ) {
601585 hist . unshift ( trimmed ) ;
602586 if ( hist . length > 50 ) hist . pop ( ) ;
603587 }
604588 historyIdxRef . current = - 1 ;
605589 savedInputRef . current = '' ;
606590
591+ // Upload attached files now (not earlier)
592+ const uploadedPaths : { name : string ; path : string } [ ] = [ ] ;
593+ if ( attachedFiles . length > 0 ) {
594+ setUploadingCount ( attachedFiles . length ) ;
595+ for ( const file of attachedFiles ) {
596+ try {
597+ const result = await api . uploadFile ( file ) ;
598+ uploadedPaths . push ( { name : file . name , path : result . path } ) ;
599+ } catch ( err ) {
600+ addLocalMessage ( `Failed to upload ${ file . name } : ${ err instanceof Error ? err . message : String ( err ) } ` ) ;
601+ } finally {
602+ setUploadingCount ( prev => prev - 1 ) ;
603+ }
604+ }
605+ }
606+
607607 if ( input . startsWith ( '/' ) ) {
608608 // Handle commands with arguments (e.g., /compact [instructions])
609609 const parts = input . trim ( ) . split ( / \s + / ) ;
@@ -624,14 +624,14 @@ export function AgentChat() {
624624 }
625625 }
626626
627- // Build message text with image paths prepended
628- const imagePrefixes = attachedImages . map ( img => {
629- const isImage = / \. ( p n g | j p e ? g | g i f | w e b p | s v g | b m p ) $ / i. test ( img . name ) ;
630- return isImage ? `[Image: ${ img . path } ]` : `[File: ${ img . path } ]` ;
627+ // Build message text with file paths prepended
628+ const filePrefixes = uploadedPaths . map ( f => {
629+ const isImage = / \. ( p n g | j p e ? g | g i f | w e b p | s v g | b m p ) $ / i. test ( f . name ) ;
630+ return isImage ? `[Image: ${ f . path } ]` : `[File: ${ f . path } ]` ;
631631 } ) . join ( '\n' ) ;
632632 const userText = input . trim ( ) ;
633- const text = imagePrefixes
634- ? ( userText ? `${ imagePrefixes } \n\n${ userText } ` : imagePrefixes )
633+ const text = filePrefixes
634+ ? ( userText ? `${ filePrefixes } \n\n${ userText } ` : filePrefixes )
635635 : userText ;
636636
637637 if ( ! text ) return ;
@@ -646,7 +646,7 @@ export function AgentChat() {
646646 } ;
647647 } ) ;
648648 setInput ( '' ) ;
649- setAttachedImages ( [ ] ) ;
649+ setAttachedFiles ( [ ] ) ;
650650 setInputRequired ( null ) ;
651651 api . sendMessage ( id , text ) ;
652652 } ;
@@ -944,8 +944,8 @@ export function AgentChat() {
944944 ) ) }
945945 </ div >
946946 ) }
947- { /* Image attachment indicator */ }
948- { ( attachedImages . length > 0 || uploadingCount > 0 ) && (
947+ { /* File attachment indicator */ }
948+ { ( attachedFiles . length > 0 || uploadingCount > 0 ) && (
949949 < div style = { {
950950 display : 'flex' ,
951951 flexWrap : 'wrap' ,
@@ -956,7 +956,7 @@ export function AgentChat() {
956956 borderRadius : 'var(--radius)' ,
957957 border : '1px solid var(--border)' ,
958958 } } >
959- { attachedImages . map ( ( img , i ) => (
959+ { attachedFiles . map ( ( file , i ) => (
960960 < div key = { i } style = { {
961961 display : 'flex' ,
962962 alignItems : 'center' ,
@@ -967,10 +967,11 @@ export function AgentChat() {
967967 fontSize : 12 ,
968968 color : 'var(--text)' ,
969969 } } >
970- < span style = { { fontSize : 14 } } > { '\uD83D\uDCCE' } </ span >
971- < span style = { { maxWidth : 120 , overflow : 'hidden' , textOverflow : 'ellipsis' , whiteSpace : 'nowrap' } } > { img . name } </ span >
970+ < span style = { { fontSize : 14 } } > { file . type . startsWith ( 'image/' ) ? '\uD83D\uDDBC' : '\uD83D\uDCCE' } </ span >
971+ < span style = { { maxWidth : 120 , overflow : 'hidden' , textOverflow : 'ellipsis' , whiteSpace : 'nowrap' } } > { file . name } </ span >
972+ < span style = { { color : 'var(--text-muted)' , fontSize : 11 } } > ({ ( file . size / 1024 ) . toFixed ( 0 ) } KB)</ span >
972973 < button
973- onClick = { ( ) => removeAttachedImage ( i ) }
974+ onClick = { ( ) => removeAttachedFile ( i ) }
974975 style = { {
975976 background : 'none' ,
976977 border : 'none' ,
@@ -995,7 +996,7 @@ export function AgentChat() {
995996 fontSize : 12 ,
996997 color : 'var(--text-muted)' ,
997998 } } >
998- Uploading { uploadingCount } image { uploadingCount > 1 ? 's' : '' } ...
999+ Uploading { uploadingCount } file { uploadingCount > 1 ? 's' : '' } ...
9991000 </ div >
10001001 ) }
10011002 </ div >
@@ -1009,7 +1010,7 @@ export function AgentChat() {
10091010 style = { { display : 'none' } }
10101011 onChange = { ( e ) => {
10111012 if ( e . target . files ) {
1012- handleFileUpload ( Array . from ( e . target . files ) ) ;
1013+ setAttachedFiles ( prev => [ ... prev , ... Array . from ( e . target . files ! ) ] ) ;
10131014 e . target . value = '' ;
10141015 }
10151016 } }
0 commit comments