diff --git a/src/components/konsole.vue b/src/components/konsole.vue
index 31d7dc1..ab1a970 100644
--- a/src/components/konsole.vue
+++ b/src/components/konsole.vue
@@ -1,5 +1,5 @@
-
-
diff --git a/src/misc/konsole.js b/src/misc/konsole.js
deleted file mode 100644
index 2f4e04b..0000000
--- a/src/misc/konsole.js
+++ /dev/null
@@ -1,344 +0,0 @@
-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',
-
- 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 = `
-
-
-
-
-
-
-
`;
-
- 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.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('/') : '');
- },
- prompt: () => `${window.cli.user}@${window.cli.hostname}:${window.cli.pwd}${window.cli.user == 'root' ? '#' : '$'}`,
- 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, silent=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) {
- window.cli.stdErr(`${window.cli.prompt()} ${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(!silent) window.cli.stdOut(`${window.cli.prompt()} ${command}${out ? '\n' + out : ''}`);
- } catch(err) {
- console.error(err);
- window.cli.stdErr(`${window.cli.prompt()} ${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);
- });
- }
-};
-
-window.cli.exec['banner'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Display login banner';
- },
- run: args => {
- window.cli.stdOut(`Konsole ${window.cli.version} LTS ${window.cli.hostname} tty1\n\n${window.cli.hostname} login: ${window.cli.user}\npassword:\n\n`);
- }
-}
-window.cli.exec['cd'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Clear console output';
- },
- run: args => {
- const path = window.cli.fs(args[0]);
- if(!path) throw new Error(`cd: \'${args[0]}\': No such file or directory`)
-
- window.cli.pwd = window.cli.path(args[0]);
- window.cli._prompt.innerText = window.cli.prompt();
- }
-}
-window.cli.exec['clear'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Clear console output';
- },
- run: args => {
- setTimeout(() => window.cli._output.innerHTML = '', 1);
- }
-}
-window.cli.exec['date'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Get current date & time';
- },
- run: args => {
- return (new Date()).toLocaleString();
- }
-}
-window.cli.exec['echo'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Output text to console';
- },
- run: args => {
- return args.join(' ');
- }
-}
-window.cli.exec['exit'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'End session';
- },
- run: args => {
- window.cli.stdIn('clear');
- window.cli._history = [];
- window.cli._index = 0;
- window.cli.stdIn('banner');
- }
-}
-window.cli.exec['help'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Display all commands';
- },
- run: args => {
- return `Konsole v${window.cli.version} - A prototype bash emulator written by Zakary Timson\n\n` +
- Object.keys(window.cli.exec).map(command => `${command} - ${window.cli.exec[command].help()}`).join('\n');
- }
-}
-window.cli.exec['hostname'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Get computer hostname';
- },
- run: args => {
- return window.cli.hostname;
- }
-}
-window.cli.exec['ls'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Display directory contents';
- },
- run: args => {
- const target = window.cli.fs(args[0]);
- if(!target) throw new Error(`ls: cannot access \'${args[0]}\': No such file or directory`)
- return Object.keys(target).reduce((acc, p) => acc + `${p}\n`, '');
- }
-}
-window.cli.exec['man'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'View command\'s manual';
- },
- run: args => {
- return window.cli.exec[args[0]].help();
- }
-}
-window.cli.exec['mkdir'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Create new directory';
- },
- run: args => {
- if(!args[0]) throw new Error('mkdir: missing operand');
- window.cli.fs(args[0], {});
- }
-}
-window.cli.exec['pwd'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Get present working directory';
- },
- run: args => {
- return window.cli.pwd;
- }
-}
-window.cli.exec['rm'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Delete file or directory';
- },
- run: args => {
- if(!args[0]) throw new Error('rm: missing operand');
- window.cli.fs(args[0], null);
- }
-}
-window.cli.exec['shower-thought'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Random shower thought';
- },
- run: args => {
- const motd = [
- 'Why do kamikaze pilots wear helmets?',
- 'How are giraffes real, but unicorns made up',
- 'When you are a kid you don\'t realize you are also watching your parents grow up',
- 'Some one at Google was like "Yea, just have someone drive down every road on earth!"',
- 'The number of people older than you never goes up',
- 'When you brush your teeth you are cleaning your skeleton',
- 'Pregnancy is like a group project where one person get\'s stuck with all the work',
- 'If the universe wasn\'t infinite it would be even scarier',
- 'Either we are alone in the universe or we are not. both are terrifying',
- 'The object of golf is to play the least amount of golf.'
- ];
- return motd[~~(Math.random() * motd.length)];
- }
-}
-window.cli.exec['whoami'] = {
- autocomplete: () => {
- return [];
- },
- help: () => {
- return 'Get current user account';
- },
- run: args => {
- return window.cli.user;
- }
-}
diff --git a/src/modules/konsole/commands/banner.js b/src/modules/konsole/commands/banner.js
new file mode 100644
index 0000000..113976e
--- /dev/null
+++ b/src/modules/konsole/commands/banner.js
@@ -0,0 +1,11 @@
+window.cli.exec['banner'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Display login banner';
+ },
+ run: args => {
+ return `Konsole ${window.cli.version} LTS ${window.cli.hostname} tty1\n\n${window.cli.hostname} login: ${window.cli.user}\npassword:\n\n`;
+ }
+}
diff --git a/src/modules/konsole/commands/cd.js b/src/modules/konsole/commands/cd.js
new file mode 100644
index 0000000..14997e5
--- /dev/null
+++ b/src/modules/konsole/commands/cd.js
@@ -0,0 +1,15 @@
+window.cli.exec['cd'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Clear console output';
+ },
+ run: args => {
+ const path = window.cli.fs(args[0]);
+ if(!path) throw new Error(`cd: \'${args[0]}\': No such file or directory`)
+
+ window.cli.pwd = window.cli.path(args[0]);
+ window.cli._prompt.innerText = window.cli._buildPrompt();
+ }
+}
diff --git a/src/modules/konsole/commands/clear.js b/src/modules/konsole/commands/clear.js
new file mode 100644
index 0000000..2342a66
--- /dev/null
+++ b/src/modules/konsole/commands/clear.js
@@ -0,0 +1,11 @@
+window.cli.exec['clear'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Clear console output';
+ },
+ run: args => {
+ setTimeout(() => window.cli._output.innerHTML = '', 1);
+ }
+}
diff --git a/src/modules/konsole/commands/date.js b/src/modules/konsole/commands/date.js
new file mode 100644
index 0000000..2c62321
--- /dev/null
+++ b/src/modules/konsole/commands/date.js
@@ -0,0 +1,11 @@
+window.cli.exec['date'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Get current date & time';
+ },
+ run: args => {
+ return new Date().toLocaleString();
+ }
+}
diff --git a/src/modules/konsole/commands/echo.js b/src/modules/konsole/commands/echo.js
new file mode 100644
index 0000000..ac0a8a2
--- /dev/null
+++ b/src/modules/konsole/commands/echo.js
@@ -0,0 +1,11 @@
+window.cli.exec['echo'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Output text to console';
+ },
+ run: args => {
+ return args.join(' ');
+ }
+}
diff --git a/src/modules/konsole/commands/exit.js b/src/modules/konsole/commands/exit.js
new file mode 100644
index 0000000..524dc00
--- /dev/null
+++ b/src/modules/konsole/commands/exit.js
@@ -0,0 +1,16 @@
+window.cli.exec['exit'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'End session';
+ },
+ run: args => {
+ setTimeout(() => {
+ window.cli._history = [];
+ window.cli._index = 0;
+ window.cli._output.innerHTML = ''
+ window.cli.stdOut(window.cli.exec['banner'].run());
+ }, 1);
+ }
+}
diff --git a/src/modules/konsole/commands/help.js b/src/modules/konsole/commands/help.js
new file mode 100644
index 0000000..33101ef
--- /dev/null
+++ b/src/modules/konsole/commands/help.js
@@ -0,0 +1,12 @@
+window.cli.exec['help'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Display all commands';
+ },
+ run: args => {
+ return `Konsole v${window.cli.version} - an experimental bash emulator written in JavaScript\nCreated By: Zakary Timson\n\n` +
+ Object.keys(window.cli.exec).map(command => `${command} - ${window.cli.exec[command].help()}`).join('\n');
+ }
+}
diff --git a/src/modules/konsole/commands/hostname.js b/src/modules/konsole/commands/hostname.js
new file mode 100644
index 0000000..e4a92ed
--- /dev/null
+++ b/src/modules/konsole/commands/hostname.js
@@ -0,0 +1,11 @@
+window.cli.exec['hostname'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Get computer hostname';
+ },
+ run: args => {
+ return window.cli.hostname;
+ }
+}
diff --git a/src/modules/konsole/commands/ls.js b/src/modules/konsole/commands/ls.js
new file mode 100644
index 0000000..19e1896
--- /dev/null
+++ b/src/modules/konsole/commands/ls.js
@@ -0,0 +1,13 @@
+window.cli.exec['ls'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Display directory contents';
+ },
+ run: args => {
+ const target = window.cli.fs(args[0]);
+ if(!target) throw new Error(`ls: cannot access \'${args[0]}\': No such file or directory`)
+ return Object.keys(target).reduce((acc, p) => acc + `${p}\n`, '');
+ }
+}
diff --git a/src/modules/konsole/commands/man.js b/src/modules/konsole/commands/man.js
new file mode 100644
index 0000000..74bccc2
--- /dev/null
+++ b/src/modules/konsole/commands/man.js
@@ -0,0 +1,11 @@
+window.cli.exec['man'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'View command\'s manual';
+ },
+ run: args => {
+ return window.cli.exec[args[0]].help();
+ }
+}
diff --git a/src/modules/konsole/commands/mkdir.js b/src/modules/konsole/commands/mkdir.js
new file mode 100644
index 0000000..930cbce
--- /dev/null
+++ b/src/modules/konsole/commands/mkdir.js
@@ -0,0 +1,12 @@
+window.cli.exec['mkdir'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Create new directory';
+ },
+ run: args => {
+ if(!args[0]) throw new Error('mkdir: missing operand');
+ window.cli.fs(args[0], {});
+ }
+}
diff --git a/src/modules/konsole/commands/pwd.js b/src/modules/konsole/commands/pwd.js
new file mode 100644
index 0000000..2cb80f0
--- /dev/null
+++ b/src/modules/konsole/commands/pwd.js
@@ -0,0 +1,11 @@
+window.cli.exec['pwd'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Get present working directory';
+ },
+ run: args => {
+ return window.cli.pwd;
+ }
+}
diff --git a/src/modules/konsole/commands/rm.js b/src/modules/konsole/commands/rm.js
new file mode 100644
index 0000000..cab3bb8
--- /dev/null
+++ b/src/modules/konsole/commands/rm.js
@@ -0,0 +1,12 @@
+window.cli.exec['rm'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Delete file or directory';
+ },
+ run: args => {
+ if(!args[0]) throw new Error('rm: missing operand');
+ window.cli.fs(args[0], null);
+ }
+}
diff --git a/src/modules/konsole/commands/shower-thoughts.js b/src/modules/konsole/commands/shower-thoughts.js
new file mode 100644
index 0000000..0ff15b3
--- /dev/null
+++ b/src/modules/konsole/commands/shower-thoughts.js
@@ -0,0 +1,23 @@
+window.cli.exec['shower-thought'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Random shower thought';
+ },
+ run: args => {
+ const motd = [
+ 'Why do kamikaze pilots wear helmets?',
+ 'How are giraffes real, but unicorns made up',
+ 'When you are a kid you don\'t realize you are also watching your parents grow up',
+ 'Some one at Google was like "Yea, just have someone drive down every road on earth!"',
+ 'The number of people older than you never goes up',
+ 'When you brush your teeth you are cleaning your skeleton',
+ 'Pregnancy is like a group project where one person get\'s stuck with all the work',
+ 'If the universe wasn\'t infinite it would be even scarier',
+ 'Either we are alone in the universe or we are not. both are terrifying',
+ 'The object of golf is to play the least amount of golf.'
+ ];
+ return motd[~~(Math.random() * motd.length)];
+ }
+}
diff --git a/src/modules/konsole/commands/whoami.js b/src/modules/konsole/commands/whoami.js
new file mode 100644
index 0000000..0837f08
--- /dev/null
+++ b/src/modules/konsole/commands/whoami.js
@@ -0,0 +1,11 @@
+window.cli.exec['whoami'] = {
+ autocomplete: () => {
+ return [];
+ },
+ help: () => {
+ return 'Get current user account';
+ },
+ run: args => {
+ return window.cli.user;
+ }
+}
diff --git a/src/modules/konsole/index.js b/src/modules/konsole/index.js
new file mode 100644
index 0000000..290bd5e
--- /dev/null
+++ b/src/modules/konsole/index.js
@@ -0,0 +1,19 @@
+import './konsole.js';
+import './konsole.css';
+
+// CLI Commands
+import './commands/banner.js';
+import './commands/cd.js'
+import './commands/clear.js';
+import './commands/date.js';
+import './commands/echo.js';
+import './commands/exit.js';
+import './commands/help.js';
+import './commands/hostname.js';
+import './commands/ls.js';
+import './commands/man.js';
+import './commands/mkdir.js';
+import './commands/pwd.js';
+import './commands/rm.js';
+import './commands/shower-thoughts.js';
+import './commands/whoami.js';
diff --git a/src/modules/konsole/konsole.css b/src/modules/konsole/konsole.css
new file mode 100644
index 0000000..0cf087f
--- /dev/null
+++ b/src/modules/konsole/konsole.css
@@ -0,0 +1,39 @@
+.cli-container {
+ padding: 1em;
+ background: #333;
+ font-family: monospace !important;
+ overflow-y: auto;
+ min-height: 150px;
+ max-height: 300px;
+}
+
+.cli-stdout {
+ flex-grow: 1;
+ color: #0f0;
+}
+
+.cli-stdout-line {
+ padding: 0;
+ margin: 0;
+ min-height: 1em;
+}
+
+.cli-stdin {
+ display: flex;
+}
+
+.cli-stdin-prompt {
+ padding-right: 0.5em;
+ text-wrap: nowrap;
+ color: #0f0;
+}
+
+.cli-stdin-input {
+ border: none;
+ outline: none;
+ font-size: 1em;
+ background-color: rgba(0, 0, 0, 0);
+ color: #0f0;
+ flex-grow: 1;
+ padding: 0;
+}
diff --git a/src/modules/konsole/konsole.js b/src/modules/konsole/konsole.js
new file mode 100644
index 0000000..b06d9d0
--- /dev/null
+++ b/src/modules/konsole/konsole.js
@@ -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 = `
+
+
+
+
+
+
+
`;
+
+ 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);
+ });
+ }
+};