import { useState, useCallback, type HTMLAttributes, type ReactElement } from 'react'; import { Check, Copy, Hash, WrapText, AlignLeft, ChevronDown, ChevronUp } from 'lucide-react'; import { copyToClipboard } from '../lib/clipboard'; /** Extract the language from the nested element's className (e.g. "language-ts"). */ function extractLanguage(children: React.ReactNode): string | null { const codeEl = children as ReactElement<{ className?: string }> | undefined; const className = codeEl?.props?.className; if (typeof className !== 'string') return null; const match = className.match(/language-(\S+)/); return match ? match[1] : null; } /** Pretty-print common language identifiers. */ const LANGUAGE_LABELS: Record = { js: 'JavaScript', jsx: 'JSX', ts: 'TypeScript', tsx: 'TSX', py: 'Python', rb: 'Ruby', rs: 'Rust', go: 'Go', sh: 'Shell', bash: 'Bash', zsh: 'Zsh', yml: 'YAML', yaml: 'YAML', md: 'Markdown', json: 'JSON', html: 'HTML', css: 'CSS', sql: 'SQL', dockerfile: 'Dockerfile', toml: 'TOML', }; function formatLanguage(lang: string): string { return LANGUAGE_LABELS[lang] || lang; } /** * Custom
 renderer for ReactMarkdown.
 * Wraps code blocks with a language label and a floating copy button.
 */
const LINE_NUMBER_KEY = 'pinchchat-line-numbers';
const WRAP_KEY = 'pinchchat-code-wrap';
const LINE_THRESHOLD = 3; // Only show line numbers for blocks with more than this many lines
const COLLAPSE_THRESHOLD = 25; // Collapse code blocks longer than this
const COLLAPSE_PREVIEW_LINES = 10; // Lines to show when collapsed

export function CodeBlock(props: HTMLAttributes) {
  const [copied, setCopied] = useState(false);
  const [showLineNumbers, setShowLineNumbers] = useState(() => {
    const stored = localStorage.getItem(LINE_NUMBER_KEY);
    return stored === null ? true : stored === 'true';
  });
  const [wordWrap, setWordWrap] = useState(() => {
    const stored = localStorage.getItem(WRAP_KEY);
    return stored === 'true';
  });
  const [isCollapsed, setIsCollapsed] = useState(true);
  const language = extractLanguage(props.children);

  const code = (props.children as ReactElement<{ children?: string }> | undefined)?.props?.children;
  const lines = typeof code === 'string' ? code.replace(/\n$/, '').split('\n') : [];
  const hasEnoughLines = lines.length > LINE_THRESHOLD;
  const isCollapsible = lines.length > COLLAPSE_THRESHOLD;

  const handleCopy = useCallback((e: React.MouseEvent) => {
    e.stopPropagation();
    e.preventDefault();
    const text = typeof code === 'string' ? code : '';
    if (!text) return;
    copyToClipboard(text).then((ok) => {
      if (ok) {
        setCopied(true);
        setTimeout(() => setCopied(false), 2000);
      }
    });
  }, [code]);

  const toggleLineNumbers = useCallback(() => {
    setShowLineNumbers(prev => {
      const next = !prev;
      localStorage.setItem(LINE_NUMBER_KEY, String(next));
      return next;
    });
  }, []);

  const toggleWrap = useCallback(() => {
    setWordWrap(prev => {
      const next = !prev;
      localStorage.setItem(WRAP_KEY, String(next));
      return next;
    });
  }, []);

  const shouldShowNumbers = showLineNumbers && hasEnoughLines;
  const collapsed = isCollapsible && isCollapsed;
  const wrapStyle = wordWrap ? { whiteSpace: 'pre-wrap' as const, overflowWrap: 'break-word' as const, wordBreak: 'break-word' as const } : undefined;
  const collapseStyle = collapsed ? { maxHeight: `${COLLAPSE_PREVIEW_LINES * 1.7142857}em`, overflow: 'hidden' as const } : undefined;

  return (
    
{language && (
{formatLanguage(language)}{isCollapsible && ({lines.length} lines)}
{hasEnoughLines && ( )}
)}
{shouldShowNumbers ? (
          
) : (
          
)} {collapsed && (
)}
{isCollapsible && ( )}
); }