mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into feat/fast-scrolling
This commit is contained in:
@@ -154,6 +154,8 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**PHP App Server**](https://github.com/cubiclesoft/php-app-server/): Create lightweight, installable almost-native applications for desktop OSes. ExecTerminal (nicely wraps the xterm.js Terminal), TerminalManager, and RunProcessSDK are self-contained, reusable ES5+ compliant Javascript components.
|
||||
- [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `<ng-terminal></ng-terminal>` into your component.
|
||||
- [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet.
|
||||
- [**Ten Hands**](https://github.com/saisandeepvaddi/ten-hands): One place to run your command-line tasks.
|
||||
- [**WebAssembly.sh**](https://webassembly.sh): A WebAssembly WASI browser terminal
|
||||
|
||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-attach",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -344,7 +344,7 @@ export class SearchAddon implements ITerminalAddon {
|
||||
}
|
||||
terminal.select(result.col, result.row, result.term.length);
|
||||
// If it is not in the viewport then we scroll else it just gets selected
|
||||
if (result.row > (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
|
||||
if (result.row >= (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
|
||||
let scroll = result.row - terminal.buffer.viewportY;
|
||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
||||
terminal.scrollLines(scroll);
|
||||
|
||||
@@ -92,10 +92,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
if (this._cursorBlinkStateManager) {
|
||||
this._cursorBlinkStateManager.dispose();
|
||||
}
|
||||
// Request a refresh from the terminal as management of rendering is being
|
||||
// moved back to the terminal
|
||||
terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY);
|
||||
}
|
||||
// Request a refresh from the terminal as management of rendering is being
|
||||
// moved back to the terminal
|
||||
terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY);
|
||||
}
|
||||
|
||||
public onCursorMove(terminal: Terminal): void {
|
||||
@@ -139,12 +139,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._clearCursor();
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._colors.cursor.css;
|
||||
this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
|
||||
const cursorStyle = terminal.getOption('cursorStyle');
|
||||
if (cursorStyle && cursorStyle !== 'block') {
|
||||
this._cursorRenderers[cursorStyle](terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
|
||||
} else {
|
||||
this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
|
||||
}
|
||||
this._ctx.restore();
|
||||
this._state.x = terminal.buffer.cursorX;
|
||||
this._state.y = viewportRelativeCursorY;
|
||||
this._state.isFocused = false;
|
||||
this._state.style = terminal.getOption('cursorStyle');
|
||||
this._state.style = cursorStyle;
|
||||
this._state.width = this._cell.getWidth();
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "xterm",
|
||||
"description": "Full xterm terminal, in your browser",
|
||||
"version": "4.0.0",
|
||||
"version": "4.1.0",
|
||||
"main": "lib/xterm.js",
|
||||
"style": "css/xterm.css",
|
||||
"types": "typings/xterm.d.ts",
|
||||
@@ -47,7 +47,7 @@
|
||||
"ts-loader": "^6.0.4",
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-consistent-codestyle": "^1.13.0",
|
||||
"typescript": "3.5",
|
||||
"typescript": "3.6",
|
||||
"utf8": "^3.0.0",
|
||||
"webpack": "^4.35.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
|
||||
@@ -108,6 +108,12 @@ describe('Keyboard', () => {
|
||||
it('should return \\x1b[5C for alt+right', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C
|
||||
});
|
||||
it('should return \\x1b[5D for alt+up', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;5A'); // CSI 5 D
|
||||
});
|
||||
it('should return \\x1b[5C for alt+down', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;5B'); // CSI 5 C
|
||||
});
|
||||
it('should return \\x1ba for alt+a', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba');
|
||||
});
|
||||
@@ -120,6 +126,12 @@ describe('Keyboard', () => {
|
||||
it('should return \\x1bf for alt+right', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C
|
||||
});
|
||||
it('should return \\x1bb for alt+up', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // CSI 5 D
|
||||
});
|
||||
it('should return \\x1bf for alt+down', () => {
|
||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // CSI 5 C
|
||||
});
|
||||
it('should return undefined for alt+a', () => {
|
||||
assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true };
|
||||
});
|
||||
|
||||
@@ -113,13 +113,16 @@ export function evaluateKeyboardEvent(
|
||||
break;
|
||||
case 37:
|
||||
// left-arrow
|
||||
if (ev.metaKey) {
|
||||
break;
|
||||
}
|
||||
if (modifiers) {
|
||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';
|
||||
// HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
|
||||
// http://unix.stackexchange.com/a/108106
|
||||
// macOS uses different escape sequences than linux
|
||||
if (result.key === C0.ESC + '[1;3D') {
|
||||
result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D';
|
||||
result.key = C0.ESC + (isMac ? 'b' : '[1;5D');
|
||||
}
|
||||
} else if (applicationCursorMode) {
|
||||
result.key = C0.ESC + 'OD';
|
||||
@@ -129,13 +132,16 @@ export function evaluateKeyboardEvent(
|
||||
break;
|
||||
case 39:
|
||||
// right-arrow
|
||||
if (ev.metaKey) {
|
||||
break;
|
||||
}
|
||||
if (modifiers) {
|
||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';
|
||||
// HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
|
||||
// http://unix.stackexchange.com/a/108106
|
||||
// macOS uses different escape sequences than linux
|
||||
if (result.key === C0.ESC + '[1;3C') {
|
||||
result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C';
|
||||
result.key = C0.ESC + (isMac ? 'f' : '[1;5C');
|
||||
}
|
||||
} else if (applicationCursorMode) {
|
||||
result.key = C0.ESC + 'OC';
|
||||
@@ -145,11 +151,15 @@ export function evaluateKeyboardEvent(
|
||||
break;
|
||||
case 38:
|
||||
// up-arrow
|
||||
if (ev.metaKey) {
|
||||
break;
|
||||
}
|
||||
if (modifiers) {
|
||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';
|
||||
// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
|
||||
// http://unix.stackexchange.com/a/108106
|
||||
if (result.key === C0.ESC + '[1;3A') {
|
||||
// macOS uses different escape sequences than linux
|
||||
if (!isMac && result.key === C0.ESC + '[1;3A') {
|
||||
result.key = C0.ESC + '[1;5A';
|
||||
}
|
||||
} else if (applicationCursorMode) {
|
||||
@@ -160,11 +170,15 @@ export function evaluateKeyboardEvent(
|
||||
break;
|
||||
case 40:
|
||||
// down-arrow
|
||||
if (ev.metaKey) {
|
||||
break;
|
||||
}
|
||||
if (modifiers) {
|
||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';
|
||||
// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
|
||||
// http://unix.stackexchange.com/a/108106
|
||||
if (result.key === C0.ESC + '[1;3B') {
|
||||
// macOS uses different escape sequences than linux
|
||||
if (!isMac && result.key === C0.ESC + '[1;3B') {
|
||||
result.key = C0.ESC + '[1;5B';
|
||||
}
|
||||
} else if (applicationCursorMode) {
|
||||
|
||||
@@ -56,6 +56,9 @@ export class Terminal implements ITerminalApi {
|
||||
this._core.resize(columns, rows);
|
||||
}
|
||||
public open(parent: HTMLElement): void {
|
||||
if (!document.body.contains(parent)) {
|
||||
throw new Error('open must be called on an element that is attached to the DOM');
|
||||
}
|
||||
this._core.open(parent);
|
||||
}
|
||||
public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
|
||||
@@ -178,8 +181,8 @@ export class Terminal implements ITerminalApi {
|
||||
|
||||
private _verifyIntegers(...values: number[]): void {
|
||||
values.forEach(value => {
|
||||
if (value % 1 !== 0) {
|
||||
throw new Error('This API does not accept floating point numbers');
|
||||
if (value === Infinity || value === NaN || value % 1 !== 0) {
|
||||
throw new Error('This API only accepts integers');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -103,10 +103,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._cursorBlinkStateManager.dispose();
|
||||
this._cursorBlinkStateManager = null;
|
||||
}
|
||||
// Request a refresh from the terminal as management of rendering is being
|
||||
// moved back to the terminal
|
||||
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||
}
|
||||
// Request a refresh from the terminal as management of rendering is being
|
||||
// moved back to the terminal
|
||||
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||
}
|
||||
|
||||
public onCursorMove(): void {
|
||||
@@ -148,12 +148,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._clearCursor();
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._colors.cursor.css;
|
||||
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||
const cursorStyle = this._optionsService.options.cursorStyle;
|
||||
if (cursorStyle && cursorStyle !== 'block') {
|
||||
this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||
} else {
|
||||
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||
}
|
||||
this._ctx.restore();
|
||||
this._state.x = this._bufferService.buffer.x;
|
||||
this._state.y = viewportRelativeCursorY;
|
||||
this._state.isFocused = false;
|
||||
this._state.style = this._optionsService.options.cursorStyle;
|
||||
this._state.style = cursorStyle;
|
||||
this._state.width = this._cell.getWidth();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`}`;
|
||||
// Cursor
|
||||
styles +=
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` outline: 1px solid ${this._colors.cursor.css};` +
|
||||
` outline-offset: -1px;` +
|
||||
`}` +
|
||||
@@ -197,10 +197,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` background-color: ${this._colors.cursor.css};` +
|
||||
` color: ${this._colors.cursorAccent.css};` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
|
||||
` box-shadow: 1px 0 0 ${this._colors.cursor.css} inset;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
|
||||
`}`;
|
||||
// Selection
|
||||
|
||||
@@ -510,6 +510,20 @@ describe('API Integration Tests', function(): void {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('dispose', async function(): Promise<any> {
|
||||
await page.evaluate(`
|
||||
window.term = new Terminal();
|
||||
window.term.dispose();
|
||||
`);
|
||||
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
|
||||
});
|
||||
|
||||
it('dispose (opened)', async function(): Promise<any> {
|
||||
await openTerminal();
|
||||
await page.evaluate(`window.term.dispose()`);
|
||||
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
|
||||
});
|
||||
});
|
||||
|
||||
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||
|
||||
@@ -4719,7 +4719,12 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@3.5, typescript@^3.5.1:
|
||||
typescript@3.6:
|
||||
version "3.6.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da"
|
||||
integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==
|
||||
|
||||
typescript@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202"
|
||||
integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==
|
||||
|
||||
Reference in New Issue
Block a user