|
|
|
@ -36,30 +36,41 @@ class ANSIParser { |
|
|
|
|
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) { |
|
|
|
|
if (!numbers.length) { |
|
|
|
|
this.handler('reset-style') |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
let type = numbers[0] |
|
|
|
|
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('reset-color-fg') |
|
|
|
|
else if (type === 49) this.handler('reset-color-bg') |
|
|
|
|
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) |
|
|
|
|
let type |
|
|
|
|
while ((type = numbers.shift())) { |
|
|
|
|
if (type === 0) this.handler('reset-style') |
|
|
|
|
else if (type === 1) this.handler('add-attrs', 1 << 2) // bold
|
|
|
|
|
else if (type === 2) this.handler('add-attrs', 1 << 9) // faint
|
|
|
|
|
else if (type === 3) this.handler('add-attrs', 1 << 6) // italic
|
|
|
|
|
else if (type === 4) this.handler('add-attrs', 1 << 3) // underline
|
|
|
|
|
else if (type === 5 || type === 6) this.handler('add-attrs', 1 << 5) // blink
|
|
|
|
|
else if (type === 7) this.handler('add-attrs', 1 << 4) // invert
|
|
|
|
|
else if (type === 9) this.handler('add-attrs', 1 << 7) // strike
|
|
|
|
|
else if (type === 20) this.handler('add-attrs', 1 << 10) // 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('reset-color-fg') |
|
|
|
|
else if (type === 49) this.handler('reset-color-bg') |
|
|
|
|
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) { |
|
|
|
|
let mode = numbers.shift() |
|
|
|
|
if (mode === 2) { |
|
|
|
|
let r = numbers.shift() |
|
|
|
|
let g = numbers.shift() |
|
|
|
|
let b = numbers.shift() |
|
|
|
|
let color = (r << 16 | g << 8 | b) + 256 |
|
|
|
|
if (type === 38) this.handler('set-color-fg', color) |
|
|
|
|
if (type === 48) this.handler('set-color-bg', color) |
|
|
|
|
} else if (mode === 5) { |
|
|
|
|
let color = (numbers.shift() | 0) & 0xFF |
|
|
|
|
if (type === 38) this.handler('set-color-fg', color) |
|
|
|
|
if (type === 48) this.handler('set-color-bg', color) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (type === 'h' || type === 'l') { |
|
|
|
@ -101,7 +112,7 @@ class ANSIParser { |
|
|
|
|
if (!this.joinChunks) this.reset() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
const TERM_DEFAULT_STYLE = 0 |
|
|
|
|
const TERM_DEFAULT_STYLE = [0, 0, 0] |
|
|
|
|
const TERM_MIN_DRAW_DELAY = 10 |
|
|
|
|
|
|
|
|
|
let getRainbowColor = t => { |
|
|
|
@ -117,19 +128,20 @@ class ScrollingTerminal { |
|
|
|
|
this.height = 25 |
|
|
|
|
this.termScreen = screen |
|
|
|
|
this.parser = new ANSIParser((...args) => this.handleParsed(...args)) |
|
|
|
|
this.buttonLabels = ['', '', '^C', '', 'Help'] |
|
|
|
|
|
|
|
|
|
this.reset() |
|
|
|
|
|
|
|
|
|
this._lastLoad = Date.now() |
|
|
|
|
this.termScreen.load(this.serialize()) |
|
|
|
|
this.loadTimer() |
|
|
|
|
|
|
|
|
|
window.showPage() |
|
|
|
|
} |
|
|
|
|
reset () { |
|
|
|
|
this.style = TERM_DEFAULT_STYLE |
|
|
|
|
this.style = TERM_DEFAULT_STYLE.slice() |
|
|
|
|
this.cursor = { x: 0, y: 0, style: 1, visible: true } |
|
|
|
|
this.trackMouse = false |
|
|
|
|
this.theme = -1 |
|
|
|
|
this.theme = 0 |
|
|
|
|
this.rainbow = false |
|
|
|
|
this.parser.reset() |
|
|
|
|
this.clear() |
|
|
|
@ -137,13 +149,13 @@ class ScrollingTerminal { |
|
|
|
|
clear () { |
|
|
|
|
this.screen = [] |
|
|
|
|
for (let i = 0; i < this.width * this.height; i++) { |
|
|
|
|
this.screen.push([' ', this.style]) |
|
|
|
|
this.screen.push([' ', this.style.slice()]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
scroll () { |
|
|
|
|
this.screen.splice(0, this.width) |
|
|
|
|
for (let i = 0; i < this.width; i++) { |
|
|
|
|
this.screen.push([' ', TERM_DEFAULT_STYLE]) |
|
|
|
|
this.screen.push([' ', TERM_DEFAULT_STYLE.slice()]) |
|
|
|
|
} |
|
|
|
|
this.cursor.y-- |
|
|
|
|
} |
|
|
|
@ -152,7 +164,7 @@ class ScrollingTerminal { |
|
|
|
|
if (this.cursor.y >= this.height) this.scroll() |
|
|
|
|
} |
|
|
|
|
writeChar (character) { |
|
|
|
|
this.screen[this.cursor.y * this.width + this.cursor.x] = [character, this.style] |
|
|
|
|
this.screen[this.cursor.y * this.width + this.cursor.x] = [character, this.style.slice()] |
|
|
|
|
this.cursor.x++ |
|
|
|
|
if (this.cursor.x >= this.width) { |
|
|
|
|
this.cursor.x = 0 |
|
|
|
@ -181,12 +193,12 @@ class ScrollingTerminal { |
|
|
|
|
} |
|
|
|
|
deleteChar () { // FIXME unused?
|
|
|
|
|
this.moveBack() |
|
|
|
|
this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) |
|
|
|
|
this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE.slice()]) |
|
|
|
|
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]) |
|
|
|
|
for (let i = 0; i < n; i++) this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE.slice()]) |
|
|
|
|
this.screen.splice(this.cursor.y * this.width + this.cursor.x, n) |
|
|
|
|
} |
|
|
|
|
clampCursor () { |
|
|
|
@ -205,11 +217,12 @@ class ScrollingTerminal { |
|
|
|
|
} else if (action === 'clear') { |
|
|
|
|
this.clear() |
|
|
|
|
} else if (action === 'bell') { |
|
|
|
|
this.termScreen.load('B') |
|
|
|
|
this.termScreen.load('U\x01B') |
|
|
|
|
} else if (action === 'back') { |
|
|
|
|
this.moveBack() |
|
|
|
|
} else if (action === 'new-line') { |
|
|
|
|
this.newLine() |
|
|
|
|
this.cursor.x = 0 |
|
|
|
|
} else if (action === 'return') { |
|
|
|
|
this.cursor.x = 0 |
|
|
|
|
} else if (action === 'set-cursor') { |
|
|
|
@ -231,17 +244,21 @@ class ScrollingTerminal { |
|
|
|
|
} 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 |
|
|
|
|
this.style = TERM_DEFAULT_STYLE.slice() |
|
|
|
|
} else if (action === 'add-attrs') { |
|
|
|
|
this.style |= (args[0] << 16) |
|
|
|
|
this.style[2] |= args[0] |
|
|
|
|
} else if (action === 'set-color-fg') { |
|
|
|
|
this.style = (this.style & 0xFFFFFF00) | (1 << 8 << 16) | args[0] |
|
|
|
|
this.style[0] = args[0] |
|
|
|
|
this.style[2] |= 1 |
|
|
|
|
} else if (action === 'set-color-bg') { |
|
|
|
|
this.style = (this.style & 0xFFFF00FF) | (1 << 9 << 16) | (args[0] << 8) |
|
|
|
|
this.style[1] = args[0] |
|
|
|
|
this.style[2] |= 1 << 1 |
|
|
|
|
} else if (action === 'reset-color-fg') { |
|
|
|
|
this.style = this.style & 0xFFFEFF00 |
|
|
|
|
this.style[0] = 0 |
|
|
|
|
if (this.style[2] & 1) this.style[2] ^= 1 |
|
|
|
|
} else if (action === 'reset-color-bg') { |
|
|
|
|
this.style = this.style & 0xFFFD00FF |
|
|
|
|
this.style[1] = 0 |
|
|
|
|
if (this.style[2] & (1 << 1)) this.style[2] ^= (1 << 1) |
|
|
|
|
} else if (action === 'hide-cursor') { |
|
|
|
|
this.cursor.visible = false |
|
|
|
|
} else if (action === 'show-cursor') { |
|
|
|
@ -250,65 +267,119 @@ class ScrollingTerminal { |
|
|
|
|
} |
|
|
|
|
write (text) { |
|
|
|
|
this.parser.write(text) |
|
|
|
|
this.scheduleLoad() |
|
|
|
|
} |
|
|
|
|
serialize () { |
|
|
|
|
let serialized = 'S' |
|
|
|
|
serialized += String.fromCodePoint(this.height + 1) + String.fromCodePoint(this.width + 1) |
|
|
|
|
serialized += String.fromCodePoint(this.cursor.y + 1) + String.fromCodePoint(this.cursor.x + 1) |
|
|
|
|
|
|
|
|
|
getScreenOpts () { |
|
|
|
|
let data = 'O' |
|
|
|
|
data += String.fromCodePoint(26) |
|
|
|
|
data += String.fromCodePoint(81) |
|
|
|
|
data += String.fromCodePoint(this.theme + 1) |
|
|
|
|
data += String.fromCodePoint(8) |
|
|
|
|
data += String.fromCodePoint(1) |
|
|
|
|
data += String.fromCodePoint(1) |
|
|
|
|
data += String.fromCodePoint(1) |
|
|
|
|
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) |
|
|
|
|
serialized += String.fromCodePoint(attributes + 1) |
|
|
|
|
data += String.fromCodePoint(attributes + 1) |
|
|
|
|
return data |
|
|
|
|
} |
|
|
|
|
getButtons () { |
|
|
|
|
let data = 'B' |
|
|
|
|
data += String.fromCodePoint(6) |
|
|
|
|
data += this.buttonLabels.map(x => x + '\x01').join('') |
|
|
|
|
return data |
|
|
|
|
} |
|
|
|
|
getCursor () { |
|
|
|
|
let data = 'C' |
|
|
|
|
data += String.fromCodePoint(this.cursor.y + 1) |
|
|
|
|
data += String.fromCodePoint(this.cursor.x + 1) |
|
|
|
|
data += String.fromCodePoint(1) |
|
|
|
|
return data |
|
|
|
|
} |
|
|
|
|
encodeColor (color) { |
|
|
|
|
if (color < 256) { |
|
|
|
|
return String.fromCodePoint(color + 1) |
|
|
|
|
} else { |
|
|
|
|
color -= 256 |
|
|
|
|
return String.fromCodePoint(((color & 0xFFF) | 0x10000) + 1) + String.fromCodePoint((color >> 12) + 1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
serializeScreen () { |
|
|
|
|
let serialized = 'S' |
|
|
|
|
serialized += String.fromCodePoint(1) + String.fromCodePoint(1) |
|
|
|
|
serialized += String.fromCodePoint(this.height + 1) + String.fromCodePoint(this.width + 1) |
|
|
|
|
|
|
|
|
|
let lastStyle = null |
|
|
|
|
let lastStyle = [null, null, null] |
|
|
|
|
let index = 0 |
|
|
|
|
for (let cell of this.screen) { |
|
|
|
|
let style = cell[1] |
|
|
|
|
let style = cell[1].slice() |
|
|
|
|
if (this.rainbow) { |
|
|
|
|
let x = index % this.width |
|
|
|
|
let y = Math.floor(index / this.width) |
|
|
|
|
// C instead of F in mask and 1 << 8 in attrs to change attr bits 8 and 9
|
|
|
|
|
style = (style & 0xFFFC0000) | (1 << 8 << 16) | getRainbowColor((x + y) / 10 + Date.now() / 1000) |
|
|
|
|
// C instead of F in mask and 1 << 8 in attrs to change attr bits 1 and 2
|
|
|
|
|
style[0] = getRainbowColor((x + y) / 10 + Date.now() / 1000) |
|
|
|
|
style[1] = 0 |
|
|
|
|
style[2] = style[2] | 1 |
|
|
|
|
if (style[2] & (1 << 1)) style[2] ^= (1 << 1) |
|
|
|
|
index++ |
|
|
|
|
} |
|
|
|
|
if (style !== lastStyle) { |
|
|
|
|
let foreground = style & 0xFF |
|
|
|
|
let background = (style >> 8) & 0xFF |
|
|
|
|
let attributes = (style >> 16) & 0xFFFF |
|
|
|
|
let setForeground = foreground !== (lastStyle & 0xFF) |
|
|
|
|
let setBackground = background !== ((lastStyle >> 8) & 0xFF) |
|
|
|
|
let setAttributes = attributes !== ((lastStyle >> 16) & 0xFFFF) |
|
|
|
|
|
|
|
|
|
if (setForeground && setBackground) serialized += '\x03' + String.fromCodePoint((style & 0xFFFF) + 1) |
|
|
|
|
else if (setForeground) serialized += '\x05' + String.fromCodePoint(foreground + 1) |
|
|
|
|
else if (setBackground) serialized += '\x06' + String.fromCodePoint(background + 1) |
|
|
|
|
if (setAttributes) serialized += '\x04' + String.fromCodePoint(attributes + 1) |
|
|
|
|
lastStyle = style |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let foreground = style[0] |
|
|
|
|
let background = style[1] |
|
|
|
|
let attributes = style[2] |
|
|
|
|
let setForeground = foreground !== lastStyle[0] |
|
|
|
|
let setBackground = background !== lastStyle[1] |
|
|
|
|
let setAttributes = attributes !== lastStyle[2] |
|
|
|
|
|
|
|
|
|
if (setForeground && setBackground) { |
|
|
|
|
if (foreground < 256 && background < 256) { |
|
|
|
|
serialized += '\x03' + String.fromCodePoint(((background << 8) | foreground) + 1) |
|
|
|
|
} else { |
|
|
|
|
serialized += '\x05' + this.encodeColor(foreground) |
|
|
|
|
serialized += '\x06' + this.encodeColor(background) |
|
|
|
|
} |
|
|
|
|
} else if (setForeground) serialized += '\x05' + this.encodeColor(foreground) |
|
|
|
|
else if (setBackground) serialized += '\x06' + this.encodeColor(background) |
|
|
|
|
if (setAttributes) serialized += '\x04' + String.fromCodePoint(attributes + 1) |
|
|
|
|
lastStyle = style |
|
|
|
|
|
|
|
|
|
serialized += cell[0] |
|
|
|
|
} |
|
|
|
|
return serialized |
|
|
|
|
} |
|
|
|
|
scheduleLoad () { |
|
|
|
|
clearTimeout(this._scheduledLoad) |
|
|
|
|
if (this._lastLoad < Date.now() - TERM_MIN_DRAW_DELAY) { |
|
|
|
|
this.termScreen.load(this.serialize(), { theme: this.theme }) |
|
|
|
|
this.theme = -1 // prevent useless theme setting next time
|
|
|
|
|
} else { |
|
|
|
|
this._scheduledLoad = setTimeout(() => { |
|
|
|
|
this.termScreen.load(this.serialize()) |
|
|
|
|
}, TERM_MIN_DRAW_DELAY - this._lastLoad) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
rainbowTimer () { |
|
|
|
|
if (!this.rainbow) return |
|
|
|
|
clearInterval(this._rainbowTimer) |
|
|
|
|
this._rainbowTimer = setInterval(() => { |
|
|
|
|
if (this.rainbow) this.scheduleLoad() |
|
|
|
|
}, 50) |
|
|
|
|
getUpdate () { |
|
|
|
|
let topics = 0 |
|
|
|
|
let topicData = [] |
|
|
|
|
let screenOpts = this.getScreenOpts() |
|
|
|
|
let buttons = this.getButtons() |
|
|
|
|
let cursor = this.getCursor() |
|
|
|
|
let screen = this.serializeScreen() |
|
|
|
|
if (this._screenOpts !== screenOpts) { |
|
|
|
|
this._screenOpts = screenOpts |
|
|
|
|
topicData.push(screenOpts) |
|
|
|
|
} |
|
|
|
|
if (this._buttons !== buttons) { |
|
|
|
|
this._buttons = buttons |
|
|
|
|
topicData.push(buttons) |
|
|
|
|
} |
|
|
|
|
if (this._cursor !== cursor) { |
|
|
|
|
this._cursor = cursor |
|
|
|
|
topicData.push(cursor) |
|
|
|
|
} |
|
|
|
|
if (this._screen !== screen) { |
|
|
|
|
this._screen = screen |
|
|
|
|
topicData.push(screen) |
|
|
|
|
} |
|
|
|
|
if (!topicData.length) return '' |
|
|
|
|
return 'U' + String.fromCodePoint(topics + 1) + topicData.join('') |
|
|
|
|
} |
|
|
|
|
loadTimer () { |
|
|
|
|
clearInterval(this._loadTimer) |
|
|
|
|
this._loadTimer = setInterval(() => { |
|
|
|
|
let update = this.getUpdate() |
|
|
|
|
if (update) this.termScreen.load(update) |
|
|
|
|
}, 30) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -329,7 +400,7 @@ let demoData = { |
|
|
|
|
buttons: { |
|
|
|
|
1: '', |
|
|
|
|
2: '', |
|
|
|
|
3: '', |
|
|
|
|
3: (terminal, shell) => shell.write('\x03'), |
|
|
|
|
4: '', |
|
|
|
|
5: function (terminal, shell) { |
|
|
|
|
if (shell.child) shell.child.destroy() |
|
|
|
@ -466,7 +537,7 @@ let demoshIndex = { |
|
|
|
|
|
|
|
|
|
if (++x < 69) { |
|
|
|
|
if (++cycles >= 3) { |
|
|
|
|
setTimeout(loop, 20) |
|
|
|
|
setTimeout(loop, 50) |
|
|
|
|
cycles = 0 |
|
|
|
|
} else loop() |
|
|
|
|
} else { |
|
|
|
@ -557,7 +628,7 @@ let demoshIndex = { |
|
|
|
|
let theme = +args[0] | 0 |
|
|
|
|
const maxnum = themes.length |
|
|
|
|
if (!args.length || !Number.isFinite(theme) || theme < 0 || theme >= maxnum) { |
|
|
|
|
this.emit('write', `\x1b[31mUsage: theme [0–${maxnum - 1}]\r\n`) |
|
|
|
|
this.emit('write', `\x1b[31mUsage: theme [0–${maxnum - 1}]\n`) |
|
|
|
|
this.destroy() |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
@ -568,6 +639,40 @@ let demoshIndex = { |
|
|
|
|
this.destroy() |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
themes: class ShowThemes extends Process { |
|
|
|
|
color (hex) { |
|
|
|
|
hex = parseInt(hex.substr(1), 16) |
|
|
|
|
let r = hex >> 16 |
|
|
|
|
let g = (hex >> 8) & 0xFF |
|
|
|
|
let b = hex & 0xFF |
|
|
|
|
this.emit('write', `\x1b[48;2;${r};${g};${b}m`) |
|
|
|
|
if (((r + g + b) / 3) > 127) { |
|
|
|
|
this.emit('write', '\x1b[38;5;16m') |
|
|
|
|
} else { |
|
|
|
|
this.emit('write', '\x1b[38;5;255m') |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
run (...args) { |
|
|
|
|
for (let i in themes) { |
|
|
|
|
let theme = themes[i] |
|
|
|
|
|
|
|
|
|
let name = ` ${i}`.substr(-2) |
|
|
|
|
|
|
|
|
|
this.emit('write', `Theme ${name}: `) |
|
|
|
|
|
|
|
|
|
for (let col = 0; col < 16; col++) { |
|
|
|
|
let text = ` ${col}`.substr(-2) |
|
|
|
|
this.color(theme[col]) |
|
|
|
|
this.emit('write', text) |
|
|
|
|
this.emit('write', '\x1b[m ') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.emit('write', '\n') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.destroy() |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
cursor: class SetCursor extends Process { |
|
|
|
|
run (...args) { |
|
|
|
|
let steady = args.includes('--steady') |
|
|
|
@ -590,7 +695,6 @@ let demoshIndex = { |
|
|
|
|
} |
|
|
|
|
run () { |
|
|
|
|
this.shell.terminal.rainbow = !this.shell.terminal.rainbow |
|
|
|
|
this.shell.terminal.rainbowTimer() |
|
|
|
|
this.emit('write', '') |
|
|
|
|
this.destroy() |
|
|
|
|
} |
|
|
|
@ -618,7 +722,7 @@ let demoshIndex = { |
|
|
|
|
} |
|
|
|
|
render () { |
|
|
|
|
this.emit('write', '\x1b[m\x1b[2J\x1b[1;1H') |
|
|
|
|
this.emit('write', '\x1b[97m\x1b[1mMouse Demo\r\n\x1b[mMouse movement, clicking and scrolling!') |
|
|
|
|
this.emit('write', '\x1b[97m\x1b[1mMouse Demo\r\n\x1b[mMouse movement, clicking, and scrolling!') |
|
|
|
|
|
|
|
|
|
// render random data for scrolling
|
|
|
|
|
for (let y = 0; y < 23; y++) { |
|
|
|
|