pradit/components/TerminalBlock.tsx
2025-11-20 17:04:57 +07:00

95 lines
3.3 KiB
TypeScript

import React, { useState, useEffect } from "react";
interface TerminalBlockProps {
title?: string;
lines: string[];
className?: string;
typingEffect?: boolean;
}
export const TerminalBlock: React.FC<TerminalBlockProps> = ({
title = "bash",
lines,
className,
typingEffect = false,
}) => {
const [displayedLines, setDisplayedLines] = useState<string[]>(
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 (
<div
className={`rounded-md overflow-hidden bg-[#1e1e1e] shadow-xl border border-gray-800 ${className}`}
>
<div className="flex items-center px-4 py-2 bg-[#252526] border-b border-gray-700">
<div className="flex space-x-2 mr-4">
<div className="w-3 h-3 rounded-full bg-red-500"></div>
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
<div className="w-3 h-3 rounded-full bg-green-500"></div>
</div>
<div className="text-xs text-gray-400 font-mono">{title}</div>
</div>
<div className="p-6 font-mono text-sm leading-relaxed min-h-[200px]">
{displayedLines.map((line, idx) => (
<div key={idx} className="text-gray-300 break-words">
{line.startsWith("$") ? (
<span className="text-green-400">{line}</span>
) : line.startsWith(">") ? (
<span className="text-blue-400 ml-4">{line}</span>
) : line.startsWith("#") ? (
<span className="text-gray-500 italic">{line}</span>
) : (
<span className="text-gray-300">{line}</span>
)}
</div>
))}
{/* Cursor */}
{typingEffect && currentLineIndex < lines.length && (
<div className="animate-pulse inline-block w-2 h-4 bg-gray-500 align-middle ml-1"></div>
)}
{/* Static Cursor when done */}
{(!typingEffect || currentLineIndex >= lines.length) && (
<div className="animate-pulse mt-2 inline-block w-2 h-4 bg-gray-500 align-middle"></div>
)}
</div>
</div>
);
};