Skip to content

Commit

Permalink
Merge pull request #17 from ste2425/xterm
Browse files Browse the repository at this point in the history
Xterm integration
  • Loading branch information
ste2425 authored Jun 10, 2019
2 parents 4f9f05b + 90e5e48 commit 80e0473
Show file tree
Hide file tree
Showing 14 changed files with 903 additions and 75 deletions.
22 changes: 19 additions & 3 deletions ReleaseNotes.md

Large diffs are not rendered by default.

110 changes: 87 additions & 23 deletions app/Components/runner/Runner.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
const { killDotnetProcessAsync, startDotnetProcess } = require('../../tasks');
const { killDotnetProcessAsync, startDotnetProcess, startCleanProcess } = require('../../tasks');
const Terminal = require('xterm').Terminal;
const fit = require('xterm/lib/addons/fit/fit');
const fullScreen = require('xterm/lib/addons/fullscreen/fullscreen');
const debounce = require('../../utils/debounce');

Terminal.applyAddon(fit);
Terminal.applyAddon(fullScreen);

const WebComponentBase = require('../WebComponentBase');

module.exports = class RunnerElement extends WebComponentBase {
Expand All @@ -14,6 +22,8 @@ module.exports = class RunnerElement extends WebComponentBase {

this._name = '';

this._terminalProcess;

this.setState(RunnerElement.states.stopped);
}

Expand All @@ -34,10 +44,48 @@ module.exports = class RunnerElement extends WebComponentBase {
this.setState(this.state);

const clearLog = shadow.querySelector('.clear-log');
const clean = shadow.querySelector('.clean');
const full = shadow.querySelector('.full');
const terminal = shadow.querySelector('.terminals');

clearLog.addEventListener('click', () => this.clearData());
clean.addEventListener('click', () => this.clean());

terminal.addEventListener('click', (e) => {
if (e.target.classList.contains('full-screen')) {
terminal.classList.remove('full-screen');
this._terminalProcess.fit();
}
});

full.addEventListener('click', () => {
terminal.classList.add('full-screen');

this.resize();
});

this._terminalProcess = new Terminal();
this._terminalProcess.setOption('disableStdin', true);
this._terminalProcess.setOption('fontFamily', "Consolas, 'Courier New', monospace");

this._terminalProcess.open(shadow.querySelector('.terminals'));

shadow.querySelector('.action').addEventListener('click', this._onToggle.bind(this));

// Terminal wont fit itself on resize.
window.addEventListener('resize', debounce(this.resize.bind(this), {
delay: 100,
executeOnFirstRun: true
}));

this.resize();
}

resize() {
this._terminalProcess.fit();

if (this._runningProccess)
this._runningProccess.resize(this._terminalProcess.cols, this._terminalProcess.rows);
}

_enableAction() {
Expand Down Expand Up @@ -67,6 +115,20 @@ module.exports = class RunnerElement extends WebComponentBase {
}
}

_enableClean() {
if (!this.shadowRoot)
return;

this.shadowRoot.querySelector('.clean').removeAttribute('disabled');
}

_disableClean() {
if (!this.shadowRoot)
return;

this.shadowRoot.querySelector('.clean').setAttribute('disabled', 'disabled');
}

onStart() {
if (this.state === RunnerElement.states.running || this.state === RunnerElement.states.starting)
return;
Expand All @@ -75,43 +137,43 @@ module.exports = class RunnerElement extends WebComponentBase {

this.setState(RunnerElement.states.starting);

this._runningProccess = startDotnetProcess(this.cwd, true, this.runCommandArguments);
this._terminalProcess.writeln("Starting...");

this._runningProccess.on('close', () => {
this._runningProccess = startDotnetProcess(this.cwd, true, this.runCommandArguments, this._terminalProcess.cols, this._terminalProcess.rows);

this._runningProccess.on('exit', () => {
this.setState(RunnerElement.states.stopped);

this._runningProccess = undefined;
});

this._runningProccess.stdout.on('data', (d) => this.onData(d.toString()));
this._runningProccess.stderr.on('data', (d) => this.onData(d.toString(), true));
}
this._runningProccess.once('data', () => this.setState(RunnerElement.states.running));

onData(d, errorData) {
if (this.state === RunnerElement.states.starting)
this.setState(RunnerElement.states.running);
this._runningProccess.on('data', (d) => {
this._terminalProcess.write(d);
});
}

const el = document.createElement('span');
const terminal = this.shadowRoot.querySelector('.terminal');
clean() {
if (this.state === RunnerElement.states.running || this.state === RunnerElement.states.starting)
return;

el.classList.add('log-item');
this._runningProccess = startCleanProcess(this.cwd);

if (errorData)
el.classList.add('error');

el.textContent = d;
this._runningProccess.on('exit', () => {
this.setState(RunnerElement.states.stopped);

terminal.appendChild(el);
this._runningProccess = undefined;
});

terminal.scrollTop = terminal.scrollHeight;
this._runningProccess.on('data', (d) => {
this.setState(RunnerElement.states.running);
this._terminalProcess.write(d);
});
}

clearData() {
const terminal = this.shadowRoot.querySelector('.terminal');

while(terminal.firstChild) {
terminal.removeChild(terminal.firstChild);
}
this._terminalProcess.clear();
}

onTerminate() {
Expand All @@ -136,6 +198,7 @@ module.exports = class RunnerElement extends WebComponentBase {

setState(state) {
this._disableAction();
this._disableClean();

this.state = state;

Expand Down Expand Up @@ -166,6 +229,7 @@ module.exports = class RunnerElement extends WebComponentBase {
stateEl.textContent = 'Stopped';
stateEl.className = 'state badge badge-secondary';
this._enableAction();
this._enableClean();
actionBtn.textContent = 'Start';
break;
}
Expand Down
45 changes: 34 additions & 11 deletions app/Components/runner/runner.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,42 @@ span.badge {
padding: 5px;
}

.terminal {
background-color: rgb(0, 36, 81);
color: #cccccc;
white-space: pre-line;
:host(:hover) .full-screen-toggle {
opacity: 1;
}

.terminals {
border: 1px solid rgba(128, 128, 128, 0.35);
border-radius: 4px;
padding: 5px;
word-break: break-word;
max-height: 200px;
overflow: auto;
flex: 1;
}

.log-item.error {
color: red;
}
}

.terminals.full-screen {
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
z-index: 200;
}

.terminals.full-screen::before {
content: 'Close';
display: block;
position: absolute;
top: 0px;
z-index: 5;
right: 20px;
color: white;
cursor: pointer;
border-radius: 3px;
border: 1px solid white;
padding: 5px;
margin: 3px;
}

.terminals {
position: relative;
}
15 changes: 14 additions & 1 deletion app/Components/runner/runner.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
<link rel="stylesheet" href="../../../node_modules/bootstrap-css-only/css/bootstrap.css">
<link rel="stylesheet" href="../../../node_modules/xterm/dist/xterm.css" />
<link rel="stylesheet" href="../../../node_modules/xterm/lib/addons/fullscreen/fullscreen.css" />
<!--
This ensures the style is applied when xterm attempts to check size of terminal container.
-->
<style>
.terminals:not(.full-screen) {
max-height: 200px;
}
</style>
<div class="d-flex align-items-center justify-content-between flex-row">
<h1 class="name"></h1>

<div class="btn-group">
<button class="action btn btn-sm btn-primary">Start</button>
<button class="full btn btn-sm btn-dark">Full Screen</button>
<drop-down class="group-left">
<button class="clean btn-sm">Clean</button>
<button class="clear-log btn-sm">Clear Log</button>
</drop-down>
</div>
</div>
<div class="d-flex justify-content-between align-items-center pb-2">
<span class="state badge"></span>
</div>
<div class="terminal"></div>
<div class="terminals">
</div>
54 changes: 53 additions & 1 deletion app/DotnetRunnerApp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { BrowserWindow, Menu } = require('electron');
const { BrowserWindow, Menu, ipcMain } = require('electron');
const { getApplications } = require('./data/applicationStore');
const { shell } = require('electron');
const ipcMessages = require('./ipcMessages');
Expand Down Expand Up @@ -82,27 +82,33 @@ module.exports = class DotnetRunnerApp {
{
label: 'Options',
submenu: [{
id: 'config-apps',
label: 'Configure Applicaions',
click: this._preferencesOnCLick.bind(this)
}, {
label: 'Preferences',
click: this._displayPreferences.bind(this)
}]
}, {
id: 'tasks',
label: 'Tasks',
submenu: [{
id: 'start-all',
label: 'Start all',
accelerator: 'Ctrl+s',
click: this._startAllApps.bind(this)
}, {
id: 'stop-all',
label: 'Stop all',
accelerator: 'Ctrl+Shift+S',
click: this._stopAllApps.bind(this)
}, {
id: 'clear-all',
label: 'Clear all',
accelerator: 'Ctrl+Shift+C',
click: this._clearAllApps.bind(this)
}, {
id: 'purge-all',
label: 'Purge',
accelerator: 'Ctrl+Shift+P',
click: this._purge.bind(this)
Expand Down Expand Up @@ -142,15 +148,24 @@ module.exports = class DotnetRunnerApp {
}

_startAllApps() {
this._disableTasks();
this._disableConfigAppMenu();
this._sendMessage(ipcMessages.startAllApplications);
ipcMain.once(ipcMessages.taskComplete, this._onTaskComplete.bind(this));
}

_stopAllApps() {
this._disableTasks();
this._disableConfigAppMenu();
this._sendMessage(ipcMessages.stopAllApplications);
ipcMain.once(ipcMessages.taskComplete, this._onTaskComplete.bind(this));
}

_clearAllApps() {
this._disableTasks();
this._disableConfigAppMenu();
this._sendMessage(ipcMessages.clearAllApplicationLogs);
ipcMain.once(ipcMessages.taskComplete, this._onTaskComplete.bind(this));
}

_purge() {
Expand All @@ -161,6 +176,43 @@ module.exports = class DotnetRunnerApp {
this._mainWindow.send(message);
}

_disableTasks() {
const menu = this._menu.getApplicationMenu().getMenuItemById('tasks');

if (!menu || !menu.submenu || !menu.submenu.items)
return;

menu.submenu.items.forEach(x => x.enabled = false);
}

_enableTasks() {
const menu = this._menu.getApplicationMenu().getMenuItemById('tasks');

if (!menu || !menu.submenu || !menu.submenu.items)
return;

menu.submenu.items.forEach(x => x.enabled = true);
}

_disableConfigAppMenu() {
const menu = this._menu.getApplicationMenu().getMenuItemById('config-apps') || {};

menu.enabled = false;
}

_enableConfigAppMenu() {
const menu = this._menu.getApplicationMenu().getMenuItemById('config-apps') || {};

menu.enabled = true;
}

_onTaskComplete(e, task) {
if (task === ipcMessages.startAllApplications || task === ipcMessages.stopAllApplications || task === ipcMessages.clearAllApplicationLogs)
this._enableConfigAppMenu();

this._enableTasks();
}

/**
* @returns {Menu}
*/
Expand Down
1 change: 0 additions & 1 deletion app/SplashScreenApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ module.exports = class SplashScreenApp {
show: false
});


this._splashWindow
.loadFile('app/browserWindows/splashScreen/splashScreen.html');

Expand Down
Loading

0 comments on commit 80e0473

Please sign in to comment.