diff --git a/apps/desktop/src/components/assistant-ui/thread.tsx b/apps/desktop/src/components/assistant-ui/thread.tsx index ac0f277d700..90d97209f41 100644 --- a/apps/desktop/src/components/assistant-ui/thread.tsx +++ b/apps/desktop/src/components/assistant-ui/thread.tsx @@ -971,6 +971,10 @@ const UserEditComposer: FC = ({ cwd, gateway, sessionId } const [triggerPlacement, setTriggerPlacement] = useState<'bottom' | 'top'>('top') const [focusRequestId, setFocusRequestId] = useState(0) const [submitting, setSubmitting] = useState(false) + // True while OS-drop files are being staged/uploaded into the session. Blocks + // submit and shows a spinner so confirming the edit can't race the async + // upload and drop the gateway-side ref before it lands in the draft. + const [staging, setStaging] = useState(false) const expanded = draft.includes('\n') const canSubmit = draft.trim().length > 0 const at = useAtCompletions({ cwd, gateway, sessionId }) @@ -1324,11 +1328,14 @@ const UserEditComposer: FC = ({ cwd, gateway, sessionId } } if (osDrops.length) { - void uploadOsDropRefs(osDrops).then(refs => { - if (insertRefStrings(refs)) { - triggerHaptic('selection') - } - }) + setStaging(true) + void uploadOsDropRefs(osDrops) + .then(refs => { + if (insertRefStrings(refs)) { + triggerHaptic('selection') + } + }) + .finally(() => setStaging(false)) } } @@ -1360,7 +1367,7 @@ const UserEditComposer: FC = ({ cwd, gateway, sessionId } const submitEdit = (editor: HTMLDivElement) => { const nextDraft = syncDraftFromEditor(editor) - if (submitting || !nextDraft.trim()) { + if (submitting || staging || !nextDraft.trim()) { return } @@ -1517,10 +1524,19 @@ const UserEditComposer: FC = ({ cwd, gateway, sessionId } suppressContentEditableWarning /> + {staging && ( + + + {copy.attachingFile} + + )}