Pulled konsole out into it's own module
This commit is contained in:
157
src/modules/konsole/konsole.js
Normal file
157
src/modules/konsole/konsole.js
Normal file
@@ -0,0 +1,157 @@
|
||||
window.cli = {
|
||||
// Element references
|
||||
_input: null,
|
||||
_parent: null,
|
||||
_prompt: null,
|
||||
_output: null,
|
||||
|
||||
// CLI State
|
||||
_history: [],
|
||||
_index: 0,
|
||||
pwd: '/',
|
||||
hostname: 'virtual',
|
||||
env: {},
|
||||
exec: {},
|
||||
filesystem: {},
|
||||
user: 'root',
|
||||
version: '0.3.0',
|
||||
|
||||
_buildPrompt: () => `${window.cli.user}@${window.cli.hostname}:${window.cli.pwd}${window.cli.user == 'root' ? '#' : '$'}`,
|
||||
|
||||
build: (elementId) => {
|
||||
window.cli._parent = document.querySelector(elementId);
|
||||
if(!window.cli._parent)
|
||||
throw new Error(`Could not create konsole, element "${elementId}" does not exist`);
|
||||
|
||||
window.cli._parent.innerHTML = `
|
||||
<div class="cli-container">
|
||||
<div class="cli-stdout"></div>
|
||||
<div class="cli-stdin" onclick="window.cli._input.focus()">
|
||||
<label for="${elementId}-cli-stdin-input" class="cli-stdin-prompt">${window.cli._buildPrompt()}</label>
|
||||
<input id="${elementId}-cli-stdin-input" class="cli-stdin-input" type="text" />
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
window.cli._input = document.querySelector(elementId + ' .cli-stdin-input');
|
||||
window.cli._prompt = document.querySelector(elementId + ' .cli-stdin-prompt');
|
||||
window.cli._output = document.querySelector(elementId + ' .cli-stdout');
|
||||
window.cli._input.addEventListener('keyup', (e) => {
|
||||
if(e.key == "Enter") {
|
||||
window.cli.disable();
|
||||
if(!!window.cli._input.value) {
|
||||
window.cli._history.push(window.cli._input.value);
|
||||
window.cli._index = window.cli._history.length;
|
||||
window.cli.stdIn(window.cli._input.value)
|
||||
}
|
||||
window.cli._input.value = '';
|
||||
window.cli.enable();
|
||||
} else if(e.key == 'Up' || e.key == 'ArrowUp') {
|
||||
if(window.cli._index > 0) window.cli._index--;
|
||||
window.cli._input.value = window.cli._index == window.cli._history.length ? '' : window.cli._history[window.cli._index];
|
||||
setTimeout(() => {
|
||||
const end = window.cli._input.value.length;
|
||||
window.cli._input.setSelectionRange(end, end);
|
||||
window.cli._input.focus();
|
||||
}, 1)
|
||||
} else if(e.key == 'Down' || e.key == 'ArrowDown') {
|
||||
if(window.cli._index < window.cli._history.length) window.cli._index++;
|
||||
window.cli._input.value = window.cli._index == window.cli._history.length ? '' : window.cli._history[window.cli._index];
|
||||
setTimeout(() => {
|
||||
const end = window.cli._input.value.length;
|
||||
window.cli._input.setSelectionRange(end, end);
|
||||
window.cli._input.focus();
|
||||
}, 1)
|
||||
}
|
||||
});
|
||||
setTimeout(() => window.cli.stdOut(window.cli.exec['banner'].run()), 1);
|
||||
},
|
||||
disable: () => {
|
||||
window.cli._input.disabled = true;
|
||||
window.cli._prompt.style.visibility = 'hidden';
|
||||
},
|
||||
enable: () => {
|
||||
window.cli._input.disabled = false;
|
||||
window.cli._input.focus();
|
||||
window.cli._prompt.style.visibility = 'visible';
|
||||
},
|
||||
path: (path=window.cli.pwd) => {
|
||||
let p = path[0] == '/'? path : (window.cli.pwd + (window.cli.pwd.endsWith('/') ? '' : '/') + path.replace('./', ''))
|
||||
.replaceAll('//', '/');
|
||||
const parts = p.split('/').filter(p => !!p);
|
||||
for(let i = 0; i < parts.length; i++) {
|
||||
if(parts[i] == '..') {
|
||||
i--;
|
||||
parts.splice(i, 2);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return '/' + (parts.length ? parts.join('/') : '');
|
||||
},
|
||||
fs: (path, set) => {
|
||||
return window.cli.path(path).split('/').filter(p => !!p).reduce((t, p, i, arr) => {
|
||||
if(!t?.hasOwnProperty(p)) {
|
||||
if(set == undefined) return undefined;
|
||||
t[p] = {};
|
||||
}
|
||||
if(set !== undefined && i == arr.length - 1) {
|
||||
if(set == null) delete t[p];
|
||||
else t[p] = set;
|
||||
}
|
||||
return t[p];
|
||||
}, window.cli.filesystem);
|
||||
},
|
||||
stdErr: (text) => {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('cli-stdout-line');
|
||||
p.classList.add('cli-stdout-error');
|
||||
p.innerText = text;
|
||||
window.cli._output.appendChild(p);
|
||||
},
|
||||
stdIn:(command, suppress=false) => {
|
||||
(Array.isArray(command) ? command.join(' ') : command).split(';').filter(c => !!c).forEach(c => {
|
||||
const parts = c.match(/(?:[^\s"]+|"[^"]*")+/g);
|
||||
if(!parts) return;
|
||||
|
||||
const exec = window.cli.exec[parts[0]];
|
||||
if(!exec?.run) {
|
||||
if(!suppress) window.cli.stdErr(`${window.cli._buildPrompt()} ${command}\n${parts[0]}: command not found`);
|
||||
} else {
|
||||
try {
|
||||
const args = parts.slice(1).map(a => (a[0] == '"' || a[0] == "'") ? a.slice(1, -1) : a);
|
||||
const out = exec.run(args);
|
||||
if(!suppress) window.cli.stdOut(`${window.cli._buildPrompt()} ${command}${out ? '\n' + out : ''}`);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
if(!suppress) {
|
||||
window.cli._output.removeChild(window.cli._output.children[window.cli._output.children.length - 1]);
|
||||
window.cli.stdErr(`${window.cli._buildPrompt()} ${command}\n${err.message || `${parts[0]}: exited with a non-zero status`}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
stdOut: (text='', html=false) => {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('cli-stdout-line');
|
||||
p[html ? 'innerHTML' : 'innerText'] = text;
|
||||
window.cli._output.appendChild(p);
|
||||
},
|
||||
type: (text, speed=150) => {
|
||||
let counter = 0;
|
||||
return new Promise(res => {
|
||||
let typing = setInterval(() => {
|
||||
if(counter < text.length) {
|
||||
window.cli._input.value += text[counter];
|
||||
} else {
|
||||
clearInterval(typing);
|
||||
setTimeout(() => {
|
||||
window.cli.stdIn(text);
|
||||
window.cli._input.value = '';
|
||||
res();
|
||||
}, 750);
|
||||
}
|
||||
counter++;
|
||||
}, speed);
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user