diff --git a/app/components/workbench/terminal/Terminal.tsx b/app/components/workbench/terminal/Terminal.tsx index 19ab3acf8..9c41f62f9 100644 --- a/app/components/workbench/terminal/Terminal.tsx +++ b/app/components/workbench/terminal/Terminal.tsx @@ -16,72 +16,74 @@ export interface TerminalProps { className?: string; theme: Theme; readonly?: boolean; + id: string; onTerminalReady?: (terminal: XTerm) => void; onTerminalResize?: (cols: number, rows: number) => void; } export const Terminal = memo( - forwardRef(({ className, theme, readonly, onTerminalReady, onTerminalResize }, ref) => { - const terminalElementRef = useRef(null); - const terminalRef = useRef(); - - useEffect(() => { - const element = terminalElementRef.current!; - - const fitAddon = new FitAddon(); - const webLinksAddon = new WebLinksAddon(); - - const terminal = new XTerm({ - cursorBlink: true, - convertEol: true, - disableStdin: readonly, - theme: getTerminalTheme(readonly ? { cursor: '#00000000' } : {}), - fontSize: 12, - fontFamily: 'Menlo, courier-new, courier, monospace', - }); - - terminalRef.current = terminal; - - terminal.loadAddon(fitAddon); - terminal.loadAddon(webLinksAddon); - terminal.open(element); - - const resizeObserver = new ResizeObserver(() => { - fitAddon.fit(); - onTerminalResize?.(terminal.cols, terminal.rows); - }); - - resizeObserver.observe(element); - - logger.info('Attach terminal'); - - onTerminalReady?.(terminal); - - return () => { - resizeObserver.disconnect(); - terminal.dispose(); - }; - }, []); - - useEffect(() => { - const terminal = terminalRef.current!; - - // we render a transparent cursor in case the terminal is readonly - terminal.options.theme = getTerminalTheme(readonly ? { cursor: '#00000000' } : {}); - - terminal.options.disableStdin = readonly; - }, [theme, readonly]); - - useImperativeHandle(ref, () => { - return { - reloadStyles: () => { - const terminal = terminalRef.current!; - terminal.options.theme = getTerminalTheme(readonly ? { cursor: '#00000000' } : {}); - }, - }; - }, []); - logger.info('Starting bolt terminal'); - - return
; - }), + forwardRef( + ({ className, theme, readonly, id, onTerminalReady, onTerminalResize }, ref) => { + const terminalElementRef = useRef(null); + const terminalRef = useRef(); + + useEffect(() => { + const element = terminalElementRef.current!; + + const fitAddon = new FitAddon(); + const webLinksAddon = new WebLinksAddon(); + + const terminal = new XTerm({ + cursorBlink: true, + convertEol: true, + disableStdin: readonly, + theme: getTerminalTheme(readonly ? { cursor: '#00000000' } : {}), + fontSize: 12, + fontFamily: 'Menlo, courier-new, courier, monospace', + }); + + terminalRef.current = terminal; + + terminal.loadAddon(fitAddon); + terminal.loadAddon(webLinksAddon); + terminal.open(element); + + const resizeObserver = new ResizeObserver(() => { + fitAddon.fit(); + onTerminalResize?.(terminal.cols, terminal.rows); + }); + + resizeObserver.observe(element); + + logger.debug(`Attach terminal [${id}]`); + + onTerminalReady?.(terminal); + + return () => { + resizeObserver.disconnect(); + terminal.dispose(); + }; + }, []); + + useEffect(() => { + const terminal = terminalRef.current!; + + // we render a transparent cursor in case the terminal is readonly + terminal.options.theme = getTerminalTheme(readonly ? { cursor: '#00000000' } : {}); + + terminal.options.disableStdin = readonly; + }, [theme, readonly]); + + useImperativeHandle(ref, () => { + return { + reloadStyles: () => { + const terminal = terminalRef.current!; + terminal.options.theme = getTerminalTheme(readonly ? { cursor: '#00000000' } : {}); + }, + }; + }, []); + + return
; + }, + ), ); diff --git a/app/components/workbench/terminal/TerminalTabs.tsx b/app/components/workbench/terminal/TerminalTabs.tsx index 033994bc1..cecc5dc9f 100644 --- a/app/components/workbench/terminal/TerminalTabs.tsx +++ b/app/components/workbench/terminal/TerminalTabs.tsx @@ -7,6 +7,9 @@ import { themeStore } from '~/lib/stores/theme'; import { workbenchStore } from '~/lib/stores/workbench'; import { classNames } from '~/utils/classNames'; import { Terminal, type TerminalRef } from './Terminal'; +import { createScopedLogger } from '~/utils/logger'; + +const logger = createScopedLogger('Terminal'); const MAX_TERMINALS = 3; export const DEFAULT_TERMINAL_SIZE = 25; @@ -137,35 +140,45 @@ export const TerminalTabs = memo(() => { onClick={() => workbenchStore.toggleTerminal(false)} />
- {Array.from({ length: terminalCount + 1 }, (_, index) => - index == 0 ? ( - { - terminalRefs.current.push(ref); - }} - onTerminalReady={(terminal) => workbenchStore.attachBoltTerminal(terminal)} - onTerminalResize={(cols, rows) => workbenchStore.onTerminalResize(cols, rows)} - theme={theme} - /> - ) : ( - { - terminalRefs.current.push(ref); - }} - onTerminalReady={(terminal) => workbenchStore.attachTerminal(terminal)} - onTerminalResize={(cols, rows) => workbenchStore.onTerminalResize(cols, rows)} - theme={theme} - /> - ), - )} + {Array.from({ length: terminalCount + 1 }, (_, index) => { + const isActive = activeTerminal === index; + + logger.debug(`Starting bolt terminal [${index}]`); + + if (index == 0) { + return ( + { + terminalRefs.current.push(ref); + }} + onTerminalReady={(terminal) => workbenchStore.attachBoltTerminal(terminal)} + onTerminalResize={(cols, rows) => workbenchStore.onTerminalResize(cols, rows)} + theme={theme} + /> + ); + } else { + return ( + { + terminalRefs.current.push(ref); + }} + onTerminalReady={(terminal) => workbenchStore.attachTerminal(terminal)} + onTerminalResize={(cols, rows) => workbenchStore.onTerminalResize(cols, rows)} + theme={theme} + /> + ); + } + })}