feat: add theme_mode, theme_light, theme_dark to UserAppearanceSettings (#25076)

Part 1: Backend portion of a change broken into 2 PRs.
Part 2: #25077 

Adds three new UserAppearanceSettings fields (theme_mode, theme_light,
theme_dark) on top of the existing theme_preference and terminal_font.
Replaces GetUserThemePreference and GetUserTerminalFont with a single
GetUserAppearanceSettings aggregate query. The PUT handler is wrapped in
db.InTx so sync-mode's mode + slot writes can never half-apply.
This commit is contained in:
Jaayden Halko
2026-05-14 05:44:05 +01:00
committed by GitHub
parent d147dd3bdd
commit 024132e8a4
23 changed files with 1209 additions and 223 deletions
+3
View File
@@ -289,6 +289,9 @@ export const updateAppearanceSettings = (
// more responsive.
queryClient.setQueryData(myAppearanceKey, {
theme_preference: patch.theme_preference,
theme_mode: patch.theme_mode,
theme_light: patch.theme_light,
theme_dark: patch.theme_dark,
terminal_font: patch.terminal_font,
});
},
+44
View File
@@ -7908,6 +7908,11 @@ export const TerminalFontNames: TerminalFontName[] = [
"",
];
// From codersdk/users.go
export type ThemeMode = "single" | "sync" | "";
export const ThemeModes: ThemeMode[] = ["single", "sync", ""];
// From codersdk/users.go
export type ThinkingDisplayMode =
| "always_collapsed"
@@ -8398,6 +8403,28 @@ export interface UpdateTemplateMeta {
// From codersdk/users.go
export interface UpdateUserAppearanceSettingsRequest {
readonly theme_preference: string;
/**
* ThemeMode is optional for backward compatibility. When empty,
* the server leaves theme_mode, theme_light, and theme_dark
* unchanged so older CLI clients do not erase sync-mode settings.
* Legacy auto preferences are the exception: they clear theme_mode
* so clients can migrate the old sync-with-system setting.
*/
readonly theme_mode: ThemeMode;
/**
* ThemeLight is required when ThemeMode is "sync". In "single"
* mode an empty value means "preserve the previously persisted
* slot" rather than "clear the slot", so partial updates that send
* only one slot keep the other intact.
*/
readonly theme_light: string;
/**
* ThemeDark is required when ThemeMode is "sync". In "single" mode
* an empty value means "preserve the previously persisted slot"
* rather than "clear the slot", so partial updates that send only
* one slot keep the other intact.
*/
readonly theme_dark: string;
readonly terminal_font: TerminalFontName;
}
@@ -8699,7 +8726,24 @@ export interface UserActivityInsightsResponse {
// From codersdk/users.go
export interface UserAppearanceSettings {
/**
* ThemePreference is the legacy single-field appearance setting. In
* "single" mode it mirrors the active theme. In "sync" mode modern
* clients normally mirror the active OS slot, but older clients can
* update only this field, so it may diverge from ThemeLight or
* ThemeDark until a modern client saves the full appearance state
* again.
*/
readonly theme_preference: string;
readonly theme_mode: ThemeMode;
/**
* Ignored when ThemeMode is "single"
*/
readonly theme_light: string;
/**
* Ignored when ThemeMode is "single"
*/
readonly theme_dark: string;
readonly terminal_font: TerminalFontName;
}