feat: support static website

This commit is contained in:
Henry Li
2026-01-24 18:01:27 +08:00
parent 3ffce7667c
commit cd63f41b4c
36 changed files with 4889 additions and 92 deletions

View File

@@ -28,7 +28,7 @@ export function Hero({ className }: { className?: string }) {
/>
</div>
<FlickeringGrid
className="absolute inset-0 z-0 mask-[url(/images/deer.svg)] mask-size-[100vw] mask-center mask-no-repeat md:mask-size-[72vh]"
className="absolute inset-0 z-0 translate-y-8 mask-[url(/images/deer.svg)] mask-size-[100vw] mask-center mask-no-repeat md:mask-size-[72vh]"
squareSize={4}
gridGap={4}
color={"white"}

View File

@@ -9,10 +9,13 @@ import {
Sparkles,
Terminal,
Play,
Pause,
} from "lucide-react";
import { motion, AnimatePresence } from "motion/react";
import { useState, useEffect, useRef } from "react";
import { Tooltip } from "@/components/workspace/tooltip";
type AnimationPhase =
| "idle"
| "user-input"
@@ -69,13 +72,19 @@ export default function ProgressiveSkillsAnimation() {
const [hasAutoPlayed, setHasAutoPlayed] = useState(false);
const chatMessagesRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const timeoutsRef = useRef<NodeJS.Timeout[]>([]);
// Additional display duration after the final step (done) completes, used to show the final result
const FINAL_DISPLAY_DURATION = 3000; // milliseconds
// Play animation only when isPlaying is true
useEffect(() => {
if (!isPlaying) return;
if (!isPlaying) {
// Clear all timeouts when paused
timeoutsRef.current.forEach(clearTimeout);
timeoutsRef.current = [];
return;
}
const timeline = [
{ phase: "user-input" as const, delay: ANIMATION_DELAYS["user-input"] },
@@ -117,7 +126,12 @@ export default function ProgressiveSkillsAnimation() {
}, totalDelay + FINAL_DISPLAY_DURATION),
);
return () => timeouts.forEach(clearTimeout);
timeoutsRef.current = timeouts;
return () => {
timeouts.forEach(clearTimeout);
timeoutsRef.current = [];
};
}, [isPlaying]);
const handlePlay = () => {
@@ -130,6 +144,20 @@ export default function ProgressiveSkillsAnimation() {
setShowWorkspace(false);
};
const handleTogglePlayPause = () => {
if (isPlaying) {
setIsPlaying(false);
} else {
// If animation hasn't started or is at idle, restart from beginning
if (phase === "idle") {
handlePlay();
} else {
// Resume from current phase
setIsPlaying(true);
}
}
};
// Auto-play when component enters viewport for the first time
useEffect(() => {
if (hasAutoPlayed || !containerRef.current) return;
@@ -308,7 +336,7 @@ export default function ProgressiveSkillsAnimation() {
>
{/* Overlay and Play Button */}
<AnimatePresence>
{!isPlaying && (
{!isPlaying && !hasPlayed && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
@@ -330,13 +358,30 @@ export default function ProgressiveSkillsAnimation() {
/>
</div>
<span className="text-lg font-medium text-white">
{hasPlayed ? "Click to replay" : "Click to play"}
Click to play
</span>
</motion.button>
</motion.div>
)}
</AnimatePresence>
{/* Bottom Left Play/Pause Button */}
<Tooltip content="Play / Pause">
<motion.button
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
onClick={handleTogglePlayPause}
className="absolute bottom-8 left-8 z-40 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 backdrop-blur-md transition-all hover:scale-110 hover:bg-white/20 active:scale-95"
aria-label={isPlaying ? "暂停" : "播放"}
>
{isPlaying ? (
<Pause size={24} className="text-white" fill="white" />
) : (
<Play size={24} className="ml-0.5 text-white" fill="white" />
)}
</motion.button>
</Tooltip>
<div className="flex h-full max-h-[700px] w-full max-w-6xl gap-8">
{/* Left: File Tree */}
<div className="flex flex-1 flex-col">
@@ -588,7 +633,7 @@ export default function ProgressiveSkillsAnimation() {
className="flex items-center gap-2 text-sm text-green-500"
>
<FileText size={14} />
<span>{file}</span>
<span>Generating {file}...</span>
<Check size={14} />
</motion.div>
))}
@@ -617,7 +662,7 @@ export default function ProgressiveSkillsAnimation() {
className="flex items-center gap-2 pl-4 text-zinc-400"
>
<Terminal size={16} />
<span>Executing deploy.sh</span>
<span>Executing scripts/deploy.sh</span>
</motion.div>
)}
</div>

View File

@@ -12,11 +12,12 @@ export function SandboxSection({ className }: { className?: string }) {
return (
<Section
className={className}
title="Sandbox"
title="Agent Runtime Environment"
subtitle={
<p>
We gave DeerFlow a computer. It can execute code, manage files, and
run long tasks all in a secure Docker sandbox
We give DeerFlow a &quot;computer&quot;, which can execute commands,
manage files, and run long tasks all in a secure Docker-based
sandbox
</p>
}
>

View File

@@ -2,13 +2,13 @@
import { cn } from "@/lib/utils";
import ProgressiveSkillsAnimation from "../components/progressive-skills-animation";
import ProgressiveSkillsAnimation from "../progressive-skills-animation";
import { Section } from "../section";
export function SkillsSection({ className }: { className?: string }) {
return (
<Section
className={cn("h-[calc(100vh-64px)] w-full bg-white/7", className)}
className={cn("h-[calc(100vh-64px)] w-full bg-white/2", className)}
title="Skill-based Architecture"
subtitle={
<div>

View File

@@ -1,10 +1,57 @@
"use client";
import MagicBento from "@/components/ui/magic-bento";
import MagicBento, { type BentoCardProps } from "@/components/ui/magic-bento";
import { cn } from "@/lib/utils";
import { Section } from "../section";
const COLOR = "#0a0a0a";
const features: BentoCardProps[] = [
{
color: COLOR,
label: "Context Engineering",
title: "Long/Short-term Memory",
description: (
<div>
<div>Now the agent can better understand you</div>
<div className="text-muted-foreground">Coming soon</div>
</div>
),
},
{
color: COLOR,
label: "Long Task Running",
title: "Planning and Reasoning",
description: "Plans ahead, reasons through complexity, then acts",
},
{
color: COLOR,
label: "Extensible",
title: "Skills and Tools",
description:
"Plug, play, or even swap built-in tools. Build the agent you want.",
},
{
color: COLOR,
label: "Persistent",
title: "Sandbox with File System",
description: "Read, write, run — like a real computer",
},
{
color: COLOR,
label: "Flexible",
title: "Multi-Model Support",
description: "Doubao, DeepSeek, OpenAI, Gemini, etc.",
},
{
color: COLOR,
label: "Free",
title: "Open Source",
description: "MIT License, self-hosted, full control",
},
];
export function WhatsNewSection({ className }: { className?: string }) {
return (
<Section
@@ -13,7 +60,7 @@ export function WhatsNewSection({ className }: { className?: string }) {
subtitle="DeerFlow is now evolving from a Deep Research agent into a full-stack Super Agent"
>
<div className="flex w-full items-center justify-center">
<MagicBento />
<MagicBento data={features} />
</div>
</Section>
);