'use client'; /** * Code block — read-only syntax highlighting via `PrettyCode`. * * Inline, the panel is height-capped (`maxLines`) so a long snippet never * blows up the transcript — beyond the cap PrettyCode scrolls internally. * When capped, a header expand (⤢) button opens the FULL code in a large * dialog (same pattern as the map / image blocks), where it reads/scrolls * comfortably and copies in one shot. */ import { useCallback, useState } from 'react'; import { Check, Copy, X } from 'lucide-react'; import { cn } from '@djangocfg/ui-core/lib'; import { Dialog, DialogClose, DialogContent, DialogTitle } from '@djangocfg/ui-core/components'; import { LazyPrettyCode } from '../../../../dev/code/PrettyCode/lazy'; import type { Language } from '../../../../dev/code/PrettyCode/lazy'; import type { CodeBlock } from '../../../types/block'; import type { BlockRendererProps } from './types'; /** Roomy on-dark button for the expanded dialog header. */ const DIALOG_BTN = 'inline-flex h-8 items-center justify-center gap-1.5 rounded-lg px-2.5 text-xs font-medium ' + 'text-white/70 transition-colors hover:bg-white/10 hover:text-white ' + 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white/40'; export default function CodeBlockRenderer({ block, ctx }: BlockRendererProps) { const compact = ctx.appearance === 'compact'; const [open, setOpen] = useState(false); const [copied, setCopied] = useState(false); const language = block.language as Language; const copyAll = useCallback(() => { void navigator.clipboard?.writeText(block.code).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1400); }); }, [block.code]); // 16 lines in the narrow chat, 24 in a full-page chat. Beyond this the // inline panel scrolls and the header shows the expand button. const maxLines = compact ? 16 : 24; return ( <> {/* Inline — capped + internally scrollable; expand opens the dialog. The dark panel is clipped to the shared 2xl corner by BLOCK_SURFACE. */} setOpen(true)} /> {/* Full code, uncapped, in a large dialog (map/image-block pattern). ONE header (language + copy + close) over a dark panel; the body renders PrettyCode in `plain` (chrome-less) so there's no second header to collide with — the dialog owns the chrome and scroll. */} {block.caption ?? language ?? 'Code'} {open && ( <> {/* Roomy dialog header: language left; actions right in the conventional order — primary "Copy" then "Close" (X) at the far edge. Taller + larger than the inline header to suit the big surface. */}
{language}
)}
); }