| 156 | const PARTICLE_COUNT = 14 |
| 157 | |
| 158 | function InstallCommand({ className }: { className?: string }) { |
| 159 | const [copied, setCopied] = useState(false) |
| 160 | const [copyCount, setCopyCount] = useState(0) |
| 161 | |
| 162 | const particles = useMemo( |
| 163 | () => |
| 164 | Array.from({ length: PARTICLE_COUNT }).map((_, i) => ({ |
| 165 | angle: (i / PARTICLE_COUNT) * 360 + (Math.random() - 0.5) * 25, |
| 166 | distance: 35 + Math.random() * 35, |
| 167 | size: 3 + Math.random() * 4, |
| 168 | durationExtra: Math.random() * 0.3, |
| 169 | })), |
| 170 | [copyCount], |
| 171 | ) |
| 172 | |
| 173 | const handleCopy = () => { |
| 174 | navigator.clipboard.writeText(INSTALL_COMMAND) |
| 175 | setCopied(true) |
| 176 | setCopyCount((c) => c + 1) |
| 177 | posthog.capture(AnalyticsEvent.FREEBUFF_HOME_INSTALL_COMMAND_COPIED) |
| 178 | setTimeout(() => setCopied(false), 1800) |
| 179 | } |
| 180 | |
| 181 | return ( |
| 182 | <div className="relative"> |
| 183 | <div |
| 184 | className={cn( |
| 185 | 'flex items-center gap-2 bg-zinc-900/80 border rounded-lg px-4 py-3 font-mono text-sm', |
| 186 | 'gradient-border-shine', |
| 187 | copied |
| 188 | ? 'border-acid-matrix shadow-[0_0_30px_rgba(124,255,63,0.45),0_0_60px_rgba(124,255,63,0.2)]' |
| 189 | : 'border-acid-matrix/60 install-box-glow hover:border-acid-matrix hover:shadow-[0_0_30px_rgba(124,255,63,0.35),0_0_60px_rgba(124,255,63,0.15)]', |
| 190 | 'transition-all duration-300', |
| 191 | className, |
| 192 | )} |
| 193 | > |
| 194 | <span className="text-acid-matrix select-none">$</span> |
| 195 | <code className="text-white/90 select-all flex-1"> |
| 196 | {INSTALL_COMMAND} |
| 197 | </code> |
| 198 | <button |
| 199 | onClick={handleCopy} |
| 200 | className="p-1.5 rounded-md transition-colors hover:bg-white/10 cursor-pointer" |
| 201 | aria-label={`Copy: ${INSTALL_COMMAND}`} |
| 202 | > |
| 203 | <AnimatePresence mode="wait" initial={false}> |
| 204 | {copied ? ( |
| 205 | <motion.span |
| 206 | key="check" |
| 207 | initial={{ scale: 0, rotate: -90 }} |
| 208 | animate={{ scale: 1, rotate: 0 }} |
| 209 | exit={{ scale: 0, rotate: 90 }} |
| 210 | transition={{ duration: 0.2 }} |
| 211 | className="block" |
| 212 | > |
| 213 | <Check className="h-4 w-4 text-acid-matrix" /> |
| 214 | </motion.span> |
| 215 | ) : ( |