Spaces:
Sleeping
Sleeping
| import "@xterm/xterm/css/xterm.css"; | |
| const THEME = { | |
| background: "#10141c", | |
| foreground: "#d6deeb", | |
| cursor: "#f7c948", | |
| cursorAccent: "#10141c", | |
| selectionBackground: "#334155", | |
| black: "#111827", | |
| red: "#f87171", | |
| green: "#34d399", | |
| yellow: "#facc15", | |
| blue: "#60a5fa", | |
| magenta: "#c084fc", | |
| cyan: "#22d3ee", | |
| white: "#f8fafc", | |
| brightBlack: "#475569", | |
| brightRed: "#fca5a5", | |
| brightGreen: "#86efac", | |
| brightYellow: "#fde68a", | |
| brightBlue: "#93c5fd", | |
| brightMagenta: "#d8b4fe", | |
| brightCyan: "#67e8f9", | |
| brightWhite: "#ffffff", | |
| }; | |
| function normalizeNewlines(text) { | |
| return String(text).replace(/\r?\n/g, "\r\n"); | |
| } | |
| function withTimeout(promise, ms, label) { | |
| return Promise.race([ | |
| promise, | |
| new Promise((_, reject) => { | |
| setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms); | |
| }), | |
| ]); | |
| } | |
| export async function createWebTerminal(container) { | |
| let terminal; | |
| let fitAddon; | |
| let engine = "ghostty-web"; | |
| try { | |
| const ghostty = await withTimeout(import("ghostty-web"), 3000, "ghostty-web import"); | |
| await withTimeout(ghostty.init(), 3000, "ghostty-web init"); | |
| terminal = new ghostty.Terminal({ | |
| cursorBlink: true, | |
| fontFamily: "JetBrains Mono, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace", | |
| fontSize: 14, | |
| rows: 30, | |
| cols: 100, | |
| scrollback: 5000, | |
| theme: THEME, | |
| }); | |
| fitAddon = new ghostty.FitAddon(); | |
| terminal.loadAddon(fitAddon); | |
| } catch (error) { | |
| const [{ Terminal }, { FitAddon }] = await Promise.all([import("@xterm/xterm"), import("@xterm/addon-fit")]); | |
| engine = "xterm"; | |
| terminal = new Terminal({ | |
| cursorBlink: true, | |
| fontFamily: "JetBrains Mono, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace", | |
| fontSize: 14, | |
| rows: 30, | |
| cols: 100, | |
| scrollback: 5000, | |
| theme: THEME, | |
| }); | |
| fitAddon = new FitAddon(); | |
| terminal.loadAddon(fitAddon); | |
| console.warn("ghostty-web failed, using xterm fallback", error); | |
| } | |
| terminal.open(container); | |
| fitAddon.fit(); | |
| terminal.focus(); | |
| const resizeObserver = new ResizeObserver(() => fitAddon.fit()); | |
| resizeObserver.observe(container); | |
| return { | |
| engine, | |
| raw: terminal, | |
| onData: (handler) => terminal.onData(handler), | |
| write: (text) => terminal.write(normalizeNewlines(text)), | |
| writeln: (text = "") => terminal.write(`${normalizeNewlines(text)}\r\n`), | |
| clear: () => terminal.clear(), | |
| focus: () => terminal.focus(), | |
| fit: () => fitAddon.fit(), | |
| dispose: () => { | |
| resizeObserver.disconnect(); | |
| terminal.dispose(); | |
| }, | |
| }; | |
| } | |