From ca558cbe32aa304da1ff116b4cc562c3711516ac Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Thu, 4 Jun 2026 00:51:40 -0500 Subject: [PATCH 1/3] test: failing repro for default-foreground --- test/default-foreground.test.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/default-foreground.test.ts diff --git a/test/default-foreground.test.ts b/test/default-foreground.test.ts new file mode 100644 index 0000000..428310e --- /dev/null +++ b/test/default-foreground.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "./suite.ts"; +import { createTerm } from "../term.ts"; +import { close, grow, open, text } from "../ops.ts"; + +const decode = (b: Uint8Array) => new TextDecoder().decode(b); + +describe("true default foreground", () => { + it("uncolored text emits no concrete foreground", async () => { + let term = await createTerm({ width: 12, height: 1 }); + let ansi = decode( + term.render([ + open("root", { layout: { width: grow(), height: grow() } }), + text("hi"), + close(), + ]).output, + ); + + let before = ansi.slice(0, ansi.indexOf("h")); + // pins: color-less text must leave the terminal default fg, not force white + expect(before).not.toContain("\x1b[38;2;255;255;255"); + }); +}); From d9f71e20bb5330bf9e0eac29a0d74b8fdd7dfa0e Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Fri, 5 Jun 2026 01:11:14 -0500 Subject: [PATCH 2/3] fix: leave terminal default foreground for uncolored text Text with no explicit color was packed as concrete white (0xFFFFFFFF), forcing white on light-background terminals. Encode absent color as ATTR_DEFAULT (0x80 in the attrs byte) so the C path skips the foreground SGR entirely, mirroring how an unset background is already left alone. Fixes #62 --- ops.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ops.ts b/ops.ts index a52485d..776bfed 100644 --- a/ops.ts +++ b/ops.ts @@ -202,14 +202,18 @@ export function pack( case OP_TEXT: { view.setUint32(o, OP_TEXT, true); o += 4; - view.setUint32(o, op.color ?? 0xFFFFFFFF, true); + // No explicit color: leave the terminal default foreground by writing + // 0 and setting ATTR_DEFAULT (0x80 in the attrs byte). The C path ORs + // it into fg and emit_attr skips the foreground SGR (mirrors unset bg). + let textDefault = op.color === undefined; + view.setUint32(o, op.color ?? 0, true); o += 4; view.setUint32( o, (op.fontSize ?? 1) | ((op.fontId ?? 0) << 8) | ((op.wrap ?? 0) << 16) | - ((op.attrs ?? 0) << 24), + (((op.attrs ?? 0) | (textDefault ? 0x80 : 0)) << 24), true, ); o += 4; From 6e380184f09cfd3c107f42b1ea8eb054fa23dfea Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 5 Jun 2026 21:22:30 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=A5tighten=20up=20the=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just testing text attributes, shouldn't need an enclosing box. --- test/default-foreground.test.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/test/default-foreground.test.ts b/test/default-foreground.test.ts index 428310e..1a51593 100644 --- a/test/default-foreground.test.ts +++ b/test/default-foreground.test.ts @@ -1,22 +1,15 @@ -import { describe, expect, it } from "./suite.ts"; +import { text } from "../ops.ts"; import { createTerm } from "../term.ts"; -import { close, grow, open, text } from "../ops.ts"; +import { describe, expect, it } from "./suite.ts"; const decode = (b: Uint8Array) => new TextDecoder().decode(b); describe("true default foreground", () => { - it("uncolored text emits no concrete foreground", async () => { + it("emits uncolored text with no concrete foreground", async () => { let term = await createTerm({ width: 12, height: 1 }); - let ansi = decode( - term.render([ - open("root", { layout: { width: grow(), height: grow() } }), - text("hi"), - close(), - ]).output, - ); + let ansi = decode(term.render([text("hi")]).output); - let before = ansi.slice(0, ansi.indexOf("h")); - // pins: color-less text must leave the terminal default fg, not force white - expect(before).not.toContain("\x1b[38;2;255;255;255"); + expect(ansi).toContain("hi"); + expect(ansi).not.toContain("\x1b[38;2;255;255;255"); }); });