diff --git a/app/elements/browsix-terminal/browsix-terminal.html b/app/elements/browsix-terminal/browsix-terminal.html
index 5b0fcb5..c75d20b 100644
--- a/app/elements/browsix-terminal/browsix-terminal.html
+++ b/app/elements/browsix-terminal/browsix-terminal.html
@@ -1,63 +1,6 @@
-
+
-
-
-
-
-
-
-
- $
-
-
-
-
-
+
+
diff --git a/app/elements/browsix-terminal/browsix-terminal.ts b/app/elements/browsix-terminal/browsix-terminal.ts
index 3189c9f..f18d6b0 100644
--- a/app/elements/browsix-terminal/browsix-terminal.ts
+++ b/app/elements/browsix-terminal/browsix-terminal.ts
@@ -1,4 +1,6 @@
-///
+///
+
+//import { Terminal } from 'xterm';
interface ExitCallback {
(pid: number, code: number): void;
@@ -14,98 +16,95 @@ interface Kernel {
kill(pid: number): void;
}
-namespace Terminal {
+namespace BrowsixTerminal {
'use strict';
const ERROR = 'FLAGRANT SYSTEM ERROR';
- @component('browsix-terminal')
- class Terminal extends polymer.Base {
- @property({type: Object})
- kernel: any;
-
- @property({type: String})
- ps1: string = '$ ';
+ class BrowsixTerminal {
+ kernel: Kernel;
+ stdin: any;
+ terminal: Terminal;
+ line: string = "";
+ lineidx: number = 0;
+
+ constructor(element: HTMLElement) {
+ this.terminal = new Terminal({
+ // According to xterm.js docs, unnecessary with pty
+ "convertEol": true
+ });
+ this.terminal.open(element);
+ this.terminal.on('key', (key: string, ev: KeyboardEvent) => this.keyCallback(key, ev));
- constructor() {
- super();
(window).Boot(
'XmlHttpRequest',
['index.json', 'fs', true],
(err: any, k: Kernel) => {
if (err) {
console.log(err);
- this.$.output.innerHTML = ERROR;
+ this.terminal.clear();
+ this.terminal.writeln(ERROR);
throw new Error(err);
}
this.kernel = k;
- },
- {readOnly: false});
- }
- attached(): void {
- this.$.input.addEventListener('keypress', this.onInput.bind(this));
- (document).body.addEventListener('click', this.focus.bind(this));
- }
-
- onInput(ev: any): void {
- // If key pressed is not Return/Enter, skip
- if (ev.keyCode !== 13) return;
-
- let cmd = this.$.input.value;
- this.$.output.innerHTML += this.ps1 + cmd + '
';
- if (cmd === '') {
- this.scrollBottom();
- return;
- }
- this.setEditable(false);
- let bg = cmd[cmd.length - 1] === '&';
- if (bg) {
- cmd = cmd.slice(0, -1).trim();
- setTimeout(() => { this.setEditable(true); }, 0);
- }
+ let completed = (pid: number, code: number) => {
+ this.stdin = null;
+ this.terminal.writeln("'sh' exited with status " + code);
+ };
- let completed = (pid: number, code: number) => {
- this.setEditable(true);
- this.$.input.value = '';
- this.focus();
- this.scrollBottom();
- };
+ let onInput = (pid: number, out: string ) => {
+ this.terminal.write(out);
+ };
- let onInput = (pid: number, out: string) => {
- // Replace all LF with HTML breaks
- out = out.split('\n').join('
');
- this.$.output.innerHTML += out;
- this.scrollBottom();
- };
+ let onHaveStdin = (stdin: any) => {
+ this.stdin = stdin;
+ }
- this.kernel.system(cmd, completed, onInput, onInput);
+ this.kernel.system("sh", completed, onInput, onInput, onHaveStdin);
+ },
+ {readOnly: false});
}
- @observe('kernel')
- kernelChanged(_: Kernel, oldKernel: Kernel): void {
- // we expect this to be called once, after
- // we've booted the kernel.
- if (oldKernel) {
- console.log('unexpected kernel change');
- return;
+ keyCallback(key: string, ev: KeyboardEvent): void {
+ // Newline
+ if (ev.keyCode == 13) {
+ this.terminal.writeln("");
+ this.line += '\n'
+ if (this.stdin !== null) {
+ this.stdin.write(new Buffer(this.line), -1, (error: any) => {});
+ }
+ this.line = '';
+ this.lineidx = 0;
+ // Backspace
+ } else if (ev.keyCode == 8) {
+ const previous = this.line.slice(0, this.lineidx - 1);
+ const rest = this.line.slice(this.lineidx);
+ this.terminal.write('\b' + rest + ' ' + '\b'.repeat(rest.length + 1));
+ this.line = previous + rest;
+ this.lineidx--;
+ // Up and down arrows
+ } else if (ev.keyCode == 38 || ev.keyCode == 40) {
+ // Left arrow
+ } else if (ev.keyCode == 37) {
+ this.terminal.write(key);
+ this.lineidx--;
+ if (this.lineidx == -1) {
+ this.lineidx = 0;
+ }
+ // Right arrow
+ } else if (ev.keyCode == 39) {
+ if (this.lineidx < this.line.length) {
+ this.lineidx++;
+ this.terminal.write(key);
+ }
+ } else {
+ this.terminal.write(key);
+ this.line += key;
+ this.lineidx++;
}
}
-
- focus(): void {
- this.$.input.focus();
- }
-
- setEditable(editable: boolean): void {
- // Hide input if not editable
- this.$.input_container.style.visibility = (editable) ? '' : 'hidden';
- }
-
- scrollBottom(): void {
- (window).scrollTo(0, document.documentElement.scrollHeight
- || document.body.scrollHeight);
- }
}
- Terminal.register();
+ var terminal = new BrowsixTerminal(document.getElementById('browsix-terminal'));
}
diff --git a/gulpfile.js b/gulpfile.js
index 3cc9a0d..5569a90 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -436,7 +436,10 @@ gulp.task('app:copy', ['index-fs'], function () {
var fs = gulp.src(['fs/**/*'])
.pipe(gulp.dest('dist/fs'));
- return merge(app, bower, elements, vulcanized, swBootstrap, swToolbox, fs)
+ var xterm = gulp.src(['node_modules/xterm/dist/**/*'])
+ .pipe(gulp.dest('dist/xterm'));
+
+ return merge(app, bower, elements, vulcanized, swBootstrap, swToolbox, fs, xterm)
.pipe($.size({title: 'copy'}));
});
@@ -506,6 +509,7 @@ gulp.task('serve', ['app:build', 'app:styles', 'app:elements', 'app:images'], fu
'/bower_components': 'bower_components',
'/fs': 'fs',
'/benchfs': 'benchfs',
+ '/xterm': 'node_modules/xterm/dist',
},
middleware: [],
}
diff --git a/package.json b/package.json
index ddde688..daad3e3 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
"mocha": "^3.4.2",
"through2": "^2.0.3",
"tslint": "^5.5.0",
- "typescript": "^2.4.1",
+ "typescript": "^3.4.3",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
},
@@ -65,7 +65,7 @@
"@types/filesystem": "0.0.28",
"@types/mocha": "^2.2.41",
"node-binary-marshal": "^0.4.2",
- "term.js": "github:bpowers/term.js"
+ "xterm": "^3.12.2"
},
"engines": {
"node": ">=4.3.0"
diff --git a/src/kernel/kernel.ts b/src/kernel/kernel.ts
index 32ebfe8..c948f3d 100644
--- a/src/kernel/kernel.ts
+++ b/src/kernel/kernel.ts
@@ -9,7 +9,7 @@ import { now } from './ipc';
import { Pipe, PipeFile, isPipe } from './pipe';
import { SocketFile, isSocket } from './socket';
import { DirFile, RegularFile, NullFile, resolve } from './file';
-import { ExitCallback, OutputCallback, SyscallContext, SyscallResult,
+import { ExitCallback, OutputCallback, StdinCallback, SyscallContext, SyscallResult,
Syscall, ConnectCallback, IKernel, ITask, IFile, Environment } from './types';
import { HTTPParser } from './http_parser';
@@ -1570,7 +1570,7 @@ export class Kernel implements IKernel {
this.portWaiters[port] = cb;
}
- system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback): void {
+ system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback, onHaveStdin: StdinCallback): void {
let splitParts: string[] = cmd.split(' ');
let parts: string[];
// only check for an = in the first part of a command,
@@ -1610,9 +1610,12 @@ export class Kernel implements IKernel {
let t = this.tasks[pid];
t.onExit = onExit;
+ let stdin = t.files[0];
let stdout = t.files[1];
let stderr = t.files[2];
+ onHaveStdin(stdin);
+
stdout.addEventListener('write', onStdout);
stderr.addEventListener('write', onStderr);
});
@@ -1860,7 +1863,7 @@ export class Kernel implements IKernel {
files[i].ref();
}
} else {
- files[0] = new NullFile();
+ files[0] = new PipeFile();
files[1] = new PipeFile();
files[2] = new PipeFile();
}
diff --git a/src/kernel/types.ts b/src/kernel/types.ts
index b801689..2a09ee0 100644
--- a/src/kernel/types.ts
+++ b/src/kernel/types.ts
@@ -17,6 +17,10 @@ export interface RWCallback {
(err: number, len?: number): void;
}
+export interface StdinCallback {
+ (stdin: IFile): void;
+}
+
export interface SyscallResult {
id: number;
name: string;
@@ -33,7 +37,7 @@ export interface IKernel {
nCPUs: number;
debug: boolean;
- system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback): void;
+ system(cmd: string, onExit: ExitCallback, onStdout: OutputCallback, onStderr: OutputCallback, onHaveStdin: StdinCallback): void;
exit(task: ITask, code: number): void;
wait(pid: number): void;
doSyscall(syscall: Syscall): void;