1- import { motion , AnimatePresence } from "framer-motion" ;
2- import { Terminal } from "lucide-react" ;
1+ import { useState , useRef , useEffect } from "react" ;
2+ import { motion , } from "framer-motion" ;
3+ import { Terminal , RefreshCw } from "lucide-react" ;
34import CommandDisplay from "../CommandDisplay" ;
45
56interface ConsoleOutputProps {
67 output : string ;
78 error : string ;
89 executionTime ?: number | null ;
10+ runCode ?: ( ) => void ;
911}
1012
11- export function ConsoleOutput ( { output, error, executionTime } : ConsoleOutputProps ) {
13+ type HistoryItem = {
14+ type : "command" | "output" | "error" | "info" ;
15+ text : string ;
16+ } ;
17+
18+ export function ConsoleOutput ( { output, error, executionTime, runCode } : ConsoleOutputProps ) {
19+ const [ terminalHistory , setTerminalHistory ] = useState < HistoryItem [ ] > ( [ ] ) ;
20+ const [ currentInput , setCurrentInput ] = useState ( "" ) ;
21+ const contentRef = useRef < HTMLDivElement > ( null ) ;
22+
23+ useEffect ( ( ) => {
24+ if ( contentRef . current ) {
25+ contentRef . current . scrollTop = contentRef . current . scrollHeight ;
26+ }
27+ } , [ terminalHistory , currentInput ] ) ;
28+
29+ useEffect ( ( ) => {
30+ if ( output ) {
31+ setTerminalHistory ( ( prev ) => [
32+ ...prev ,
33+ { type : "output" , text : output } ,
34+ { type : "info" , text : `✓ Executed in ${ executionTime ?. toFixed ( 2 ) ?? 0 } ms` } ,
35+ ] ) ;
36+ }
37+ } , [ output , executionTime ] ) ;
38+
39+ useEffect ( ( ) => {
40+ if ( error ) {
41+ setTerminalHistory ( ( prev ) => [
42+ ...prev ,
43+ { type : "error" , text : error } ,
44+ { type : "info" , text : "✗ Process exited with code 1" } ,
45+ ] ) ;
46+ }
47+ } , [ error ] ) ;
48+
49+ const handleKeyDown = ( e : React . KeyboardEvent < HTMLDivElement > ) => {
50+ e . preventDefault ( ) ;
51+ if ( e . key === "Enter" ) {
52+ const trimmed = currentInput . trim ( ) ;
53+ setTerminalHistory ( ( prev ) => [ ...prev , { type : "command" , text : trimmed } ] ) ;
54+ if ( trimmed === "node main.js" || trimmed === "deno run main.js" || trimmed === "bjs run main.bjs" ) {
55+ setTerminalHistory ( ( prev ) => [ ...prev , { type : "info" , text : "Running..." } ] ) ;
56+ runCode ?.( ) ;
57+ } else if ( trimmed === "clear" ) {
58+ setTerminalHistory ( [ ] ) ;
59+ } else if ( trimmed ) {
60+ setTerminalHistory ( ( prev ) => [ ...prev , { type : "error" , text : `command not found: ${ trimmed } ` } ] ) ;
61+ }
62+ setCurrentInput ( "" ) ;
63+ return ;
64+ }
65+ if ( e . key === "Backspace" ) {
66+ setCurrentInput ( ( prev ) => prev . slice ( 0 , - 1 ) ) ;
67+ return ;
68+ }
69+ if ( e . key . length === 1 && ! e . ctrlKey && ! e . metaKey ) {
70+ setCurrentInput ( ( prev ) => prev + e . key ) ;
71+ return ;
72+ }
73+ } ;
74+
1275 return (
13- < div className = "h-full flex flex-col bg-[#0a0a0a] font-mono text-sm" >
14- < AnimatePresence mode = "wait" >
15- { error ? (
16- < motion . div
17- key = "error"
18- initial = { { opacity : 0 , y : 10 } }
19- animate = { { opacity : 1 , y : 0 } }
20- exit = { { opacity : 0 , y : - 10 } }
21- transition = { { duration : 0.2 } }
22- className = "flex-1 overflow-y-auto p-4 space-y-2"
23- >
24- < div className = "flex items-center gap-2 text-gray-400 text-xs mb-3 " >
25- < span className = "text-emerald-500" > ➜</ span >
26- < span className = "text-cyan-400" > ~/banglascript</ span >
27- < span className = "text-gray-500" > $</ span >
28- < span className = "text-gray-300" > node script.js</ span >
29- </ div >
30- < div className = "bg-red-950/20 border-l-4 border-red-500 p-3 rounded-r" >
31- < div className = "flex items-center gap-2 mb-2" >
32- < span className = "text-red-400 text-lg" > ⚠</ span >
33- < span className = "text-red-400 font-semibold" > Runtime Error</ span >
76+ < div className = "h-full flex flex-col bg-[#0d0d0d] font-mono text-sm relative" >
77+ { /* Terminal Header Bar */ }
78+ < div className = "bg-[#1a1a1a] border-b border-gray-800 px-4 py-2 flex items-center gap-2 select-none" >
79+ < div className = "flex gap-1.5" >
80+ < div className = "w-3 h-3 rounded-full bg-red-500/80 cursor-pointer hover:brightness-110 transition-all" > </ div >
81+ < div className = "w-3 h-3 rounded-full bg-yellow-500/80 cursor-pointer hover:brightness-110 transition-all" > </ div >
82+ < div
83+ className = "w-3 h-3 rounded-full bg-green-500/80 cursor-pointer hover:brightness-110 transition-all"
84+ > </ div >
85+ </ div >
86+ < div className = "flex-1 flex items-center justify-center gap-2 text-xs" >
87+ < Terminal className = "h-3.5 w-3.5 text-amber-100" />
88+ < span className = "text-gray-500" > BanglaScript Terminal</ span >
89+ </ div >
90+ < div className = "flex items-center justify-end w-16 pr-4"
91+ title = "Clear the terminal."
92+ >
93+ < RefreshCw
94+
95+ className = "h-4 w-4 text-gray-500 hover:text-gray-300 cursor-pointer"
96+ onClick = { ( ) => setTerminalHistory ( [ ] ) }
97+ />
98+ </ div >
99+ </ div >
100+
101+ { /* Terminal Content */ }
102+ < div
103+ ref = { contentRef }
104+ tabIndex = { 0 }
105+ onKeyDown = { handleKeyDown }
106+ onClick = { ( ) => contentRef . current ?. focus ( ) }
107+ className = "flex-1 overflow-y-auto p-4 space-y-2 outline-none"
108+ style = { {
109+ scrollbarWidth : "thin" ,
110+ scrollbarColor : "#374151 #1a1a1a" ,
111+ } }
112+ >
113+ { terminalHistory . length === 0 && (
114+ < >
115+ < div className = "text-gray-600 text-[13px]" > BanglaScript Terminal v1.3.7</ div >
116+ < div className = "text-gray-700 text-xs" > Type commands like '
117+ < span className = "text-gray-50/25" > node main.js</ span > ' or '
118+ < span className = "text-gray-50/25" > deno run main.js</ span > ' to run the code | run `
119+ < span className = "text-gray-50/25" > clear</ span > ` to clean the terminal
34120 </ div >
35- < pre className = "text-red-300 whitespace-pre-wrap leading-relaxed text-xs" >
36- { error }
37- </ pre >
38- </ div >
39- < div className = "flex items-center gap-2 text-gray-500 text-xs pt-2" >
40- < span className = "text-red-500" > ✗</ span >
41- < span > Process exited with code 1</ span >
42- </ div >
43- </ motion . div >
44- ) : output ? (
45- < motion . div
46- key = "output"
47- initial = { { opacity : 0 , y : 10 } }
48- animate = { { opacity : 1 , y : 0 } }
49- exit = { { opacity : 0 , y : - 10 } }
50- transition = { { duration : 0.2 } }
51- className = "flex-1 overflow-y-scroll p-4 space-y-2"
52- style = { {
53- scrollbarWidth : 'thin' ,
54- scrollbarColor : '#eeeee'
55- } }
56- >
57- < div className = "flex items-center gap-2 text-gray-400 text-xs mb-3" >
58- < span className = "text-emerald-500" > ➜</ span >
59- < span className = "text-cyan-400" > ~/banglascript</ span >
60- < span className = "text-gray-500" > $</ span >
61- < CommandDisplay />
62- </ div >
63- < div className = "bg-gray-900/30 border border-green-800/50 rounded p-3" >
64- < pre className = "text-gray-100 whitespace-pre-wrap leading-relaxed text-xs" >
65- { output }
66- </ pre >
67- </ div >
68- < div className = "flex items-center gap-2 text-gray-500 text-xs pt-2" >
69- < span className = "text-emerald-500" > ✓</ span >
70- < span > Process completed successfully in { executionTime ?. toFixed ( 2 ) } ms</ span >
71- </ div >
72- </ motion . div >
73- ) : (
74- < motion . div
75- key = "empty"
76- initial = { { opacity : 0 } }
77- animate = { { opacity : 1 } }
78- exit = { { opacity : 0 } }
79- className = "h-full flex flex-col p-4"
80- >
81- < div className = "space-y-2 mb-4" >
82- < div className = "flex items-center gap-2 text-gray-600 text-xs" >
83- < Terminal className = "h-3.5 w-3.5" />
84- < span > BanglaScript Terminal v1.0.0</ span >
121+ < div className = "w-full border-t border-gray-800/50 my-2" > </ div >
122+ </ >
123+ ) }
124+
125+ { terminalHistory . map ( ( item , index ) => (
126+ < div key = { index } >
127+ { item . type === "command" && (
128+ < div className = "flex items-center gap-2 text-[13px]" >
129+ < span className = "text-emerald-400" > ➜</ span >
130+ < span className = "text-sky-400" > ~/banglascript</ span >
131+ < span className = "text-green-600" > $</ span >
132+ < span className = "text-gray-300" > { item . text } </ span >
85133 </ div >
86- < div className = "w-full border-t border-gray-800/50" > </ div >
87- < div className = "flex items-center gap-2 text-gray-500 text-xs" >
88- < span className = "text-emerald-500" > ➜</ span >
89- < span className = "text-cyan-400" > ~/banglascript</ span >
90- < span className = "text-gray-600" > $</ span >
91- < motion . span
92- className = "inline-block w-2 h-4 bg-gray-500 ml-1"
93- animate = { { opacity : [ 1 , 0 , 1 ] } }
94- transition = { { duration : 1 , repeat : Infinity } }
95- />
134+ ) }
135+ { item . type === "output" && (
136+ < div className = "text-gray-200 text-[13px] leading-relaxed mt-2 bg-gray-900/30 border border-green-800/50 rounded p-3" >
137+ < pre className = "whitespace-pre-wrap" > { item . text } </ pre >
96138 </ div >
97- </ div >
98- < div className = "flex-1 flex flex-col items-center justify-center text-gray-500" >
99- < div className = "relative mb-6" >
100- < Terminal className = "h-16 w-16 opacity-20 text-cyan-400" />
101- < motion . div
102- className = "absolute inset-0 bg-cyan-500/10 blur-xl rounded-full"
103- animate = { { scale : [ 1 , 1.2 , 1 ] } }
104- transition = { { duration : 3 , repeat : Infinity } }
105- />
139+ ) }
140+ { item . type === "error" && (
141+ < div className = "text-red-400 text-[13px] mt-2" >
142+ < pre className = "whitespace-pre-wrap" > { item . text } </ pre >
106143 </ div >
107- < p className = "text-center" >
108- < span className = "text-gray-400 font-medium block mb-1" > কোড রান করতে "কোড চালান" বাটনে ক্লিক করুন</ span >
109- < span className = "text-xs text-gray-600" > Waiting for execution...</ span >
110- </ p >
111- </ div >
112- </ motion . div >
113- ) }
114- </ AnimatePresence >
144+ ) }
145+ { item . type === "info" && (
146+ < div className = "text-green-600 text-[13px] mt-2" > { item . text } </ div >
147+ ) }
148+ </ div >
149+ ) ) }
150+
151+ < div className = "flex items-center gap-2 text-[13px] pt-2" >
152+ < span className = "text-emerald-400" > ➜</ span >
153+ < span className = "text-sky-400" > ~/banglascript</ span >
154+ < span className = "text-green-600" > $</ span >
155+ < span className = "text-gray-300" > { currentInput } </ span >
156+ < motion . span
157+ className = "inline-block w-1 h-[14px] bg-gray-500 ml-0.5"
158+ animate = { { opacity : [ 1 , 0.3 , 1 ] } }
159+ transition = { { duration : 0.8 , repeat : Infinity , ease : "linear" } }
160+ />
161+ </ div >
162+ </ div >
115163 </ div >
116164 ) ;
117165}
0 commit comments