Skip to content

Commit ceb96b5

Browse files
committed
fix(tui): back pending selection with opaque ground
1 parent 192a649 commit ceb96b5

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

‎src/tui/shell/chrome.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ function syncPendingRows(shell: AppShell): void {
290290
row.tag === null ? UI.textFaint : selected ? UI.text : UI.textDim,
291291
)(fitted.text),
292292
]),
293+
...(selected ? { bg: UI.ground } : {}),
293294
}),
294295
);
295296
}

‎src/tui/theme-transparent.test.ts‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { rgbToHex, type CapturedSpan, type RGBA } from "@opentui/core";
44
import { defined } from "../../tests/helpers/defined.js";
55
import { withTestRenderer } from "./harness.js";
66
import { runProviderSetup } from "./provider/setup.js";
7+
import { setPendingQueue } from "./shell/chrome.js";
78
import { createAppShell } from "./shell/index.js";
89
import { openSettingsOverlay } from "./shell/palette.js";
910
import { runWelcome, WELCOME_LINE } from "./welcome.js";
@@ -237,6 +238,78 @@ describe("rendered semantic surfaces", () => {
237238
});
238239
}
239240

241+
for (const [mode, env, unselectedAlpha] of [
242+
["default", {}, 255],
243+
["transparent", REQUESTED, 0],
244+
] as const) {
245+
test(`${themeName} ${mode} pending selection has opaque backing`, async () => {
246+
setTheme(themeName);
247+
configureTransparentBackground(env);
248+
249+
await withTestRenderer(
250+
async (h) => {
251+
const shell = createAppShell(h.renderer, {
252+
terminal: { columns: 80, rows: 24 },
253+
wireKeys: true,
254+
});
255+
try {
256+
setPendingQueue(shell, 3);
257+
await h.renderOnce();
258+
const idle = h.captureSpans();
259+
for (const text of ["pad-1", "pad-2", "pad-3"]) {
260+
expect(alpha(findSpan(idle.lines, text).bg)).toBe(
261+
unselectedAlpha,
262+
);
263+
}
264+
265+
h.pressKey("ARROW_UP");
266+
await h.renderOnce();
267+
const initialSelection = h.captureSpans();
268+
const initialMarker = findSpan(initialSelection.lines, "▸");
269+
const initialSelected = findSpan(initialSelection.lines, "pad-3");
270+
expect(alpha(initialMarker.bg)).toBe(255);
271+
expect(alpha(initialSelected.bg)).toBe(255);
272+
expect(
273+
rgbToHex(initialSelected.bg).toLowerCase().slice(0, 7),
274+
).toBe(theme.ground);
275+
expect(
276+
rgbToHex(initialSelected.fg).toLowerCase().slice(0, 7),
277+
).toBe(theme.text);
278+
expect(
279+
contrast(initialSelected.fg, initialSelected.bg),
280+
).toBeGreaterThanOrEqual(4.5);
281+
282+
h.pressKey("ARROW_UP");
283+
await h.renderOnce();
284+
const movedSelection = h.captureSpans();
285+
const unselected = findSpan(movedSelection.lines, "pad-3");
286+
const selected = findSpan(movedSelection.lines, "pad-2");
287+
const untouched = findSpan(movedSelection.lines, "pad-1");
288+
expect(alpha(findSpan(movedSelection.lines, "▸").bg)).toBe(255);
289+
expect(alpha(selected.bg)).toBe(255);
290+
expect(alpha(unselected.bg)).toBe(unselectedAlpha);
291+
expect(alpha(untouched.bg)).toBe(unselectedAlpha);
292+
expect(rgbToHex(unselected.fg).toLowerCase().slice(0, 7)).toBe(
293+
theme.textDim,
294+
);
295+
expect(rgbToHex(selected.bg).toLowerCase().slice(0, 7)).toBe(
296+
theme.ground,
297+
);
298+
expect(rgbToHex(selected.fg).toLowerCase().slice(0, 7)).toBe(
299+
theme.text,
300+
);
301+
expect(contrast(selected.fg, selected.bg)).toBeGreaterThanOrEqual(
302+
4.5,
303+
);
304+
} finally {
305+
shell.dispose();
306+
}
307+
},
308+
{ width: 80, height: 24 },
309+
);
310+
});
311+
}
312+
240313
test(`${themeName} transparent welcome uses selected text roles`, async () => {
241314
setTheme(themeName);
242315
configureTransparentBackground(REQUESTED);

0 commit comments

Comments
 (0)