import React, { useState, useEffect } from "react"; interface TerminalBlockProps { title?: string; lines: string[]; className?: string; typingEffect?: boolean; } export const TerminalBlock: React.FC = ({ title = "bash", lines, className, typingEffect = false, }) => { const [displayedLines, setDisplayedLines] = useState( typingEffect ? [] : lines, ); const [currentLineIndex, setCurrentLineIndex] = useState(0); const [currentCharIndex, setCurrentCharIndex] = useState(0); useEffect(() => { if (!typingEffect) return; if (currentLineIndex < lines.length) { const currentLineFull = lines[currentLineIndex]; if (currentCharIndex < currentLineFull.length) { const timeout = setTimeout( () => { setDisplayedLines((prev) => { const newLines = [...prev]; if (newLines[currentLineIndex] === undefined) { newLines[currentLineIndex] = currentLineFull[currentCharIndex]; } else { newLines[currentLineIndex] += currentLineFull[currentCharIndex]; } return newLines; }); setCurrentCharIndex((prev) => prev + 1); }, 15 + Math.random() * 20, ); // Random variance for realism return () => clearTimeout(timeout); } else { // Line finished, wait a bit then move to next const timeout = setTimeout(() => { setCurrentLineIndex((prev) => prev + 1); setCurrentCharIndex(0); }, 300); return () => clearTimeout(timeout); } } }, [typingEffect, lines, currentLineIndex, currentCharIndex]); return (
{title}
{displayedLines.map((line, idx) => (
{line.startsWith("$") ? ( {line} ) : line.startsWith(">") ? ( {line} ) : line.startsWith("#") ? ( {line} ) : ( {line} )}
))} {/* Cursor */} {typingEffect && currentLineIndex < lines.length && (
)} {/* Static Cursor when done */} {(!typingEffect || currentLineIndex >= lines.length) && (
)}
); };