Compare commits

...
Author SHA1 Message Date
l0ng-ai 045689cb31 fix(terminal): keep Nerd Font icons wider than their cell above the next cell's background
Nerd Font / Powerline icons (Private Use Area characters) are often drawn wider
than one cell. xterm's DOM renderer paints each span's background in order, so
the next prompt segment's background covered the icon's right part and
powerline prompts showed half icons. Spans holding such characters are lifted
over the following ones.
2026-11-13 17:28:02 +08:00
3 changed files with 30 additions and 3 deletions

No files matched your search

@@ -110,6 +110,11 @@
line-height: normal;
transform: translateY(-50%);
}
/* An icon wider than its cell stays over the next cell's background (domRowCache). */
.pane :global(.xterm-rows .openidea-terminal-icon) {
position: relative;
z-index: 1;
}
.pane :global(.xterm-viewport) {
background-color: var(--bg-island, var(--bg-app)) !important;
}
+11 -1
View File
@@ -1,7 +1,7 @@
// @vitest-environment jsdom
import { describe, expect, it } from "vitest";
import type { Terminal } from "@xterm/xterm";
import { installDomRowCache } from "./domRowCache";
import { ICON_SPAN_CLASS, installDomRowCache } from "./domRowCache";
/** Mirrors the shape of xterm 6's private DomRenderer closely enough for the cache. */
function fakeRendererClass() {
@@ -76,6 +76,16 @@ describe("installDomRowCache", () => {
expect(rowAt(renderer, 1).textContent).toBe("ce");
});
it("lifts spans with Nerd Font icons over the next cell's background", () => {
const Renderer = fakeRendererClass();
// U+E606 (Python logo) and U+F0035 (a Material Design icon) are drawn wider than one cell.
const renderer = new Renderer(["\uE606 py \u{F0035} ~/src"]);
installDomRowCache(terminalWith(renderer));
renderer.renderRows(0, 0);
const lifted = [...rowAt(renderer, 0).children].map((span) => span.classList.contains(ICON_SPAN_CLASS));
expect(lifted).toEqual([true, false, true, false]);
});
it("re-renders rows after clear() and link underlines rebuilt them", () => {
const Renderer = fakeRendererClass();
const renderer = new Renderer(["a b"]);
+14 -2
View File
@@ -7,12 +7,21 @@
* several times a second while only one or two actually changed; the window then falls
* behind on keystrokes. Rows are still built (cheap); only identical ones are left in place.
*
* Remove once xterm's DomRenderer does this itself (xterm 6.0.0 does not).
* Rows are also where wide icons get lifted over the next cell ([`ICON_SPAN_CLASS`]): a Nerd
* Font icon is often drawn wider than one cell, and the DOM renderer paints the next span's
* background over its right part (powerline prompts showed half icons).
*
* Remove the cache once xterm's DomRenderer does this itself (xterm 6.0.0 does not).
*/
import type { Terminal } from "@xterm/xterm";
const MARKUP = Symbol("rowMarkup");
/** Private Use Area characters: Nerd Font / Powerline icons, often drawn wider than their cell. */
const ICON = /[\uE000-\uF8FF]|[\u{F0000}-\u{10FFFD}]/u;
/** Lifts a span holding such an icon over the next one, so the next cell's background doesn't cover it. */
export const ICON_SPAN_CLASS = "openidea-terminal-icon";
type RowElement = HTMLElement & { [MARKUP]?: string };
type BufferLine = unknown;
@@ -92,7 +101,10 @@ function renderRows(this: DomRenderer, start: number, end: number) {
-1,
);
let markup = "";
for (const span of spans) markup += span.outerHTML;
for (const span of spans) {
if (ICON.test(span.textContent ?? "")) span.classList.add(ICON_SPAN_CLASS);
markup += span.outerHTML;
}
if (element[MARKUP] === markup) continue;
element[MARKUP] = markup;
element.replaceChildren(...spans);