Pi-CLI-Web / src /webTerminal.js
Mike0021's picture
Deploy pi cli web docker server
aab0173 verified
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();
},
};
}