Skip to content

Commit 84bd4dd

Browse files
committed
Block uploads of files larger than 100MB
This is arbitrary for now until I figure out how to better handle larger files
1 parent 536ca79 commit 84bd4dd

File tree

8 files changed

+783
-4
lines changed

8 files changed

+783
-4
lines changed

package-lock.json

+409-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@ffmpeg/ffmpeg": "^0.12.10",
1414
"@ffmpeg/util": "^0.12.1",
1515
"@radix-ui/react-slot": "^1.1.0",
16+
"@radix-ui/react-toast": "^1.2.2",
1617
"class-variance-authority": "^0.7.0",
1718
"clsx": "^2.1.1",
1819
"lodash": "^4.17.21",

src/components/media-upload.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
CircleX,
77
} from "lucide-react";
88

9+
import { useToast } from "@/hooks/use-toast";
910
import { fetchFile } from "@ffmpeg/util";
1011
import { useFFmpeg } from "@/hooks/ffmpeg";
1112
import { Button } from "@/components/ui/button";
@@ -20,6 +21,7 @@ const MediaUpload: React.FC<ComponentProps> = ({
2021
...props
2122
}) => {
2223
const { loadFFmpeg } = useFFmpeg();
24+
const { toast } = useToast();
2325

2426
const mountFS = async (file: File): Promise<void> => {
2527
const ffmpeg = await loadFFmpeg();
@@ -59,8 +61,19 @@ const MediaUpload: React.FC<ComponentProps> = ({
5961
};
6062
// @ts-ignore
6163
let [fileHandle] = await window.showOpenFilePicker(pickerOpts);
62-
6364
let file = await fileHandle.getFile();
65+
const fileSizeMB = file.size / (1024 * 1024);
66+
67+
if (fileSizeMB > 100) {
68+
toast({
69+
variant: "destructive",
70+
title: "Uh oh! Something went wrong.",
71+
description:
72+
"The file is too large, please upload a files smaller than 100MB.",
73+
});
74+
75+
return;
76+
}
6477

6578
await mountFS(file);
6679
};

src/components/ui/toast.tsx

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import * as React from "react"
2+
import * as ToastPrimitives from "@radix-ui/react-toast"
3+
import { cva, type VariantProps } from "class-variance-authority"
4+
import { X } from "lucide-react"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const ToastProvider = ToastPrimitives.Provider
9+
10+
const ToastViewport = React.forwardRef<
11+
React.ElementRef<typeof ToastPrimitives.Viewport>,
12+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
13+
>(({ className, ...props }, ref) => (
14+
<ToastPrimitives.Viewport
15+
ref={ref}
16+
className={cn(
17+
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
18+
className
19+
)}
20+
{...props}
21+
/>
22+
))
23+
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
24+
25+
const toastVariants = cva(
26+
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
27+
{
28+
variants: {
29+
variant: {
30+
default: "border bg-background text-foreground",
31+
destructive:
32+
"destructive group border-destructive bg-destructive text-destructive-foreground",
33+
},
34+
},
35+
defaultVariants: {
36+
variant: "default",
37+
},
38+
}
39+
)
40+
41+
const Toast = React.forwardRef<
42+
React.ElementRef<typeof ToastPrimitives.Root>,
43+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
44+
VariantProps<typeof toastVariants>
45+
>(({ className, variant, ...props }, ref) => {
46+
return (
47+
<ToastPrimitives.Root
48+
ref={ref}
49+
className={cn(toastVariants({ variant }), className)}
50+
{...props}
51+
/>
52+
)
53+
})
54+
Toast.displayName = ToastPrimitives.Root.displayName
55+
56+
const ToastAction = React.forwardRef<
57+
React.ElementRef<typeof ToastPrimitives.Action>,
58+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
59+
>(({ className, ...props }, ref) => (
60+
<ToastPrimitives.Action
61+
ref={ref}
62+
className={cn(
63+
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
64+
className
65+
)}
66+
{...props}
67+
/>
68+
))
69+
ToastAction.displayName = ToastPrimitives.Action.displayName
70+
71+
const ToastClose = React.forwardRef<
72+
React.ElementRef<typeof ToastPrimitives.Close>,
73+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
74+
>(({ className, ...props }, ref) => (
75+
<ToastPrimitives.Close
76+
ref={ref}
77+
className={cn(
78+
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
79+
className
80+
)}
81+
toast-close=""
82+
{...props}
83+
>
84+
<X className="h-4 w-4" />
85+
</ToastPrimitives.Close>
86+
))
87+
ToastClose.displayName = ToastPrimitives.Close.displayName
88+
89+
const ToastTitle = React.forwardRef<
90+
React.ElementRef<typeof ToastPrimitives.Title>,
91+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
92+
>(({ className, ...props }, ref) => (
93+
<ToastPrimitives.Title
94+
ref={ref}
95+
className={cn("text-sm font-semibold", className)}
96+
{...props}
97+
/>
98+
))
99+
ToastTitle.displayName = ToastPrimitives.Title.displayName
100+
101+
const ToastDescription = React.forwardRef<
102+
React.ElementRef<typeof ToastPrimitives.Description>,
103+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
104+
>(({ className, ...props }, ref) => (
105+
<ToastPrimitives.Description
106+
ref={ref}
107+
className={cn("text-sm opacity-90", className)}
108+
{...props}
109+
/>
110+
))
111+
ToastDescription.displayName = ToastPrimitives.Description.displayName
112+
113+
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
114+
115+
type ToastActionElement = React.ReactElement<typeof ToastAction>
116+
117+
export {
118+
type ToastProps,
119+
type ToastActionElement,
120+
ToastProvider,
121+
ToastViewport,
122+
Toast,
123+
ToastTitle,
124+
ToastDescription,
125+
ToastClose,
126+
ToastAction,
127+
}

src/components/ui/toaster.tsx

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useToast } from "@/hooks/use-toast"
2+
import {
3+
Toast,
4+
ToastClose,
5+
ToastDescription,
6+
ToastProvider,
7+
ToastTitle,
8+
ToastViewport,
9+
} from "@/components/ui/toast"
10+
11+
export function Toaster() {
12+
const { toasts } = useToast()
13+
14+
return (
15+
<ToastProvider>
16+
{toasts.map(function ({ id, title, description, action, ...props }) {
17+
return (
18+
<Toast key={id} {...props}>
19+
<div className="grid gap-1">
20+
{title && <ToastTitle>{title}</ToastTitle>}
21+
{description && (
22+
<ToastDescription>{description}</ToastDescription>
23+
)}
24+
</div>
25+
{action}
26+
<ToastClose />
27+
</Toast>
28+
)
29+
})}
30+
<ToastViewport />
31+
</ToastProvider>
32+
)
33+
}

src/hooks/chat.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ export const useChatMessages = () => {
8080
const fileURL = await getFile(output, outputMimetype);
8181

8282
// Can't do jsx so just pass the reactnode fn instead, hack for now
83-
setMediaMessage(FileDisplay({ src: fileURL, type: outputMimetype }));
83+
setMediaMessage(
84+
FileDisplay({ src: fileURL, type: outputMimetype, ext: fileExt })
85+
);
8486
} catch (err) {
8587
console.error(err);
8688
setMessages([

0 commit comments

Comments
 (0)