1- import React , { useEffect , useRef , useState } from 'react' ;
1+ import React , { useState , useCallback , memo } from 'react' ;
22import { useTerminalStore } from '@/stores/terminal' ;
33import { useProjectStore } from '@/stores/project' ;
44import { Button } from '@/components/ui/button' ;
55import { X , Plus , Split , Trash2 } from 'lucide-react' ;
66import { cn } from '@/utils/tailwind' ;
7+ import { XTerminal } from './xterm-component' ;
78
89// Simple terminal component using pre-styled div (will be replaced with xterm.js later)
910interface TerminalViewProps {
@@ -12,81 +13,17 @@ interface TerminalViewProps {
1213 onWrite : ( data : string ) => void ;
1314}
1415
15- function TerminalView ( { terminalId, isActive, onWrite } : TerminalViewProps ) {
16- const [ output , setOutput ] = useState < string > ( '' ) ;
17- const [ input , setInput ] = useState < string > ( '' ) ;
18- const outputRef = useRef < HTMLDivElement > ( null ) ;
19- const inputRef = useRef < HTMLInputElement > ( null ) ;
20-
21- useEffect ( ( ) => {
22- if ( isActive && inputRef . current ) {
23- inputRef . current . focus ( ) ;
24- }
25- } , [ isActive ] ) ;
26-
27- useEffect ( ( ) => {
28- if ( outputRef . current ) {
29- outputRef . current . scrollTop = outputRef . current . scrollHeight ;
30- }
31- } , [ output ] ) ;
32-
33- useEffect ( ( ) => {
34- const unsubscribeData = window . terminalApi . onData ( ( data ) => {
35- if ( data . terminalId === terminalId ) {
36- setOutput ( prev => prev + data . data ) ;
37- }
38- } ) ;
39-
40- const unsubscribeExit = window . terminalApi . onExit ( ( data ) => {
41- if ( data . terminalId === terminalId ) {
42- setOutput ( prev => prev + `\n[Process exited with code ${ data . exitCode } ]\n` ) ;
43- }
44- } ) ;
45-
46- return ( ) => {
47- unsubscribeData ( ) ;
48- unsubscribeExit ( ) ;
49- } ;
50- } , [ terminalId ] ) ;
51-
52- const handleKeyDown = ( e : React . KeyboardEvent < HTMLInputElement > ) => {
53- if ( e . key === 'Enter' ) {
54- const command = input + '\n' ;
55- onWrite ( command ) ;
56- setInput ( '' ) ;
57- } else if ( e . key === 'Tab' ) {
58- e . preventDefault ( ) ;
59- // Handle tab completion later
60- }
61- } ;
62-
16+ const TerminalView = memo ( function TerminalView ( { terminalId, isActive, onWrite } : TerminalViewProps ) {
6317 return (
64- < div className = { cn (
65- "flex flex-col h-full bg-black text-white font-mono text-sm" ,
66- ! isActive && "hidden"
67- ) } >
68- < div
69- ref = { outputRef }
70- className = "flex-1 p-2 overflow-y-auto whitespace-pre-wrap"
71- style = { { fontFamily : 'Monaco, Menlo, "Ubuntu Mono", monospace' } }
72- >
73- { output }
74- </ div >
75- < div className = "flex items-center p-2 border-t border-gray-700" >
76- < span className = "text-blue-400 mr-2" > $</ span >
77- < input
78- ref = { inputRef }
79- type = "text"
80- value = { input }
81- onChange = { ( e ) => setInput ( e . target . value ) }
82- onKeyDown = { handleKeyDown }
83- className = "flex-1 bg-transparent border-none outline-none text-green-400"
84- placeholder = "Type your command..."
85- />
86- </ div >
18+ < div className = "h-full w-full" >
19+ < XTerminal
20+ terminalId = { terminalId }
21+ isActive = { isActive }
22+ onWrite = { onWrite }
23+ />
8724 </ div >
8825 ) ;
89- }
26+ } ) ;
9027
9128export function TerminalPanel ( ) {
9229 const { currentProject } = useProjectStore ( ) ;
@@ -167,13 +104,13 @@ export function TerminalPanel() {
167104 }
168105 } ;
169106
170- const handleWrite = async ( terminalId : string , data : string ) => {
107+ const handleWrite = useCallback ( async ( terminalId : string , data : string ) => {
171108 try {
172109 await window . terminalApi . write ( terminalId , data ) ;
173110 } catch ( error ) {
174111 console . error ( 'Failed to write to terminal:' , error ) ;
175112 }
176- } ;
113+ } , [ ] ) ;
177114
178115 if ( ! isVisible ) {
179116 return null ;
@@ -182,18 +119,18 @@ export function TerminalPanel() {
182119 const activeSplits = getActiveSplits ( ) ;
183120
184121 return (
185- < div className = "flex flex-col bg-gray-900 border-t border-gray-700 " style = { { height } } >
122+ < div className = "flex flex-col bg-background border-t border-gray-800 " style = { { height } } >
186123 { /* Tab Bar */ }
187- < div className = "flex items-center justify-between bg-gray-800 border-b border-gray-700 px-2 py-1" >
124+ < div className = "flex items-center justify-between bg-muted/30 border-b px-2 py-1" >
188125 < div className = "flex items-center space-x-1" >
189126 { tabs . map ( ( tab ) => (
190127 < div
191128 key = { tab . id }
192129 className = { cn (
193130 "group flex items-center px-3 py-1 rounded-t-md cursor-pointer text-sm" ,
194131 tab . isActive
195- ? "bg-gray-900 text-white border-b-2 border-blue -500"
196- : "bg-gray-700 text-gray-300 hover:bg-gray-600 "
132+ ? "bg-background text-white border-b-2 border-emerald -500"
133+ : "bg-muted/70 text-gray-300 hover:bg-[#3e3e42] "
197134 ) }
198135 onClick = { ( ) => setActiveTab ( tab . id ) }
199136 >
@@ -275,52 +212,60 @@ export function TerminalPanel() {
275212 </ div >
276213
277214 { /* Terminal Content */ }
278- < div className = "flex-1 relative" >
279- { tabs . map ( ( tab ) => (
280- < div
281- key = { tab . id }
282- className = { cn (
283- "absolute inset-0" ,
284- ! tab . isActive && "hidden"
285- ) }
286- >
287- { activeSplits . length === 0 ? (
288- < TerminalView
289- terminalId = { tab . id }
290- isActive = { tab . isActive }
291- onWrite = { ( data ) => handleWrite ( tab . id , data ) }
292- />
293- ) : (
294- < div className = "flex h-full" >
295- < div className = "flex-1" >
296- < TerminalView
297- terminalId = { tab . id }
298- isActive = { tab . isActive }
299- onWrite = { ( data ) => handleWrite ( tab . id , data ) }
300- />
301- </ div >
302- < div className = "w-px bg-gray-600" />
303- < div className = "flex-1" >
304- { activeSplits . map ( ( split ) => (
305- < div
306- key = { split . id }
307- className = { cn (
308- "h-full" ,
309- split . id !== activeSplitId && "hidden"
310- ) }
311- >
312- < TerminalView
313- terminalId = { split . terminalId }
314- isActive = { split . id === activeSplitId }
315- onWrite = { ( data ) => handleWrite ( split . terminalId , data ) }
316- />
317- </ div >
318- ) ) }
215+ < div className = "flex-1 relative overflow-hidden" >
216+ { tabs . map ( ( tab ) => {
217+ const handleTabWrite = useCallback ( ( data : string ) => handleWrite ( tab . id , data ) , [ tab . id , handleWrite ] ) ;
218+
219+ return (
220+ < div
221+ key = { tab . id }
222+ className = { cn (
223+ "absolute inset-0" ,
224+ ! tab . isActive && "hidden"
225+ ) }
226+ >
227+ { activeSplits . length === 0 ? (
228+ < TerminalView
229+ terminalId = { tab . id }
230+ isActive = { tab . isActive }
231+ onWrite = { handleTabWrite }
232+ />
233+ ) : (
234+ < div className = "flex h-full" >
235+ < div className = "flex-1" >
236+ < TerminalView
237+ terminalId = { tab . id }
238+ isActive = { tab . isActive }
239+ onWrite = { handleTabWrite }
240+ />
241+ </ div >
242+ < div className = "w-px bg-[#3e3e42]" />
243+ < div className = "flex-1" >
244+ { activeSplits . map ( ( split ) => {
245+ const handleSplitWrite = useCallback ( ( data : string ) => handleWrite ( split . terminalId , data ) , [ split . terminalId , handleWrite ] ) ;
246+
247+ return (
248+ < div
249+ key = { split . id }
250+ className = { cn (
251+ "h-full" ,
252+ split . id !== activeSplitId && "hidden"
253+ ) }
254+ >
255+ < TerminalView
256+ terminalId = { split . terminalId }
257+ isActive = { split . id === activeSplitId }
258+ onWrite = { handleSplitWrite }
259+ />
260+ </ div >
261+ ) ;
262+ } ) }
263+ </ div >
319264 </ div >
320- </ div >
321- ) }
322- </ div >
323- ) ) }
265+ ) }
266+ </ div >
267+ ) ;
268+ } ) }
324269
325270 { tabs . length === 0 && (
326271 < div className = "flex items-center justify-center h-full text-gray-500" >
0 commit comments