|
import type { WebContainer, WebContainerProcess } from '@webcontainer/api'; |
|
import { atom, type WritableAtom } from 'nanostores'; |
|
import type { ITerminal } from '~/types/terminal'; |
|
import { newBoltShellProcess, newShellProcess } from '~/utils/shell'; |
|
import { coloredText } from '~/utils/terminal'; |
|
|
|
export class TerminalStore { |
|
#webcontainer: Promise<WebContainer>; |
|
#terminals: Array<{ terminal: ITerminal; process: WebContainerProcess }> = []; |
|
#boltTerminal = newBoltShellProcess(); |
|
|
|
showTerminal: WritableAtom<boolean> = import.meta.hot?.data.showTerminal ?? atom(true); |
|
|
|
constructor(webcontainerPromise: Promise<WebContainer>) { |
|
this.#webcontainer = webcontainerPromise; |
|
|
|
if (import.meta.hot) { |
|
import.meta.hot.data.showTerminal = this.showTerminal; |
|
} |
|
} |
|
get boltTerminal() { |
|
return this.#boltTerminal; |
|
} |
|
|
|
toggleTerminal(value?: boolean) { |
|
this.showTerminal.set(value !== undefined ? value : !this.showTerminal.get()); |
|
} |
|
async attachBoltTerminal(terminal: ITerminal) { |
|
try { |
|
const wc = await this.#webcontainer; |
|
await this.#boltTerminal.init(wc, terminal); |
|
} catch (error: any) { |
|
terminal.write(coloredText.red('Failed to spawn bolt shell\n\n') + error.message); |
|
return; |
|
} |
|
} |
|
|
|
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 }); |
|
} |
|
} |
|
} |
|
|