mirror of
https://github.com/HeyPuter/puter.git
synced 2026-01-23 21:50:45 -06:00
131 lines
3.5 KiB
JavaScript
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();
|
|
};
|