Loading…
Creator · Aceternity UI
View original source ↗
Prompt
// components/ui/file-upload.tsx
"use client";
import { cn } from "@/lib/utils";
import React, { useRef, useState } from "react";
import { motion } from "motion/react";
import { IconUpload } from "@tabler/icons-react";
import { useDropzone } from "react-dropzone";
const mainVariant = {
initial: {
x: 0,
y: 0,
},
animate: {
x: 20,
y: -20,
opacity: 0.9,
},
};
const secondaryVariant = {
initial: {
opacity: 0,
},
animate: {
opacity: 1,
},
};
export const FileUpload = ({
onChange,
}: {
onChange?: (files: File[]) => void;
}) => {
const [files, setFiles] = useState<File[]>([]);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFileChange = (newFiles: File[]) => {
setFiles((prevFiles) => [...prevFiles, ...newFiles]);
onChange && onChange(newFiles);
};
const handleClick = () => {
fileInputRef.current?.click();
};
const { getRootProps, isDragActive } = useDropzone({
multiple: false,
noClick: true,
onDrop: handleFileChange,
onDropRejected: (error) => {
console.log(error);
},
});
return (
<div className="w-full" {...getRootProps()}>
<motion.div
onClick={handleClick}
whileHover="animate"
className="group/file relative block w-full cursor-pointer overflow-hidden rounded-lg p-10"
>
<input
ref={fileInputRef}
id="file-upload-handle"
type="file"
onChange={(e) => handleFileChange(Array.from(e.target.files || []))}
className="hidden"
/>
<div className="absolute inset-0 [mask-image:radial-gradient(ellipse_at_center,white,transparent)]">
<GridPattern />
</div>
<div className="flex flex-col items-center justify-center">
<p className="relative z-20 font-sans text-base font-bold text-neutral-700 dark:text-neutral-300">
Upload file
</p>
<p className="relative z-20 mt-2 font-sans text-base font-normal text-neutral-400 dark:text-neutral-400">
Drag or drop your files here or click to upload
</p>
<div className="relative mx-auto mt-10 w-full max-w-xl">
{files.length > 0 &&
files.map((file, idx) => (
<motion.div
key={"file" + idx}
layoutId={idx === 0 ? "file-upload" : "file-upload-" + idx}
className={cn(
"relative z-40 mx-auto mt-4 flex w-full flex-col items-start justify-start overflow-hidden rounded-md bg-white p-4 md:h-24 dark:bg-neutral-900",
"shadow-sm",
)}
>
<div className="flex w-full items-center justify-between gap-4">
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
className="max-w-xs truncate text-base text-neutral-700 dark:text-neutral-300"
>
{file.name}
</motion.p>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
className="shadow-input w-fit shrink-0 rounded-lg px-2 py-1 text-sm text-neutral-600 dark:bg-neutral-800 dark:text-white"
>
{(file.size / (1024 * 1024)).toFixed(2)} MB
</motion.p>
</div>
<div className="mt-2 flex w-full flex-col items-start justify-between text-sm text-neutral-600 md:flex-row md:items-center dark:text-neutral-400">
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
className="rounded-md bg-gray-100 px-1 py-0.5 dark:bg-neutral-800"
>
{file.type}
</motion.p>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
>
modified{" "}
{new Date(file.lastModified).toLocaleDateString()}
</motion.p>
</div>
</motion.div>
))}
{!files.length && (
<motion.div
layoutId="file-upload"
variants={mainVariant}
transition={{
type: "spring",
stiffness: 300,
damping: 20,
}}
className={cn(
"relative z-40 mx-auto mt-4 flex h-32 w-full max-w-[8rem] items-center justify-center rounded-md bg-white group-hover/file:shadow-2xl dark:bg-neutral-900",
"shadow-[0px_10px_50px_rgba(0,0,0,0.1)]",
)}
>
{isDragActive ? (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="flex flex-col items-center text-neutral-600"
>
Drop it
<IconUpload className="h-4 w-4 text-neutral-600 dark:text-neutral-400" />
</motion.p>
) : (
<IconUpload className="h-4 w-4 text-neutral-600 dark:text-neutral-300" />
)}
</motion.div>
)}
{!files.length && (
<motion.div
variants={secondaryVariant}
className="absolute inset-0 z-30 mx-auto mt-4 flex h-32 w-full max-w-[8rem] items-center justify-center rounded-md border border-dashed border-sky-400 bg-transparent opacity-0"
></motion.div>
)}
</div>
</div>
</motion.div>
</div>
);
};
export function GridPattern() {
const columns = 41;
const rows = 11;
return (
<div className="flex shrink-0 scale-105 flex-wrap items-center justify-center gap-x-px gap-y-px bg-gray-100 dark:bg-neutral-900">
{Array.from({ length: rows }).map((_, row) =>
Array.from({ length: columns }).map((_, col) => {
const index = row * columns + col;
return (
<div
key={`${col}-${row}`}
className={`flex h-10 w-10 shrink-0 rounded-[2px] ${
index % 2 === 0
? "bg-gray-50 dark:bg-neutral-950"
: "bg-gray-50 shadow-[0px_0px_1px_3px_rgba(255,255,255,1)_inset] dark:bg-neutral-950 dark:shadow-[0px_0px_1px_3px_rgba(0,0,0,1)_inset]"
}`}
/>
);
}),
)}
</div>
);
}Models & stability
Recommended models
Keep exploring
A webpage case by @Oluwaphilemon1 using Claude Fable 5 · Three.js · Blender · GSAP, including the public result, full prompt, and original source.
Prompt
Claude Fable 5 and GPT-5.6 are powerful🥵🥵🥵 Here Is How I built with Claude 👇 Prompt: "Design an agency site where services are presented as supermarket product packaging in 3D" Use Three.js with WebGL to render al
复制这段 TSX 组件源码,交给 Cursor、v0、Claude 等 AI 编程工具,说明这是「3D Animated Pin」的 Aceternity UI 组件实现,即可让它接入你的 React 项目,再按需替换文案、配色与触发参数复用。
Prompt
// components/ui/3d-pin.tsx "use client"; import React, { useState } from "react"; import { motion } from "motion/react"; import { cn } from "@/lib/utils"; export const PinContainer = ({ children, title, href, className, containerClassName…

一个复杂的提示词,用于利用 Three.js 风格优化和逼真光照,在浏览器中生成精细的实时 3D 森林环境。
Prompt
First search for the "/unlazy" skill, read it fully, and use it throughout the entire task. Treat this as a major visual build lasting at least {argument name="minimum duration" d