import { useEffect, useState } from 'react'; import { Check, Copy } from 'lucide-react'; import { copyToClipboard } from 'helpers/clipboard'; import { useFlash } from './useFlash'; /** * The output of something that ran, in the shape of the thing that ran it. * * A console, so a transcript from git or drizzle looks like a transcript rather than like a paragraph * of the form around it. Scrolled, not truncated: the useful line of a failure is usually the last one. * * ── A success goes away; a failure never does ── * * Nobody reads a successful transcript twice, and one that stays turns every panel into a log. A * FAILURE has no timer at all rather than a long one — nothing should be able to take it off the screen * except the next attempt, because it is the only thing here somebody has to act on. * * ── The copy button is pinned to the box, not to the text ── * * A button that scrolls away with a long transcript is missing at exactly the moment the transcript is * worth copying. `pr-10` on the `pre` keeps the text out from under it. * * ── Re-running with identical output ── * * The effect keys on `output`, so pressing a button twice and getting the same text back would not * re-show a console that had timed out. Callers clear it to `null` before starting, which they do * anyway to avoid showing the previous run's words next to a spinner. */ export const Console = ({ output, failed, ms = 6000 }: { output: string | null; failed: boolean; ms?: number }) => { const [visible, setVisible] = useState(false); const [copied, flashCopied] = useFlash(1500); useEffect(() => { if (!output) { setVisible(false); return; } setVisible(true); if (failed) return; const timer = setTimeout(() => setVisible(false), ms); return () => clearTimeout(timer); }, [output, failed, ms]); if (!output || !visible) return null; return (
        {output}
      
); };