fix(coding-agent): keep theme markers visible (#8950)

- follow up of missed commits for feat(coding-agent): adjust TUI selections in thinking-mode, models and scoped models #8900

* fix(coding-agent): keep fixed theme marker visible

- distinguish the saved theme from the preview cursor
- align the automatic option and update coverage

* fix(coding-agent): keep automatic theme marker visible

- mark configured light and dark themes during preview
- reuse shared theme formatting and update coverage
This commit is contained in:
Ramiz Wachtler 2026-09-01 12:01:50 +02:00 • committed by GitHub
parent a63fb12c13
commit 3fc3ef532b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 8 deletions

View file

@ -24,7 +24,7 @@
### Changed
- Changed selectors in `/thinking`, `/model`, `/scoped-models`, `/trust`, and per-model thinking settings to keep active options marked while browsing. `/scoped-models` now uses consistent per-item toggles and strikes through unavailable models ([#8900](https://github.com/earendil-works/pi/pull/8900)).
- Changed selectors in `/thinking`, `/model`, `/scoped-models`, `/trust`, per-model thinking settings, and theme settings to keep active options marked while browsing. `/scoped-models` now uses consistent per-item toggles and strikes through unavailable models ([#8900](https://github.com/earendil-works/pi/pull/8900)).
### Fixed

View file

@ -191,20 +191,23 @@ function modelItemLabel(model: Model<any>): string {
return `${model.id} ${theme.fg("muted", `[${model.provider}]`)}`;
}
function themeItems(availableThemes: string[]): SelectItem[] {
return availableThemes.map((name) => ({ value: name, label: name }));
function themeItems(availableThemes: string[], currentTheme: string): SelectItem[] {
return availableThemes.map((name) => ({
value: name,
label: `${name === currentTheme ? "✓ " : " "}${name}`,
}));
}
const AUTOMATIC_THEME_VALUE = "/";
function singleModeThemeItems(availableThemes: string[]): SelectItem[] {
function singleModeThemeItems(availableThemes: string[], currentTheme: string): SelectItem[] {
return [
{
value: AUTOMATIC_THEME_VALUE,
label: "Automatic",
label: " Automatic",
description: "Use separate themes for light and dark terminal appearance",
},
...themeItems(availableThemes),
...themeItems(availableThemes, currentTheme),
];
}
@ -285,7 +288,7 @@ class ThemeSubmenu extends Container {
const menu = new SelectSubmenu(
"Theme",
"Select a theme, or choose Automatic to follow terminal appearance.",
singleModeThemeItems(this.availableThemes),
singleModeThemeItems(this.availableThemes, this.singleTheme),
this.singleTheme,
(value) => {
if (value === AUTOMATIC_THEME_VALUE) {
@ -401,7 +404,7 @@ class ThemeSubmenu extends Container {
return new SelectSubmenu(
title,
description,
themeItems(this.availableThemes),
themeItems(this.availableThemes, currentValue),
currentValue,
onSelect,
() => {

View file

@ -57,6 +57,56 @@ describe("SettingsSelectorComponent", () => {
expect(onCopyOnSelectChange.mock.calls.flat()).toEqual([false, true]);
});
it("keeps the configured fixed theme marked while browsing", () => {
const config = {
defaultModel: "not set",
availableDefaultModels: [],
modelThinkingLevels: {},
currentTheme: "dark",
terminalTheme: "dark",
availableThemes: ["dark", "light"],
warnings: {},
} as unknown as SettingsConfig;
const callbacks = { onThemePreview: vi.fn(), onCancel: () => {} } as unknown as SettingsCallbacks;
const list = new SettingsSelectorComponent(config, callbacks).getSettingsList();
list.selectItem("theme");
list.handleInput("\r");
let output = stripAnsi(list.render(120).join("\n"));
expect(output).toContain(" Automatic");
expect(output).toContain("→ ✓ dark");
list.handleInput("\x1b[B");
output = stripAnsi(list.render(120).join("\n"));
expect(output).toContain(" ✓ dark");
expect(output).toContain("→ light");
});
it("keeps a configured automatic theme marked while browsing", () => {
const config = {
defaultModel: "not set",
availableDefaultModels: [],
modelThinkingLevels: {},
currentTheme: "light/dark",
terminalTheme: "dark",
availableThemes: ["dark", "light", "other"],
warnings: {},
} as unknown as SettingsConfig;
const callbacks = { onThemePreview: vi.fn(), onCancel: () => {} } as unknown as SettingsCallbacks;
const list = new SettingsSelectorComponent(config, callbacks).getSettingsList();
list.selectItem("theme");
list.handleInput("\r");
list.handleInput("\r");
let output = stripAnsi(list.render(120).join("\n"));
expect(output).toContain("→ ✓ light");
list.handleInput("\x1b[B");
output = stripAnsi(list.render(120).join("\n"));
expect(output).toContain(" ✓ light");
expect(output).toContain("→ other");
});
it("keeps the configured per-model thinking level marked while browsing", async () => {
harness = await createHarness({
models: [{ id: "thinking-model", reasoning: true }],