Files
puter/src/terminal/src/main.js
2024-09-30 18:11:25 -04:00

131 lines
3.5 KiB
JavaScript

/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { ImageAddon } from '@xterm/addon-image';
import { PTY } from './pty/PTY';
import { XDocumentANSIShell } from './pty/XDocumentANSIShell';
class XTermIO {
constructor ({ term, pty }) {
this.term = term;
this.pty = pty;
}
bind () {
this.term.onData(this.handleData.bind(this));
(async () => {
for ( ;; ) {
const chunk = (await this.pty.in.read()).value;
this.term.write(chunk);
}
})();
}
handleData ( data ) {
this.pty.out.write(data);
}
}
const TRUSTED_ORIGINS = [
'https://puter.com',
'https://github.com',
];
/*
* Replaces xterm.js's default link handler to avoid warning users when we link
* to trusted origins.
*/
const linkHandler = {};
linkHandler.activate = (e, url) => {
// check for trusted origins
const uri = new URL(url);
if ( ! TRUSTED_ORIGINS.includes(uri.origin) ) {
const answer = confirm(`Do you want to navigate to ${uri}?\n\nWARNING: This link could potentially be dangerous`);
if ( ! answer ) return;
}
const newWindow = window.open();
if ( ! newWindow ) {
console.warn('Opening link blocked as opener could not be cleared');
return;
}
try {
newWindow.opener = null;
} catch {
// no-op, Electron can throw
}
newWindow.document.write('Redirecting from Puter Terminal...');
newWindow.location.href = uri;
}
window.main_term = async () => {
const pty = new PTY();
const ptt = pty.getPTT();
const shell = new XDocumentANSIShell({
ptt
});
const pos_args = puter.args?.command_line?.args;
const phoenix = pos_args
? await puter.ui.launchApp(pos_args.shift(), {
command_line: { args: pos_args }
})
: await puter.ui.launchApp('phoenix') ;
shell.attachToApp(phoenix);
// Close the shell when we exit
puter.ui.onWindowClose(() => {
phoenix.close();
puter.exit();
});
const termEl = document.createElement('div');
termEl.id = 'terminal';
document.body.append(termEl);
const term = new Terminal({
linkHandler,
});
const imageAddon = new ImageAddon();
term.loadAddon(imageAddon);
term.open(document.getElementById('terminal'));
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
term.onResize(evt => {
shell.resize(evt);
});
fitAddon.fit();
const termObserver = new ResizeObserver(() => {
fitAddon.fit();
});
termObserver.observe(termEl);
const ioController = new XTermIO({ term, pty });
ioController.bind();
term.focus();
};