Live Demo
My First Terminal
Code
css
@import url('https://unpkg.com/xterminal/dist/xterminal.css');
.error {
color: rgb(248, 88, 88);
}
.spinner:after {
animation: changeContent 0.8s linear infinite;
content: "⠋";
}
@keyframes changeContent {
10% { content: "⠙"; }
20% { content: "⠹"; }
30% { content: "⠸"; }
40% { content: "⠼"; }
50% { content: "⠴"; }
60% { content: "⠦"; }
70% { content: "⠧"; }
80% { content: "⠇"; }
90% { content: "⠏"; }
}
html
<link rel="stylesheet" href="styles.css">
<div id="app"></div>
<script src="https://unpkg.com/xterminal/dist/xterminal.umd.js"></script>
<script src="createShell.js"></script>
<script src="createTerminal.js"></script>
<script>
window.onload = () => createTerminal('#app');
</script>
js
function createTerminal(target) {
const term = new XTerminal({ target });
const state = {
username: "user",
hostname: "web"
};
// input evaluator
const shell = createShell();
// print prompt and get ready for user input
function promptUser() {
term.write(`┌[${state.username}@${state.hostname}]\n`);
term.write("└$ ");
term.resume();
term.focus();
}
// user input handler
term.on("data", async input => {
// deactivate until the execution is done
term.pause();
// execute command
await shell.execute(term, input)
.then(res => res && term.writeln(res))
.catch(err => err && term.writeln(`<span class="error">${err}</span>\n`))
.finally(promptUser);
});
// greeting message
term.writeln("Welcome to XTerminal (v" + XTerminal.version + ")");
term.writeln("Type `help` for available commands\n");
// kickstart
promptUser();
// remember to free resources
window.addEventListener('unload', () => term.dispose());
}
js
function createShell() {
// Help
const manual = `XTerminal : version ${XTerminal.version}
Type 'help' to see this list
Commands:
gh (username) search for github users
js [expr] execute a JS expression
clear clear the terminal screen
help display this list
`;
// Get public github user information
async function fetchGitHubUser(username) {
return fetch('https://api.github.com/users/' + username)
.then(res => res.json())
.then(res => {
return(
'<table border="0">' +
'<tr>' +
`<td rowspan="3" width="100"><img width="75" src="${res.avatar_url}" alt="${res.name}" /></td>` +
`<td>Name</td>` +
`<td>${res.name}</td>` +
'</tr>' +
'<tr>' +
`<td>Bio</td>` +
`<td>${res.bio}</td>` +
'</tr>' +
'<tr>' +
`<td>Repos</td>` +
`<td>${res.public_repos}</td>` +
'</tr>' +
'</table>'
);
});
}
// evaluate user input from the terminal
// -> can be shared among several terminal objects
function execute(term, command = '') {
let args = command.split(' ');
let cmd = args.shift();
// GitHub User Search
if (cmd == 'gh') {
return new Promise(async (res, rej) => {
let output, error;
term.write('<span class="spinner"></span> Searching...');
await fetchGitHubUser(args.join(''))
.then(val => output = val)
.catch(err => error = ':( Not found!')
.finally(() => term.clearLast());
if (error) rej(error);
else res(output);
});
}
// JavaScript Evaluation
else if (cmd == 'js') {
return new Promise((res, rej) => {
try {
let output = eval(args.join(' ')) + '\n';
res(output);
} catch (error) {
rej(error);
}
});
}
// Help menu
else if (cmd == 'help') {
return Promise.resolve(manual);
}
// Clear the terminal
else if (cmd == 'clear') {
term.clear();
return Promise.resolve(null);
}
// Oopps!
else {
return Promise.reject(`sh: '${cmd}' command not found`);
}
}
return { execute };
}