mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat: Themes can adapt to colour
This commit is contained in:
@@ -40,7 +40,8 @@
|
||||
coverImage: null,
|
||||
isEditable: true,
|
||||
hideThemeName: false,
|
||||
forceDark: undefined
|
||||
forceDark: undefined,
|
||||
adaptiveCssVariables: [],
|
||||
})
|
||||
let closedAccordions = $state<string[]>([])
|
||||
let themeLoaded = $state(false);
|
||||
@@ -80,7 +81,10 @@
|
||||
}))
|
||||
}
|
||||
|
||||
theme = loadedTheme
|
||||
theme = {
|
||||
...loadedTheme,
|
||||
adaptiveCssVariables: loadedTheme.adaptiveCssVariables ?? [],
|
||||
}
|
||||
themeLoaded = true
|
||||
} else {
|
||||
themeLoaded = true
|
||||
@@ -317,6 +321,27 @@
|
||||
|
||||
<Divider />
|
||||
|
||||
<div class="py-3">
|
||||
<h2 class="text-sm font-bold">Adaptive CSS variables</h2>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400">
|
||||
One per line, each must start with <code class="text-xs">--</code>. These receive the same colour as the adaptive accent when "Adaptive theme colour" is enabled in general settings. Use them in Custom CSS, e.g. <code class="text-xs">border-color: var(--my-accent);</code>
|
||||
</p>
|
||||
<textarea
|
||||
placeholder="--my-accent --class-banner"
|
||||
value={theme.adaptiveCssVariables?.join('\n') ?? ''}
|
||||
oninput={(e) => {
|
||||
const lines = e.currentTarget.value
|
||||
.split(/\r?\n/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
theme = { ...theme, adaptiveCssVariables: lines };
|
||||
}}
|
||||
class="p-2 mt-2 w-full min-h-[5rem] font-mono text-sm rounded-lg border-0 transition dark:placeholder-zinc-400 bg-zinc-200 dark:bg-zinc-700 focus:outline-none focus:ring-1 focus:ring-zinc-100 dark:focus:ring-zinc-700 focus:bg-zinc-300/50 dark:focus:bg-zinc-600"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
{#each [
|
||||
{
|
||||
type: 'switch',
|
||||
|
||||
@@ -3,6 +3,11 @@ import browser from "webextension-polyfill";
|
||||
import type { CustomTheme, LoadedCustomTheme } from "@/types/CustomThemes";
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
import debounce from "@/seqta/utils/debounce";
|
||||
import { updateAllColors } from "@/seqta/ui/colors/Manager";
|
||||
import {
|
||||
clearCustomThemeAdaptiveCssVariables,
|
||||
setCustomThemeAdaptiveCssVariables,
|
||||
} from "@/seqta/ui/colors/customThemeAdaptiveBindings";
|
||||
|
||||
type ThemeContent = {
|
||||
id: string;
|
||||
@@ -14,6 +19,7 @@ type ThemeContent = {
|
||||
CustomCSS?: string;
|
||||
hideThemeName?: boolean;
|
||||
forceDark?: boolean;
|
||||
adaptiveCssVariables?: string[];
|
||||
images: { id: string; variableName: string; data: string }[]; // data: base64
|
||||
};
|
||||
|
||||
@@ -240,6 +246,7 @@ export class ThemeManager {
|
||||
this.currentTheme = theme;
|
||||
settingsState.selectedTheme = themeId;
|
||||
}
|
||||
void updateAllColors();
|
||||
} catch (error) {
|
||||
console.error("[ThemeManager] Error setting theme:", error);
|
||||
}
|
||||
@@ -289,6 +296,8 @@ export class ThemeManager {
|
||||
);
|
||||
settingsState.selectedColor = theme.defaultColour;
|
||||
}
|
||||
|
||||
setCustomThemeAdaptiveCssVariables(theme.adaptiveCssVariables ?? []);
|
||||
} catch (error) {
|
||||
console.error("[ThemeManager] Error applying theme:", error);
|
||||
}
|
||||
@@ -373,6 +382,7 @@ export class ThemeManager {
|
||||
if (clearSelectedTheme) {
|
||||
settingsState.selectedTheme = "";
|
||||
}
|
||||
clearCustomThemeAdaptiveCssVariables();
|
||||
} catch (error) {
|
||||
console.error("[ThemeManager] Error removing theme:", error);
|
||||
}
|
||||
@@ -585,6 +595,7 @@ export class ThemeManager {
|
||||
isEditable: false,
|
||||
hideThemeName: themeData.hideThemeName ?? false,
|
||||
forceDark: themeData.forceDark,
|
||||
adaptiveCssVariables: themeData.adaptiveCssVariables,
|
||||
};
|
||||
|
||||
await this.saveTheme(theme);
|
||||
@@ -704,6 +715,9 @@ export class ThemeManager {
|
||||
if (defaultColour) {
|
||||
settingsState.selectedColor = defaultColour;
|
||||
}
|
||||
|
||||
setCustomThemeAdaptiveCssVariables(theme.adaptiveCssVariables ?? []);
|
||||
void updateAllColors();
|
||||
} catch (error) {
|
||||
console.error("[ThemeManager] Error previewing theme:", error);
|
||||
}
|
||||
@@ -778,6 +792,9 @@ export class ThemeManager {
|
||||
if (!theme.webURL && theme.defaultColour) {
|
||||
settingsState.selectedColor = theme.defaultColour;
|
||||
}
|
||||
|
||||
setCustomThemeAdaptiveCssVariables(theme.adaptiveCssVariables ?? []);
|
||||
void updateAllColors();
|
||||
} catch (error) {
|
||||
console.error("[ThemeManager] Error updating theme preview:", error);
|
||||
}
|
||||
@@ -815,6 +832,8 @@ export class ThemeManager {
|
||||
this.previewStyleElement = null;
|
||||
}
|
||||
|
||||
clearCustomThemeAdaptiveCssVariables();
|
||||
|
||||
// Restore original settings
|
||||
const storedColor = localStorage.getItem("originalPreviewColor");
|
||||
|
||||
@@ -844,6 +863,8 @@ export class ThemeManager {
|
||||
settingsState.DarkMode = this.originalPreviewTheme;
|
||||
this.originalPreviewTheme = null;
|
||||
}
|
||||
|
||||
void updateAllColors();
|
||||
} catch (error) {
|
||||
console.error("[ThemeManager] Error clearing preview:", error);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { lightenAndPaleColor } from "./lightenAndPaleColor";
|
||||
import ColorLuminance from "./ColorLuminance";
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
import { getAdaptiveColour } from "@/seqta/utils/adaptiveThemeColour";
|
||||
import { getCustomThemeAdaptiveCssVariables } from "@/seqta/ui/colors/customThemeAdaptiveBindings";
|
||||
|
||||
import darkLogo from "@/resources/icons/betterseqta-light-full.png";
|
||||
import lightLogo from "@/resources/icons/betterseqta-dark-full.png";
|
||||
@@ -127,6 +128,12 @@ function applyColorsWith(selectedColor: string) {
|
||||
// Apply all the properties
|
||||
applyProperties({ ...commonProps, ...modeProps, ...dynamicProps });
|
||||
|
||||
if (settingsState.selectedTheme) {
|
||||
for (const name of getCustomThemeAdaptiveCssVariables()) {
|
||||
setCSSVar(name, selectedColor);
|
||||
}
|
||||
}
|
||||
|
||||
let alliframes = document.getElementsByTagName("iframe");
|
||||
|
||||
for (let i = 0; i < alliframes.length; i++) {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/** Tracks which author-declared CSS variables mirror the effective accent; not persisted in settings storage. */
|
||||
|
||||
const VALID_CUSTOM_PROP = /^--[a-zA-Z0-9_-]{1,120}$/;
|
||||
|
||||
let boundNames: string[] = [];
|
||||
|
||||
export function normalizeAdaptiveCssVariableNames(
|
||||
names: string[] | undefined,
|
||||
): string[] {
|
||||
if (!names?.length) return [];
|
||||
const out: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const raw of names) {
|
||||
const s = raw.trim();
|
||||
if (!VALID_CUSTOM_PROP.test(s) || seen.has(s)) continue;
|
||||
seen.add(s);
|
||||
out.push(s);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function setCustomThemeAdaptiveCssVariables(
|
||||
names: string[] | undefined,
|
||||
): void {
|
||||
for (const n of boundNames) {
|
||||
document.documentElement.style.removeProperty(n);
|
||||
}
|
||||
boundNames = normalizeAdaptiveCssVariableNames(names);
|
||||
}
|
||||
|
||||
export function getCustomThemeAdaptiveCssVariables(): string[] {
|
||||
return boundNames;
|
||||
}
|
||||
|
||||
export function clearCustomThemeAdaptiveCssVariables(): void {
|
||||
for (const n of boundNames) {
|
||||
document.documentElement.style.removeProperty(n);
|
||||
}
|
||||
boundNames = [];
|
||||
}
|
||||
@@ -20,6 +20,7 @@ export class StorageChangeHandler {
|
||||
settingsState.register("adaptiveThemeColourTransition", () =>
|
||||
void updateAllColors(),
|
||||
);
|
||||
settingsState.register("selectedTheme", () => void updateAllColors());
|
||||
settingsState.register("DarkMode", this.handleDarkModeChange.bind(this));
|
||||
settingsState.register("onoff", this.handleOnOffChange.bind(this));
|
||||
settingsState.register("shortcuts", this.handleShortcutsChange.bind(this));
|
||||
|
||||
@@ -13,6 +13,8 @@ export type CustomTheme = {
|
||||
webURL?: string;
|
||||
selectedColor?: string;
|
||||
forceDark?: boolean;
|
||||
/** CSS custom property names (e.g. `--my-accent`) that receive the same value as `--better-main` when adaptive colours apply. */
|
||||
adaptiveCssVariables?: string[];
|
||||
};
|
||||
|
||||
export type LoadedCustomTheme = CustomTheme & {
|
||||
|
||||
Reference in New Issue
Block a user