From c7a5215fe1d63a2a32725134bfb5ee9b27ccc2b1 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Mon, 11 Sep 2017 20:06:42 +0200 Subject: [PATCH 1/8] Add basic terminal echoing input --- base.php | 1 - build.sh | 1 + js/demo.js | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ js/term_conn.js | 4 +- pages/_head.php | 1 - 5 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 js/demo.js diff --git a/base.php b/base.php index 717365d..9181445 100644 --- a/base.php +++ b/base.php @@ -25,7 +25,6 @@ define('JS_WEB_ROOT', $root); define('ESP_DEMO', (bool)getenv('ESP_DEMO')); if (ESP_DEMO) { - define('DEMO_SCREEN', '"S\u0019\u0001Q\u0001\u0018\u0001P\u0001\u0014\u0003\u0001\u0005\b\u0001 \u0002P\u0001\u0005\u0005\u0001\u0004\u0002\u0001~ $ \u0005\b\u0001\u0004\u0001\u0001archey3|lolcat -F .3 \u0002\t\u0002\u0005^\u0001 \u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 +\u0005,\u0001 \u00052\u0001 \u00051\u0001 \u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 O\u0005\u0016\u0002S\u0005:\u0002: A\u00054\u0002r\u0005X\u0002ch \u0005R\u0002Lin\u0005M\u0002ux x\u0005H\u000286_\u0005I\u000264\u0005\b\u0001 \u0002\u001b\u0001\u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 # \u00051\u0001 \u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 Ho\u00054\u0002s\u0005X\u0002tna\u0005R\u0002me:\u0005M\u0002 N20\u0005H\u00022\u0005\b\u0001 \u0002\"\u0001\u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001###\u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002Ker\u0005R\u0002nel\u0005M\u0002 Rel\u0005H\u0002eas\u0005I\u0002e: \u0005%\u00024\u0005&\u0002.9.\u0005\u0002\u00024\u0005\u0003\u00027-1\u0005^\u0001-lt\u0005@\u0001s\u0005\b\u0001 \u0002\u0014\u0001\u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001 #\u0005T\u0001####\u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002 \u0005R\u0002Upt\u0005M\u0002ime:\u0005H\u0002 19\u0005I\u0002:26\u0005\b\u0001 \u0002#\u0001\u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001 \u0005T\u0001 ##\u0005w\u0001###\u0005\u001c\u0002# \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002 \u0005R\u0002 \u0005M\u0002WM: \u0005H\u0002KWi\u0005I\u0002n\u0005\b\u0001 \u0002(\u0001\u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001 \u0005T\u0001 \u0005w\u0001; #\u0005\u001c\u0002###\u0005\u0016\u0002#\u0005:\u0002; \u00054\u0002 \u0005X\u0002 \u0005R\u0002 \u0005M\u0002 D\u0005H\u0002E: \u0005I\u0002KDE\u0005\b\u0001 \u0002)\u0001\u00052\u0001 \u00051\u0001 \u0005T\u0001 \u0005w\u0001 +\u0005\u001c\u0002##.\u0005\u0016\u0002#\u0005:\u0002###\u00054\u0002#\u0005X\u0002 \u0005R\u0002 \u0005M\u0002 \u0005H\u0002 P\u0005I\u0002ack\u0005%\u0002a\u0005&\u0002ges\u0005\u0002\u0002:\u0005\u0003\u0002 18\u0005^\u000121\u0005\b\u0001 \u0002\"\u0001\u00051\u0001 \u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 +#\u0005\u0016\u0002#\u0005:\u0002###\u00054\u0002#\u0005X\u0002###\u0005R\u0002# \u0005M\u0002 \u0005H\u0002 \u0005I\u0002 R\u0005%\u0002A\u0005&\u0002M: \u0005\u0002\u00029\u0005\u0003\u0002256\u0005^\u0001 MB\u0005@\u0001 / 1\u0005\"\u0001599\u0005(\u00019 M\u0005\'\u0001B\u0005\b\u0001 \u0002\u0019\u0001\u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002#\u0005:\u0002###\u00054\u0002#\u0005X\u0002###\u0005R\u0002###\u0005M\u0002##; \u0005H\u0002 \u0005I\u0002 \u0005%\u0002 \u0005&\u0002 Pr\u0005\u0002\u0002o\u0005\u0003\u0002ces\u0005^\u0001sor\u0005@\u0001 Typ\u0005\"\u0001e: \u0005(\u0001Int\u0005\'\u0001e\u0005-\u0001l(R\u0005,\u0001)\u00052\u0001 Co\u00051\u0001re(\u0005T\u0001TM) \u0005w\u0001i5-\u0005\u001c\u0002640\u0005\u0016\u00020\u0005:\u0002 CP\u00054\u0002U\u0005X\u0002 @ \u0005R\u00022.7\u0005M\u00020GHz\u0005R\u0002 \u0002I\u0001\u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 ##\u00054\u0002#\u0005X\u0002###\u0005R\u0002###\u0005M\u0002####\u0005H\u0002##+\u0005I\u0002 \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002$ED\u0005^\u0001ITO\u0005@\u0001R: n\u0005\"\u0001ano\u0005\b\u0001 \u0002#\u0001\u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002#\u0005X\u0002###\u0005R\u0002###\u0005M\u0002 #\u0005H\u0002###\u0005I\u0002###\u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001Roo\u0005@\u0001t: 1\u0005\"\u000160G\u0005(\u0001 / \u0005\'\u00011\u0005-\u000196G\u0005,\u0001 \u00052\u0001(81\u00051\u0001%) \u0005T\u0001(ext\u0005w\u00014)\u0005\b\u0001 \u0002\u0012\u0001\u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002.##\u0005R\u0002###\u0005M\u0002#; \u0005H\u0002 \u0005I\u0002;##\u0005%\u0002#\u0005&\u0002;`\"\u0005\u0002\u0002.\u0005\u0003\u0002 \u0005^\u0001 \u0005\b\u0001 \u00020\u0001\u0005:\u0002 \u00054\u0002 \u0005X\u0002 .\u0005R\u0002###\u0005M\u0002####\u0005H\u0002; \u0005I\u0002 \u0005%\u0002;\u0005&\u0002###\u0005\u0002\u0002#\u0005\u0003\u0002#. \u0005^\u0001 \u0005@\u0001 \u0005\b\u0001 \u00020\u0001\u0005X\u0002 \u0005R\u0002 #\u0005M\u0002####\u0005H\u0002###\u0005I\u0002#. \u0005%\u0002 \u0005&\u0002 .#\u0005\u0002\u0002#\u0005\u0003\u0002###\u0005^\u0001###\u0005@\u0001` \u0005\"\u0001 \u0005\b\u0001 \u00020\u0001\u0005R\u0002 \u0005M\u0002 ###\u0005H\u0002###\u0005I\u0002\' \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \'#\u0005@\u0001####\u0005\"\u0001# \u0005(\u0001 \u0005\b\u0001 \u00020\u0001\u0005M\u0002 ;\u0005H\u0002###\u0005I\u0002# \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \u0005@\u0001 \u0005\"\u0001###\u0005(\u0001#; \u0005\'\u0001 \u0005-\u0001 \u0005\b\u0001 \u00020\u0001\u0005H\u0002 #\u0005I\u0002#\' \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \'\u0005\'\u0001#\u0005-\u0001# \u0005,\u0001 \u0005\b\u0001 \u00020\u0001\u0005I\u0002 #\'\u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001`\u00052\u0001# \u0005\b\u0001 \u0002\u0001\u0002\u0005\u0005\u0001\u0004\u0002\u0001~ $ \u0005\b\u0001\u0004\u0001\u0001This is a static demo of the web interface. Have a look through the menus ↓ "'); define('DEMO_APS', <<= this.height) this.scroll() + } + writeChar (character) { + this.screen[this.cursor.y * this.width + this.cursor.x] = [character, this.style] + this.cursor.x++ + if (this.cursor.x >= this.width) { + this.cursor.x = 0 + this.newLine() + } + } + deleteChar () { + this.cursor.x-- + if (this.cursor.x < 0) { + this.cursor.x = this.width - 1 + this.cursor.y = Math.max(0, this.cursor.y - 1) + } + this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) + this.screen.splice(this.cursor.y * this.width + this.cursor.x, 1) + } + clampCursor () { + if (this.cursor.x < 0) this.cursor.x = 0 + if (this.cursor.y < 0) this.cursor.y = 0 + if (this.cursor.x > this.width - 1) this.cursor.x = this.width - 1 + if (this.cursor.y > this.height - 1) this.cursor.y = this.height - 1 + } + applySequence (sequence) { + if (sequence[0] === '[') { + let type = sequence[sequence.length - 1] + let content = sequence.substring(1, sequence.length - 1) + + let numbers = content ? content.split(';').map(i => +i.replace(/\D/g, '')) : [] + let numOr1 = numbers.length ? numbers[0] : 1 + if (type === 'H') { + this.cursor.x = (numbers[0] | 0) - 1 + this.cursor.y = (numbers[1] | 0) - 1 + this.clampCursor() + } else if (type >= 'A' && type <= 'D') { + this.cursor[type <= 'B' ? 'y' : 'x'] += ((type === 'B' || type === 'C') ? 1 : -1) * numOr1 + this.clampCursor() + } else if (type === 'E' || type === 'F') { + this.cursor.x = 0 + this.cursor.y += (type === 'E' ? 1 : -1) * numOr1 + this.clampCursor() + } else if (type === 'G') { + this.cursor.x = numOr1 - 1 + this.clampCursor() + } else if (type === 'q') this.cursor.style = numOr1 + else if (type === 'm') { + if (!numbers.length) { + this.style = TERM_DEFAULT_STYLE + return + } + let type = numbers[0] + if (type === 1) this.style |= 1 << 16 // bold + else if (type === 2) this.style |= 1 << 1 << 16 // faint + else if (type === 3) this.style |= 1 << 2 << 16 // italic + else if (type === 4) this.style |= 1 << 3 << 16 // underline + else if (type === 5 || type === 6) this.style |= 1 << 4 << 16 // blink + else if (type === 7) { + // invert + this.style = (this.style & 0xFF0000) | ((this.style >> 8) & 0xFF) | ((this.style & 0xFF) << 8) + } else if (type === 9) this.style |= 1 << 6 << 16 // strike + else if (type === 20) this.style |= 1 << 5 << 16 // fraktur + else if (type >= 30 && type <= 38) this.style = (this.style & 0xFFFF00) | (type % 10) + else if (type >= 40 && type <= 48) this.style = (this.style & 0xFF00FF) | ((type % 10) << 8) + else if (type === 39) this.style = (this.style & 0xFFFF00) | 7 + else if (type === 49) this.style = (this.style & 0xFF00FF) | (7 << 8) + else if (type >= 90 && type <= 98) this.style = (this.style & 0xFFFF00) | ((type % 10) + 8) + else if (type >= 100 && type <= 108) this.style = (this.style & 0xFF00FF) | (((type % 10) + 8) << 8) + else if (type === 38 || type === 48) { + if (numbers[1] === 5) { + let color = (numbers[2] | 0) & 0xFF + if (type === 38) this.style = (this.style & 0xFFFF00) | color + if (type === 48) this.style = (this.style & 0xFF00FF) | (color << 8) + } + } + } + } + } + write (text) { + for (let character of text) { + let code = character.codePointAt(0) + if (code === 0x1b) this.currentSequence = 1 + else if (this.currentSequence === 1 && character === '[') { + this.currentSequence = 2 + this.sequence += '[' + } else if (this.currentSequence && character.match(/[\x40-\x7e]/)) { + this.applySequence(this.sequence + character) + this.currentSequence = 0 + this.sequence = '' + } else if (this.currentSequence > 1) this.sequence += character + else if (this.currentSequence === 1) { + // something something nothing + this.currentSequence = 0 + this.writeChar(character) + } else if (code === 0x07) this.termScreen.load('B') + else if (code === 0x08) this.deleteChar() + else if (code === 0x0a) this.newLine() + else if (code === 0x0d) this.cursor.x = 0 + else this.writeChar(character) + } + this.scheduleLoad() + } + serialize () { + let serialized = 'S' + serialized += encode2B(this.height) + encode2B(this.width) + serialized += encode2B(this.cursor.y) + encode2B(this.cursor.x) + + let attributes = 1 // cursor always visible + attributes |= (3 << 5) * +this.trackMouse // track mouse controls both + attributes |= 3 << 7 // buttons/links always visible + attributes |= (this.cursor.shape << 9) + serialized += encode3B(attributes) + + let lastStyle = null + for (let cell of this.screen) { + if (cell[1] !== lastStyle) { + let foreground = cell[1] & 0xFF + let background = (cell[1] >> 8) & 0xFF + let attributes = (cell[1] >> 16) & 0xFF + let setForeground = foreground !== (lastStyle & 0xFF) + let setBackground = background !== ((lastStyle >> 8) & 0xFF) + let setAttributes = attributes !== ((lastStyle >> 16) & 0xFF) + + if (setForeground && setBackground) serialized += '\x03' + encode3B(cell[1] & 0xFFFF) + else if (setForeground) serialized += '\x05' + encode2B(foreground) + else if (setBackground) serialized += '\x06' + encode2B(background) + if (setAttributes) serialized += '\x04' + encode2B(attributes) + + lastStyle = cell[1] + } + serialized += cell[0] + } + return serialized + } + scheduleLoad () { + clearInterval(this._scheduledLoad) + if (this._lastLoad < Date.now() - TERM_MIN_DRAW_DELAY) { + this.termScreen.load(this.serialize()) + } else { + this._scheduledLoad = setTimeout(() => { + this.termScreen.load(this.serialize()) + }, TERM_MIN_DRAW_DELAY - this._lastLoad) + } + } +} + +window.demoInterface = { + input (data) { + let type = data[0] + let content = data.substr(1) + + if (type === 's') { + this.terminal.write(content) + } else if (type === 'b') { + let button = content.charCodeAt(0) + console.log(`button ${button} pressed`) + } else if (type === 'm' || type === 'p' || type === 'r') { + console.log(JSON.stringify(data)) + } + }, + init (screen) { + this.terminal = new ScrollingTerminal(screen) + } +} diff --git a/js/term_conn.js b/js/term_conn.js index 4521e3d..65506d7 100644 --- a/js/term_conn.js +++ b/js/term_conn.js @@ -67,7 +67,7 @@ window.Conn = function (screen) { function doSend (message) { if (_demo) { - console.log('TX: ', message) + window.demoInterface.input(message) return true // Simulate success } if (xoff) { @@ -91,7 +91,7 @@ window.Conn = function (screen) { function init () { if (window._demo) { console.log('Demo mode!') - screen.load(_demo_screen) + demoInterface.init(screen) showPage() return } diff --git a/pages/_head.php b/pages/_head.php index 3a39b02..cd47c53 100644 --- a/pages/_head.php +++ b/pages/_head.php @@ -10,7 +10,6 @@ From ff8f2cef810548d6f505b8beae1b93adeedc97ce Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Mon, 11 Sep 2017 22:08:22 +0200 Subject: [PATCH 2/8] Add basic shell --- js/demo.js | 298 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 217 insertions(+), 81 deletions(-) diff --git a/js/demo.js b/js/demo.js index 1cce42b..099e106 100644 --- a/js/demo.js +++ b/js/demo.js @@ -1,3 +1,90 @@ +class ANSIParser { + constructor (handler) { + this.reset() + this.handler = handler + this.joinChunks = true + } + reset () { + this.currentSequence = 0 + this.sequence = '' + } + parseSequence (sequence) { + if (sequence[0] === '[') { + let type = sequence[sequence.length - 1] + let content = sequence.substring(1, sequence.length - 1) + + let numbers = content ? content.split(';').map(i => +i.replace(/\D/g, '')) : [] + let numOr1 = numbers.length ? numbers[0] : 1 + if (type === 'H') { + this.handler('set-cursor', (numbers[0] | 0) - 1, (numbers[1] | 0) - 1) + } else if (type >= 'A' && type <= 'D') { + this.handler(`move-cursor-${type <= 'B' ? 'y' : 'x'}`, ((type === 'B' || type === 'C') ? 1 : -1) * numOr1) + } else if (type === 'E' || type === 'F') { + this.handler('move-cursor-line', (type === 'E' ? 1 : -1) * numOr1) + } else if (type === 'G') { + this.handler('set-cursor-x', numOr1 - 1) + } else if (type === 'P') { + this.handler('delete', numOr1) + } else if (type === '@') { + this.handler('insert-blanks', numOr1) + } else if (type === 'q') this.handler('set-cursor-style', numOr1) + else if (type === 'm') { + if (!numbers.length || numbers[0] === 0) { + this.handler('reset-style') + return + } + let type = numbers[0] + if (type === 1) this.handler('set-attrs', 1) // bold + else if (type === 2) this.handler('set-attrs', 1 << 1) // faint + else if (type === 3) this.handler('set-attrs', 1 << 2) // italic + else if (type === 4) this.handler('set-attrs', 1 << 3) // underline + else if (type === 5 || type === 6) this.handler('set-attrs', 1 << 4) // blink + else if (type === 7) this.handler('set-attrs', -1) // invert + else if (type === 9) this.handler('set-attrs', 1 << 6) // strike + else if (type === 20) this.handler('set-attrs', 1 << 5) // fraktur + else if (type >= 30 && type <= 37) this.handler('set-color-fg', type % 10) + else if (type >= 40 && type <= 47) this.handler('set-color-bg', type % 10) + else if (type === 39) this.handler('set-color-fg', 7) + else if (type === 49) this.handler('set-color-bg', 0) + else if (type >= 90 && type <= 98) this.handler('set-color-fg', (type % 10) + 8) + else if (type >= 100 && type <= 108) this.handler('set-color-bg', (type % 10) + 8) + else if (type === 38 || type === 48) { + if (numbers[1] === 5) { + let color = (numbers[2] | 0) & 0xFF + if (type === 38) this.handler('set-color-fg', color) + if (type === 48) this.handler('set-color-bg', color) + } + } + } + } + } + write (text) { + for (let character of text.toString()) { + let code = character.codePointAt(0) + if (code === 0x1b) this.currentSequence = 1 + else if (this.currentSequence === 1 && character === '[') { + this.currentSequence = 2 + this.sequence += '[' + } else if (this.currentSequence && character.match(/[\x40-\x7e]/)) { + this.parseSequence(this.sequence + character) + this.currentSequence = 0 + this.sequence = '' + } else if (this.currentSequence > 1) this.sequence += character + else if (this.currentSequence === 1) { + // something something nothing + this.currentSequence = 0 + this.handler('write', character) + } else if (code === 0x07) this.handler('bell') + else if (code === 0x08) this.handler('back') + else if (code === 0x0a) this.handler('new-line') + else if (code === 0x0d) this.handler('return') + else if (code === 0x15) this.handler('delete-line') + else if (code === 0x17) this.handler('delete-word') + else this.handler('write', character) + } + if (!this.joinChunks) this.reset() + } +} const TERM_DEFAULT_STYLE = 7 const TERM_MIN_DRAW_DELAY = 10 @@ -6,6 +93,7 @@ class ScrollingTerminal { this.width = 80 this.height = 25 this.termScreen = screen + this.parser = new ANSIParser((...args) => this.handleParsed(...args)) this.reset() @@ -16,8 +104,7 @@ class ScrollingTerminal { this.style = TERM_DEFAULT_STYLE this.cursor = { x: 0, y: 0, style: 1 } this.trackMouse = false - this.currentSequence = 0 - this.sequence = '' + this.parser.reset() this.clear() } clear () { @@ -45,97 +132,91 @@ class ScrollingTerminal { this.newLine() } } - deleteChar () { - this.cursor.x-- - if (this.cursor.x < 0) { - this.cursor.x = this.width - 1 - this.cursor.y = Math.max(0, this.cursor.y - 1) + moveBack (n = 1) { + for (let i = 0; i < n; i++) { + this.cursor.x-- + if (this.cursor.x < 0) { + if (this.cursor.y > 0) this.cursor.x = this.width - 1 + else this.cursor.x = 0 + this.cursor.y = Math.max(0, this.cursor.y - 1) + } + } + } + moveForward (n = 1) { + for (let i = 0; i < n; i++) { + this.cursor.x++ + if (this.cursor.x >= this.width) { + this.cursor.x = 0 + this.cursor.y++ + if (this.cursor.y >= this.height) this.scroll() + } } + } + deleteChar () { + this.moveBack() this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) this.screen.splice(this.cursor.y * this.width + this.cursor.x, 1) } + deleteForward (n) { + n = Math.min(this.width, n) + for (let i = 0; i < n; i++) this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) + this.screen.splice(this.cursor.y * this.width + this.cursor.x, n) + } clampCursor () { if (this.cursor.x < 0) this.cursor.x = 0 if (this.cursor.y < 0) this.cursor.y = 0 if (this.cursor.x > this.width - 1) this.cursor.x = this.width - 1 if (this.cursor.y > this.height - 1) this.cursor.y = this.height - 1 } - applySequence (sequence) { - if (sequence[0] === '[') { - let type = sequence[sequence.length - 1] - let content = sequence.substring(1, sequence.length - 1) - - let numbers = content ? content.split(';').map(i => +i.replace(/\D/g, '')) : [] - let numOr1 = numbers.length ? numbers[0] : 1 - if (type === 'H') { - this.cursor.x = (numbers[0] | 0) - 1 - this.cursor.y = (numbers[1] | 0) - 1 - this.clampCursor() - } else if (type >= 'A' && type <= 'D') { - this.cursor[type <= 'B' ? 'y' : 'x'] += ((type === 'B' || type === 'C') ? 1 : -1) * numOr1 - this.clampCursor() - } else if (type === 'E' || type === 'F') { - this.cursor.x = 0 - this.cursor.y += (type === 'E' ? 1 : -1) * numOr1 - this.clampCursor() - } else if (type === 'G') { - this.cursor.x = numOr1 - 1 - this.clampCursor() - } else if (type === 'q') this.cursor.style = numOr1 - else if (type === 'm') { - if (!numbers.length) { - this.style = TERM_DEFAULT_STYLE - return - } - let type = numbers[0] - if (type === 1) this.style |= 1 << 16 // bold - else if (type === 2) this.style |= 1 << 1 << 16 // faint - else if (type === 3) this.style |= 1 << 2 << 16 // italic - else if (type === 4) this.style |= 1 << 3 << 16 // underline - else if (type === 5 || type === 6) this.style |= 1 << 4 << 16 // blink - else if (type === 7) { - // invert - this.style = (this.style & 0xFF0000) | ((this.style >> 8) & 0xFF) | ((this.style & 0xFF) << 8) - } else if (type === 9) this.style |= 1 << 6 << 16 // strike - else if (type === 20) this.style |= 1 << 5 << 16 // fraktur - else if (type >= 30 && type <= 38) this.style = (this.style & 0xFFFF00) | (type % 10) - else if (type >= 40 && type <= 48) this.style = (this.style & 0xFF00FF) | ((type % 10) << 8) - else if (type === 39) this.style = (this.style & 0xFFFF00) | 7 - else if (type === 49) this.style = (this.style & 0xFF00FF) | (7 << 8) - else if (type >= 90 && type <= 98) this.style = (this.style & 0xFFFF00) | ((type % 10) + 8) - else if (type >= 100 && type <= 108) this.style = (this.style & 0xFF00FF) | (((type % 10) + 8) << 8) - else if (type === 38 || type === 48) { - if (numbers[1] === 5) { - let color = (numbers[2] | 0) & 0xFF - if (type === 38) this.style = (this.style & 0xFFFF00) | color - if (type === 48) this.style = (this.style & 0xFF00FF) | (color << 8) - } - } + handleParsed (action, ...args) { + if (action === 'write') { + this.writeChar(args[0]) + } else if (action === 'delete') { + this.deleteForward(args[0]) + } else if (action === 'insert-blanks') { + this.insertBlanks(args[0]) + } else if (action === 'bell') { + this.terminal.load('B') + } else if (action === 'back') { + this.moveBack() + } else if (action === 'new-line') { + this.newLine() + } else if (action === 'return') { + this.cursor.x = 0 + } else if (action === 'set-cursor') { + this.cursor.x = args[0] + this.cursor.y = args[1] + this.clampCursor() + } else if (action === 'move-cursor-y') { + this.cursor.y += args[0] + this.clampCursor() + } else if (action === 'move-cursor-x') { + this.cursor.x += args[0] + this.clampCursor() + } else if (action === 'move-cursor-line') { + this.cursor.x = 0 + this.cursor.y += args[0] + this.clampCursor() + } else if (action === 'set-cursor-x') { + this.cursor.x = args[0] + } else if (action === 'set-cursor-style') { + this.cursor.style = Math.max(0, Math.min(6, args[0])) + } else if (action === 'reset-style') { + this.style = TERM_DEFAULT_STYLE + } else if (action === 'set-attrs') { + if (args[0] === -1) { + this.style = (this.style & 0xFF0000) | ((this.style >> 8) & 0xFF) | ((this.style & 0xFF) << 8) + } else { + this.style = (this.style & 0x00FFFF) | (args[0] << 16) } + } else if (action === 'set-color-fg') { + this.style = (this.style & 0xFFFF00) | args[0] + } else if (action === 'set-color-bg') { + this.style = (this.style & 0xFF00FF) | (args[0] << 8) } } write (text) { - for (let character of text) { - let code = character.codePointAt(0) - if (code === 0x1b) this.currentSequence = 1 - else if (this.currentSequence === 1 && character === '[') { - this.currentSequence = 2 - this.sequence += '[' - } else if (this.currentSequence && character.match(/[\x40-\x7e]/)) { - this.applySequence(this.sequence + character) - this.currentSequence = 0 - this.sequence = '' - } else if (this.currentSequence > 1) this.sequence += character - else if (this.currentSequence === 1) { - // something something nothing - this.currentSequence = 0 - this.writeChar(character) - } else if (code === 0x07) this.termScreen.load('B') - else if (code === 0x08) this.deleteChar() - else if (code === 0x0a) this.newLine() - else if (code === 0x0d) this.cursor.x = 0 - else this.writeChar(character) - } + this.parser.write(text) this.scheduleLoad() } serialize () { @@ -146,7 +227,7 @@ class ScrollingTerminal { let attributes = 1 // cursor always visible attributes |= (3 << 5) * +this.trackMouse // track mouse controls both attributes |= 3 << 7 // buttons/links always visible - attributes |= (this.cursor.shape << 9) + attributes |= (this.cursor.style << 9) serialized += encode3B(attributes) let lastStyle = null @@ -182,13 +263,67 @@ class ScrollingTerminal { } } +class DemoShell { + constructor (terminal) { + this.terminal = terminal + this.terminal.reset() + this.parser = new ANSIParser((...args) => this.handleParsed(...args)) + this.prompt() + this.input = '' + this.cursorPos = 0 + this.child = null + } + write (text) { + if (this.child) { + if (text.codePointAt(0) === 3) this.child.destroy() + else this.child.write(text) + } else this.parser.write(text) + } + prompt () { + if (this.terminal.cursor.x !== 0) this.terminal.write('\x1b[38;5;238m⏎\r\n') + this.terminal.write('\x1b[38;5;27mdemosh \x1b[m$ ') + this.input = '' + this.cursorPos = 0 + } + handleParsed (action, ...args) { + this.terminal.write('\b\x1b[P'.repeat(this.cursorPos)) + if (action === 'write') { + this.input = this.input.substr(0, this.cursorPos) + args[0] + this.input.substr(this.cursorPos) + this.cursorPos++ + } else if (action === 'back') { + this.input = this.input.substr(0, this.cursorPos - 1) + this.input.substr(this.cursorPos) + this.cursorPos-- + if (this.cursorPos < 0) this.cursorPos = 0 + } else if (action === 'move-cursor-x') { + this.cursorPos = Math.max(0, Math.min(this.input.length, this.cursorPos + args[0])) + } else if (action === 'delete-line') { + this.input = '' + this.cursorPos = 0 + } else if (action === 'delete-word') { + let words = this.input.substr(0, this.cursorPos).split(' ') + words.pop() + this.input = words.join(' ') + this.input.substr(this.cursorPos) + this.cursorPos = words.join(' ').length + } + + this.terminal.write(this.input) + this.terminal.write('\b'.repeat(this.input.length)) + this.terminal.moveForward(this.cursorPos) + this.terminal.write('') // dummy. Apply the moveFoward + + if (action === 'return') { + this.prompt() + } + } +} + window.demoInterface = { input (data) { let type = data[0] let content = data.substr(1) if (type === 's') { - this.terminal.write(content) + this.shell.write(content) } else if (type === 'b') { let button = content.charCodeAt(0) console.log(`button ${button} pressed`) @@ -198,5 +333,6 @@ window.demoInterface = { }, init (screen) { this.terminal = new ScrollingTerminal(screen) + this.shell = new DemoShell(this.terminal) } } From 758d3c58a6da00d89507f6f7269ca456c40db577 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Mon, 11 Sep 2017 22:57:07 +0200 Subject: [PATCH 3/8] Add fake screenfetch and clear --- js/demo.js | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/js/demo.js b/js/demo.js index 099e106..265b88e 100644 --- a/js/demo.js +++ b/js/demo.js @@ -23,6 +23,9 @@ class ANSIParser { this.handler('move-cursor-line', (type === 'E' ? 1 : -1) * numOr1) } else if (type === 'G') { this.handler('set-cursor-x', numOr1 - 1) + } else if (type === 'J') { + let number = numbers.length ? numbers[0] : 2 + if (number === 2) this.handler('clear') } else if (type === 'P') { this.handler('delete', numOr1) } else if (type === '@') { @@ -175,6 +178,8 @@ class ScrollingTerminal { this.deleteForward(args[0]) } else if (action === 'insert-blanks') { this.insertBlanks(args[0]) + } else if (action === 'clear') { + this.clear() } else if (action === 'bell') { this.terminal.load('B') } else if (action === 'back') { @@ -263,6 +268,99 @@ class ScrollingTerminal { } } +class Process { + constructor (args) { + // event listeners + this._listeners = {} + } + on (event, listener) { + if (!this._listeners[event]) this._listeners[event] = [] + this._listeners[event].push({ listener }) + } + once (event, listener) { + if (!this._listeners[event]) this._listeners[event] = [] + this._listeners[event].push({ listener, once: true }) + } + off (event, listener) { + let listeners = this._listeners[event] + if (listeners) { + for (let i in listeners) { + if (listeners[i].listener === listener) { + listeners.splice(i, 1) + break + } + } + } + } + emit (event, ...args) { + let listeners = this._listeners[event] + if (listeners) { + let remove = [] + for (let listener of listeners) { + try { + listener.listener(...args) + if (listener.once) remove.push(listener) + } catch (err) { + console.error(err) + } + } + for (let listener of remove) { + listeners.splice(listeners.indexOf(listener), 1) + } + } + } + write (data) { + this.emit('in', data) + } + destroy () { + // death. + this.emit('exit', 0) + } + run () { + // noop + } +} + +let demoshIndex = { + clear: class Clear extends Process { + run () { + this.emit('write', '\x1b[2J\x1b[1;1H') + this.emit('exit') + } + }, + screenfetch: class Screenfetch extends Process { + run () { + let lines = [ + '\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m+\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208mO\x1b[0m\x1b[38;5;203mS\x1b[0m\x1b[38;5;203m:\x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203mA\x1b[0m\x1b[38;5;198mr\x1b[0m\x1b[38;5;198mc\x1b[0m\x1b[38;5;198mh\x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199mL\x1b[0m\x1b[38;5;199mi\x1b[0m\x1b[38;5;163mn\x1b[0m\x1b[38;5;164mu\x1b[0m\x1b[38;5;164mx\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128mx\x1b[0m\x1b[38;5;129m8\x1b[0m\x1b[38;5;129m6\x1b[0m\x1b[38;5;129m_\x1b[0m\x1b[38;5;93m6\x1b[0m\x1b[38;5;93m4\x1b[0m', + '\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m#\x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203mH\x1b[0m\x1b[38;5;203mo\x1b[0m\x1b[38;5;198ms\x1b[0m\x1b[38;5;198mt\x1b[0m\x1b[38;5;198mn\x1b[0m\x1b[38;5;199ma\x1b[0m\x1b[38;5;199mm\x1b[0m\x1b[38;5;199me\x1b[0m\x1b[38;5;163m:\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164mN\x1b[0m\x1b[38;5;164m2\x1b[0m\x1b[38;5;128m0\x1b[0m\x1b[38;5;129m2\x1b[0m', + '\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198mK\x1b[0m\x1b[38;5;198me\x1b[0m\x1b[38;5;199mr\x1b[0m\x1b[38;5;199mn\x1b[0m\x1b[38;5;199me\x1b[0m\x1b[38;5;163ml\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164mR\x1b[0m\x1b[38;5;164me\x1b[0m\x1b[38;5;128ml\x1b[0m\x1b[38;5;129me\x1b[0m\x1b[38;5;129ma\x1b[0m\x1b[38;5;129ms\x1b[0m\x1b[38;5;93me\x1b[0m\x1b[38;5;93m:\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m4\x1b[0m\x1b[38;5;63m.\x1b[0m\x1b[38;5;63m9\x1b[0m\x1b[38;5;63m.\x1b[0m\x1b[38;5;33m4\x1b[0m\x1b[38;5;33m7\x1b[0m\x1b[38;5;33m-\x1b[0m\x1b[38;5;39m1\x1b[0m\x1b[38;5;39m-\x1b[0m\x1b[38;5;39ml\x1b[0m\x1b[38;5;38mt\x1b[0m\x1b[38;5;44ms\x1b[0m', + '\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199mU\x1b[0m\x1b[38;5;199mp\x1b[0m\x1b[38;5;163mt\x1b[0m\x1b[38;5;164mi\x1b[0m\x1b[38;5;164mm\x1b[0m\x1b[38;5;164me\x1b[0m\x1b[38;5;128m:\x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m1\x1b[0m\x1b[38;5;129m9\x1b[0m\x1b[38;5;93m:\x1b[0m\x1b[38;5;93m2\x1b[0m\x1b[38;5;93m6\x1b[0m', + '\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164mW\x1b[0m\x1b[38;5;164mM\x1b[0m\x1b[38;5;164m:\x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129mK\x1b[0m\x1b[38;5;129mW\x1b[0m\x1b[38;5;129mi\x1b[0m\x1b[38;5;93mn\x1b[0m', + '\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m;\x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m;\x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128mD\x1b[0m\x1b[38;5;129mE\x1b[0m\x1b[38;5;129m:\x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93mK\x1b[0m\x1b[38;5;93mD\x1b[0m\x1b[38;5;93mE\x1b[0m', + '\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m+\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m.\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129mP\x1b[0m\x1b[38;5;93ma\x1b[0m\x1b[38;5;93mc\x1b[0m\x1b[38;5;93mk\x1b[0m\x1b[38;5;63ma\x1b[0m\x1b[38;5;63mg\x1b[0m\x1b[38;5;63me\x1b[0m\x1b[38;5;63ms\x1b[0m\x1b[38;5;33m:\x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m1\x1b[0m\x1b[38;5;39m8\x1b[0m\x1b[38;5;39m2\x1b[0m\x1b[38;5;39m1\x1b[0m', + '\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m+\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93mR\x1b[0m\x1b[38;5;63mA\x1b[0m\x1b[38;5;63mM\x1b[0m\x1b[38;5;63m:\x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m9\x1b[0m\x1b[38;5;33m2\x1b[0m\x1b[38;5;33m5\x1b[0m\x1b[38;5;39m6\x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39mM\x1b[0m\x1b[38;5;38mB\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m/\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m1\x1b[0m\x1b[38;5;49m5\x1b[0m\x1b[38;5;49m9\x1b[0m\x1b[38;5;49m9\x1b[0m\x1b[38;5;48m9\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48mM\x1b[0m\x1b[38;5;83mB\x1b[0m', + '\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m;\x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63mP\x1b[0m\x1b[38;5;63mr\x1b[0m\x1b[38;5;33mo\x1b[0m\x1b[38;5;33mc\x1b[0m\x1b[38;5;33me\x1b[0m\x1b[38;5;39ms\x1b[0m\x1b[38;5;39ms\x1b[0m\x1b[38;5;39mo\x1b[0m\x1b[38;5;38mr\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44mT\x1b[0m\x1b[38;5;44my\x1b[0m\x1b[38;5;43mp\x1b[0m\x1b[38;5;49me\x1b[0m\x1b[38;5;49m:\x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48mI\x1b[0m\x1b[38;5;48mn\x1b[0m\x1b[38;5;48mt\x1b[0m\x1b[38;5;83me\x1b[0m\x1b[38;5;83ml\x1b[0m\x1b[38;5;83m(\x1b[0m\x1b[38;5;83mR\x1b[0m\x1b[38;5;118m)\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118mC\x1b[0m\x1b[38;5;154mo\x1b[0m\x1b[38;5;154mr\x1b[0m\x1b[38;5;154me\x1b[0m\x1b[38;5;148m(\x1b[0m\x1b[38;5;184mT\x1b[0m\x1b[38;5;184mM\x1b[0m\x1b[38;5;184m)\x1b[0m\x1b[38;5;178m \x1b[0m\x1b[38;5;214mi\x1b[0m\x1b[38;5;214m5\x1b[0m\x1b[38;5;214m-\x1b[0m\x1b[38;5;208m6\x1b[0m\x1b[38;5;208m4\x1b[0m\x1b[38;5;208m0\x1b[0m\x1b[38;5;203m0\x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203mC\x1b[0m\x1b[38;5;203mP\x1b[0m\x1b[38;5;198mU\x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m@\x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m2\x1b[0m\x1b[38;5;199m.\x1b[0m\x1b[38;5;163m8\x1b[0m\x1b[38;5;164m0\x1b[0m\x1b[38;5;164mG\x1b[0m\x1b[38;5;164mH\x1b[0m\x1b[38;5;128mz\x1b[0m', + '\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m+\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m$\x1b[0m\x1b[38;5;33mE\x1b[0m\x1b[38;5;39mD\x1b[0m\x1b[38;5;39mI\x1b[0m\x1b[38;5;39mT\x1b[0m\x1b[38;5;38mO\x1b[0m\x1b[38;5;44mR\x1b[0m\x1b[38;5;44m:\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43mv\x1b[0m\x1b[38;5;49mi\x1b[0m\x1b[38;5;49mm\x1b[0m', + '\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39mR\x1b[0m\x1b[38;5;39mo\x1b[0m\x1b[38;5;38mo\x1b[0m\x1b[38;5;44mt\x1b[0m\x1b[38;5;44m:\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m1\x1b[0m\x1b[38;5;49m6\x1b[0m\x1b[38;5;49m0\x1b[0m\x1b[38;5;49mG\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m/\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m1\x1b[0m\x1b[38;5;83m9\x1b[0m\x1b[38;5;83m6\x1b[0m\x1b[38;5;83mG\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m(\x1b[0m\x1b[38;5;118m8\x1b[0m\x1b[38;5;154m1\x1b[0m\x1b[38;5;154m%\x1b[0m\x1b[38;5;154m)\x1b[0m\x1b[38;5;148m \x1b[0m\x1b[38;5;184m(\x1b[0m\x1b[38;5;184me\x1b[0m\x1b[38;5;184mx\x1b[0m\x1b[38;5;178mt\x1b[0m\x1b[38;5;214m4\x1b[0m\x1b[38;5;214m)\x1b[0m', + '\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m.\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m;\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m;\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;63m;\x1b[0m\x1b[38;5;63m`\x1b[0m\x1b[38;5;63m"\x1b[0m\x1b[38;5;33m.\x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m', + '\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m.\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m;\x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m;\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m.\x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m', + '\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m.\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m.\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;39m#\x1b[0m\x1b[38;5;39m#\x1b[0m\x1b[38;5;39m#\x1b[0m\x1b[38;5;38m#\x1b[0m\x1b[38;5;44m`\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m', + '\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m\'\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m\'\x1b[0m\x1b[38;5;38m#\x1b[0m\x1b[38;5;44m#\x1b[0m\x1b[38;5;44m#\x1b[0m\x1b[38;5;44m#\x1b[0m\x1b[38;5;43m#\x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m', + '\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m;\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;48m#\x1b[0m\x1b[38;5;48m;\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m', + '\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m\'\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m\'\x1b[0m\x1b[38;5;83m#\x1b[0m\x1b[38;5;83m#\x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m', + '\x1b[38;5;93m \x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m\'\x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m`\x1b[0m\x1b[38;5;118m#\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m' + ] + + let loop = () => { + if (lines.length) setTimeout(loop, 50) + else this.destroy() + this.emit('write', lines.shift() + '\r\n') + } + loop() + } + } +} + class DemoShell { constructor (terminal) { this.terminal = terminal @@ -272,6 +370,7 @@ class DemoShell { this.input = '' this.cursorPos = 0 this.child = null + this.index = demoshIndex } write (text) { if (this.child) { @@ -279,9 +378,11 @@ class DemoShell { else this.child.write(text) } else this.parser.write(text) } - prompt () { + prompt (success = true) { if (this.terminal.cursor.x !== 0) this.terminal.write('\x1b[38;5;238m⏎\r\n') - this.terminal.write('\x1b[38;5;27mdemosh \x1b[m$ ') + this.terminal.write('\x1b[34;1mdemosh \x1b[m') + if (!success) this.terminal.write('\x1b[31m') + this.terminal.write('$ \x1b[m') this.input = '' this.cursorPos = 0 } @@ -312,8 +413,45 @@ class DemoShell { this.terminal.write('') // dummy. Apply the moveFoward if (action === 'return') { - this.prompt() + this.terminal.write('\r\n') + this.run(this.input) + } + } + run (command) { + let parts = [''] + + let inQuote = false + for (let character of command.trim()) { + if (inQuote && character !== inQuote) { + parts[parts.length - 1] += character + } else if (inQuote) { + inQuote = false + } else if (character === '"' || character === "'") { + inQuote = character + } else if (character.match(/\s/)) { + if (parts[parts.length - 1]) parts.push('') + } else parts[parts.length - 1] += character } + + let name = parts.shift() + if (name in this.index) { + this.spawn(name, parts) + } else { + this.terminal.write(`demosh: Unknown command: ${name}\r\n`) + this.prompt(false) + } + } + spawn (name, args = []) { + let Process = this.index[name] + this.child = new Process(...args) + let write = data => this.terminal.write(data) + this.child.on('write', write) + this.child.on('exit', code => { + this.child.off('write', write) + this.child = null + this.prompt(!code) + }) + this.child.run() } } From 8d994009fb964a20234e78dd9e71fef1b2b87486 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Mon, 11 Sep 2017 23:24:56 +0200 Subject: [PATCH 4/8] Add local-echo --- js/demo.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/js/demo.js b/js/demo.js index 265b88e..6422e83 100644 --- a/js/demo.js +++ b/js/demo.js @@ -325,7 +325,7 @@ let demoshIndex = { clear: class Clear extends Process { run () { this.emit('write', '\x1b[2J\x1b[1;1H') - this.emit('exit') + this.destroy() } }, screenfetch: class Screenfetch extends Process { @@ -358,6 +358,11 @@ let demoshIndex = { } loop() } + }, + 'local-echo': class LocalEcho extends Process { + write (data) { + this.emit('write', data) + } } } @@ -414,9 +419,13 @@ class DemoShell { if (action === 'return') { this.terminal.write('\r\n') - this.run(this.input) + this.parse(this.input) } } + parse (input) { + // TODO: basic chaining (i.e. semicolon) + this.run(input) + } run (command) { let parts = [''] @@ -451,7 +460,7 @@ class DemoShell { this.child = null this.prompt(!code) }) - this.child.run() + this.child.run(...args) } } From cc1dae16bc21eb09d0b7d330b574af51f8c8601a Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Tue, 12 Sep 2017 12:56:14 +0200 Subject: [PATCH 5/8] Add commands and things --- js/demo.js | 170 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 24 deletions(-) diff --git a/js/demo.js b/js/demo.js index 6422e83..49b8e36 100644 --- a/js/demo.js +++ b/js/demo.js @@ -37,14 +37,14 @@ class ANSIParser { return } let type = numbers[0] - if (type === 1) this.handler('set-attrs', 1) // bold - else if (type === 2) this.handler('set-attrs', 1 << 1) // faint - else if (type === 3) this.handler('set-attrs', 1 << 2) // italic - else if (type === 4) this.handler('set-attrs', 1 << 3) // underline - else if (type === 5 || type === 6) this.handler('set-attrs', 1 << 4) // blink - else if (type === 7) this.handler('set-attrs', -1) // invert - else if (type === 9) this.handler('set-attrs', 1 << 6) // strike - else if (type === 20) this.handler('set-attrs', 1 << 5) // fraktur + if (type === 1) this.handler('add-attrs', 1) // bold + else if (type === 2) this.handler('add-attrs', 1 << 1) // faint + else if (type === 3) this.handler('add-attrs', 1 << 2) // italic + else if (type === 4) this.handler('add-attrs', 1 << 3) // underline + else if (type === 5 || type === 6) this.handler('add-attrs', 1 << 4) // blink + else if (type === 7) this.handler('add-attrs', -1) // invert + else if (type === 9) this.handler('add-attrs', 1 << 6) // strike + else if (type === 20) this.handler('add-attrs', 1 << 5) // fraktur else if (type >= 30 && type <= 37) this.handler('set-color-fg', type % 10) else if (type >= 40 && type <= 47) this.handler('set-color-bg', type % 10) else if (type === 39) this.handler('set-color-fg', 7) @@ -208,11 +208,11 @@ class ScrollingTerminal { this.cursor.style = Math.max(0, Math.min(6, args[0])) } else if (action === 'reset-style') { this.style = TERM_DEFAULT_STYLE - } else if (action === 'set-attrs') { + } else if (action === 'add-attrs') { if (args[0] === -1) { this.style = (this.style & 0xFF0000) | ((this.style >> 8) & 0xFF) | ((this.style & 0xFF) << 8) } else { - this.style = (this.style & 0x00FFFF) | (args[0] << 16) + this.style |= (args[0] << 16) } } else if (action === 'set-color-fg') { this.style = (this.style & 0xFFFF00) | args[0] @@ -360,22 +360,139 @@ let demoshIndex = { } }, 'local-echo': class LocalEcho extends Process { + run (...args) { + if (!args.includes('--suppress-note')) { + this.emit('write', 'Note: not all terminal features are supported or bug-free in this demo\x1b[0m\r\n') + } + } write (data) { this.emit('write', data) } - } + }, + 'print-demo': class PrintDemo extends Process { + run () { + // lots of printing + this.emit('write', '\r\n') + this.emit('write', '┌ESPTerm─Demo──') + this.emit('write', '\x1b[31m31\x1b[32m32\x1b[33m33\x1b[34m34\x1b[35m35\x1b[36m36\x1b[37m37') + this.emit('write', '\x1b[90m90\x1b[91m91\x1b[92m92\x1b[93m93\x1b[94m94\x1b[95m95\x1b[96m96\x1b[97m97') + this.emit('write', '\x1b[0m─────────────┐\r\n') + this.emit('write', '│') + for (let i = 0; i < 57; i++) this.emit('write', ' ') + this.emit('write', '│') + this.emit('write', ' │││││││││\r\n') + this.emit('write', '│') + this.emit('write', '\x1b[1mBold \x1b[m\x1b[2mFaint \x1b[m\x1b[3mItalic \x1b[m\x1b[4mUnderline\x1b[0m \x1b[m\x1b[5mBlink') + this.emit('write', ' \x1b[m\x1b[7mInverse\x1b[m \x1b[9mStrike\x1b[m \x1b[20mFraktur \x1b[m') + this.emit('write', '│') + this.emit('write', ' ──\x1b[100m \x1b[m──\r\n') + this.emit('write', '│') + for (let i = 0; i < 57; i++) this.emit('write', ' ') + this.emit('write', '│') + this.emit('write', ' ──\x1b[100;30m ESP8266 \x1b[m──\r\n') + this.emit('write', '└') + for (let i = 0; i < 57; i++) this.emit('write', '─') + this.emit('write', '┤') + this.emit('write', ' ──\x1b[100m \x1b[m──\r\n') + for (let i = 0; i < 58; i++) this.emit('write', ' ') + this.emit('write', '│') + this.emit('write', ' ──\x1b[100;30m (@)#### \x1b[m──\r\n') + this.emit('write', ' \x1b[44;96m This is a demo of the ESPTerm Web Interface \x1b[m ') + this.emit('write', '│') + this.emit('write', ' ──\x1b[100m \x1b[m──\r\n') + this.emit('write', ' \x1b[44;96m \x1b[m ') + this.emit('write', '│') + this.emit('write', ' │││││││││\r\n') + this.emit('write', ' \x1b[44;96m Try the links beneath this screen to browse the menu. \x1b[m ♦\r\n') + this.emit('write', ' \x1b[44;96m \x1b[m\r\n') + this.emit('write', ' \x1b[44;96m <°)))>< ESPTerm fully supports UTF-8 お は よ ー ><(((°> \x1b[m\r\n') + this.emit('write', ' \x1b[44;96m \x1b[m\r\n') + this.emit('write', '\r\n') + this.emit('write', ' \x1b[92mOther interesting features:\x1b[m ↓\r\n\n') + this.emit('write', ' \x1b[32m- Almost full VT100 emulation \x1b[35m() ()') + this.emit('write', '\x1b[0m Funguje tu čeština!\r\n') + this.emit('write', ' \x1b[34m- Xterm-like mouse tracking \x1b[37m==\x1b[100m°.°\x1b[40m==') + this.emit('write', ' \x1b[35m<---,\r\n') + this.emit('write', " \x1b[33m- File upload utility \x1b[0m'' '' \x1b[35mmouse\r\n") + this.emit('write', ' \x1b[31m- User-friendly config interface\r\n') + this.emit('write', ' \x1b[95m- Advanced WiFi & network settings') + for (let i = 0; i < 17; i++) this.emit('write', ' ') + this.emit('write', '\x1b[93mTry ESPTerm today!\r\n') + this.emit('write', ' \x1b[37m- Built-in help page') + for (let i = 0; i < 26; i++) this.emit('write', ' ') + this.emit('write', '\x1b[36m--> \x1b[93mPre-built binaries are\r\n') + for (let i = 0; i < 30; i++) this.emit('write', ' ') + this.emit('write', '\x1b[36mlink on the About page \x1b[93mavailable on GitHub!\r\n') + this.destroy() + } + }, + colors: class PrintColors extends Process { + run () { + this.emit('write', '\r\n') + let fgtext = 'foreground-color' + this.emit('write', ' ') + for (let i = 0; i < 16; i++) { + this.emit('write', '\x1b[' + (i < 8 ? `3${i}` : `9${i - 8}`) + 'm') + this.emit('write', fgtext[i] + ' ') + } + this.emit('write', '\r\n ') + for (let i = 0; i < 16; i++) { + this.emit('write', '\x1b[' + (i < 8 ? `4${i}` : `10${i - 8}`) + 'm ') + } + this.emit('write', '\x1b[m\r\n') + for (let r = 0; r < 6; r++) { + this.emit('write', ' ') + for (let g = 0; g < 6; g++) { + for (let b = 0; b < 6; b++) { + this.emit('write', `\x1b[48;5;${16 + r * 36 + g * 6 + b}m `) + } + this.emit('write', '\x1b[m') + } + this.emit('write', '\r\n') + } + this.emit('write', ' ') + for (let g = 0; g < 24; g++) { + this.emit('write', `\x1b[48;5;${232 + g}m `) + } + this.emit('write', '\x1b[m\r\n\n') + this.destroy() + } + }, + ls: class ListCommands extends Process { + run () { + this.emit('write', '\x1b[92mList of demo commands\x1b[m\r\n') + for (let i in demoshIndex) { + if (typeof demoshIndex[i] === 'string') continue + this.emit('write', i + '\r\n') + } + this.destroy() + } + }, + pwd: '/this/is/a/demo\r\n', + cd: '\x1b[38;5;239mNo directories to change to\r\n', + whoami: `${window.navigator.userAgent}\r\n`, + hostname: `${window.location.hostname}`, + uname: 'ESPTerm Demo\r\n', + mkdir: '\x1b[38;5;239mDid not create a directory because this is a demo.\r\n', + rm: '\x1b[38;5;239mDid not delete anything because this is a demo.\r\n', + cp: '\x1b[38;5;239mNothing to copy because this is a demo.\r\n', + mv: '\x1b[38;5;239mNothing to move because this is a demo.\r\n', + ln: '\x1b[38;5;239mNothing to link because this is a demo.\r\n', + touch: '\x1b[38;5;239mNothing to touch\r\n' } class DemoShell { - constructor (terminal) { + constructor (terminal, printDemo) { this.terminal = terminal this.terminal.reset() this.parser = new ANSIParser((...args) => this.handleParsed(...args)) - this.prompt() this.input = '' this.cursorPos = 0 this.child = null this.index = demoshIndex + + if (printDemo) this.run('print-demo') + else this.prompt() } write (text) { if (this.child) { @@ -384,7 +501,7 @@ class DemoShell { } else this.parser.write(text) } prompt (success = true) { - if (this.terminal.cursor.x !== 0) this.terminal.write('\x1b[38;5;238m⏎\r\n') + if (this.terminal.cursor.x !== 0) this.terminal.write('\x1b[m\x1b[38;5;238m⏎\r\n') this.terminal.write('\x1b[34;1mdemosh \x1b[m') if (!success) this.terminal.write('\x1b[31m') this.terminal.write('$ \x1b[m') @@ -452,15 +569,20 @@ class DemoShell { } spawn (name, args = []) { let Process = this.index[name] - this.child = new Process(...args) - let write = data => this.terminal.write(data) - this.child.on('write', write) - this.child.on('exit', code => { - this.child.off('write', write) - this.child = null - this.prompt(!code) - }) - this.child.run(...args) + if (Process instanceof Function) { + this.child = new Process(...args) + let write = data => this.terminal.write(data) + this.child.on('write', write) + this.child.on('exit', code => { + this.child.off('write', write) + this.child = null + this.prompt(!code) + }) + this.child.run(...args) + } else { + this.terminal.write(Process) + this.prompt() + } } } @@ -480,6 +602,6 @@ window.demoInterface = { }, init (screen) { this.terminal = new ScrollingTerminal(screen) - this.shell = new DemoShell(this.terminal) + this.shell = new DemoShell(this.terminal, true) } } From 0875182d38c8264d76ddca767b27e9991ce40398 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Tue, 12 Sep 2017 13:43:53 +0200 Subject: [PATCH 6/8] Rename print-demo to info and make it print slowly --- js/demo.js | 158 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 58 deletions(-) diff --git a/js/demo.js b/js/demo.js index 49b8e36..47002d9 100644 --- a/js/demo.js +++ b/js/demo.js @@ -321,6 +321,25 @@ class Process { } } +let demoData = { + buttons: { + 1: '', + 2: '', + 3: '', + 4: '', + 5: function (terminal, shell) { + if (shell.child) shell.child.destroy() + let chars = 'info\r' + let loop = function () { + shell.write(chars[0]) + chars = chars.substr(1) + if (chars) setTimeout(loop, 100) + } + setTimeout(loop, 200) + } + } +} + let demoshIndex = { clear: class Clear extends Process { run () { @@ -369,61 +388,80 @@ let demoshIndex = { this.emit('write', data) } }, - 'print-demo': class PrintDemo extends Process { - run () { + 'info': class Info extends Process { + run (...args) { // lots of printing - this.emit('write', '\r\n') - this.emit('write', '┌ESPTerm─Demo──') - this.emit('write', '\x1b[31m31\x1b[32m32\x1b[33m33\x1b[34m34\x1b[35m35\x1b[36m36\x1b[37m37') - this.emit('write', '\x1b[90m90\x1b[91m91\x1b[92m92\x1b[93m93\x1b[94m94\x1b[95m95\x1b[96m96\x1b[97m97') - this.emit('write', '\x1b[0m─────────────┐\r\n') - this.emit('write', '│') - for (let i = 0; i < 57; i++) this.emit('write', ' ') - this.emit('write', '│') - this.emit('write', ' │││││││││\r\n') - this.emit('write', '│') - this.emit('write', '\x1b[1mBold \x1b[m\x1b[2mFaint \x1b[m\x1b[3mItalic \x1b[m\x1b[4mUnderline\x1b[0m \x1b[m\x1b[5mBlink') - this.emit('write', ' \x1b[m\x1b[7mInverse\x1b[m \x1b[9mStrike\x1b[m \x1b[20mFraktur \x1b[m') - this.emit('write', '│') - this.emit('write', ' ──\x1b[100m \x1b[m──\r\n') - this.emit('write', '│') - for (let i = 0; i < 57; i++) this.emit('write', ' ') - this.emit('write', '│') - this.emit('write', ' ──\x1b[100;30m ESP8266 \x1b[m──\r\n') - this.emit('write', '└') - for (let i = 0; i < 57; i++) this.emit('write', '─') - this.emit('write', '┤') - this.emit('write', ' ──\x1b[100m \x1b[m──\r\n') - for (let i = 0; i < 58; i++) this.emit('write', ' ') - this.emit('write', '│') - this.emit('write', ' ──\x1b[100;30m (@)#### \x1b[m──\r\n') - this.emit('write', ' \x1b[44;96m This is a demo of the ESPTerm Web Interface \x1b[m ') - this.emit('write', '│') - this.emit('write', ' ──\x1b[100m \x1b[m──\r\n') - this.emit('write', ' \x1b[44;96m \x1b[m ') - this.emit('write', '│') - this.emit('write', ' │││││││││\r\n') - this.emit('write', ' \x1b[44;96m Try the links beneath this screen to browse the menu. \x1b[m ♦\r\n') - this.emit('write', ' \x1b[44;96m \x1b[m\r\n') - this.emit('write', ' \x1b[44;96m <°)))>< ESPTerm fully supports UTF-8 お は よ ー ><(((°> \x1b[m\r\n') - this.emit('write', ' \x1b[44;96m \x1b[m\r\n') - this.emit('write', '\r\n') - this.emit('write', ' \x1b[92mOther interesting features:\x1b[m ↓\r\n\n') - this.emit('write', ' \x1b[32m- Almost full VT100 emulation \x1b[35m() ()') - this.emit('write', '\x1b[0m Funguje tu čeština!\r\n') - this.emit('write', ' \x1b[34m- Xterm-like mouse tracking \x1b[37m==\x1b[100m°.°\x1b[40m==') - this.emit('write', ' \x1b[35m<---,\r\n') - this.emit('write', " \x1b[33m- File upload utility \x1b[0m'' '' \x1b[35mmouse\r\n") - this.emit('write', ' \x1b[31m- User-friendly config interface\r\n') - this.emit('write', ' \x1b[95m- Advanced WiFi & network settings') - for (let i = 0; i < 17; i++) this.emit('write', ' ') - this.emit('write', '\x1b[93mTry ESPTerm today!\r\n') - this.emit('write', ' \x1b[37m- Built-in help page') - for (let i = 0; i < 26; i++) this.emit('write', ' ') - this.emit('write', '\x1b[36m--> \x1b[93mPre-built binaries are\r\n') - for (let i = 0; i < 30; i++) this.emit('write', ' ') - this.emit('write', '\x1b[36mlink on the About page \x1b[93mavailable on GitHub!\r\n') - this.destroy() + let parts = [] + parts.push('\r\n') + parts.push('┌ESPTerm─Demo──') + parts.push('\x1b[31m31\x1b[32m32\x1b[33m33\x1b[34m34\x1b[35m35\x1b[36m36\x1b[37m37') + parts.push('\x1b[90m90\x1b[91m91\x1b[92m92\x1b[93m93\x1b[94m94\x1b[95m95\x1b[96m96\x1b[97m97') + parts.push('\x1b[0m─────────────┐\r\n') + parts.push('│') + for (let i = 0; i < 57; i++) parts.push(' ') + parts.push('│') + parts.push(' │││││││││\r\n') + parts.push('│') + parts.push('\x1b[1mBold \x1b[m\x1b[2mFaint \x1b[m\x1b[3mItalic \x1b[m\x1b[4mUnderline\x1b[0m \x1b[m\x1b[5mBlink') + parts.push(' \x1b[m\x1b[7mInverse\x1b[m \x1b[9mStrike\x1b[m \x1b[20mFraktur \x1b[m') + parts.push('│') + parts.push(' ──\x1b[100m \x1b[m──\r\n') + parts.push('│') + for (let i = 0; i < 57; i++) parts.push(' ') + parts.push('│') + parts.push(' ──\x1b[100;30m ESP8266 \x1b[m──\r\n') + parts.push('└') + for (let i = 0; i < 57; i++) parts.push('─') + parts.push('┤') + parts.push(' ──\x1b[100m \x1b[m──\r\n') + for (let i = 0; i < 58; i++) parts.push(' ') + parts.push('│') + parts.push(' ──\x1b[100;30m (@)#### \x1b[m──\r\n') + parts.push(' \x1b[44;96m This is a demo of the ESPTerm Web Interface \x1b[m ') + parts.push('│') + parts.push(' ──\x1b[100m \x1b[m──\r\n') + parts.push(' \x1b[44;96m \x1b[m ') + parts.push('│') + parts.push(' │││││││││\r\n') + parts.push(' \x1b[44;96m Try the links beneath this screen to browse the menu. \x1b[m ♦\r\n') + parts.push(' \x1b[44;96m \x1b[m\r\n') + parts.push(' \x1b[44;96m <°)))>< ESPTerm fully supports UTF-8 お は よ ー ><(((°> \x1b[m\r\n') + parts.push(' \x1b[44;96m \x1b[m\r\n') + parts.push('\r\n') + parts.push(' \x1b[92mOther interesting features:\x1b[m ↓\r\n\n') + parts.push(' \x1b[32m- Almost full VT100 emulation \x1b[35m() ()') + parts.push('\x1b[0m Funguje tu čeština!\r\n') + parts.push(' \x1b[34m- Xterm-like mouse tracking \x1b[37m==\x1b[100m°.°\x1b[40m==') + parts.push(' \x1b[35m<---,\r\n') + parts.push(" \x1b[33m- File upload utility \x1b[0m'' '' \x1b[35mmouse\r\n") + parts.push(' \x1b[31m- User-friendly config interface\r\n') + parts.push(' \x1b[95m- Advanced WiFi & network settings') + for (let i = 0; i < 17; i++) parts.push(' ') + parts.push('\x1b[93mTry ESPTerm today!\r\n') + parts.push(' \x1b[37m- Built-in help page') + for (let i = 0; i < 26; i++) parts.push(' ') + parts.push('\x1b[36m--> \x1b[93mPre-built binaries are\r\n') + for (let i = 0; i < 30; i++) parts.push(' ') + parts.push('\x1b[36mlink on the About page \x1b[93mavailable on GitHub!\r\n') + + let chars = parts.join('') + if (args.includes('--fast')) { + this.emit('write', chars) + this.destroy() + } else { + const self = this + let loop = function () { + while (true) { + let character = chars[0] + chars = chars.substr(1) + self.emit('write', character) + if (character === '\n') break + } + if (chars) setTimeout(loop, 17) + else self.destroy() + } + loop() + } } }, colors: class PrintColors extends Process { @@ -482,7 +520,7 @@ let demoshIndex = { } class DemoShell { - constructor (terminal, printDemo) { + constructor (terminal, printInfo) { this.terminal = terminal this.terminal.reset() this.parser = new ANSIParser((...args) => this.handleParsed(...args)) @@ -491,7 +529,7 @@ class DemoShell { this.child = null this.index = demoshIndex - if (printDemo) this.run('print-demo') + if (printInfo) this.run('info --fast') else this.prompt() } write (text) { @@ -574,7 +612,7 @@ class DemoShell { let write = data => this.terminal.write(data) this.child.on('write', write) this.child.on('exit', code => { - this.child.off('write', write) + if (this.child) this.child.off('write', write) this.child = null this.prompt(!code) }) @@ -595,7 +633,11 @@ window.demoInterface = { this.shell.write(content) } else if (type === 'b') { let button = content.charCodeAt(0) - console.log(`button ${button} pressed`) + let action = demoData.buttons[button] + if (action) { + if (typeof action === 'string') this.shell.write(action) + else if (action instanceof Function) action(this.terminal, this.shell) + } } else if (type === 'm' || type === 'p' || type === 'r') { console.log(JSON.stringify(data)) } From 720a9ecaa1904d12f18fb6db7b0122acf9444894 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Tue, 12 Sep 2017 16:48:32 +0200 Subject: [PATCH 7/8] Add cursor and theme commands Also I didn't have to re-create that horrible demo screen Pork made by manually typing in esc sequences --- js/demo.js | 202 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 69 deletions(-) diff --git a/js/demo.js b/js/demo.js index 47002d9..de19fd0 100644 --- a/js/demo.js +++ b/js/demo.js @@ -58,6 +58,11 @@ class ANSIParser { if (type === 48) this.handler('set-color-bg', color) } } + } else if (type === 'h' || type === 'l') { + if (content === '?25') { + if (type === 'h') this.handler('show-cursor') + else if (type === 'l') this.handler('hide-cursor') + } } } } @@ -105,8 +110,9 @@ class ScrollingTerminal { } reset () { this.style = TERM_DEFAULT_STYLE - this.cursor = { x: 0, y: 0, style: 1 } + this.cursor = { x: 0, y: 0, style: 1, visible: true } this.trackMouse = false + this.theme = 0 this.parser.reset() this.clear() } @@ -218,6 +224,10 @@ class ScrollingTerminal { this.style = (this.style & 0xFFFF00) | args[0] } else if (action === 'set-color-bg') { this.style = (this.style & 0xFF00FF) | (args[0] << 8) + } else if (action === 'hide-cursor') { + this.cursor.visible = false + } else if (action === 'show-cursor') { + this.cursor.visible = true } } write (text) { @@ -229,7 +239,7 @@ class ScrollingTerminal { serialized += encode2B(this.height) + encode2B(this.width) serialized += encode2B(this.cursor.y) + encode2B(this.cursor.x) - let attributes = 1 // cursor always visible + let attributes = +this.cursor.visible attributes |= (3 << 5) * +this.trackMouse // track mouse controls both attributes |= 3 << 7 // buttons/links always visible attributes |= (this.cursor.style << 9) @@ -259,7 +269,7 @@ class ScrollingTerminal { scheduleLoad () { clearInterval(this._scheduledLoad) if (this._lastLoad < Date.now() - TERM_MIN_DRAW_DELAY) { - this.termScreen.load(this.serialize()) + this.termScreen.load(this.serialize(), this.theme) } else { this._scheduledLoad = setTimeout(() => { this.termScreen.load(this.serialize()) @@ -381,7 +391,7 @@ let demoshIndex = { 'local-echo': class LocalEcho extends Process { run (...args) { if (!args.includes('--suppress-note')) { - this.emit('write', 'Note: not all terminal features are supported or bug-free in this demo\x1b[0m\r\n') + this.emit('write', '\x1b[38;5;239mNote: not all terminal features are supported or and may not work as expected in this demo\x1b[0m\r\n') } } write (data) { @@ -390,74 +400,92 @@ let demoshIndex = { }, 'info': class Info extends Process { run (...args) { + let fast = args.includes('--fast') + this.showSplash().then(() => { + this.printText(fast) + }) + } + showSplash () { + let splash = ` + -#####- -###*..#####- ######- + -#* -#- .## .##. *#- + -##### .-###*..#####- *#- -*##*- #*-#--#**#-*##- + -#* -#-.##. *#- *##-#* ##. -#* *# .#* + -#####--####- .##. *#- -*###- ##. -#* *# .#* + `.split('\n').filter(line => line.trim()) + let levels = { + ' ': -231, + '.': 4, + '-': 8, + '*': 17, + '#': 24 + } + for (let i in splash) { + if (splash[i].length < 79) splash[i] += ' '.repeat(79 - splash[i].length) + } + this.emit('write', '\r\n'.repeat(splash.length + 1)) + this.emit('write', '\x1b[A'.repeat(splash.length)) + this.emit('write', '\x1b[?25l') + + let cursorX = 0 + let cursorY = 0 + let moveTo = (x, y) => { + let moveX = x - cursorX + let moveY = y - cursorY + this.emit('write', `\x1b[${Math.abs(moveX)}${moveX > 0 ? 'C' : 'D'}`) + this.emit('write', `\x1b[${Math.abs(moveY)}${moveY > 0 ? 'B' : 'A'}`) + cursorX = x + cursorY = y + } + let drawCell = (x, y) => { + moveTo(x, y) + this.emit('write', `\x1b[48;5;${231 + levels[splash[y][x]]}m \b`) + } + return new Promise((resolve, reject) => { + const self = this + let x = 14 + let cycles = 0 + let loop = function () { + for (let y = 0; y < splash.length; y++) { + let dx = x - y + if (dx > 0) drawCell(dx, y) + } + + if (++x < 79) { + if (++cycles >= 3) { + setTimeout(loop, 20) + cycles = 0 + } else loop() + } else { + moveTo(0, splash.length) + self.emit('write', '\x1b[m\x1b[?25h') + resolve() + } + } + loop() + }) + } + printText (fast = false) { // lots of printing - let parts = [] - parts.push('\r\n') - parts.push('┌ESPTerm─Demo──') - parts.push('\x1b[31m31\x1b[32m32\x1b[33m33\x1b[34m34\x1b[35m35\x1b[36m36\x1b[37m37') - parts.push('\x1b[90m90\x1b[91m91\x1b[92m92\x1b[93m93\x1b[94m94\x1b[95m95\x1b[96m96\x1b[97m97') - parts.push('\x1b[0m─────────────┐\r\n') - parts.push('│') - for (let i = 0; i < 57; i++) parts.push(' ') - parts.push('│') - parts.push(' │││││││││\r\n') - parts.push('│') - parts.push('\x1b[1mBold \x1b[m\x1b[2mFaint \x1b[m\x1b[3mItalic \x1b[m\x1b[4mUnderline\x1b[0m \x1b[m\x1b[5mBlink') - parts.push(' \x1b[m\x1b[7mInverse\x1b[m \x1b[9mStrike\x1b[m \x1b[20mFraktur \x1b[m') - parts.push('│') - parts.push(' ──\x1b[100m \x1b[m──\r\n') - parts.push('│') - for (let i = 0; i < 57; i++) parts.push(' ') - parts.push('│') - parts.push(' ──\x1b[100;30m ESP8266 \x1b[m──\r\n') - parts.push('└') - for (let i = 0; i < 57; i++) parts.push('─') - parts.push('┤') - parts.push(' ──\x1b[100m \x1b[m──\r\n') - for (let i = 0; i < 58; i++) parts.push(' ') - parts.push('│') - parts.push(' ──\x1b[100;30m (@)#### \x1b[m──\r\n') - parts.push(' \x1b[44;96m This is a demo of the ESPTerm Web Interface \x1b[m ') - parts.push('│') - parts.push(' ──\x1b[100m \x1b[m──\r\n') - parts.push(' \x1b[44;96m \x1b[m ') - parts.push('│') - parts.push(' │││││││││\r\n') - parts.push(' \x1b[44;96m Try the links beneath this screen to browse the menu. \x1b[m ♦\r\n') - parts.push(' \x1b[44;96m \x1b[m\r\n') - parts.push(' \x1b[44;96m <°)))>< ESPTerm fully supports UTF-8 お は よ ー ><(((°> \x1b[m\r\n') - parts.push(' \x1b[44;96m \x1b[m\r\n') - parts.push('\r\n') - parts.push(' \x1b[92mOther interesting features:\x1b[m ↓\r\n\n') - parts.push(' \x1b[32m- Almost full VT100 emulation \x1b[35m() ()') - parts.push('\x1b[0m Funguje tu čeština!\r\n') - parts.push(' \x1b[34m- Xterm-like mouse tracking \x1b[37m==\x1b[100m°.°\x1b[40m==') - parts.push(' \x1b[35m<---,\r\n') - parts.push(" \x1b[33m- File upload utility \x1b[0m'' '' \x1b[35mmouse\r\n") - parts.push(' \x1b[31m- User-friendly config interface\r\n') - parts.push(' \x1b[95m- Advanced WiFi & network settings') - for (let i = 0; i < 17; i++) parts.push(' ') - parts.push('\x1b[93mTry ESPTerm today!\r\n') - parts.push(' \x1b[37m- Built-in help page') - for (let i = 0; i < 26; i++) parts.push(' ') - parts.push('\x1b[36m--> \x1b[93mPre-built binaries are\r\n') - for (let i = 0; i < 30; i++) parts.push(' ') - parts.push('\x1b[36mlink on the About page \x1b[93mavailable on GitHub!\r\n') + let parts = [ + '', + ' ESPTerm is a VT100-like terminal emulator running on the ESP8266 WiFi chip.', + '', + ' \x1b[93mThis is an online demo of the web user interface.\x1b[m', + '', + ' Type \x1b[92mls\x1b[m to list available commands.', + ' Use the \x1b[94mlinks\x1b[m below this screen for a demo of the options and more info.', + '' + ] - let chars = parts.join('') - if (args.includes('--fast')) { - this.emit('write', chars) + if (fast) { + this.emit('write', parts.join('\r\n') + '\r\n') this.destroy() } else { const self = this let loop = function () { - while (true) { - let character = chars[0] - chars = chars.substr(1) - self.emit('write', character) - if (character === '\n') break - } - if (chars) setTimeout(loop, 17) + self.emit('write', parts.shift() + '\r\n') + if (parts.length) setTimeout(loop, 17) else self.destroy() } loop() @@ -506,6 +534,40 @@ let demoshIndex = { this.destroy() } }, + theme: class SetTheme extends Process { + constructor (shell) { + super() + this.shell = shell + } + run (...args) { + let theme = args[0] | 0 + if (!args.length || !Number.isFinite(theme) || theme < 0 || theme > 5) { + this.emit('write', '\x1b[31mUsage: theme [0–5]\r\n') + this.destroy() + return + } + this.shell.terminal.theme = theme + // HACK: reset drawn screen to prevent only partly redrawn screen + this.shell.terminal.termScreen.drawnScreenFG = [] + this.emit('write', '') + this.destroy() + } + }, + cursor: class SetCursor extends Process { + run (...args) { + let steady = args.includes('--steady') + if (args.includes('block')) { + this.emit('write', `\x1b[${0 + 2 * steady} q`) + } else if (args.includes('line')) { + this.emit('write', `\x1b[${3 + steady} q`) + } else if (args.includes('bar') || args.includes('beam')) { + this.emit('write', `\x1b[${5 + steady} q`) + } else { + this.emit('write', '\x1b[31mUsage: cursor [block|line|bar] [--steady]\r\n') + } + this.destroy() + } + }, pwd: '/this/is/a/demo\r\n', cd: '\x1b[38;5;239mNo directories to change to\r\n', whoami: `${window.navigator.userAgent}\r\n`, @@ -516,7 +578,8 @@ let demoshIndex = { cp: '\x1b[38;5;239mNothing to copy because this is a demo.\r\n', mv: '\x1b[38;5;239mNothing to move because this is a demo.\r\n', ln: '\x1b[38;5;239mNothing to link because this is a demo.\r\n', - touch: '\x1b[38;5;239mNothing to touch\r\n' + touch: '\x1b[38;5;239mNothing to touch\r\n', + exit: '\x1b[38;5;239mNowhere to go\r\n' } class DemoShell { @@ -529,7 +592,7 @@ class DemoShell { this.child = null this.index = demoshIndex - if (printInfo) this.run('info --fast') + if (printInfo) this.run('info') else this.prompt() } write (text) { @@ -578,6 +641,7 @@ class DemoShell { } } parse (input) { + if (input === 'help') input = 'info' // TODO: basic chaining (i.e. semicolon) this.run(input) } @@ -608,7 +672,7 @@ class DemoShell { spawn (name, args = []) { let Process = this.index[name] if (Process instanceof Function) { - this.child = new Process(...args) + this.child = new Process(this) let write = data => this.terminal.write(data) this.child.on('write', write) this.child.on('exit', code => { From e1a761cd7f52cf8450c88f397c3e29c5b54ee243 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Tue, 12 Sep 2017 22:13:35 +0200 Subject: [PATCH 8/8] r a i n b o w --- js/demo.js | 113 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 34 deletions(-) diff --git a/js/demo.js b/js/demo.js index de19fd0..48946ec 100644 --- a/js/demo.js +++ b/js/demo.js @@ -96,6 +96,13 @@ class ANSIParser { const TERM_DEFAULT_STYLE = 7 const TERM_MIN_DRAW_DELAY = 10 +let getRainbowColor = t => { + let r = Math.floor(Math.sin(t) * 2.5 + 2.5) + let g = Math.floor(Math.sin(t + 2 / 3 * Math.PI) * 2.5 + 2.5) + let b = Math.floor(Math.sin(t + 4 / 3 * Math.PI) * 2.5 + 2.5) + return 16 + 36 * r + 6 * g + b +} + class ScrollingTerminal { constructor (screen) { this.width = 80 @@ -113,6 +120,7 @@ class ScrollingTerminal { this.cursor = { x: 0, y: 0, style: 1, visible: true } this.trackMouse = false this.theme = 0 + this.rainbow = false this.parser.reset() this.clear() } @@ -246,28 +254,36 @@ class ScrollingTerminal { serialized += encode3B(attributes) let lastStyle = null + let index = 0 for (let cell of this.screen) { - if (cell[1] !== lastStyle) { - let foreground = cell[1] & 0xFF - let background = (cell[1] >> 8) & 0xFF - let attributes = (cell[1] >> 16) & 0xFF + let style = cell[1] + if (this.rainbow) { + let x = index % this.width + let y = Math.floor(index / this.width) + style = (style & 0xFF0000) | getRainbowColor((x + y) / 10 + Date.now() / 1000) + index++ + } + if (style !== lastStyle) { + let foreground = style & 0xFF + let background = (style >> 8) & 0xFF + let attributes = (style >> 16) & 0xFF let setForeground = foreground !== (lastStyle & 0xFF) let setBackground = background !== ((lastStyle >> 8) & 0xFF) let setAttributes = attributes !== ((lastStyle >> 16) & 0xFF) - if (setForeground && setBackground) serialized += '\x03' + encode3B(cell[1] & 0xFFFF) + if (setForeground && setBackground) serialized += '\x03' + encode3B(style & 0xFFFF) else if (setForeground) serialized += '\x05' + encode2B(foreground) else if (setBackground) serialized += '\x06' + encode2B(background) if (setAttributes) serialized += '\x04' + encode2B(attributes) - lastStyle = cell[1] + lastStyle = style } serialized += cell[0] } return serialized } scheduleLoad () { - clearInterval(this._scheduledLoad) + clearTimeout(this._scheduledLoad) if (this._lastLoad < Date.now() - TERM_MIN_DRAW_DELAY) { this.termScreen.load(this.serialize(), this.theme) } else { @@ -276,6 +292,13 @@ class ScrollingTerminal { }, TERM_MIN_DRAW_DELAY - this._lastLoad) } } + rainbowTimer () { + if (!this.rainbow) return + clearInterval(this._rainbowTimer) + this._rainbowTimer = setInterval(() => { + if (this.rainbow) this.scheduleLoad() + }, 50) + } } class Process { @@ -359,31 +382,36 @@ let demoshIndex = { }, screenfetch: class Screenfetch extends Process { run () { - let lines = [ - '\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m+\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208mO\x1b[0m\x1b[38;5;203mS\x1b[0m\x1b[38;5;203m:\x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203mA\x1b[0m\x1b[38;5;198mr\x1b[0m\x1b[38;5;198mc\x1b[0m\x1b[38;5;198mh\x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199mL\x1b[0m\x1b[38;5;199mi\x1b[0m\x1b[38;5;163mn\x1b[0m\x1b[38;5;164mu\x1b[0m\x1b[38;5;164mx\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128mx\x1b[0m\x1b[38;5;129m8\x1b[0m\x1b[38;5;129m6\x1b[0m\x1b[38;5;129m_\x1b[0m\x1b[38;5;93m6\x1b[0m\x1b[38;5;93m4\x1b[0m', - '\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m#\x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203mH\x1b[0m\x1b[38;5;203mo\x1b[0m\x1b[38;5;198ms\x1b[0m\x1b[38;5;198mt\x1b[0m\x1b[38;5;198mn\x1b[0m\x1b[38;5;199ma\x1b[0m\x1b[38;5;199mm\x1b[0m\x1b[38;5;199me\x1b[0m\x1b[38;5;163m:\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164mN\x1b[0m\x1b[38;5;164m2\x1b[0m\x1b[38;5;128m0\x1b[0m\x1b[38;5;129m2\x1b[0m', - '\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198mK\x1b[0m\x1b[38;5;198me\x1b[0m\x1b[38;5;199mr\x1b[0m\x1b[38;5;199mn\x1b[0m\x1b[38;5;199me\x1b[0m\x1b[38;5;163ml\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164mR\x1b[0m\x1b[38;5;164me\x1b[0m\x1b[38;5;128ml\x1b[0m\x1b[38;5;129me\x1b[0m\x1b[38;5;129ma\x1b[0m\x1b[38;5;129ms\x1b[0m\x1b[38;5;93me\x1b[0m\x1b[38;5;93m:\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m4\x1b[0m\x1b[38;5;63m.\x1b[0m\x1b[38;5;63m9\x1b[0m\x1b[38;5;63m.\x1b[0m\x1b[38;5;33m4\x1b[0m\x1b[38;5;33m7\x1b[0m\x1b[38;5;33m-\x1b[0m\x1b[38;5;39m1\x1b[0m\x1b[38;5;39m-\x1b[0m\x1b[38;5;39ml\x1b[0m\x1b[38;5;38mt\x1b[0m\x1b[38;5;44ms\x1b[0m', - '\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199mU\x1b[0m\x1b[38;5;199mp\x1b[0m\x1b[38;5;163mt\x1b[0m\x1b[38;5;164mi\x1b[0m\x1b[38;5;164mm\x1b[0m\x1b[38;5;164me\x1b[0m\x1b[38;5;128m:\x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m1\x1b[0m\x1b[38;5;129m9\x1b[0m\x1b[38;5;93m:\x1b[0m\x1b[38;5;93m2\x1b[0m\x1b[38;5;93m6\x1b[0m', - '\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;184m#\x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164mW\x1b[0m\x1b[38;5;164mM\x1b[0m\x1b[38;5;164m:\x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129mK\x1b[0m\x1b[38;5;129mW\x1b[0m\x1b[38;5;129mi\x1b[0m\x1b[38;5;93mn\x1b[0m', - '\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m;\x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m;\x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128mD\x1b[0m\x1b[38;5;129mE\x1b[0m\x1b[38;5;129m:\x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93mK\x1b[0m\x1b[38;5;93mD\x1b[0m\x1b[38;5;93mE\x1b[0m', - '\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m+\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;208m.\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129mP\x1b[0m\x1b[38;5;93ma\x1b[0m\x1b[38;5;93mc\x1b[0m\x1b[38;5;93mk\x1b[0m\x1b[38;5;63ma\x1b[0m\x1b[38;5;63mg\x1b[0m\x1b[38;5;63me\x1b[0m\x1b[38;5;63ms\x1b[0m\x1b[38;5;33m:\x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m1\x1b[0m\x1b[38;5;39m8\x1b[0m\x1b[38;5;39m2\x1b[0m\x1b[38;5;39m1\x1b[0m', - '\x1b[38;5;154m \x1b[0m\x1b[38;5;154m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m+\x1b[0m\x1b[38;5;208m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93mR\x1b[0m\x1b[38;5;63mA\x1b[0m\x1b[38;5;63mM\x1b[0m\x1b[38;5;63m:\x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m9\x1b[0m\x1b[38;5;33m2\x1b[0m\x1b[38;5;33m5\x1b[0m\x1b[38;5;39m6\x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39mM\x1b[0m\x1b[38;5;38mB\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m/\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m1\x1b[0m\x1b[38;5;49m5\x1b[0m\x1b[38;5;49m9\x1b[0m\x1b[38;5;49m9\x1b[0m\x1b[38;5;48m9\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48mM\x1b[0m\x1b[38;5;83mB\x1b[0m', - '\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;184m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m;\x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63mP\x1b[0m\x1b[38;5;63mr\x1b[0m\x1b[38;5;33mo\x1b[0m\x1b[38;5;33mc\x1b[0m\x1b[38;5;33me\x1b[0m\x1b[38;5;39ms\x1b[0m\x1b[38;5;39ms\x1b[0m\x1b[38;5;39mo\x1b[0m\x1b[38;5;38mr\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44mT\x1b[0m\x1b[38;5;44my\x1b[0m\x1b[38;5;43mp\x1b[0m\x1b[38;5;49me\x1b[0m\x1b[38;5;49m:\x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48mI\x1b[0m\x1b[38;5;48mn\x1b[0m\x1b[38;5;48mt\x1b[0m\x1b[38;5;83me\x1b[0m\x1b[38;5;83ml\x1b[0m\x1b[38;5;83m(\x1b[0m\x1b[38;5;83mR\x1b[0m\x1b[38;5;118m)\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118mC\x1b[0m\x1b[38;5;154mo\x1b[0m\x1b[38;5;154mr\x1b[0m\x1b[38;5;154me\x1b[0m\x1b[38;5;148m(\x1b[0m\x1b[38;5;184mT\x1b[0m\x1b[38;5;184mM\x1b[0m\x1b[38;5;184m)\x1b[0m\x1b[38;5;178m \x1b[0m\x1b[38;5;214mi\x1b[0m\x1b[38;5;214m5\x1b[0m\x1b[38;5;214m-\x1b[0m\x1b[38;5;208m6\x1b[0m\x1b[38;5;208m4\x1b[0m\x1b[38;5;208m0\x1b[0m\x1b[38;5;203m0\x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203mC\x1b[0m\x1b[38;5;203mP\x1b[0m\x1b[38;5;198mU\x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m@\x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m2\x1b[0m\x1b[38;5;199m.\x1b[0m\x1b[38;5;163m8\x1b[0m\x1b[38;5;164m0\x1b[0m\x1b[38;5;164mG\x1b[0m\x1b[38;5;164mH\x1b[0m\x1b[38;5;128mz\x1b[0m', - '\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;214m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;203m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m+\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m$\x1b[0m\x1b[38;5;33mE\x1b[0m\x1b[38;5;39mD\x1b[0m\x1b[38;5;39mI\x1b[0m\x1b[38;5;39mT\x1b[0m\x1b[38;5;38mO\x1b[0m\x1b[38;5;44mR\x1b[0m\x1b[38;5;44m:\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43mv\x1b[0m\x1b[38;5;49mi\x1b[0m\x1b[38;5;49mm\x1b[0m', - '\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;208m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39mR\x1b[0m\x1b[38;5;39mo\x1b[0m\x1b[38;5;38mo\x1b[0m\x1b[38;5;44mt\x1b[0m\x1b[38;5;44m:\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m1\x1b[0m\x1b[38;5;49m6\x1b[0m\x1b[38;5;49m0\x1b[0m\x1b[38;5;49mG\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m/\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m1\x1b[0m\x1b[38;5;83m9\x1b[0m\x1b[38;5;83m6\x1b[0m\x1b[38;5;83mG\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;118m(\x1b[0m\x1b[38;5;118m8\x1b[0m\x1b[38;5;154m1\x1b[0m\x1b[38;5;154m%\x1b[0m\x1b[38;5;154m)\x1b[0m\x1b[38;5;148m \x1b[0m\x1b[38;5;184m(\x1b[0m\x1b[38;5;184me\x1b[0m\x1b[38;5;184mx\x1b[0m\x1b[38;5;178mt\x1b[0m\x1b[38;5;214m4\x1b[0m\x1b[38;5;214m)\x1b[0m', - '\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m.\x1b[0m\x1b[38;5;198m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m;\x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m;\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;63m;\x1b[0m\x1b[38;5;63m`\x1b[0m\x1b[38;5;63m"\x1b[0m\x1b[38;5;33m.\x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m', - '\x1b[38;5;203m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;198m \x1b[0m\x1b[38;5;199m.\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;199m#\x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m;\x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m;\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m.\x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m', - '\x1b[38;5;198m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;199m \x1b[0m\x1b[38;5;163m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m.\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m.\x1b[0m\x1b[38;5;63m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;33m#\x1b[0m\x1b[38;5;39m#\x1b[0m\x1b[38;5;39m#\x1b[0m\x1b[38;5;39m#\x1b[0m\x1b[38;5;38m#\x1b[0m\x1b[38;5;44m`\x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m', - '\x1b[38;5;199m \x1b[0m\x1b[38;5;163m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;164m#\x1b[0m\x1b[38;5;128m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m\'\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m\'\x1b[0m\x1b[38;5;38m#\x1b[0m\x1b[38;5;44m#\x1b[0m\x1b[38;5;44m#\x1b[0m\x1b[38;5;44m#\x1b[0m\x1b[38;5;43m#\x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m', - '\x1b[38;5;164m \x1b[0m\x1b[38;5;164m \x1b[0m\x1b[38;5;128m;\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;49m#\x1b[0m\x1b[38;5;48m#\x1b[0m\x1b[38;5;48m;\x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m', - '\x1b[38;5;129m \x1b[0m\x1b[38;5;129m \x1b[0m\x1b[38;5;129m#\x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m\'\x1b[0m\x1b[38;5;93m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m\'\x1b[0m\x1b[38;5;83m#\x1b[0m\x1b[38;5;83m#\x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m \x1b[0m', - '\x1b[38;5;93m \x1b[0m\x1b[38;5;93m#\x1b[0m\x1b[38;5;93m\'\x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;63m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;33m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;39m \x1b[0m\x1b[38;5;38m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;44m \x1b[0m\x1b[38;5;43m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;49m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;48m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;83m \x1b[0m\x1b[38;5;118m`\x1b[0m\x1b[38;5;118m#\x1b[0m\x1b[38;5;118m \x1b[0m\x1b[38;5;154m \x1b[0m' - ] + let image = ` + ###. ESPTerm Demo + '###. Hostname: ${window.location.hostname} + '###. Shell: ESPTerm Demo Shell + '###. Resolution: 80x25@${window.devicePixelRatio}x + :###- + .###' + .###' + .###' ############### + ###' ############### + `.split('\n').filter(line => line.trim()) + let chars = '' + for (let y = 0; y < image.length; y++) { + for (let x = 0; x < 80; x++) { + if (image[y][x]) { + chars += `\x1b[38;5;${getRainbowColor((x + y) / 10)}m${image[y][x]}` + } else chars += ' ' + } + } + + this.emit('write', '\r\n\x1b[?25l') let loop = () => { - if (lines.length) setTimeout(loop, 50) - else this.destroy() - this.emit('write', lines.shift() + '\r\n') + this.emit('write', chars.substr(0, 80)) + chars = chars.substr(80) + if (chars.length) setTimeout(loop, 50) + else { + this.emit('write', '\r\n\x1b[?25h') + this.destroy() + } } loop() } @@ -410,8 +438,8 @@ let demoshIndex = { -#####- -###*..#####- ######- -#* -#- .## .##. *#- -##### .-###*..#####- *#- -*##*- #*-#--#**#-*##- - -#* -#-.##. *#- *##-#* ##. -#* *# .#* - -#####--####- .##. *#- -*###- ##. -#* *# .#* + -#* -#-.##. *#- *##@#* ##. -#* *# .#* + -#####--####- .##. *#- -*#@@- ##. -#* *# .#* `.split('\n').filter(line => line.trim()) let levels = { ' ': -231, @@ -439,7 +467,11 @@ let demoshIndex = { } let drawCell = (x, y) => { moveTo(x, y) - this.emit('write', `\x1b[48;5;${231 + levels[splash[y][x]]}m \b`) + if (splash[y][x] === '@') { + this.emit('write', '\x1b[48;5;8m\x1b[38;5;255m▄\b') + } else { + this.emit('write', `\x1b[48;5;${231 + levels[splash[y][x]]}m \b`) + } } return new Promise((resolve, reject) => { const self = this @@ -471,7 +503,8 @@ let demoshIndex = { '', ' ESPTerm is a VT100-like terminal emulator running on the ESP8266 WiFi chip.', '', - ' \x1b[93mThis is an online demo of the web user interface.\x1b[m', + ' \x1b[93mThis is an online demo of the web user interface, simulating a simple ', + ' terminal in your browser.\x1b[m', '', ' Type \x1b[92mls\x1b[m to list available commands.', ' Use the \x1b[94mlinks\x1b[m below this screen for a demo of the options and more info.', @@ -568,6 +601,18 @@ let demoshIndex = { this.destroy() } }, + rainbow: class ToggleRainbow extends Process { + constructor (shell) { + super() + this.shell = shell + } + run () { + this.shell.terminal.rainbow = !this.shell.terminal.rainbow + this.shell.terminal.rainbowTimer() + this.emit('write', '') + this.destroy() + } + }, pwd: '/this/is/a/demo\r\n', cd: '\x1b[38;5;239mNo directories to change to\r\n', whoami: `${window.navigator.userAgent}\r\n`,