miller/src/app/terminal.ts

129 lines
3.7 KiB
TypeScript

import { Displayable } from "./editorgrid";
import * as xterm from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import van from "vanjs-core";
const v = van.tags;
export class Terminal implements Displayable {
term: xterm.Terminal;
currentTitle: string = "Terminal";
del: () => void;
dom: HTMLElement;
private terminalId: string | null = null;
private fitAddon: FitAddon;
private resizeObserver: ResizeObserver;
private unsubTerminalData?: () => void;
private unsubTerminalExit?: () => void;
setDeleteFunction(del: () => void): void {
this.del = del;
}
title(): string {
return this.currentTitle;
}
constructor() {
this.term = new xterm.Terminal({
// cursorBlink: true,
// fontSize: 14,
// fontFamily: 'Menlo, Monaco, "Courier New", monospace',
});
this.fitAddon = new FitAddon();
this.term.loadAddon(this.fitAddon);
this.dom = v.div({ class: "h-full w-lg resize-x overflow-x-hidden" });
const loaded = van.state(false);
van.derive(() => {
if (loaded.val) {
this.initializeTerminal();
}
});
loaded.val = true;
}
private async initializeTerminal() {
this.term.open(this.dom);
// Create terminal in main process
try {
this.terminalId = await window.electronAPI.createTerminal();
// Set up data handling (subscribe/unsubscribe)
this.unsubTerminalData = window.electronAPI.onTerminalData(
(id, data) => {
if (id === this.terminalId) {
this.term.write(data);
}
},
);
this.unsubTerminalExit = window.electronAPI.onTerminalExit(
(id, exitCode, signal) => {
if (id === this.terminalId) {
this.term.writeln(
`\r\n[Process exited with code ${exitCode}]`,
);
}
},
);
// Handle user input
this.term.onData((data) => {
if (this.terminalId) {
window.electronAPI.writeToTerminal(this.terminalId, data);
}
});
// Set up resize handling
this.resizeObserver = new ResizeObserver(() => {
this.handleResize();
});
this.resizeObserver.observe(this.dom);
// Initial fit
setTimeout(() => {
this.handleResize();
}, 100);
} catch (error) {
console.error("Failed to initialize terminal:", error);
this.term.writeln(
"Failed to initialize terminal. Check console for details.",
);
}
}
private handleResize() {
if (
this.terminalId &&
this.dom.clientWidth > 0 &&
this.dom.clientHeight > 0
) {
console.log("Old size: ", this.term.rows, this.term.cols);
this.fitAddon.fit();
const { cols, rows } = this.term;
console.log("New size: ", rows, cols);
window.electronAPI.resizeTerminal(this.terminalId, cols, rows);
}
}
focus() {
this.term.focus();
}
close() {
if (this.terminalId) {
window.electronAPI.closeTerminal(this.terminalId);
}
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
if (this.unsubTerminalData) this.unsubTerminalData();
if (this.unsubTerminalExit) this.unsubTerminalExit();
this.term.dispose();
this.del();
}
}