@@ -60,6 +60,7 @@ import { ImageAttachments, type ImageAttachment } from "../ImageAttachments";
6060import {
6161 extractImagesFromClipboard ,
6262 extractImagesFromDrop ,
63+ imageAttachmentsToImageParts ,
6364 processImageFiles ,
6465} from "@/browser/utils/imageHandling" ;
6566
@@ -241,10 +242,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
241242 ( ) => createTokenCountResource ( tokenCountPromise ) ,
242243 [ tokenCountPromise ]
243244 ) ;
244- const hasTypedText = input . trim ( ) . length > 0 ;
245- const hasImages = imageAttachments . length > 0 ;
246- const hasReviews = attachedReviews . length > 0 ;
247- const canSend = ( hasTypedText || hasImages || hasReviews ) && ! disabled && ! isSending ;
245+
248246 // Setter for model - updates localStorage directly so useSendMessageOptions picks it up
249247 const setPreferredModel = useCallback (
250248 ( model : string ) => {
@@ -272,6 +270,12 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
272270 }
273271 ) ;
274272
273+ const isSendInFlight = variant === "creation" ? creationState . isSending : isSending ;
274+ const hasTypedText = input . trim ( ) . length > 0 ;
275+ const hasImages = imageAttachments . length > 0 ;
276+ const hasReviews = attachedReviews . length > 0 ;
277+ const canSend = ( hasTypedText || hasImages || hasReviews ) && ! disabled && ! isSendInFlight ;
278+
275279 // When entering creation mode, initialize the project-scoped model to the
276280 // default so previous manual picks don't bleed into new creation flows.
277281 // Only runs once per creation session (not when defaultModel changes, which
@@ -640,12 +644,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
640644 // Route to creation handler for creation variant
641645 if ( variant === "creation" ) {
642646 // Creation variant: simple message send + workspace creation
643- setIsSending ( true ) ;
644- // Convert image attachments to image parts
645- const creationImageParts = imageAttachments . map ( ( img ) => ( {
646- url : img . url ,
647- mediaType : img . mediaType ,
648- } ) ) ;
647+ const creationImageParts = imageAttachmentsToImageParts ( imageAttachments ) ;
649648 const ok = await creationState . handleSend (
650649 messageText ,
651650 creationImageParts . length > 0 ? creationImageParts : undefined
@@ -659,7 +658,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
659658 inputRef . current . style . height = "" ;
660659 }
661660 }
662- setIsSending ( false ) ;
663661 return ;
664662 }
665663
@@ -1066,33 +1064,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
10661064
10671065 try {
10681066 // Prepare image parts if any
1069- const imageParts = imageAttachments . map ( ( img , index ) => {
1070- // Validate before sending to help with debugging
1071- if ( ! img . url || typeof img . url !== "string" ) {
1072- console . error (
1073- `Image attachment [${ index } ] has invalid url:` ,
1074- typeof img . url ,
1075- img . url ?. slice ( 0 , 50 )
1076- ) ;
1077- }
1078- if ( ! img . url ?. startsWith ( "data:" ) ) {
1079- console . error (
1080- `Image attachment [${ index } ] url is not a data URL:` ,
1081- img . url ?. slice ( 0 , 100 )
1082- ) ;
1083- }
1084- if ( ! img . mediaType || typeof img . mediaType !== "string" ) {
1085- console . error (
1086- `Image attachment [${ index } ] has invalid mediaType:` ,
1087- typeof img . mediaType ,
1088- img . mediaType
1089- ) ;
1090- }
1091- return {
1092- url : img . url ,
1093- mediaType : img . mediaType ,
1094- } ;
1095- } ) ;
1067+ const imageParts = imageAttachmentsToImageParts ( imageAttachments , { validate : true } ) ;
10961068
10971069 // When editing a /compact command, regenerate the actual summarization request
10981070 let actualMessageText = messageText ;
@@ -1308,7 +1280,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
13081280 const placeholder = ( ( ) => {
13091281 // Creation variant has simple placeholder
13101282 if ( variant === "creation" ) {
1311- return `Type your first message to create a workspace... (${ formatKeybind ( KEYBINDS . SEND_MESSAGE ) } to send, Esc to cancel)` ;
1283+ return `Type your first message to create a workspace... (${ formatKeybind ( KEYBINDS . SEND_MESSAGE ) } to send, ${ formatKeybind ( KEYBINDS . CANCEL ) } to cancel)` ;
13121284 }
13131285
13141286 // Workspace variant placeholders
@@ -1353,17 +1325,9 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
13531325 { variant === "creation" && (
13541326 < CreationCenterContent
13551327 projectName = { props . projectName }
1356- isSending = { creationState . isSending || isSending }
1357- workspaceName = {
1358- creationState . isSending || isSending
1359- ? creationState . creatingWithIdentity ?. name
1360- : undefined
1361- }
1362- workspaceTitle = {
1363- creationState . isSending || isSending
1364- ? creationState . creatingWithIdentity ?. title
1365- : undefined
1366- }
1328+ isSending = { isSendInFlight }
1329+ workspaceName = { isSendInFlight ? creationState . creatingWithIdentity ?. name : undefined }
1330+ workspaceTitle = { isSendInFlight ? creationState . creatingWithIdentity ?. title : undefined }
13671331 />
13681332 ) }
13691333
@@ -1444,7 +1408,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
14441408 onRuntimeModeChange = { creationState . setRuntimeMode }
14451409 onSetDefaultRuntime = { creationState . setDefaultRuntimeMode }
14461410 onSshHostChange = { creationState . setSshHost }
1447- disabled = { creationState . isSending || isSending }
1411+ disabled = { isSendInFlight }
14481412 projectName = { props . projectName }
14491413 nameState = { creationState . nameState }
14501414 />
@@ -1486,7 +1450,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
14861450 onEscapeInNormalMode = { handleEscapeInNormalMode }
14871451 suppressKeys = { showCommandSuggestions ? COMMAND_SUGGESTION_KEYS : undefined }
14881452 placeholder = { placeholder }
1489- disabled = { ! editingMessage && ( disabled || isSending ) }
1453+ disabled = { ! editingMessage && ( disabled || isSendInFlight ) }
14901454 aria-label = { editingMessage ? "Edit your last message" : "Message Claude" }
14911455 aria-autocomplete = "list"
14921456 aria-controls = {
@@ -1505,7 +1469,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
15051469 shouldShowUI = { voiceInput . shouldShowUI }
15061470 requiresSecureContext = { voiceInput . requiresSecureContext }
15071471 onToggle = { voiceInput . toggle }
1508- disabled = { disabled || isSending }
1472+ disabled = { disabled || isSendInFlight }
15091473 mode = { mode }
15101474 />
15111475 </ div >
0 commit comments