"use client"; import { Check, Copy, FileCode2, LoaderCircle } from "lucide-react"; import { motion, useReducedMotion } from "motion/react"; import { type ReactNode, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react"; import { type AgentCodeLanguage, AgentCodeLine, useAgentCodeTokens, } from "@/components/agents/agent-code"; import { SPRING_PRESS } from "@/lib/ease"; import { cn } from "@/lib/utils"; export type CodeBlockStatus = "streaming" | "complete"; export interface CodeBlockProps { code: string; language?: AgentCodeLanguage; filename?: ReactNode; status?: CodeBlockStatus; showLineNumbers?: boolean; highlightLines?: number[]; maxHeight?: number; wrap?: boolean; copyable?: boolean; onCopy?: () => void | Promise; className?: string; } export function CodeBlock({ code, language = "typescript", filename, status = "complete", showLineNumbers = true, highlightLines = [], maxHeight = 280, wrap = false, copyable = true, onCopy, className, }: CodeBlockProps) { const reduce = useReducedMotion() ?? false; const viewportRef = useRef(null); const copyTimer = useRef(undefined); const [copied, setCopied] = useState(false); const streaming = status === "streaming"; const tokens = useAgentCodeTokens(code, language); const highlighted = useMemo( () => new Set(highlightLines), [highlightLines], ); let offset = 0; const lines = code.split("\n").map((content) => { const line = { content, offset }; offset += content.length + 1; return line; }); useEffect( () => () => { if (copyTimer.current) window.clearTimeout(copyTimer.current); }, [], ); useLayoutEffect(() => { const viewport = viewportRef.current; if (!viewport || !streaming) return; const frame = requestAnimationFrame(() => { if (viewport.scrollHeight <= viewport.clientHeight) return; if (typeof viewport.scrollTo === "function") { viewport.scrollTo({ top: viewport.scrollHeight, behavior: reduce ? "auto" : "smooth", }); } else { viewport.scrollTop = viewport.scrollHeight; } }); return () => cancelAnimationFrame(frame); }); const handleCopy = useCallback(async () => { if (onCopy) await onCopy(); else await navigator.clipboard?.writeText(code); setCopied(true); if (copyTimer.current) window.clearTimeout(copyTimer.current); copyTimer.current = window.setTimeout(() => setCopied(false), 1600); }, [code, onCopy]); return (
          
            {lines.map((line, index) => {
              const lineNumber = index + 1;
              return (
                
                  {showLineNumbers ? (
                    
                      {lineNumber}
                    
                  ) : null}
                  
                
              );
            })}
          
        
); }