add handlers for individual Channels

This commit is contained in:
2026-04-20 21:43:05 +09:30
parent 55d96a5e8f
commit 0ca0c7cf43
3 changed files with 85 additions and 19 deletions
+2 -1
View File
@@ -22,4 +22,5 @@ betterseqtaplus-safari/
.parcel-cache
.env
.env.submit
dependency-graph.svg
dependency-graph.svg
/src/resources/themes
+34 -3
View File
@@ -5,7 +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 { getCustomThemeAdaptiveCssVariableBindings } from "@/seqta/ui/colors/customThemeAdaptiveBindings";
import darkLogo from "@/resources/icons/betterseqta-light-full.png";
import lightLogo from "@/resources/icons/betterseqta-dark-full.png";
@@ -84,6 +84,21 @@ function cancelColorTransition() {
}
}
function getRepresentativeRgbChannels(s: string): { r: number; g: number; b: number } | null {
const parsedHex = parseRepresentativeHex(s);
if (!parsedHex) return null;
try {
const [r, g, b] = Color(parsedHex).rgb().array();
return {
r: Math.round(r),
g: Math.round(g),
b: Math.round(b),
};
} catch {
return null;
}
}
function applyColorsWith(selectedColor: string) {
if (settingsState.transparencyEffects) {
document.documentElement.classList.add("transparencyEffects");
@@ -129,8 +144,24 @@ function applyColorsWith(selectedColor: string) {
applyProperties({ ...commonProps, ...modeProps, ...dynamicProps });
if (settingsState.selectedTheme) {
for (const name of getCustomThemeAdaptiveCssVariables()) {
setCSSVar(name, selectedColor);
const channels = getRepresentativeRgbChannels(selectedColor);
for (const binding of getCustomThemeAdaptiveCssVariableBindings()) {
if (!binding.channel) {
setCSSVar(binding.cssVarName, selectedColor);
continue;
}
if (!channels) {
continue;
}
if (binding.channel === "r") {
setCSSVar(binding.cssVarName, String(channels.r));
} else if (binding.channel === "g") {
setCSSVar(binding.cssVarName, String(channels.g));
} else {
setCSSVar(binding.cssVarName, String(channels.b));
}
}
}
@@ -1,20 +1,49 @@
/** 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}$/;
const VALID_CHANNEL = /^(r|g|b)$/;
let boundNames: string[] = [];
export type AdaptiveChannel = "r" | "g" | "b";
export function normalizeAdaptiveCssVariableNames(
export type AdaptiveCssVariableBinding = {
cssVarName: string;
channel?: AdaptiveChannel;
};
let boundBindings: AdaptiveCssVariableBinding[] = [];
function parseAdaptiveBinding(
rawBinding: string,
): AdaptiveCssVariableBinding | null {
const trimmed = rawBinding.trim();
if (!trimmed) return null;
const [rawName, rawChannel] = trimmed.split(":", 2);
const cssVarName = rawName?.trim() ?? "";
if (!VALID_CUSTOM_PROP.test(cssVarName)) return null;
if (!rawChannel) return { cssVarName };
const channel = rawChannel.trim().toLowerCase();
if (!VALID_CHANNEL.test(channel)) return null;
return { cssVarName, channel: channel as AdaptiveChannel };
}
export function normalizeAdaptiveCssVariableBindings(
names: string[] | undefined,
): string[] {
): AdaptiveCssVariableBinding[] {
if (!names?.length) return [];
const out: string[] = [];
const out: AdaptiveCssVariableBinding[] = [];
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);
const parsed = parseAdaptiveBinding(raw);
if (!parsed) continue;
const key = `${parsed.cssVarName}:${parsed.channel ?? "full"}`;
if (seen.has(key)) continue;
seen.add(key);
out.push(parsed);
}
return out;
}
@@ -22,19 +51,24 @@ export function normalizeAdaptiveCssVariableNames(
export function setCustomThemeAdaptiveCssVariables(
names: string[] | undefined,
): void {
for (const n of boundNames) {
document.documentElement.style.removeProperty(n);
for (const binding of boundBindings) {
document.documentElement.style.removeProperty(binding.cssVarName);
}
boundNames = normalizeAdaptiveCssVariableNames(names);
boundBindings = normalizeAdaptiveCssVariableBindings(names);
}
export function getCustomThemeAdaptiveCssVariableBindings(): AdaptiveCssVariableBinding[] {
return boundBindings;
}
// Backward-compatible helper for existing callsites.
export function getCustomThemeAdaptiveCssVariables(): string[] {
return boundNames;
return boundBindings.map((b) => b.cssVarName);
}
export function clearCustomThemeAdaptiveCssVariables(): void {
for (const n of boundNames) {
document.documentElement.style.removeProperty(n);
for (const binding of boundBindings) {
document.documentElement.style.removeProperty(binding.cssVarName);
}
boundNames = [];
boundBindings = [];
}