mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-29 02:21:41 +00:00
Add custom theming (#2081)
Adds support for user-created custom themes. Custom theme interface is tucked into the global settings in a non-invasive manner to avoid major design changes. Builds on the theme structure established by the dark theme update. <img width="1486" height="953" alt="image" src="https://github.com/user-attachments/assets/716bcfc7-af74-41dc-b14a-cfc2f2d2caa9" /> <img width="1486" height="956" alt="image" src="https://github.com/user-attachments/assets/a00f9620-0b1d-4f67-b010-e94dda5dc212" /> Here's a few examples of what teams could do, using a few color schemes from local teams. Imagine the possibilities! <img width="1485" height="951" alt="image" src="https://github.com/user-attachments/assets/c3da37b8-f6be-4152-81e0-533297f517fc" /> <img width="1483" height="951" alt="image" src="https://github.com/user-attachments/assets/0d453f7a-cf6f-4c27-97db-603b54c1f73e" /> <img width="1485" height="952" alt="image" src="https://github.com/user-attachments/assets/bf8c7770-e60d-4875-9580-ed7e54e089f4" /> <img width="1484" height="952" alt="image" src="https://github.com/user-attachments/assets/326d89e6-dd6e-4e05-a9fa-c9fc6f880847" /> <img width="1482" height="951" alt="image" src="https://github.com/user-attachments/assets/eb5a2a5d-c103-482c-a62a-5ccd5ba21cc5" /> <img width="1482" height="950" alt="image" src="https://github.com/user-attachments/assets/4831ca56-f322-4345-97af-8963ae8539b1" /> Looking for high contrast? Just moments away: <img width="1484" height="949" alt="image" src="https://github.com/user-attachments/assets/7ffc65c6-7000-4566-b4f0-c8247f75fb3d" />
This commit is contained in:
65
photon-client/src/lib/ThemeManager.ts
Normal file
65
photon-client/src/lib/ThemeManager.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { type ThemeInstance } from "vuetify";
|
||||
import { LightTheme, DarkTheme } from "@/plugins/vuetify";
|
||||
|
||||
export const resetTheme = (theme: ThemeInstance) => {
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
localStorage.removeItem(`${themeType}-background`);
|
||||
localStorage.removeItem(`${themeType}-primary`);
|
||||
localStorage.removeItem(`${themeType}-secondary`);
|
||||
localStorage.removeItem(`${themeType}-surface`);
|
||||
|
||||
restoreThemeConfig(theme);
|
||||
};
|
||||
|
||||
export const getThemeColor = (theme: ThemeInstance, color: string): string => {
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
const defaultTheme = theme.global.name.value === "LightTheme" ? LightTheme : DarkTheme;
|
||||
return localStorage.getItem(`${themeType}-${color}`) ?? defaultTheme.colors![color]!;
|
||||
};
|
||||
|
||||
export const setThemeColor = (theme: ThemeInstance, color: string, value: string | null) => {
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
if (value) localStorage.setItem(`${themeType}-${color}`, value);
|
||||
else localStorage.removeItem(`${themeType}-${color}`);
|
||||
|
||||
restoreThemeConfig(theme);
|
||||
};
|
||||
|
||||
export const toggleTheme = (theme: ThemeInstance) => {
|
||||
const currentTheme = localStorage.getItem("theme");
|
||||
localStorage.setItem("theme", currentTheme === "LightTheme" ? "DarkTheme" : "LightTheme");
|
||||
|
||||
restoreThemeConfig(theme);
|
||||
};
|
||||
|
||||
export const restoreThemeConfig = (theme: ThemeInstance) => {
|
||||
// Restore theme preference
|
||||
const storedTheme = localStorage.getItem("theme");
|
||||
if (storedTheme) theme.global.name.value = storedTheme;
|
||||
|
||||
// Restore custom theme colors
|
||||
const themeType = theme.global.name.value === "LightTheme" ? "light" : "dark";
|
||||
const defaultTheme = theme.global.name.value === "LightTheme" ? LightTheme : DarkTheme;
|
||||
|
||||
const customBackground = localStorage.getItem(`${themeType}-background`);
|
||||
const customPrimary = localStorage.getItem(`${themeType}-primary`);
|
||||
const customSecondary = localStorage.getItem(`${themeType}-secondary`);
|
||||
const customSurface = localStorage.getItem(`${themeType}-surface`);
|
||||
|
||||
theme.themes.value[theme.global.name.value].colors.background = customBackground ?? defaultTheme.colors!.background!;
|
||||
theme.themes.value[theme.global.name.value].colors.sidebar = theme.themes.value[theme.global.name.value].dark
|
||||
? (customBackground ?? defaultTheme.colors!.sidebar!)
|
||||
: (customSurface ?? defaultTheme.colors!.sidebar!);
|
||||
|
||||
theme.themes.value[theme.global.name.value].colors.primary = customPrimary ?? defaultTheme.colors!.primary!;
|
||||
theme.themes.value[theme.global.name.value].colors.buttonActive = customPrimary ?? defaultTheme.colors!.buttonActive!;
|
||||
|
||||
theme.themes.value[theme.global.name.value].colors.secondary = customSecondary ?? defaultTheme.colors!.secondary!;
|
||||
theme.themes.value[theme.global.name.value].colors.buttonPassive =
|
||||
customSecondary ?? defaultTheme.colors!.buttonPassive!;
|
||||
|
||||
theme.themes.value[theme.global.name.value].colors.accent = customSecondary ?? defaultTheme.colors!.accent!;
|
||||
theme.themes.value[theme.global.name.value].colors.toggle = customSecondary ?? defaultTheme.colors!.toggle!;
|
||||
|
||||
theme.themes.value[theme.global.name.value].colors.surface = customSurface ?? defaultTheme.colors!.surface!;
|
||||
};
|
||||
Reference in New Issue
Block a user