import type { WebContainer, WebContainerProcess } from '@webcontainer/api'; import { atom, type WritableAtom } from 'nanostores'; import type { ITerminal } from '~/types/terminal'; import { newShellProcess } from '~/utils/shell'; import { coloredText } from '~/utils/terminal'; export class TerminalStore { #webcontainer: Promise; #terminals: Array<{ terminal: ITerminal; process: WebContainerProcess }> = []; showTerminal: WritableAtom = import.meta.hot?.data.showTerminal ?? atom(false); constructor(webcontainerPromise: Promise) { this.#webcontainer = webcontainerPromise; if (import.meta.hot) { import.meta.hot.data.showTerminal = this.showTerminal; } } toggleTerminal(value?: boolean) { this.showTerminal.set(value !== undefined ? value : !this.showTerminal.get()); } async attachTerminal(terminal: ITerminal) { try { const shellProcess = await newShellProcess(await this.#webcontainer, terminal); this.#terminals.push({ terminal, process: shellProcess }); } catch (error: any) { terminal.write(coloredText.red('Failed to spawn shell\n\n') + error.message); return; } } onTerminalResize(cols: number, rows: number) { for (const { process } of this.#terminals) { process.resize({ cols, rows }); } } }