diff --git a/app/components/timeline/MediaBin.tsx b/app/components/timeline/MediaBin.tsx index 454fe96..4c82127 100644 --- a/app/components/timeline/MediaBin.tsx +++ b/app/components/timeline/MediaBin.tsx @@ -230,6 +230,29 @@ const AudioPreview = ({ src }: { src: string }) => { ); }; +// Empty state component for when no media files are present +const EmptyState = memo(({ onUploadClick }: { onUploadClick: () => void }) => { + return ( +
+ +
+ ); +}); + // This is required for the data router export function loader() { return null; @@ -251,6 +274,9 @@ export default function MediaBin() { // Drag & Drop state for external file imports const [isDragOver, setIsDragOver] = useState(false); + // File input ref for click-to-upload + const fileInputRef = useRef(null); + // Arrange & sorting state const [arrangeMode, setArrangeMode] = useState<"default" | "group">( "default" @@ -360,6 +386,28 @@ export default function MediaBin() { [onAddMedia] ); + const handleUploadClick = useCallback(() => { + fileInputRef.current?.click(); + }, []); + + const handleFileInputChange = useCallback( + async (e: React.ChangeEvent) => { + const files = Array.from(e.target.files || []); + for (const file of files) { + try { + await onAddMedia(file); + } catch (err) { + console.error("Failed to import file:", file.name, err); + } + } + // Reset input so same file can be selected again + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }, + [onAddMedia] + ); + const getMediaIcon = (mediaType: string) => { switch (mediaType) { case "video": @@ -711,13 +759,7 @@ export default function MediaBin() { ))} {defaultArrangedItems.length === 0 && ( -
- -

No media files

-

- Import videos, images, or audio to get started -

-
+ )} )} @@ -884,13 +926,7 @@ export default function MediaBin() { ))} {counts.all === 0 && ( -
- -

No media files

-

- Import videos, images, or audio to get started -

-
+ )} )} @@ -1019,6 +1055,16 @@ export default function MediaBin() { )} + + {/* Hidden file input for click-to-upload */} + ); }