303 lines
14 KiB
TypeScript
303 lines
14 KiB
TypeScript
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|||
|
|
import { TerminalWindow } from './TerminalWindow';
|
|||
|
|
|
|||
|
|
export const MultiModelAnimation: React.FC = () => {
|
|||
|
|
const [stage, setStage] = useState(0);
|
|||
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|||
|
|
const [isVisible, setIsVisible] = useState(false);
|
|||
|
|
|
|||
|
|
// Intersection Observer
|
|||
|
|
useEffect(() => {
|
|||
|
|
const observer = new IntersectionObserver(
|
|||
|
|
([entry]) => {
|
|||
|
|
if (entry.isIntersecting) {
|
|||
|
|
setIsVisible(true);
|
|||
|
|
observer.disconnect();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{ threshold: 0.3 }
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (containerRef.current) {
|
|||
|
|
observer.observe(containerRef.current);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return () => observer.disconnect();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
// Animation Sequence
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (!isVisible) return;
|
|||
|
|
|
|||
|
|
const timeline = [
|
|||
|
|
{ s: 1, delay: 500 }, // Start typing command
|
|||
|
|
{ s: 2, delay: 1300 }, // Opus line
|
|||
|
|
{ s: 3, delay: 1900 }, // Sonnet line
|
|||
|
|
{ s: 4, delay: 2500 }, // Haiku line
|
|||
|
|
{ s: 5, delay: 3100 }, // Subagent line
|
|||
|
|
{ s: 6, delay: 3600 }, // Connected success
|
|||
|
|
{ s: 7, delay: 4200 }, // Draw lines
|
|||
|
|
{ s: 8, delay: 5000 }, // Msg 1
|
|||
|
|
{ s: 9, delay: 5500 }, // Msg 2
|
|||
|
|
{ s: 10, delay: 6000 }, // Msg 3
|
|||
|
|
{ s: 11, delay: 7000 }, // Tagline 1
|
|||
|
|
{ s: 12, delay: 7500 }, // Tagline 2
|
|||
|
|
{ s: 13, delay: 8200 }, // Tagline 3
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
let timeouts: ReturnType<typeof setTimeout>[] = [];
|
|||
|
|
|
|||
|
|
timeline.forEach(step => {
|
|||
|
|
timeouts.push(setTimeout(() => setStage(step.s), step.delay));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return () => timeouts.forEach(clearTimeout);
|
|||
|
|
}, [isVisible]);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div ref={containerRef} className="max-w-6xl mx-auto my-16 relative">
|
|||
|
|
{/* Background Ambience */}
|
|||
|
|
<div className={`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[120%] h-[120%] bg-claude-ish/5 blur-[120px] rounded-full transition-opacity duration-1000 pointer-events-none ${stage >= 6 ? 'opacity-100' : 'opacity-0'}`} />
|
|||
|
|
|
|||
|
|
<div className="bg-[#050505] rounded-3xl p-1 border border-white/5 shadow-2xl relative overflow-hidden flex flex-col gap-8">
|
|||
|
|
|
|||
|
|
{/* Terminal Section */}
|
|||
|
|
<div className="relative z-10 px-4 pt-4 md:px-12 md:pt-12">
|
|||
|
|
<TerminalWindow
|
|||
|
|
title="claudish — zsh — 120×24"
|
|||
|
|
className="w-full shadow-2xl border-white/10 min-h-[300px] bg-[#0c0c0c]"
|
|||
|
|
noPadding={false}
|
|||
|
|
>
|
|||
|
|
<div className="font-mono text-xs md:text-[13px] space-y-2.5 leading-relaxed text-gray-300">
|
|||
|
|
{/* Command */}
|
|||
|
|
<div className={`transition-opacity duration-300 flex items-center ${stage >= 1 ? 'opacity-100' : 'opacity-0'}`}>
|
|||
|
|
<span className="text-claude-ish mr-2 font-bold">➜</span>
|
|||
|
|
<span className="text-white font-semibold">claudish</span>
|
|||
|
|
<span className="text-gray-600 ml-2">\</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Flags */}
|
|||
|
|
<div className="flex flex-col gap-1.5 ml-1">
|
|||
|
|
<CommandRow
|
|||
|
|
visible={stage >= 2}
|
|||
|
|
flag="--model-opus"
|
|||
|
|
flagColor="text-purple-400"
|
|||
|
|
value="google/gemini-3-pro-preview"
|
|||
|
|
comment="Complex planning & vision"
|
|||
|
|
/>
|
|||
|
|
<CommandRow
|
|||
|
|
visible={stage >= 3}
|
|||
|
|
flag="--model-sonnet"
|
|||
|
|
flagColor="text-blue-400"
|
|||
|
|
value="openai/gpt-5.1-codex"
|
|||
|
|
comment="Main coding logic"
|
|||
|
|
/>
|
|||
|
|
<CommandRow
|
|||
|
|
visible={stage >= 4}
|
|||
|
|
flag="--model-haiku"
|
|||
|
|
flagColor="text-green-400"
|
|||
|
|
value="x-ai/grok-code-fast-1"
|
|||
|
|
comment="Fast context processing"
|
|||
|
|
/>
|
|||
|
|
<CommandRow
|
|||
|
|
visible={stage >= 5}
|
|||
|
|
flag="--model-subagent"
|
|||
|
|
flagColor="text-orange-400"
|
|||
|
|
value="minimax/minimax-m2"
|
|||
|
|
comment="Background worker agents"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Success State */}
|
|||
|
|
<div className={`pt-6 space-y-1 transition-opacity duration-500 ${stage >= 6 ? 'opacity-100' : 'opacity-0'}`}>
|
|||
|
|
<div className="flex items-center gap-2 text-[#3fb950]">
|
|||
|
|
<span>✓</span> Connection established to 4 distinct providers
|
|||
|
|
</div>
|
|||
|
|
<div className="flex items-center gap-2 text-[#3fb950]">
|
|||
|
|
<span>✓</span> Semantic complexity router: <b>Active</b>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Ready State */}
|
|||
|
|
<div className={`pt-4 transition-all duration-500 flex items-center ${stage >= 6 ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-2'}`}>
|
|||
|
|
<span className="text-claude-ish font-bold mr-2 text-base">»</span>
|
|||
|
|
<span className="text-white font-bold">Ready. Orchestrating multi-model mesh.</span>
|
|||
|
|
<span className={`inline-block w-2.5 h-4 bg-claude-ish/50 ml-2 ${stage >= 13 ? 'hidden' : 'animate-cursor-blink'}`}></span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</TerminalWindow>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Visual Badges & Lines */}
|
|||
|
|
<div className="relative pb-12 px-2 md:px-8">
|
|||
|
|
{/* Connection Lines (SVG) */}
|
|||
|
|
<svg className="absolute inset-0 w-full h-full pointer-events-none z-0" overflow="visible">
|
|||
|
|
<defs>
|
|||
|
|
<filter id="neon-glow" x="-20%" y="-20%" width="140%" height="140%">
|
|||
|
|
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
|
|||
|
|
<feMerge>
|
|||
|
|
<feMergeNode in="coloredBlur"/>
|
|||
|
|
<feMergeNode in="SourceGraphic"/>
|
|||
|
|
</feMerge>
|
|||
|
|
</filter>
|
|||
|
|
<linearGradient id="line-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|||
|
|
<stop offset="0%" stopColor="rgba(168, 85, 247, 0.4)" />
|
|||
|
|
<stop offset="33%" stopColor="rgba(59, 130, 246, 0.4)" />
|
|||
|
|
<stop offset="66%" stopColor="rgba(34, 197, 94, 0.4)" />
|
|||
|
|
<stop offset="100%" stopColor="rgba(249, 115, 22, 0.4)" />
|
|||
|
|
</linearGradient>
|
|||
|
|
</defs>
|
|||
|
|
{stage >= 7 && (
|
|||
|
|
<g className="stroke-[url(#line-gradient)] stroke-[2] fill-none opacity-60" style={{ filter: 'drop-shadow(0 0 4px rgba(0, 212, 170, 0.3))' }}>
|
|||
|
|
{/* Desktop: Connecting Top to Bottom */}
|
|||
|
|
<path d="M15% 90 Q 15% 120, 38% 120" className="hidden md:block animate-draw [stroke-dasharray:1000] [stroke-dashoffset:1000]" />
|
|||
|
|
<path d="M38% 120 L 62% 120" className="hidden md:block animate-draw [stroke-dasharray:1000] [stroke-dashoffset:1000]" />
|
|||
|
|
<path d="M85% 90 Q 85% 120, 62% 120" className="hidden md:block animate-draw [stroke-dasharray:1000] [stroke-dashoffset:1000]" />
|
|||
|
|
|
|||
|
|
{/* Mobile: Simple connections */}
|
|||
|
|
<path d="M25% 120 L 75% 120" className="md:hidden animate-draw" />
|
|||
|
|
</g>
|
|||
|
|
)}
|
|||
|
|
</svg>
|
|||
|
|
|
|||
|
|
{/* Badges Grid */}
|
|||
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6 relative z-10 px-2 md:px-4">
|
|||
|
|
<Badge
|
|||
|
|
active={stage >= 2}
|
|||
|
|
color="purple"
|
|||
|
|
role="PLANNING NODE"
|
|||
|
|
modelName="GEMINI 3"
|
|||
|
|
icon="◈"
|
|||
|
|
mapping="maps to --model-opus"
|
|||
|
|
/>
|
|||
|
|
<Badge
|
|||
|
|
active={stage >= 3}
|
|||
|
|
color="blue"
|
|||
|
|
role="CODING NODE"
|
|||
|
|
modelName="GPT 5.1"
|
|||
|
|
icon="❖"
|
|||
|
|
mapping="maps to --model-sonnet"
|
|||
|
|
/>
|
|||
|
|
<Badge
|
|||
|
|
active={stage >= 4}
|
|||
|
|
color="green"
|
|||
|
|
role="FAST NODE"
|
|||
|
|
modelName="GROK FAST"
|
|||
|
|
icon="⚡"
|
|||
|
|
mapping="maps to --model-haiku"
|
|||
|
|
/>
|
|||
|
|
<Badge
|
|||
|
|
active={stage >= 5}
|
|||
|
|
color="orange"
|
|||
|
|
role="BACKGROUND"
|
|||
|
|
modelName="MINIMAX M2"
|
|||
|
|
icon="⟁"
|
|||
|
|
mapping="maps to --model-subagent"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Info Pills */}
|
|||
|
|
<div className="flex flex-wrap justify-center gap-4 mt-8 md:mt-12 relative z-10">
|
|||
|
|
<InfoPill visible={stage >= 8} text="Unified Context Window" delay={0} />
|
|||
|
|
<InfoPill visible={stage >= 9} text="Standardized Tool Use" delay={100} />
|
|||
|
|
<InfoPill visible={stage >= 10} text="Complexity Routing" delay={200} />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Tagline Reveal */}
|
|||
|
|
<div className="mt-12 text-center min-h-[4rem] flex flex-col md:flex-row items-center justify-center gap-2 md:gap-8">
|
|||
|
|
<div className={`text-xl md:text-2xl font-bold transition-opacity duration-300 ${stage >= 11 ? 'opacity-100' : 'opacity-0'} ${stage >= 13 ? 'text-gray-600 line-through decoration-gray-700' : 'text-gray-400'}`}>
|
|||
|
|
Not switching.
|
|||
|
|
</div>
|
|||
|
|
<div className={`text-xl md:text-2xl font-bold transition-opacity duration-300 ${stage >= 12 ? 'opacity-100' : 'opacity-0'} ${stage >= 13 ? 'text-gray-600 line-through decoration-gray-700' : 'text-gray-400'}`}>
|
|||
|
|
Not merging.
|
|||
|
|
</div>
|
|||
|
|
<div className={`text-2xl md:text-4xl font-black text-transparent bg-clip-text bg-gradient-to-r from-claude-ish to-white transition-all duration-500 ${stage >= 13 ? 'opacity-100 scale-110 blur-0' : 'opacity-0 scale-90 blur-md'}`}>
|
|||
|
|
Collaborating.
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Helper: Command Row in Terminal
|
|||
|
|
const CommandRow: React.FC<{ visible: boolean; flag: string; flagColor: string; value: string; comment: string }> = ({
|
|||
|
|
visible, flag, flagColor, value, comment
|
|||
|
|
}) => (
|
|||
|
|
<div className={`pl-6 md:pl-8 flex flex-wrap items-baseline gap-x-3 gap-y-1 transition-all duration-300 ${visible ? 'opacity-100 translate-x-0' : 'opacity-0 -translate-x-4'}`}>
|
|||
|
|
<span className={`${flagColor} font-bold tracking-tight min-w-[140px]`}>{flag}</span>
|
|||
|
|
<span className="text-gray-200">{value}</span>
|
|||
|
|
<span className="text-gray-600 italic text-[11px] md:text-xs"># {comment}</span>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// Helper: Badge Component
|
|||
|
|
const Badge: React.FC<{
|
|||
|
|
active: boolean;
|
|||
|
|
color: 'purple' | 'blue' | 'green' | 'orange';
|
|||
|
|
role: string;
|
|||
|
|
modelName: string;
|
|||
|
|
icon: string;
|
|||
|
|
mapping: string;
|
|||
|
|
}> = ({ active, color, role, modelName, icon, mapping }) => {
|
|||
|
|
|
|||
|
|
const colors = {
|
|||
|
|
purple: { border: 'border-purple-500', text: 'text-purple-400', bg: 'bg-purple-500/10', glow: 'shadow-purple-500/20' },
|
|||
|
|
blue: { border: 'border-blue-500', text: 'text-blue-400', bg: 'bg-blue-500/10', glow: 'shadow-blue-500/20' },
|
|||
|
|
green: { border: 'border-green-500', text: 'text-green-400', bg: 'bg-green-500/10', glow: 'shadow-green-500/20' },
|
|||
|
|
orange: { border: 'border-orange-500', text: 'text-orange-400', bg: 'bg-orange-500/10', glow: 'shadow-orange-500/20' },
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const c = colors[color];
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className={`
|
|||
|
|
relative overflow-hidden rounded-xl border transition-all duration-700 ease-out group
|
|||
|
|
${active
|
|||
|
|
? `${c.border} ${c.bg} shadow-[0_0_40px_-10px_rgba(0,0,0,0)] ${c.glow} translate-y-0 opacity-100`
|
|||
|
|
: 'border-white/5 bg-[#0f0f0f] shadow-none translate-y-4 opacity-40'
|
|||
|
|
}
|
|||
|
|
`}>
|
|||
|
|
<div className="p-5 flex flex-col h-full min-h-[140px]">
|
|||
|
|
<div className="flex justify-between items-start mb-4">
|
|||
|
|
<span className={`text-[10px] font-bold tracking-[0.2em] uppercase ${active ? c.text : 'text-gray-600'}`}>
|
|||
|
|
{role}
|
|||
|
|
</span>
|
|||
|
|
<span className={`text-lg transition-all duration-500 ${active ? 'text-white scale-110 drop-shadow-[0_0_8px_rgba(255,255,255,0.5)]' : 'text-gray-700'}`}>
|
|||
|
|
{icon}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className={`text-2xl md:text-3xl font-bold tracking-tight mb-auto transition-colors duration-500 ${active ? 'text-white' : 'text-gray-600'}`}>
|
|||
|
|
{modelName}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="mt-4 pt-3 border-t border-white/5">
|
|||
|
|
<div className="text-[10px] font-mono text-gray-500 flex items-center gap-1.5">
|
|||
|
|
<span className={`w-1 h-1 rounded-full ${active ? `bg-${color}-500` : 'bg-gray-700'}`}></span>
|
|||
|
|
{mapping}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Active Glow Line */}
|
|||
|
|
<div className={`absolute bottom-0 left-0 h-[2px] w-full transition-all duration-1000 ${active ? `bg-${color}-500 opacity-100` : 'opacity-0'}`} />
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Helper: Info Pill
|
|||
|
|
const InfoPill: React.FC<{ visible: boolean; text: string; delay: number }> = ({ visible, text, delay }) => (
|
|||
|
|
<div
|
|||
|
|
className={`
|
|||
|
|
border border-white/10 bg-[#111] rounded-full py-2 px-6 text-xs md:text-sm font-mono text-gray-400
|
|||
|
|
transition-all duration-700 backdrop-blur-md shadow-lg
|
|||
|
|
${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}
|
|||
|
|
`}
|
|||
|
|
style={{ transitionDelay: `${delay}ms` }}
|
|||
|
|
>
|
|||
|
|
{text}
|
|||
|
|
</div>
|
|||
|
|
);
|