Compare commits

...
Author SHA1 Message Date
R. Richard 4f3a6057a4 Prevent IDE reload race condition 2020-05-26 15:34:54 +02:00
R. Richard ea5bf30a95 Reconnect websockets automatically 2020-05-21 17:44:01 +02:00
R. Richard d8545c48be Stop filtering JS code in bot messages 2020-05-18 18:01:30 +02:00
ni-richard 031a9916a0 Make webservice optional 2020-05-15 14:46:33 +02:00
R. Richard 68f9a2c57d Enable Kotlin syntax highlighting 2020-03-31 14:42:11 +02:00
11 changed files with 6888 additions and 21 deletions
+2 -1
View File
@@ -5,7 +5,8 @@
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build --prod --aot --build-optimizer"
"build": "ng build --prod --aot --build-optimizer",
"lint": "ng lint --fix"
},
"private": true,
"dependencies": {
+2 -1
View File
@@ -17,7 +17,7 @@ import { TerminalComponent } from './terminal/terminal.component';
import { AppRoutingModule } from './app-routing.module';
import { TestmessengerComponent } from './testmessenger/testmessenger.component';
import { DeploymentNotificationService } from './services/deployment-notification.service';
import { SafePipe } from './pipes/safe.pipe';
import { SafePipe, SafeHtmlPipe } from './pipes/safe.pipe';
import { ConsoleComponent } from './console/console.component';
import { MonacoEditorModule } from 'ngx-monaco-editor';
import {
@@ -41,6 +41,7 @@ import { LoaderComponent } from './loader/loader.component';
DashboardComponent,
TestmessengerComponent,
SafePipe,
SafeHtmlPipe,
ConsoleComponent,
LoaderComponent
],
+11 -1
View File
@@ -32,6 +32,7 @@ export class IdeComponent implements OnInit {
files: string[];
filename = '';
lastFilename = '';
reloadFile = false;
code = 'Loading your file...';
codeState = CodeState.SAVED;
@@ -85,10 +86,18 @@ export class IdeComponent implements OnInit {
}
reloadHandler(message: WebSocketMessage) {
if (!this.reloadFile) {
this.reloadFile = true;
this.requestCode();
}
}
readHandler(message: IDEMessage) {
if (!this.reloadFile && message.reload) {
return;
}
this.reloadFile = false;
if (this.codeState === CodeState.SAVED) {
if (this.filename !== message.filename) {
this.filename = message.filename;
@@ -189,7 +198,8 @@ export class IdeComponent implements OnInit {
requestCode() {
this.webSocketService.send({
'key': 'ide.read',
'filename': this.filename
'filename': this.filename,
'reload': this.reloadFile
});
}
}
+3 -1
View File
@@ -30,5 +30,7 @@ export const LanguageMap: { [extension: string]: string; } = {
css: 'css',
less: 'less',
scss: 'scss',
sh: 'shell'
sh: 'shell',
kt: 'kotlin',
kts: 'kotlin'
};
+1
View File
@@ -4,4 +4,5 @@ export interface IDEMessage extends WebSocketMessage {
filename: string;
content?: string;
files?: string[];
reload?: boolean;
}
+1 -1
View File
@@ -8,7 +8,7 @@
<div class="tao-grid-center-left originator">{{message.originator}}</div>
<div class="timestamp tao-grid-center-right">{{message.timestamp | date:'HH:mm:ss'}}</div>
</div>
<div class="tfw-grid-message-body" [innerHtml]="message.message"></div>
<div class="tfw-grid-message-body" [innerHtml]="message.message | safeHtml"></div>
</div>
<div *ngIf="showTypingIndicator"
class="tfw-grid-message jumping-circle-container"
+11
View File
@@ -11,3 +11,14 @@ export class SafePipe implements PipeTransform {
return this.sanitizer.bypassSecurityTrustResourceUrl(value);
}
}
@Pipe({
name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ export class DashboardConfigService extends ConfigServiceBase {
layout = new BehaviorSubject<string>('terminal-ide-web');
hideMessages = new BehaviorSubject<boolean>(false);
iframeUrl = new BehaviorSubject<string>('/webservice');
iframeUrl = new BehaviorSubject<string>('');
showUrlBar = new BehaviorSubject<boolean>(false);
terminalMenuItem = new BehaviorSubject<string>('terminal');
reloadIframeOnDeploy = new BehaviorSubject<boolean>(false);
+42 -11
View File
@@ -8,11 +8,16 @@ export class TerminadoService {
xterm: Terminal;
ws: WebSocket;
attached = false;
private dataListener: any;
private resizeListener: any;
constructor() {
Terminal.applyAddon(fit);
Terminal.applyAddon(terminado);
this.xterm = new Terminal({
this.xterm = this.createTerminal();
}
createTerminal() {
return new Terminal({
theme: {
foreground: '#ffffff',
background: '#0C0C0C', // $tao-gray-800
@@ -37,26 +42,52 @@ export class TerminadoService {
},
fontSize: 14
});
const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
this.ws = new WebSocket(wsproto + window.location.host + '/terminal');
}
attach(element: HTMLElement) {
if (this.attached) {
return;
}
const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
this.ws = new WebSocket(wsproto + window.location.host + '/terminal');
this.ws.onopen = () => {
(<any>this.xterm).terminadoAttach(this.ws);
this.attached = true;
this.xterm = this.createTerminal();
this.xterm.open(element);
this.fit();
this.xterm.blur();
this.attached = true;
// In order to reset the terminal state after a broken socket, we need to register the listeners manually.
(<any>this.xterm)._core.register(this.dataListener = this.xterm.onData(data => {
this.ws.send(JSON.stringify(['stdin', data]));
}));
(<any>this.xterm)._core.register(this.resizeListener = this.xterm.onResize((size: { rows: number, cols: number }) => {
this.ws.send(JSON.stringify(['set_size', size.rows, size.cols]));
}));
};
this.ws.onclose = () => {
this.detach();
this.xterm.destroy();
this.attach(element);
};
this.ws.onmessage = msg => {
const data = JSON.parse(msg.data);
if (data[0] === 'stdout') {
this.xterm.write(data[1]);
}
};
}
detach() {
(<any>this.xterm).terminadoDetach(this.ws);
this.xterm.destroy();
this.ws.close();
if (!this.attached) {
return;
}
this.attached = false;
this.dataListener.dispose();
this.resizeListener.dispose();
}
fit() {
+17 -3
View File
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Observable, Subject, Subscription } from 'rxjs';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { filter, map } from 'rxjs/operators';
import { WebSocketMessage } from '../message-types/websocket-message';
@@ -17,14 +17,28 @@ export enum Intent {
@Injectable()
export class WebSocketService {
private ws: WebSocketSubject<WebSocketMessage>;
private subject: Subject<WebSocketMessage> = new Subject<WebSocketMessage>();
private subscription: Subscription;
constructor() {}
public connect() {
if (!this.ws) {
if (this.subscription) {
this.subscription.unsubscribe();
}
const wsproto = (location.protocol === 'https:') ? 'wss://' : 'ws://';
const connAddr = wsproto + window.location.host + '/ws';
this.ws = webSocket<WebSocketMessage>(connAddr);
this.ws = webSocket<WebSocketMessage>({
url: connAddr,
closeObserver: {
next: closeEvent => {
this.ws = null;
this.connect();
}
}
});
this.subscription = this.ws.subscribe(msg => this.subject.next(msg));
}
}
@@ -35,7 +49,7 @@ export class WebSocketService {
}
public observeAll<T extends WebSocketMessage>(key: string): Observable<T> {
return this.ws.pipe(
return this.subject.pipe(
filter(message => message.key.startsWith(key)),
map(message => <T> message)
);
+6796
View File
File diff suppressed because it is too large Load Diff