27 lines · 1.0 KB
import { cn } from "~/utils/cn";

export type TerminalLine = { text: string; tone?: "muted" | "danger" | "success" | "accent" | "plain" };

const TONES = {
  plain: "text-kumo-default",
  muted: "text-kumo-subtle",
  danger: "text-kumo-danger",
  success: "text-kumo-success",
  accent: "text-accent-300",
} as const;

/** Monospace scrollback that reveals `visible` lines and shows a caret after the last one. */
export function Terminal({ lines, visible }: { lines: ReadonlyArray<TerminalLine>; visible: number }) {
  return (
    <div className="min-h-64 space-y-0.5 bg-black/40 p-4 font-mono text-xs leading-5">
      {lines.slice(0, visible).map((line, index) => (
        // oxlint-disable-next-line react/no-array-index-key -- scrollback lines are positional
        <p key={index} className={cn("break-all whitespace-pre-wrap", TONES[line.tone ?? "plain"])}>
          {line.text || " "}
        </p>
      ))}
      <span aria-hidden className="animate-caret inline-block h-4 w-2 translate-y-0.5 bg-gray-400" />
    </div>
  );
}