Skip to content

Commit 45a7452

Browse files
committed
refactor: extract theme settings panel
1 parent 8a58383 commit 45a7452

2 files changed

Lines changed: 177 additions & 131 deletions

File tree

lib/codex-manager/settings-hub.ts

Lines changed: 11 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ import type { PluginConfig } from "../types.js";
2828
import { ANSI } from "../ui/ansi.js";
2929
import { UI_COPY } from "../ui/copy.js";
3030
import { getUiRuntimeOptions, setUiRuntimeOptions } from "../ui/runtime.js";
31-
import { type MenuItem, select, type SelectOptions } from "../ui/select.js";
31+
import { type MenuItem, type SelectOptions, select } from "../ui/select.js";
3232
import { getUnifiedSettingsPath } from "../unified-settings.js";
3333
import { sleep } from "../utils.js";
34+
import { promptThemeSettingsPanel } from "./theme-settings-panel.js";
3435

3536
type DashboardDisplaySettingKey =
3637
| "menuShowStatusBadge"
@@ -177,13 +178,6 @@ type BehaviorConfigAction =
177178
| { type: "save" }
178179
| { type: "cancel" };
179180

180-
type ThemeConfigAction =
181-
| { type: "set-palette"; palette: DashboardThemePreset }
182-
| { type: "set-accent"; accent: DashboardAccentColor }
183-
| { type: "reset" }
184-
| { type: "save" }
185-
| { type: "cancel" };
186-
187181
type BackendToggleSettingKey =
188182
| "liveAccountSync"
189183
| "sessionAffinity"
@@ -1997,129 +1991,15 @@ async function promptBehaviorSettings(
19971991
async function promptThemeSettings(
19981992
initial: DashboardDisplaySettings,
19991993
): Promise<DashboardDisplaySettings | null> {
2000-
if (!input.isTTY || !output.isTTY) return null;
2001-
const baseline = cloneDashboardSettings(initial);
2002-
let draft = cloneDashboardSettings(initial);
2003-
let focus: ThemeConfigAction = {
2004-
type: "set-palette",
2005-
palette: draft.uiThemePreset ?? "green",
2006-
};
2007-
while (true) {
2008-
const ui = getUiRuntimeOptions();
2009-
const palette = draft.uiThemePreset ?? "green";
2010-
const accent = draft.uiAccentColor ?? "green";
2011-
const paletteItems: MenuItem<ThemeConfigAction>[] =
2012-
THEME_PRESET_OPTIONS.map((candidate, index) => {
2013-
const color: MenuItem<ThemeConfigAction>["color"] =
2014-
palette === candidate ? "green" : "yellow";
2015-
return {
2016-
label: `${palette === candidate ? "[x]" : "[ ]"} ${index + 1}. ${candidate === "green" ? "Green base" : "Blue base"}`,
2017-
hint:
2018-
candidate === "green"
2019-
? "High-contrast default."
2020-
: "Codex-style blue look.",
2021-
value: { type: "set-palette", palette: candidate },
2022-
color,
2023-
};
2024-
});
2025-
const accentItems: MenuItem<ThemeConfigAction>[] = ACCENT_COLOR_OPTIONS.map(
2026-
(candidate) => {
2027-
const color: MenuItem<ThemeConfigAction>["color"] =
2028-
accent === candidate ? "green" : "yellow";
2029-
return {
2030-
label: `${accent === candidate ? "[x]" : "[ ]"} ${candidate}`,
2031-
value: { type: "set-accent", accent: candidate },
2032-
color,
2033-
};
2034-
},
2035-
);
2036-
const items: MenuItem<ThemeConfigAction>[] = [
2037-
{
2038-
label: UI_COPY.settings.baseTheme,
2039-
value: { type: "cancel" },
2040-
kind: "heading",
2041-
},
2042-
...paletteItems,
2043-
{ label: "", value: { type: "cancel" }, separator: true },
2044-
{
2045-
label: UI_COPY.settings.accentColor,
2046-
value: { type: "cancel" },
2047-
kind: "heading",
2048-
},
2049-
...accentItems,
2050-
{ label: "", value: { type: "cancel" }, separator: true },
2051-
{
2052-
label: UI_COPY.settings.resetDefault,
2053-
value: { type: "reset" },
2054-
color: "yellow",
2055-
},
2056-
{
2057-
label: UI_COPY.settings.saveAndBack,
2058-
value: { type: "save" },
2059-
color: "green",
2060-
},
2061-
{
2062-
label: UI_COPY.settings.backNoSave,
2063-
value: { type: "cancel" },
2064-
color: "red",
2065-
},
2066-
];
2067-
const initialCursor = items.findIndex((item) => {
2068-
const value = item.value;
2069-
if (value.type !== focus.type) return false;
2070-
if (value.type === "set-palette" && focus.type === "set-palette") {
2071-
return value.palette === focus.palette;
2072-
}
2073-
if (value.type === "set-accent" && focus.type === "set-accent") {
2074-
return value.accent === focus.accent;
2075-
}
2076-
return true;
2077-
});
2078-
const result = await select<ThemeConfigAction>(items, {
2079-
message: UI_COPY.settings.themeTitle,
2080-
subtitle: UI_COPY.settings.themeSubtitle,
2081-
help: UI_COPY.settings.themeHelp,
2082-
clearScreen: true,
2083-
theme: ui.theme,
2084-
selectedEmphasis: "minimal",
2085-
initialCursor: initialCursor >= 0 ? initialCursor : undefined,
2086-
onCursorChange: ({ cursor }) => {
2087-
const item = items[cursor];
2088-
if (item && !item.separator && item.kind !== "heading") {
2089-
focus = item.value;
2090-
}
2091-
},
2092-
onInput: (raw) => {
2093-
const lower = raw.toLowerCase();
2094-
if (lower === "q") return { type: "cancel" };
2095-
if (lower === "s") return { type: "save" };
2096-
if (lower === "r") return { type: "reset" };
2097-
if (raw === "1") return { type: "set-palette", palette: "green" };
2098-
if (raw === "2") return { type: "set-palette", palette: "blue" };
2099-
return undefined;
2100-
},
2101-
});
2102-
if (!result || result.type === "cancel") {
2103-
applyUiThemeFromDashboardSettings(baseline);
2104-
return null;
2105-
}
2106-
if (result.type === "save") return draft;
2107-
if (result.type === "reset") {
2108-
draft = applyDashboardDefaultsForKeys(draft, THEME_PANEL_KEYS);
2109-
focus = { type: "set-palette", palette: draft.uiThemePreset ?? "green" };
2110-
applyUiThemeFromDashboardSettings(draft);
2111-
continue;
2112-
}
2113-
if (result.type === "set-palette") {
2114-
draft = { ...draft, uiThemePreset: result.palette };
2115-
focus = result;
2116-
applyUiThemeFromDashboardSettings(draft);
2117-
continue;
2118-
}
2119-
draft = { ...draft, uiAccentColor: result.accent };
2120-
focus = result;
2121-
applyUiThemeFromDashboardSettings(draft);
2122-
}
1994+
return promptThemeSettingsPanel(initial, {
1995+
cloneDashboardSettings,
1996+
applyDashboardDefaultsForKeys,
1997+
applyUiThemeFromDashboardSettings,
1998+
THEME_PRESET_OPTIONS,
1999+
ACCENT_COLOR_OPTIONS,
2000+
THEME_PANEL_KEYS,
2001+
UI_COPY,
2002+
});
21232003
}
21242004

21252005
function resolveFocusedBackendNumberKey(
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { stdin as input, stdout as output } from "node:process";
2+
import type {
3+
DashboardAccentColor,
4+
DashboardDisplaySettings,
5+
DashboardThemePreset,
6+
} from "../dashboard-settings.js";
7+
import type { UI_COPY } from "../ui/copy.js";
8+
import { getUiRuntimeOptions } from "../ui/runtime.js";
9+
import { type MenuItem, select } from "../ui/select.js";
10+
11+
export type ThemeConfigAction =
12+
| { type: "set-palette"; palette: DashboardThemePreset }
13+
| { type: "set-accent"; accent: DashboardAccentColor }
14+
| { type: "reset" }
15+
| { type: "save" }
16+
| { type: "cancel" };
17+
18+
export interface ThemeSettingsPanelDeps {
19+
cloneDashboardSettings: (
20+
settings: DashboardDisplaySettings,
21+
) => DashboardDisplaySettings;
22+
applyDashboardDefaultsForKeys: (
23+
draft: DashboardDisplaySettings,
24+
keys: readonly (keyof DashboardDisplaySettings)[],
25+
) => DashboardDisplaySettings;
26+
applyUiThemeFromDashboardSettings: (
27+
settings: DashboardDisplaySettings,
28+
) => void;
29+
THEME_PRESET_OPTIONS: readonly DashboardThemePreset[];
30+
ACCENT_COLOR_OPTIONS: readonly DashboardAccentColor[];
31+
THEME_PANEL_KEYS: readonly (keyof DashboardDisplaySettings)[];
32+
UI_COPY: typeof UI_COPY;
33+
}
34+
35+
export async function promptThemeSettingsPanel(
36+
initial: DashboardDisplaySettings,
37+
deps: ThemeSettingsPanelDeps,
38+
): Promise<DashboardDisplaySettings | null> {
39+
if (!input.isTTY || !output.isTTY) return null;
40+
const baseline = deps.cloneDashboardSettings(initial);
41+
let draft = deps.cloneDashboardSettings(initial);
42+
let focus: ThemeConfigAction = {
43+
type: "set-palette",
44+
palette: draft.uiThemePreset ?? "green",
45+
};
46+
47+
while (true) {
48+
const ui = getUiRuntimeOptions();
49+
const palette = draft.uiThemePreset ?? "green";
50+
const accent = draft.uiAccentColor ?? "green";
51+
const paletteItems: MenuItem<ThemeConfigAction>[] =
52+
deps.THEME_PRESET_OPTIONS.map((candidate, index) => {
53+
const color: MenuItem<ThemeConfigAction>["color"] =
54+
palette === candidate ? "green" : "yellow";
55+
return {
56+
label: `${palette === candidate ? "[x]" : "[ ]"} ${index + 1}. ${candidate === "green" ? "Green base" : "Blue base"}`,
57+
hint:
58+
candidate === "green"
59+
? "High-contrast default."
60+
: "Codex-style blue look.",
61+
value: { type: "set-palette", palette: candidate },
62+
color,
63+
};
64+
});
65+
const accentItems: MenuItem<ThemeConfigAction>[] =
66+
deps.ACCENT_COLOR_OPTIONS.map((candidate) => {
67+
const color: MenuItem<ThemeConfigAction>["color"] =
68+
accent === candidate ? "green" : "yellow";
69+
return {
70+
label: `${accent === candidate ? "[x]" : "[ ]"} ${candidate}`,
71+
value: { type: "set-accent", accent: candidate },
72+
color,
73+
};
74+
});
75+
76+
const items: MenuItem<ThemeConfigAction>[] = [
77+
{
78+
label: deps.UI_COPY.settings.baseTheme,
79+
value: { type: "cancel" },
80+
kind: "heading",
81+
},
82+
...paletteItems,
83+
{ label: "", value: { type: "cancel" }, separator: true },
84+
{
85+
label: deps.UI_COPY.settings.accentColor,
86+
value: { type: "cancel" },
87+
kind: "heading",
88+
},
89+
...accentItems,
90+
{ label: "", value: { type: "cancel" }, separator: true },
91+
{
92+
label: deps.UI_COPY.settings.resetDefault,
93+
value: { type: "reset" },
94+
color: "yellow",
95+
},
96+
{
97+
label: deps.UI_COPY.settings.saveAndBack,
98+
value: { type: "save" },
99+
color: "green",
100+
},
101+
{
102+
label: deps.UI_COPY.settings.backNoSave,
103+
value: { type: "cancel" },
104+
color: "red",
105+
},
106+
];
107+
108+
const initialCursor = items.findIndex((item) => {
109+
const value = item.value;
110+
if (value.type !== focus.type) return false;
111+
if (value.type === "set-palette" && focus.type === "set-palette") {
112+
return value.palette === focus.palette;
113+
}
114+
if (value.type === "set-accent" && focus.type === "set-accent") {
115+
return value.accent === focus.accent;
116+
}
117+
return true;
118+
});
119+
120+
const result = await select<ThemeConfigAction>(items, {
121+
message: deps.UI_COPY.settings.themeTitle,
122+
subtitle: deps.UI_COPY.settings.themeSubtitle,
123+
help: deps.UI_COPY.settings.themeHelp,
124+
clearScreen: true,
125+
theme: ui.theme,
126+
selectedEmphasis: "minimal",
127+
initialCursor: initialCursor >= 0 ? initialCursor : undefined,
128+
onCursorChange: ({ cursor }) => {
129+
const item = items[cursor];
130+
if (item && !item.separator && item.kind !== "heading") {
131+
focus = item.value;
132+
}
133+
},
134+
onInput: (raw) => {
135+
const lower = raw.toLowerCase();
136+
if (lower === "q") return { type: "cancel" };
137+
if (lower === "s") return { type: "save" };
138+
if (lower === "r") return { type: "reset" };
139+
if (raw === "1") return { type: "set-palette", palette: "green" };
140+
if (raw === "2") return { type: "set-palette", palette: "blue" };
141+
return undefined;
142+
},
143+
});
144+
145+
if (!result || result.type === "cancel") {
146+
deps.applyUiThemeFromDashboardSettings(baseline);
147+
return null;
148+
}
149+
if (result.type === "save") return draft;
150+
if (result.type === "reset") {
151+
draft = deps.applyDashboardDefaultsForKeys(draft, deps.THEME_PANEL_KEYS);
152+
focus = { type: "set-palette", palette: draft.uiThemePreset ?? "green" };
153+
deps.applyUiThemeFromDashboardSettings(draft);
154+
continue;
155+
}
156+
if (result.type === "set-palette") {
157+
draft = { ...draft, uiThemePreset: result.palette };
158+
focus = result;
159+
deps.applyUiThemeFromDashboardSettings(draft);
160+
continue;
161+
}
162+
draft = { ...draft, uiAccentColor: result.accent };
163+
focus = result;
164+
deps.applyUiThemeFromDashboardSettings(draft);
165+
}
166+
}

0 commit comments

Comments
 (0)