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 */}
+
);
}