({ key: f.key, name: f.name }))}
+ />,
+ nextOffset,
+ ] as const;
+};
+
+export default async function HomePage() {
+ const { files: uploadedFiles } = await utapi.listFiles({
+ offset: 0,
+ limit: PAGE_SIZE,
+ });
+
+ return (
+
+
+
+
+ Uploaded files
+ View the uploaded files here
+
+
+
+ ({
+ key: f.key,
+ name: f.name,
+ }))}
+ />
+
+
+
+
+ );
+}
diff --git a/playground/src/app/gallery/rhf.tsx b/playground/src/app/gallery/rhf.tsx
new file mode 100644
index 0000000000..2921d8feaa
--- /dev/null
+++ b/playground/src/app/gallery/rhf.tsx
@@ -0,0 +1,76 @@
+"use client";
+
+import * as React from "react";
+import { useRouter } from "next/navigation";
+import { toast } from "sonner";
+import { z } from "zod";
+
+import { FileUploader } from "~/components/file-uploader";
+import { Button } from "~/components/ui/button";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ useForm,
+} from "~/components/ui/form";
+import { fileWithStateValidator } from "~/utils";
+
+export function ReactHookFormDemo() {
+ const form = useForm({
+ schema: z.object({
+ images: fileWithStateValidator.array(),
+ }),
+ defaultValues: {
+ images: [],
+ },
+ mode: "onChange",
+ });
+
+ const router = useRouter();
+ const onSubmit = form.handleSubmit((data) => {
+ toast(
+
+ {JSON.stringify(data, null, 4)}
+
,
+ );
+ form.reset();
+ router.refresh();
+ });
+
+ return (
+
+
+ );
+}
diff --git a/playground/src/app/globals.css b/playground/src/app/globals.css
new file mode 100644
index 0000000000..60ada06638
--- /dev/null
+++ b/playground/src/app/globals.css
@@ -0,0 +1,178 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 240 10% 3.9%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 240 10% 3.9%;
+
+ --primary: 240 5.9% 10%;
+ --primary-foreground: 0 0% 98%;
+
+ --secondary: 240 4.8% 95.9%;
+ --secondary-foreground: 240 5.9% 10%;
+
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 3.8% 46.1%;
+
+ --accent: 240 4.8% 95.9%;
+ --accent-foreground: 240 5.9% 10%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+
+ --border: 240 5.9% 90%;
+ --input: 240 5.9% 90%;
+ --ring: 240 10% 3.9%;
+
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 240 10% 3.9%;
+ --foreground: 0 0% 98%;
+
+ --card: 240 10% 3.9%;
+ --card-foreground: 0 0% 98%;
+
+ --popover: 240 10% 3.9%;
+ --popover-foreground: 0 0% 98%;
+
+ --primary: 0 0% 98%;
+ --primary-foreground: 240 5.9% 10%;
+
+ --secondary: 240 3.7% 15.9%;
+ --secondary-foreground: 0 0% 98%;
+
+ --muted: 240 3.7% 15.9%;
+ --muted-foreground: 240 5% 64.9%;
+
+ --accent: 240 3.7% 15.9%;
+ --accent-foreground: 0 0% 98%;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+
+ --border: 240 3.7% 15.9%;
+ --input: 240 3.7% 15.9%;
+ --ring: 240 4.9% 83.9%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+@media (max-width: 640px) {
+ .container {
+ @apply px-4;
+ }
+}
+
+/**
+ * Spinner from Geist: https://vercel.com/geist/spinner
+ */
+.spinner_spinner__fqUfx,
+.spinner_wrapper__zbFtL {
+ height: var(--spinner-size, 20px);
+ width: var(--spinner-size, 20px);
+}
+
+.spinner_spinner__fqUfx {
+ position: relative;
+ top: 50%;
+ left: 50%;
+}
+
+.spinner_bar__VysK5 {
+ animation: spinner_spin__7lZMA 1.2s linear infinite;
+ background: var(--spinner-color, var(--muted-foreground));
+ border-radius: var(--radius);
+ height: 8%;
+ left: -10%;
+ position: absolute;
+ top: -3.9%;
+ width: 24%;
+}
+
+.spinner_bar__VysK5:first-child {
+ animation-delay: -1.2s;
+ transform: rotate(0.0001deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(2) {
+ animation-delay: -1.1s;
+ transform: rotate(30deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(3) {
+ animation-delay: -1s;
+ transform: rotate(60deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(4) {
+ animation-delay: -0.9s;
+ transform: rotate(90deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(5) {
+ animation-delay: -0.8s;
+ transform: rotate(120deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(6) {
+ animation-delay: -0.7s;
+ transform: rotate(150deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(7) {
+ animation-delay: -0.6s;
+ transform: rotate(180deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(8) {
+ animation-delay: -0.5s;
+ transform: rotate(210deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(9) {
+ animation-delay: -0.4s;
+ transform: rotate(240deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(10) {
+ animation-delay: -0.3s;
+ transform: rotate(270deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(11) {
+ animation-delay: -0.2s;
+ transform: rotate(300deg) translate(146%);
+}
+
+.spinner_bar__VysK5:nth-child(12) {
+ animation-delay: -0.1s;
+ transform: rotate(330deg) translate(146%);
+}
+
+@keyframes spinner_spin__7lZMA {
+ 0% {
+ opacity: 1;
+ }
+
+ to {
+ opacity: 0.15;
+ }
+}
diff --git a/playground/src/app/layout.tsx b/playground/src/app/layout.tsx
new file mode 100644
index 0000000000..6318c813c1
--- /dev/null
+++ b/playground/src/app/layout.tsx
@@ -0,0 +1,48 @@
+import { Inter } from "next/font/google";
+
+import "./globals.css";
+
+import Link from "next/link";
+import { Toaster } from "sonner";
+import { twMerge } from "tailwind-merge";
+
+import { NextSSRPlugin } from "@uploadthing/react/next-ssr-plugin";
+import { extractRouterConfig } from "uploadthing/server";
+
+import { buttonVariants } from "~/components/ui/button";
+import { uploadRouter } from "~/uploadthing/server";
+
+const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/playground/src/app/page.tsx b/playground/src/app/page.tsx
new file mode 100644
index 0000000000..4f6643484f
--- /dev/null
+++ b/playground/src/app/page.tsx
@@ -0,0 +1,3 @@
+export default function Home() {
+ return Select a demo
;
+}
diff --git a/playground/src/app/rhf-builtin/page.tsx b/playground/src/app/rhf-builtin/page.tsx
new file mode 100644
index 0000000000..e77cb6b8e9
--- /dev/null
+++ b/playground/src/app/rhf-builtin/page.tsx
@@ -0,0 +1,5 @@
+import { SimpleRHFDemo } from "./rhf";
+
+export default function DemoPage() {
+ return ;
+}
diff --git a/playground/src/app/rhf-builtin/rhf.tsx b/playground/src/app/rhf-builtin/rhf.tsx
new file mode 100644
index 0000000000..5bfbb587aa
--- /dev/null
+++ b/playground/src/app/rhf-builtin/rhf.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import { toast } from "sonner";
+import { z } from "zod";
+
+import { Button } from "~/components/ui/button";
+import {
+ Form,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ useForm,
+} from "~/components/ui/form";
+import { UploadDropzone } from "~/uploadthing/client";
+import { fileWithStateValidator } from "~/utils";
+
+/**
+ * A demo using RHF with built-in components from UT
+ */
+export const SimpleRHFDemo = () => {
+ const form = useForm({
+ schema: z.object({
+ images: fileWithStateValidator.array(),
+ }),
+ defaultValues: {
+ images: [],
+ },
+ mode: "onChange",
+ });
+
+ const onSubmit = form.handleSubmit((data) => {
+ toast(
+
+ {JSON.stringify(data, null, 4)}
+
,
+ );
+ form.reset();
+ });
+
+ return (
+
+
+
+
+ Form: {JSON.stringify(form.watch(), null, 4)}
+
+
+ Errors: {JSON.stringify(form.formState.errors, null, 4)}
+
+
+ );
+};
diff --git a/playground/src/app/rhf-custom/page.tsx b/playground/src/app/rhf-custom/page.tsx
new file mode 100644
index 0000000000..e77cb6b8e9
--- /dev/null
+++ b/playground/src/app/rhf-custom/page.tsx
@@ -0,0 +1,5 @@
+import { SimpleRHFDemo } from "./rhf";
+
+export default function DemoPage() {
+ return ;
+}
diff --git a/playground/src/app/rhf-custom/rhf.tsx b/playground/src/app/rhf-custom/rhf.tsx
new file mode 100644
index 0000000000..9d07fe9a92
--- /dev/null
+++ b/playground/src/app/rhf-custom/rhf.tsx
@@ -0,0 +1,134 @@
+"use client";
+
+import { UploadIcon } from "@radix-ui/react-icons";
+import { toast } from "sonner";
+import { twMerge } from "tailwind-merge";
+import { z } from "zod";
+
+import { useDropzone } from "@uploadthing/react";
+import { FileWithState } from "uploadthing/types";
+
+import { Button } from "~/components/ui/button";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ useForm,
+} from "~/components/ui/form";
+import { useUploadThing } from "~/uploadthing/client";
+import { fileWithStateValidator } from "~/utils";
+
+const MyDropzone = (props: {
+ files: FileWithState[];
+ onFilesChange: (files: FileWithState[]) => void;
+}) => {
+ const { routeConfig, startUpload } = useUploadThing("imageUploader", {
+ skipPolling: true,
+ files: props.files,
+ onFilesChange: props.onFilesChange,
+ });
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
+ routeConfig,
+ onDrop: (files) => {
+ const updatedFiles = [...props.files, ...files];
+ props.onFilesChange(updatedFiles);
+ startUpload(updatedFiles);
+ },
+ });
+
+ return (
+
+
+
+
+
+
+
+
+ Drag {`'n'`} drop files here, or click to select files
+
+
+ You can upload: {Object.keys(routeConfig ?? {}).join(", ")}
+
+
+
+
+ );
+};
+
+/**
+ * A demo using RHF with a custom dropzone components
+ */
+export const SimpleRHFDemo = () => {
+ const form = useForm({
+ schema: z.object({
+ images: fileWithStateValidator.array(),
+ }),
+ defaultValues: {
+ images: [],
+ },
+ mode: "onChange",
+ });
+
+ const onSubmit = form.handleSubmit((data) => {
+ toast(
+
+ {JSON.stringify(data, null, 4)}
+
,
+ );
+ form.reset();
+ });
+
+ return (
+
+
+
+
+ Form: {JSON.stringify(form.watch(), null, 4)}
+
+
+ formstate: {JSON.stringify(form.formState, null, 4)}
+
+
+ );
+};
diff --git a/playground/src/components/file-preview.tsx b/playground/src/components/file-preview.tsx
new file mode 100644
index 0000000000..989921df2f
--- /dev/null
+++ b/playground/src/components/file-preview.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import React from "react";
+import Image from "next/image";
+import { ImageIcon } from "@radix-ui/react-icons";
+import { toast } from "sonner";
+import { twMerge } from "tailwind-merge";
+
+import { Card, CardDescription, CardTitle } from "./ui/card";
+
+interface EmptyCardProps extends React.ComponentPropsWithoutRef {
+ title: string;
+ description?: string;
+ Icon?: React.ComponentType<{ className?: string }>;
+}
+
+export const EmptyCard = ({
+ title,
+ description,
+ Icon = ImageIcon,
+ className,
+ ...props
+}: EmptyCardProps) => {
+ return (
+
+
+
+
+
+ {title}
+ {description ? {description} : null}
+
+
+ );
+};
+
+export const FilePreview = ({
+ file,
+}: {
+ file: { key: string; name: string };
+}) => {
+ const [errored, setErrored] = React.useState(false);
+
+ const ext = file.name.split(".")!.pop()!;
+ if (errored || !["png", "jpg", "jpeg", "gif"].includes(ext)) {
+ const Icon = ImageIcon; // TODO: Dynamic icon
+
+ return (
+
+
+ {file.name}
+ {errored && Failed to load file}
+
+ );
+ }
+
+ return (
+
+ {
+ setErrored(true);
+ toast.error(`Failed to load file ${file.name}`);
+ }}
+ />
+
+ );
+};
diff --git a/playground/src/components/file-uploader.tsx b/playground/src/components/file-uploader.tsx
new file mode 100644
index 0000000000..1a446f65c2
--- /dev/null
+++ b/playground/src/components/file-uploader.tsx
@@ -0,0 +1,194 @@
+import "client-only";
+
+import * as React from "react";
+import Image from "next/image";
+import { Cross2Icon, UploadIcon } from "@radix-ui/react-icons";
+import { twMerge } from "tailwind-merge";
+
+import { useDropzone } from "@uploadthing/react";
+import { bytesToFileSize } from "uploadthing/client";
+import { FileWithState } from "uploadthing/types";
+
+import { Button } from "~/components/ui/button";
+import { Progress } from "~/components/ui/progress";
+import { useUploadThing } from "~/uploadthing/client";
+import { LoadingSpinner } from "./ui/loading";
+
+interface FileUploaderProps extends React.HTMLAttributes {
+ files: FileWithState[];
+ onFilesChange: (files: FileWithState[]) => void;
+}
+
+export function FileUploader({
+ files,
+ onFilesChange,
+ className,
+ ...dropzoneProps
+}: FileUploaderProps) {
+ const [progresses, setProgresses] = React.useState(new Map());
+ const { routeConfig, startUpload } = useUploadThing("imageUploader", {
+ files,
+ onFilesChange,
+ skipPolling: true,
+ onUploadError: (e) => console.error(e),
+ onUploadProgress: (_, e) => {
+ if (!e) return;
+ setProgresses((p) => new Map(p).set(e?.file, e?.progress));
+ },
+ });
+
+ const onDrop = React.useCallback(
+ (acceptedFiles: FileWithState[]) => {
+ const newFiles = acceptedFiles.map((file) =>
+ Object.assign(file, {
+ preview: URL.createObjectURL(file),
+ }),
+ );
+
+ const updatedFiles = files ? [...files, ...newFiles] : newFiles;
+ onFilesChange(updatedFiles);
+ void startUpload(updatedFiles);
+ },
+ [files],
+ );
+
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
+ onDrop,
+ routeConfig,
+ });
+
+ // Revoke preview url when component unmounts
+ React.useEffect(() => {
+ return () => {
+ if (!files) return;
+ files.forEach((file) => {
+ if (isFileWithPreview(file)) {
+ URL.revokeObjectURL(file.preview);
+ }
+ });
+ };
+ }, []);
+
+ return (
+
+
+
+ {isDragActive ? (
+
+
+
+
+
+ Drop the files here
+
+
+ ) : (
+
+
+
+
+
+
+ Drag {`'n'`} drop files here, or click to select files
+
+
You can upload
+
+
+ )}
+
+
+ {files?.length ? (
+
+ {files?.map((file, index) => (
+ {
+ if (!files) return;
+ const newFiles = files.filter((_, i) => i !== index);
+ onFilesChange?.(newFiles);
+ }}
+ progress={progresses.get(file.name)}
+ />
+ ))}
+
+ ) : null}
+
+ );
+}
+
+interface FileCardProps {
+ file: FileWithState;
+ onRemove: () => void;
+ progress?: number;
+}
+
+function FileCard({ file, progress = 0, onRemove }: FileCardProps) {
+ return (
+
+
+ {isFileWithPreview(file) ? (
+
+ ) : null}
+
+
+
+
+ {file.name}
+
+
+ {bytesToFileSize(file.size)}
+
+
+ {file.status === "uploading" && (
+
+ {progress < 100 ? : null}
+ {progress}%
+
+ )}
+
+
+
+
+ {file.status === "pending" && (
+
+ )}
+
+ );
+}
+
+function isFileWithPreview(file: File): file is File & { preview: string } {
+ return "preview" in file && typeof file.preview === "string";
+}
diff --git a/playground/src/components/load-more.tsx b/playground/src/components/load-more.tsx
new file mode 100644
index 0000000000..5f8c7434b9
--- /dev/null
+++ b/playground/src/components/load-more.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+/**
+ * Shamlessly stolen from Gabriel
+ * @see https://github.com/gabrielelpidio/next-infinite-scroll-server-actions/blob/main/src/components/loadMore.tsx
+ */
+import * as React from "react";
+import { twMerge } from "tailwind-merge";
+
+import { LoadingDots } from "./ui/loading";
+
+type LoadMoreAction = (
+ offset: number,
+) => Promise;
+
+const LoadMore = ({
+ children,
+ initialOffset,
+ loadMoreAction,
+}: React.PropsWithChildren<{
+ initialOffset: number;
+ loadMoreAction: LoadMoreAction;
+}>) => {
+ const ref = React.useRef(null);
+ const [elements, setElements] = React.useState([] as React.ReactNode[]);
+
+ const currentOffset = React.useRef(initialOffset);
+ const [loading, setLoading] = React.useState(false);
+
+ const loadMore = React.useCallback(
+ async (abortController?: AbortController) => {
+ if (currentOffset.current === null) return;
+
+ setLoading(true);
+ loadMoreAction(currentOffset.current)
+ .then(([node, next]) => {
+ if (abortController?.signal.aborted) return;
+
+ setElements((prev) => [...prev, node]);
+ currentOffset.current = next;
+ })
+ .catch(() => {})
+ .finally(() => setLoading(false));
+ },
+ [loadMoreAction],
+ );
+
+ React.useEffect(() => {
+ const signal = new AbortController();
+
+ const element = ref.current;
+
+ const observer = new IntersectionObserver(([entry]) => {
+ if (entry?.isIntersecting && !loading) {
+ loadMore(signal);
+ }
+ });
+
+ if (element) {
+ observer.observe(element);
+ }
+
+ return () => {
+ signal.abort();
+ if (element) {
+ observer.unobserve(element);
+ }
+ };
+ }, [loadMore]);
+
+ return (
+ <>
+
+ {children}
+ {elements}
+
+
+ {currentOffset.current === null && (
+ No more files
+ )}
+ {loading && }
+
+ >
+ );
+};
+
+export { LoadMore };
diff --git a/playground/src/components/ui/button.tsx b/playground/src/components/ui/button.tsx
new file mode 100644
index 0000000000..3865f6830b
--- /dev/null
+++ b/playground/src/components/ui/button.tsx
@@ -0,0 +1,56 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+import { twMerge } from "tailwind-merge";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "size-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ return (
+
+ );
+ },
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/playground/src/components/ui/card.tsx b/playground/src/components/ui/card.tsx
new file mode 100644
index 0000000000..03de6dc9f1
--- /dev/null
+++ b/playground/src/components/ui/card.tsx
@@ -0,0 +1,82 @@
+import * as React from "react";
+import { twMerge } from "tailwind-merge";
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+Card.displayName = "Card";
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardHeader.displayName = "CardHeader";
+
+const CardTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardTitle.displayName = "CardTitle";
+
+const CardDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardDescription.displayName = "CardDescription";
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardContent.displayName = "CardContent";
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+CardFooter.displayName = "CardFooter";
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardDescription,
+ CardContent,
+};
diff --git a/playground/src/components/ui/form.tsx b/playground/src/components/ui/form.tsx
new file mode 100644
index 0000000000..ed68e4c3bc
--- /dev/null
+++ b/playground/src/components/ui/form.tsx
@@ -0,0 +1,197 @@
+import * as React from "react";
+import { zodResolver } from "@hookform/resolvers/zod";
+import type * as LabelPrimitive from "@radix-ui/react-label";
+import { Slot } from "@radix-ui/react-slot";
+import {
+ Controller,
+ FormProvider,
+ useFormContext,
+ useForm as useFormHook,
+ UseFormProps,
+ type ControllerProps,
+ type FieldPath,
+ type FieldValues,
+} from "react-hook-form";
+import { twMerge } from "tailwind-merge";
+import { ZodType } from "zod";
+
+import { Label } from "~/components/ui/label";
+
+export const useForm = (
+ props: Omit, "resolver"> & {
+ schema: TSchema;
+ },
+) => {
+ const form = useFormHook({
+ ...props,
+ resolver: zodResolver(props.schema, undefined),
+ });
+
+ return form;
+};
+
+const Form = FormProvider;
+
+type FormFieldContextValue<
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath,
+> = {
+ name: TName;
+};
+
+const FormFieldContext = React.createContext(
+ {} as FormFieldContextValue,
+);
+
+const FormField = <
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath,
+>({
+ ...props
+}: ControllerProps) => {
+ return (
+
+
+
+ );
+};
+
+const useFormField = () => {
+ const fieldContext = React.useContext(FormFieldContext);
+ const itemContext = React.useContext(FormItemContext);
+ const { getFieldState, formState } = useFormContext();
+
+ const fieldState = getFieldState(fieldContext.name, formState);
+
+ if (!fieldContext) {
+ throw new Error("useFormField should be used within ");
+ }
+
+ const { id } = itemContext;
+
+ return {
+ id,
+ name: fieldContext.name,
+ formItemId: `${id}-form-item`,
+ formDescriptionId: `${id}-form-item-description`,
+ formMessageId: `${id}-form-item-message`,
+ ...fieldState,
+ };
+};
+
+type FormItemContextValue = {
+ id: string;
+};
+
+const FormItemContext = React.createContext(
+ {} as FormItemContextValue,
+);
+
+const FormItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const id = React.useId();
+
+ return (
+
+
+
+ );
+});
+FormItem.displayName = "FormItem";
+
+const FormLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ const { error, formItemId } = useFormField();
+
+ return (
+
+ );
+});
+FormLabel.displayName = "FormLabel";
+
+const FormControl = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ ...props }, ref) => {
+ const { error, formItemId, formDescriptionId, formMessageId } =
+ useFormField();
+
+ return (
+
+ );
+});
+FormControl.displayName = "FormControl";
+
+const FormDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { formDescriptionId } = useFormField();
+
+ return (
+
+ );
+});
+FormDescription.displayName = "FormDescription";
+
+const FormMessage = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, children, ...props }, ref) => {
+ const { error, formMessageId } = useFormField();
+ const body = error ? String(error?.message) : children;
+
+ if (!body) {
+ return null;
+ }
+
+ return (
+
+ {body}
+
+ );
+});
+FormMessage.displayName = "FormMessage";
+
+export {
+ useFormField,
+ Form,
+ FormItem,
+ FormLabel,
+ FormControl,
+ FormDescription,
+ FormMessage,
+ FormField,
+};
diff --git a/playground/src/components/ui/input.tsx b/playground/src/components/ui/input.tsx
new file mode 100644
index 0000000000..a5af6a227f
--- /dev/null
+++ b/playground/src/components/ui/input.tsx
@@ -0,0 +1,24 @@
+import * as React from "react";
+import { twMerge } from "tailwind-merge";
+
+export interface InputProps
+ extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Input.displayName = "Input";
+
+export { Input };
diff --git a/playground/src/components/ui/label.tsx b/playground/src/components/ui/label.tsx
new file mode 100644
index 0000000000..a152146d40
--- /dev/null
+++ b/playground/src/components/ui/label.tsx
@@ -0,0 +1,25 @@
+"use client";
+
+import * as React from "react";
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { cva, type VariantProps } from "class-variance-authority";
+import { twMerge } from "tailwind-merge";
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
+);
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+));
+Label.displayName = LabelPrimitive.Root.displayName;
+
+export { Label };
diff --git a/playground/src/components/ui/loading.tsx b/playground/src/components/ui/loading.tsx
new file mode 100644
index 0000000000..089fd13718
--- /dev/null
+++ b/playground/src/components/ui/loading.tsx
@@ -0,0 +1,73 @@
+import type { ComponentProps } from "react";
+import { twMerge } from "tailwind-merge";
+
+export function LoadingDots(props: ComponentProps<"svg">) {
+ return (
+
+ );
+}
+
+export function LoadingSpinner(
+ props: ComponentProps<"div"> & { size?: number },
+) {
+ const { size = 30 } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/playground/src/components/ui/progress.tsx b/playground/src/components/ui/progress.tsx
new file mode 100644
index 0000000000..68c7f4a640
--- /dev/null
+++ b/playground/src/components/ui/progress.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import * as React from "react";
+import * as ProgressPrimitive from "@radix-ui/react-progress";
+import { twMerge } from "tailwind-merge";
+
+const Progress = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, value, ...props }, ref) => (
+
+
+
+));
+Progress.displayName = ProgressPrimitive.Root.displayName;
+
+export { Progress };
diff --git a/playground/src/uploadthing/client.ts b/playground/src/uploadthing/client.ts
new file mode 100644
index 0000000000..9286533dbf
--- /dev/null
+++ b/playground/src/uploadthing/client.ts
@@ -0,0 +1,10 @@
+import {
+ generateReactHelpers,
+ generateUploadDropzone,
+} from "@uploadthing/react";
+
+import type { UploadRouter } from "~/uploadthing/server";
+
+export const UploadDropzone = generateUploadDropzone();
+export const { useUploadThing, uploadFiles } =
+ generateReactHelpers();
diff --git a/playground/src/uploadthing/server.ts b/playground/src/uploadthing/server.ts
new file mode 100644
index 0000000000..a808047e6d
--- /dev/null
+++ b/playground/src/uploadthing/server.ts
@@ -0,0 +1,53 @@
+import { type NextRequest } from "next/server";
+
+import { createUploadthing, type FileRouter } from "uploadthing/next";
+import { UploadThingError, UTApi } from "uploadthing/server";
+
+export const utapi = new UTApi();
+
+const f = createUploadthing({
+ /**
+ * Log out more information about the error, but don't return it to the client
+ * @see https://docs.uploadthing.com/errors#error-formatting
+ */
+ errorFormatter: (err) => {
+ console.log("Error uploading file", err.message);
+ console.log(" - Above error caused by:", err.cause);
+
+ return { message: err.message };
+ },
+});
+
+// Fake auth function
+async function auth(req: NextRequest) {
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ return { id: "fakeId" };
+}
+
+// FileRouter for your app, can contain multiple FileRoutes
+export const uploadRouter = {
+ // Define as many FileRoutes as you like, each with a unique routeSlug
+ imageUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 8 } })
+ // Set permissions and file types for this FileRoute
+ .middleware(async ({ req }) => {
+ // This code runs on your server before upload
+ const user = await auth(req);
+
+ // If you throw, the user will not be able to upload
+ if (!user) throw new UploadThingError("Unauthorized");
+
+ // Whatever is returned here is accessible in onUploadComplete as `metadata`
+ return { userId: user.id };
+ })
+ .onUploadComplete(async ({ metadata, file }) => {
+ // This code RUNS ON YOUR SERVER after upload
+ console.log("Upload complete for userId:", metadata.userId);
+
+ console.log("file url", file.url);
+
+ // !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
+ return { uploadedBy: metadata.userId };
+ }),
+} satisfies FileRouter;
+
+export type UploadRouter = typeof uploadRouter;
diff --git a/playground/src/utils.ts b/playground/src/utils.ts
new file mode 100644
index 0000000000..fef53dd847
--- /dev/null
+++ b/playground/src/utils.ts
@@ -0,0 +1,8 @@
+import { z } from "zod";
+
+import { FileWithState } from "uploadthing/types";
+
+export const fileWithStateValidator = z.custom((value) => {
+ if (!(value instanceof File)) return false;
+ return "status" in value && value.status === "uploaded";
+}, "File must be uploaded");
diff --git a/playground/tailwind.config.ts b/playground/tailwind.config.ts
new file mode 100644
index 0000000000..eb4185d8a1
--- /dev/null
+++ b/playground/tailwind.config.ts
@@ -0,0 +1,77 @@
+import animate from "tailwindcss-animate";
+import { fontFamily } from "tailwindcss/defaultTheme";
+
+import { withUt } from "uploadthing/tw";
+
+export default withUt({
+ content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
+ theme: {
+ container: {
+ center: true,
+ padding: "2rem",
+ screens: {
+ "2xl": "1400px",
+ },
+ },
+ extend: {
+ fontFamily: {
+ sans: ["var(--font-sans)", ...fontFamily.sans],
+ },
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: "0" },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: "0" },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ },
+ },
+ },
+ plugins: [animate],
+});
diff --git a/playground/tsconfig.json b/playground/tsconfig.json
new file mode 100644
index 0000000000..7355a4122c
--- /dev/null
+++ b/playground/tsconfig.json
@@ -0,0 +1,37 @@
+{
+ "compilerOptions": {
+ /* Base Options: */
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
+ "moduleDetection": "force",
+ "isolatedModules": true,
+
+ /* Strictness */
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "checkJs": true,
+
+ /* Bundled projects */
+ "lib": ["dom", "dom.iterable", "ES2022"],
+ "noEmit": true,
+ "module": "Preserve",
+ "jsx": "preserve",
+ "plugins": [{ "name": "next" }],
+ "incremental": true,
+
+ /* Path Aliases */
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ "*.js",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index aba07d6414..05e3b42732 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1083,6 +1083,12 @@ importers:
packages/dropzone:
dependencies:
+ '@uploadthing/shared':
+ specifier: workspace:*
+ version: link:../shared
+ effect:
+ specifier: 3.4.2
+ version: 3.4.2
file-selector:
specifier: ^0.6.0
version: 0.6.0
@@ -1558,6 +1564,97 @@ importers:
specifier: ^7.2.0
version: 7.2.0
+ playground:
+ dependencies:
+ '@hookform/resolvers':
+ specifier: ^3.3.4
+ version: 3.6.0(react-hook-form@7.52.0(react@18.3.1))
+ '@radix-ui/react-dialog':
+ specifier: ^1.0.5
+ version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-dropdown-menu':
+ specifier: ^2.0.6
+ version: 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-icons':
+ specifier: ^1.3.0
+ version: 1.3.0(react@18.3.1)
+ '@radix-ui/react-label':
+ specifier: ^2.0.2
+ version: 2.0.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-progress':
+ specifier: ^1.0.3
+ version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-scroll-area':
+ specifier: ^1.0.5
+ version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot':
+ specifier: ^1.0.2
+ version: 1.0.2(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-tabs':
+ specifier: ^1.0.4
+ version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-tooltip':
+ specifier: ^1.0.7
+ version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@uploadthing/react':
+ specifier: 6.6.0
+ version: link:../packages/react
+ class-variance-authority:
+ specifier: ^0.7.0
+ version: 0.7.0
+ client-only:
+ specifier: ^0.0.1
+ version: 0.0.1
+ next:
+ specifier: 14.2.3
+ version: 14.2.3(@babel/core@7.24.4)(@playwright/test@1.45.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react:
+ specifier: 18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: 18.3.1
+ version: 18.3.1(react@18.3.1)
+ react-hook-form:
+ specifier: ^7.51.2
+ version: 7.52.0(react@18.3.1)
+ server-only:
+ specifier: ^0.0.1
+ version: 0.0.1
+ sonner:
+ specifier: ^1.4.41
+ version: 1.4.41(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ tailwind-merge:
+ specifier: ^2.2.1
+ version: 2.3.0
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.3)
+ uploadthing:
+ specifier: 6.12.0
+ version: link:../packages/uploadthing
+ zod:
+ specifier: ^3.23.8
+ version: 3.23.8
+ devDependencies:
+ '@types/node':
+ specifier: ^20.11.21
+ version: 20.14.0
+ '@types/react':
+ specifier: 18.3.3
+ version: 18.3.3
+ '@types/react-dom':
+ specifier: 18.3.0
+ version: 18.3.0
+ postcss:
+ specifier: 8.4.38
+ version: 8.4.38
+ tailwindcss:
+ specifier: ^3.4.1
+ version: 3.4.3
+ typescript:
+ specifier: ^5.4.5
+ version: 5.5.2
+
tooling/eslint-config:
dependencies:
'@next/eslint-plugin-next':
@@ -3629,6 +3726,11 @@ packages:
hono: '>=3.9.0'
zod: ^3.19.1
+ '@hookform/resolvers@3.6.0':
+ resolution: {integrity: sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==}
+ peerDependencies:
+ react-hook-form: ^7.0.0
+
'@humanwhocodes/config-array@0.11.14':
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
engines: {node: '>=10.10.0'}
@@ -4364,12 +4466,18 @@ packages:
peerDependencies:
prettier: '*'
+ '@radix-ui/number@1.1.0':
+ resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
+
'@radix-ui/primitive@1.0.0':
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
'@radix-ui/primitive@1.0.1':
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
+ '@radix-ui/primitive@1.1.0':
+ resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==}
+
'@radix-ui/react-accordion@1.1.2':
resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==}
peerDependencies:
@@ -4396,6 +4504,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-arrow@1.1.0':
+ resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-collapsible@1.0.3':
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
peerDependencies:
@@ -4422,6 +4543,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-collection@1.1.0':
+ resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-compose-refs@1.0.0':
resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==}
peerDependencies:
@@ -4436,6 +4570,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-compose-refs@1.1.0':
+ resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-context@1.0.0':
resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==}
peerDependencies:
@@ -4450,6 +4593,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-context@1.1.0':
+ resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-dialog@1.0.0':
resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==}
peerDependencies:
@@ -4478,6 +4630,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-direction@1.1.0':
+ resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-dismissable-layer@1.0.0':
resolution: {integrity: sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==}
peerDependencies:
@@ -4497,6 +4658,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-dismissable-layer@1.1.0':
+ resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-dropdown-menu@2.0.6':
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
peerDependencies:
@@ -4543,6 +4717,11 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-icons@1.3.0':
+ resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==}
+ peerDependencies:
+ react: ^16.x || ^17.x || ^18.x
+
'@radix-ui/react-id@1.0.0':
resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==}
peerDependencies:
@@ -4557,6 +4736,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-id@1.1.0':
+ resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-label@2.0.2':
resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==}
peerDependencies:
@@ -4609,6 +4797,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-popper@1.2.0':
+ resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-portal@1.0.0':
resolution: {integrity: sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==}
peerDependencies:
@@ -4628,6 +4829,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-portal@1.1.1':
+ resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-presence@1.0.0':
resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==}
peerDependencies:
@@ -4647,6 +4861,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-presence@1.1.0':
+ resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-primitive@1.0.0':
resolution: {integrity: sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==}
peerDependencies:
@@ -4666,6 +4893,32 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-primitive@2.0.0':
+ resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-progress@1.1.0':
+ resolution: {integrity: sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-roving-focus@1.0.4':
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
@@ -4679,6 +4932,32 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-roving-focus@1.1.0':
+ resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-scroll-area@1.1.0':
+ resolution: {integrity: sha512-9ArIZ9HWhsrfqS765h+GZuLoxaRHD/j0ZWOWilsCvYTpYJp8XwCqNG7Dt9Nu/TItKOdgLGkOPCodQvDc+UMwYg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-slot@1.0.0':
resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==}
peerDependencies:
@@ -4698,6 +4977,41 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-slot@1.1.0':
+ resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.0':
+ resolution: {integrity: sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tooltip@1.1.1':
+ resolution: {integrity: sha512-LLE8nzNE4MzPMw3O2zlVlkLFid3y9hMUs7uCbSHyKSo+tCN4yMCf+ZCCcfrYgsOC0TiHBPQ1mtpJ2liY3ZT3SQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-use-callback-ref@1.0.0':
resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==}
peerDependencies:
@@ -4712,6 +5026,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-callback-ref@1.1.0':
+ resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-controllable-state@1.0.0':
resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==}
peerDependencies:
@@ -4726,6 +5049,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-controllable-state@1.1.0':
+ resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-escape-keydown@1.0.0':
resolution: {integrity: sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==}
peerDependencies:
@@ -4740,6 +5072,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-escape-keydown@1.1.0':
+ resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-layout-effect@1.0.0':
resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==}
peerDependencies:
@@ -4754,6 +5095,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-layout-effect@1.1.0':
+ resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-rect@1.0.1':
resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
peerDependencies:
@@ -4763,6 +5113,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-rect@1.1.0':
+ resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-size@1.0.1':
resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
peerDependencies:
@@ -4772,9 +5131,34 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-size@1.1.0':
+ resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-visually-hidden@1.1.0':
+ resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/rect@1.0.1':
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
+ '@radix-ui/rect@1.1.0':
+ resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
+
'@react-native-community/cli-clean@13.6.6':
resolution: {integrity: sha512-cBwJTwl0NyeA4nyMxbhkWZhxtILYkbU3TW3k8AXLg+iGphe0zikYMGB3T+haTvTc6alTyEFwPbimk9bGIqkjAQ==}
@@ -12797,6 +13181,12 @@ packages:
peerDependencies:
react: '>=17.0.0'
+ react-hook-form@7.52.0:
+ resolution: {integrity: sha512-mJX506Xc6mirzLsmXUJyqlAI3Kj9Ph2RhplYhUVffeOQSnubK2uVqBFOBJmvKikvbFV91pxVXmDiR+QMF19x6A==}
+ engines: {node: '>=12.22.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+
react-image-crop@11.0.5:
resolution: {integrity: sha512-A/Y/kspOzki1zDL/bSgwWIY1X3CQ9F1QwpdnncWLBVAktnKfAZDIQnWmjXzuzEjZHDMsBlArytIcPBVi6DNklg==}
peerDependencies:
@@ -17771,6 +18161,10 @@ snapshots:
hono: 4.2.6
zod: 3.23.8
+ '@hookform/resolvers@3.6.0(react-hook-form@7.52.0(react@18.3.1))':
+ dependencies:
+ react-hook-form: 7.52.0(react@18.3.1)
+
'@humanwhocodes/config-array@0.11.14':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
@@ -19115,6 +19509,8 @@ snapshots:
make-synchronized: 0.2.9
prettier: 3.3.2
+ '@radix-ui/number@1.1.0': {}
+
'@radix-ui/primitive@1.0.0':
dependencies:
'@babel/runtime': 7.24.4
@@ -19123,6 +19519,8 @@ snapshots:
dependencies:
'@babel/runtime': 7.24.4
+ '@radix-ui/primitive@1.1.0': {}
+
'@radix-ui/react-accordion@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19151,6 +19549,15 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19181,6 +19588,18 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-compose-refs@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19193,6 +19612,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-context@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19205,6 +19630,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-context@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-dialog@1.0.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19257,6 +19688,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-direction@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19282,6 +19719,19 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-dismissable-layer@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19331,6 +19781,10 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-icons@1.3.0(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+
'@radix-ui/react-id@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19345,6 +19799,13 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-id@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-label@2.0.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19425,6 +19886,24 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-popper@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@floating-ui/react-dom': 2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-arrow': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/rect': 1.1.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-portal@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19442,6 +19921,16 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-portal@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-presence@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19461,6 +19950,16 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-presence@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-primitive@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19478,6 +19977,25 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
+ '@radix-ui/react-progress@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19496,6 +20014,40 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
+ '@radix-ui/react-scroll-area@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-slot@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19516,6 +20068,49 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-slot@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
+ '@radix-ui/react-tabs@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
+ '@radix-ui/react-tooltip@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-use-callback-ref@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19528,6 +20123,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-use-controllable-state@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19542,6 +20143,13 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-use-escape-keydown@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19556,6 +20164,13 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-use-layout-effect@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19568,6 +20183,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-use-rect@1.0.1(@types/react@18.3.3)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19576,6 +20197,13 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/rect': 1.1.0
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-use-size@1.0.1(@types/react@18.3.3)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.4
@@ -19584,10 +20212,28 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-use-size@1.1.0(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
+ '@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/rect@1.0.1':
dependencies:
'@babel/runtime': 7.24.4
+ '@radix-ui/rect@1.1.0': {}
+
'@react-native-community/cli-clean@13.6.6(encoding@0.1.13)':
dependencies:
'@react-native-community/cli-tools': 13.6.6(encoding@0.1.13)
@@ -30016,6 +30662,10 @@ snapshots:
dependencies:
react: 18.3.1
+ react-hook-form@7.52.0(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+
react-image-crop@11.0.5(react@18.3.1):
dependencies:
react: 18.3.1
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 8b39f4aa09..9601be7f5a 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -5,3 +5,4 @@ packages:
- "packages/*"
- "!packages/raycast-extension"
- "tooling/*"
+ - "playground"