From 3e61bfdeaa69c2dbaeae692c700a1b4d443c5819 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 3 Feb 2019 15:10:21 -0800 Subject: [PATCH] Replace console view with proper ts --- .../assets/scripts/components/server/index.ts | 2 +- .../components/server/subpages/Console.ts | 187 ++++++++++++++++++ .../components/server/subpages/Console.vue | 183 ----------------- .../assets/scripts/pterodactyl-shims.d.ts | 3 + resources/assets/styles/main.css | 2 + webpack.config.js | 2 +- 6 files changed, 194 insertions(+), 185 deletions(-) create mode 100644 resources/assets/scripts/components/server/subpages/Console.ts delete mode 100644 resources/assets/scripts/components/server/subpages/Console.vue diff --git a/resources/assets/scripts/components/server/index.ts b/resources/assets/scripts/components/server/index.ts index b35c3382..ed068548 100644 --- a/resources/assets/scripts/components/server/index.ts +++ b/resources/assets/scripts/components/server/index.ts @@ -1,6 +1,6 @@ export {default as Server} from './Server'; export {default as ServerAllocations} from './ServerAllocations.vue'; -export {default as ConsolePage} from './subpages/Console.vue'; +export {default as ConsolePage} from './subpages/Console'; export {default as DatabasesPage} from './subpages/Databases.vue'; export {default as FileManagerPage} from './subpages/FileManager.vue'; export {default as ServerSchedules} from './ServerSchedules.vue'; diff --git a/resources/assets/scripts/components/server/subpages/Console.ts b/resources/assets/scripts/components/server/subpages/Console.ts new file mode 100644 index 00000000..87e0ea90 --- /dev/null +++ b/resources/assets/scripts/components/server/subpages/Console.ts @@ -0,0 +1,187 @@ +import Vue from 'vue'; +import {mapState} from "vuex"; +import {Terminal} from 'xterm'; +import * as TerminalFit from 'xterm/lib/addons/fit/fit'; +import {Socketio} from "@/mixins/socketio"; + +type DataStructure = { + terminal: Terminal | null, + command: string, + commandHistory: Array, + commandHistoryIndex: number, +} + +export default Vue.component('server-console', { + mixins: [Socketio], + computed: { + ...mapState('socket', ['connected']), + }, + + watch: { + /** + * Watch the connected variable and when it becomes true request the server logs. + */ + connected: function (state: boolean) { + if (state) { + this.$nextTick(() => { + this.mountTerminal(); + }); + } else { + this.terminal && this.terminal.clear(); + } + }, + }, + + /** + * Listen for specific socket.io emits from the server. + */ + sockets: { + 'server log': function (data: string) { + data.split(/\n/g).forEach((line: string): void => { + if (this.terminal) { + this.terminal.writeln(line + '\u001b[0m'); + } + }); + }, + + 'console': function (data: { line: string }) { + data.line.split(/\n/g).forEach((line: string): void => { + if (this.terminal) { + this.terminal.writeln(line + '\u001b[0m'); + } + }); + }, + }, + + /** + * Mount the component and setup all of the terminal actions. Also fetches the initial + * logs from the server to populate into the terminal if the socket is connected. If the + * socket is not connected this will occur automatically when it connects. + */ + mounted: function () { + if (this.connected) { + this.mountTerminal(); + } + }, + + data: function (): DataStructure { + return { + terminal: null, + command: '', + commandHistory: [], + commandHistoryIndex: -1, + }; + }, + + methods: { + /** + * Mount the terminal and grab the most recent server logs. + */ + mountTerminal: function () { + // Get a new instance of the terminal setup. + this.terminal = this._terminalInstance(); + + this.terminal.open((this.$refs.terminal as HTMLElement)); + // @ts-ignore + this.terminal.fit(); + this.terminal.clear(); + + this.$socket().instance().emit('send server log'); + }, + + /** + * Send a command to the server using the configured websocket. + */ + sendCommand: function () { + this.commandHistoryIndex = -1; + // this.commandHistory.unshift(this.command); + this.commandHistory.unshift(); + this.$socket().instance().emit('send command', this.command); + this.command = ''; + }, + + /** + * Handle a user pressing up/down arrows when in the command field to scroll through thier + * command history for this server. + */ + handleArrowKey: function (e: KeyboardEvent) { + if (['ArrowUp', 'ArrowDown'].indexOf(e.key) < 0 || e.key === 'ArrowDown' && this.commandHistoryIndex < 0) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + if (e.key === 'ArrowUp' && (this.commandHistoryIndex + 1 > (this.commandHistory.length - 1))) { + return; + } + + this.commandHistoryIndex += (e.key === 'ArrowUp') ? 1 : -1; + this.command = this.commandHistoryIndex < 0 ? '' : this.commandHistory[this.commandHistoryIndex]; + }, + + /** + * Returns a new instance of the terminal to be used. + * + * @private + */ + _terminalInstance() { + Terminal.applyAddon(TerminalFit); + + return new Terminal({ + disableStdin: true, + cursorStyle: 'underline', + allowTransparency: true, + fontSize: 12, + fontFamily: 'Menlo, Monaco, Consolas, monospace', + rows: 30, + theme: { + background: 'transparent', + cursor: 'transparent', + black: '#000000', + red: '#E54B4B', + green: '#9ECE58', + yellow: '#FAED70', + blue: '#396FE2', + magenta: '#BB80B3', + cyan: '#2DDAFD', + white: '#d0d0d0', + brightBlack: 'rgba(255, 255, 255, 0.2)', + brightRed: '#FF5370', + brightGreen: '#C3E88D', + brightYellow: '#FFCB6B', + brightBlue: '#82AAFF', + brightMagenta: '#C792EA', + brightCyan: '#89DDFF', + brightWhite: '#ffffff', + }, + }); + } + }, + + template: ` +
+
+
+
+
+
+
+
+
+
+ $ +
+
+ +
+
+
+
+ `, +}); diff --git a/resources/assets/scripts/components/server/subpages/Console.vue b/resources/assets/scripts/components/server/subpages/Console.vue deleted file mode 100644 index e32aebbe..00000000 --- a/resources/assets/scripts/components/server/subpages/Console.vue +++ /dev/null @@ -1,183 +0,0 @@ - - - - - diff --git a/resources/assets/scripts/pterodactyl-shims.d.ts b/resources/assets/scripts/pterodactyl-shims.d.ts index e8ab21b3..d762d2f2 100644 --- a/resources/assets/scripts/pterodactyl-shims.d.ts +++ b/resources/assets/scripts/pterodactyl-shims.d.ts @@ -27,6 +27,9 @@ declare module 'vue/types/options' { [s: string]: (data: any) => void, } }, + sockets?: { + [s: string]: (data: any) => void, + } } } diff --git a/resources/assets/styles/main.css b/resources/assets/styles/main.css index de128a8e..b084b13f 100644 --- a/resources/assets/styles/main.css +++ b/resources/assets/styles/main.css @@ -4,6 +4,8 @@ @import "tailwindcss/preflight"; @import "tailwindcss/components"; +@import "xterm/src/xterm.css"; + /** * Pterodactyl Specific CSS */ diff --git a/webpack.config.js b/webpack.config.js index d27f3544..9f71b883 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -42,7 +42,7 @@ if (isProduction) { // Don't let PurgeCSS remove classes ending with -enter or -leave-active // They're used by Vue transitions and are therefore not specifically defined // in any of the files are are checked by PurgeCSS. - whitelistPatterns: [/-enter$/, /-leave-active$/], + whitelistPatterns: [/^xterm/, /-enter$/, /-leave-active$/], extractors: [ { extractor: class {