fix: keep local file picker mounted during composer swaps

Moves the hidden file input out of the attachment controls so it stays mounted.
Prevents file selections from being lost when the composer variant changes.
Restores reliable local attachment uploads after opening the OS picker.
This commit is contained in:
Bohdan Triapitsyn
2026-07-07 10:32:13 +03:00
parent 9ceb027e75
commit 5101fab962
+15 -19
View File
@@ -527,8 +527,6 @@ type ComposerAttachmentControlsProps = {
isVSCode: boolean;
footerIconButtonClass: string;
iconSizeClass: string;
fileInputRef: React.RefObject<HTMLInputElement | null>;
handleLocalFileSelect: (event: React.ChangeEvent<HTMLInputElement>) => void | Promise<void>;
handlePickLocalFiles: () => void;
openIssuePicker: () => void;
openPrPicker: () => void;
@@ -544,8 +542,6 @@ const ComposerAttachmentControls = React.memo(function ComposerAttachmentControl
isVSCode,
footerIconButtonClass,
iconSizeClass,
fileInputRef,
handleLocalFileSelect,
handlePickLocalFiles,
openIssuePicker,
openPrPicker,
@@ -554,15 +550,6 @@ const ComposerAttachmentControls = React.memo(function ComposerAttachmentControl
return (
<div className="flex items-center gap-x-1.5">
<input
ref={fileInputRef}
type="file"
multiple
className="hidden"
onChange={handleLocalFileSelect}
accept="*/*"
/>
<div className="relative inline-flex">
{props.onOpenMobileSheet ? (
<button
@@ -4907,8 +4894,6 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
isVSCode={isVSCode}
footerIconButtonClass={footerIconButtonClass}
iconSizeClass={iconSizeClass}
fileInputRef={fileInputRef}
handleLocalFileSelect={handleLocalFileSelect}
handlePickLocalFiles={handlePickLocalFiles}
openIssuePicker={openIssuePicker}
openPrPicker={openPrPicker}
@@ -5285,8 +5270,6 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
isVSCode={isVSCode}
footerIconButtonClass={footerIconButtonClass}
iconSizeClass={iconSizeClass}
fileInputRef={fileInputRef}
handleLocalFileSelect={handleLocalFileSelect}
handlePickLocalFiles={handlePickLocalFiles}
openIssuePicker={openIssuePicker}
openPrPicker={openPrPicker}
@@ -5350,8 +5333,6 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
isVSCode={isVSCode}
footerIconButtonClass={footerIconButtonClass}
iconSizeClass={iconSizeClass}
fileInputRef={fileInputRef}
handleLocalFileSelect={handleLocalFileSelect}
handlePickLocalFiles={handlePickLocalFiles}
openIssuePicker={openIssuePicker}
openPrPicker={openPrPicker}
@@ -5480,6 +5461,21 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
isMobile={isMobile}
/>
{/* Single always-mounted picker input. It must NOT live inside
ComposerAttachmentControls: that component mounts once per composer
variant (pill / expanded footer), so a shared ref got nulled when a
variant unmounted, and a variant swap while the OS file picker was
open detached the clicked input its change event was silently
lost and the picked files never attached. */}
<input
ref={fileInputRef}
type="file"
multiple
className="hidden"
onChange={handleLocalFileSelect}
accept="*/*"
/>
{/* Mobile attachment sheet: replaces the dropdown (which stole focus and
dismissed the keyboard) and leaves room for more actions later. */}
{isMobile ? (