diff --git a/src/Renderer.ts b/src/Renderer.ts index db4d6a63..165594fd 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -170,16 +170,13 @@ export class Renderer { let data: any = line[i][0]; const ch = line[i][1]; const ch_width: any = line[i][2]; + const isCursor: boolean = i === x; if (!ch_width) { continue; } - if (i === x) { - data = -1; - } - - if (data !== attr) { - if (attr !== this._terminal.defAttr) { + if (data !== attr || isCursor) { + if (attr !== this._terminal.defAttr && !isCursor) { if (innerHTML) { currentElement.innerHTML = innerHTML; innerHTML = ''; @@ -187,7 +184,7 @@ export class Renderer { documentFragment.appendChild(currentElement); currentElement = null; } - if (data !== this._terminal.defAttr) { + if (data !== this._terminal.defAttr || isCursor) { if (innerHTML && !currentElement) { currentElement = this._spanElementObjectPool.acquire(); } @@ -199,71 +196,73 @@ export class Renderer { documentFragment.appendChild(currentElement); } currentElement = this._spanElementObjectPool.acquire(); - if (data === -1) { + + let bg = data & 0x1ff; + let fg = (data >> 9) & 0x1ff; + let flags = data >> 18; + + if (isCursor) { currentElement.classList.add('reverse-video'); currentElement.classList.add('terminal-cursor'); - } else { - let bg = data & 0x1ff; - let fg = (data >> 9) & 0x1ff; - let flags = data >> 18; + } - if (flags & FLAGS.BOLD) { - if (!brokenBold) { - currentElement.classList.add('xterm-bold'); - } - // See: XTerm*boldColors - if (fg < 8) { - fg += 8; - } + if (flags & FLAGS.BOLD) { + if (!brokenBold) { + currentElement.classList.add('xterm-bold'); } - - if (flags & FLAGS.UNDERLINE) { - currentElement.classList.add('xterm-underline'); - } - - if (flags & FLAGS.BLINK) { - currentElement.classList.add('xterm-blink'); - } - - // If inverse flag is on, then swap the foreground and background variables. - if (flags & FLAGS.INVERSE) { - let temp = bg; - bg = fg; - fg = temp; - // Should inverse just be before the above boldColors effect instead? - if ((flags & 1) && fg < 8) { - fg += 8; - } - } - - if (flags & FLAGS.INVISIBLE) { - currentElement.classList.add('xterm-hidden'); - } - - /** - * Weird situation: Invert flag used black foreground and white background results - * in invalid background color, positioned at the 256 index of the 256 terminal - * color map. Pin the colors manually in such a case. - * - * Source: https://github.com/sourcelair/xterm.js/issues/57 - */ - if (flags & FLAGS.INVERSE) { - if (bg === 257) { - bg = 15; - } - if (fg === 256) { - fg = 0; - } - } - - if (bg < 256) { - currentElement.classList.add(`xterm-bg-color-${bg}`); - } - - if (fg < 256) { - currentElement.classList.add(`xterm-color-${fg}`); + // See: XTerm*boldColors + if (fg < 8) { + fg += 8; } } + + if (flags & FLAGS.UNDERLINE) { + currentElement.classList.add('xterm-underline'); + } + + if (flags & FLAGS.BLINK) { + currentElement.classList.add('xterm-blink'); + } + + // If inverse flag is on, then swap the foreground and background variables. + if (flags & FLAGS.INVERSE) { + let temp = bg; + bg = fg; + fg = temp; + // Should inverse just be before the above boldColors effect instead? + if ((flags & 1) && fg < 8) { + fg += 8; + } + } + + if (flags & FLAGS.INVISIBLE && !isCursor) { + currentElement.classList.add('xterm-hidden'); + } + + /** + * Weird situation: Invert flag used black foreground and white background results + * in invalid background color, positioned at the 256 index of the 256 terminal + * color map. Pin the colors manually in such a case. + * + * Source: https://github.com/sourcelair/xterm.js/issues/57 + */ + if (flags & FLAGS.INVERSE) { + if (bg === 257) { + bg = 15; + } + if (fg === 256) { + fg = 0; + } + } + + if (bg < 256) { + currentElement.classList.add(`xterm-bg-color-${bg}`); + } + + if (fg < 256) { + currentElement.classList.add(`xterm-color-${fg}`); + } + } } @@ -295,7 +294,10 @@ export class Renderer { } } - attr = data; + // The cursor needs its own element, therefore we set attr to -1 + // which will cause the next character to be rendered in a new element + attr = isCursor ? -1 : data; + } if (innerHTML && !currentElement) { diff --git a/src/xterm.css b/src/xterm.css index 6d6f48fa..89daf9e3 100644 --- a/src/xterm.css +++ b/src/xterm.css @@ -89,53 +89,40 @@ text-decoration: none; } -.terminal.focus:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor { - background-color: #fff; - color: #000; +.terminal .terminal-cursor { + position: relative; } .terminal:not(.focus) .terminal-cursor { outline: 1px solid #fff; outline-offset: -1px; - background-color: transparent; } -.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink-on .terminal-cursor { - background-color: transparent; - color: inherit; +.terminal.xterm-cursor-style-block.focus:not(.xterm-cursor-blink-on) .terminal-cursor { + background-color: #fff; + color: #000; } -.terminal.xterm-cursor-style-bar .terminal-cursor, -.terminal.xterm-cursor-style-underline .terminal-cursor { - position: relative; -} -.terminal.xterm-cursor-style-bar .terminal-cursor::before, -.terminal.xterm-cursor-style-underline .terminal-cursor::before { - content: ""; - display: block; +.terminal.focus.xterm-cursor-style-bar:not(.xterm-cursor-blink-on) .terminal-cursor::before, +.terminal.focus.xterm-cursor-style-underline:not(.xterm-cursor-blink-on) .terminal-cursor::before { + content: ''; position: absolute; background-color: #fff; } -.terminal.xterm-cursor-style-bar .terminal-cursor::before { + +.terminal.focus.xterm-cursor-style-bar:not(.xterm-cursor-blink-on) .terminal-cursor::before { top: 0; - bottom: 0; left: 0; + bottom: 0; width: 1px; } -.terminal.xterm-cursor-style-underline .terminal-cursor::before { + +.terminal.focus.xterm-cursor-style-underline:not(.xterm-cursor-blink-on) .terminal-cursor::before { bottom: 0; left: 0; right: 0; height: 1px; } -.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before, -.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before { - background-color: transparent; -} -.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before, -.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before { - background-color: #fff; -} .terminal .composition-view { background: #000; @@ -217,6 +204,10 @@ text-decoration: blink; } +.terminal .xterm-blink.xterm-underline { + text-decoration: blink underline; +} + .terminal .xterm-hidden { visibility: hidden; } diff --git a/src/xterm.js b/src/xterm.js index a3f425d1..1bb44a9a 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -429,7 +429,7 @@ Terminal.prototype.setOption = function(key, value) { switch (key) { case 'cursorBlink': this.setCursorBlinking(value); break; case 'cursorStyle': - // Style 'block' applies with no class + this.element.classList.toggle(`xterm-cursor-style-block`, value === 'block'); this.element.classList.toggle(`xterm-cursor-style-underline`, value === 'underline'); this.element.classList.toggle(`xterm-cursor-style-bar`, value === 'bar'); break; @@ -639,6 +639,7 @@ Terminal.prototype.open = function(parent, focus) { this.element.classList.add('terminal'); this.element.classList.add('xterm'); this.element.classList.add('xterm-theme-' + this.theme); + this.element.classList.add(`xterm-cursor-style-${this.options.cursorStyle}`); this.setCursorBlinking(this.options.cursorBlink); this.element.setAttribute('tabindex', 0); @@ -1025,6 +1026,9 @@ Terminal.prototype.bindMouse = function() { } on(el, 'mousedown', function(ev) { + // prevent the focus on the textarea from getting lost + ev.preventDefault(); + if (!self.mouseEvents) return; // send the button