Skip to content

Instantly share code, notes, and snippets.

@camwhite
Last active February 27, 2019 02:31
Show Gist options
  • Select an option

  • Save camwhite/d17de0a41dcb87d235b386106386f746 to your computer and use it in GitHub Desktop.

Select an option

Save camwhite/d17de0a41dcb87d235b386106386f746 to your computer and use it in GitHub Desktop.
class TermStream extends Duplex {
constructor(vm) {
super()
this.result = ''
this.$terminal = vm.$terminal
this.$vm = vm
this.encoding = vm.encoding || 'utf-8'
if (!this.$terminal) {
throw new TypeError('VueTerm instance only')
}
this.$terminal.on('data', data => {
this.result += data
this.write(Buffer.from(data, this.encoding))
})
this.$terminal.on('keydown', ({ code }) => {
if (code === 'Enter') {
this.push(Buffer.from(this.result, this.encoding))
this.$terminal.write('\n')
this._flush()
}
if (code === 'Backspace') {
this.$terminal.write('\b \b')
}
})
}
_write(chunk, encoding, callback) {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(this.encoding)
}
this.$terminal.write(chunk)
callback()
}
_final(callback) {
this.$vm.$emit('final')
if (callback) callback()
}
_flush() {
this.result = ''
this.push(Buffer.from(this.result, this.encoding))
}
_read(size) {
return this.result
}
}
<template>
<div class="xterm"></div>
</template>
<script>
import { Duplex } from 'stream'
import { mapState } from 'vuex'
import { Terminal } from 'xterm'
import { fit } from 'xterm/lib/addons/fit/fit'
import figlet from 'figlet'
import 'xterm/dist/xterm.css'
class TermStream extends Duplex {
constructor(vm) {
super()
this.result = ''
this.$terminal = vm.$terminal
this.$vm = vm
this.encoding = vm.encoding || 'utf-8'
if (!this.$terminal) {
throw new TypeError('xterm instance only')
}
this.$terminal.on('data', data => {
this.result += data
this.write(Buffer.from(data, this.encoding))
})
this.$terminal.on('keydown', ({ code }) => {
if (code === 'Enter') {
this.push(Buffer.from(this.result, this.encoding))
this.$terminal.write('\n')
this._flush()
}
if (code === 'Backspace') {
this.$terminal.write('\b \b')
}
})
}
_write(chunk, encoding, callback) {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(this.encoding)
}
this.$terminal.write(chunk)
callback()
}
_final(callback) {
this.$vm.$emit('final')
if (callback) callback()
}
_flush() {
this.result = ''
this.push(Buffer.from(this.result, this.encoding))
}
_read(size) {
return this.result
}
}
export default {
name: 'prompt',
data() {
return {
intro: ''
}
},
computed: {
...mapState(['currentUser'])
},
async mounted() {
this.intro = await this.onFiglet(
`${this.currentUser.name}'s terminal`
)
const term = new Terminal()
term.open(this.$el, true)
fit(term)
term.on('blur', () => this.$emit('blur'))
term.on('focus', () => this.$emit('focus'))
term.on('resize', size => fit(term))
term.write(this.intro.replace(/\n/g, '\r\n') + '\r\n')
term.focus()
this.$terminal = term
this.$stream = new TermStream(this)
this.$stream.on('data', this.onCommand)
},
methods: {
async onCommand(text) {
text = text.toString().trim()
console.log(text)
if (text.startsWith('!')) {
const [cmd, ...args] = text.split(' ')
if (typeof args === 'array') {
args = args.join(' ')
}
let result
switch (cmd) {
case '!figlet':
result = await this.onFiglet(args)
default:
if (result) {
this.$terminal.write(
result.replace(/\n/g, '\r\n') + '\r\n'
)
}
break
}
}
},
onFiglet(text) {
return new Promise((resolve, reject) => {
figlet(
text,
{
font: 'Graffiti',
horizontalLayout: 'full'
},
(err, text) => {
if (err) return reject(err)
resolve(text)
}
)
})
}
}
}
</script>
<style lang="stylus" scoped></style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment