Skip to content

Live Demo


View fullpage 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 };

}