feat(workspaces): Implement gradient theme with customization options

This commit introduces a new gradient theme for workspaces, offering a more visually appealing and customizable interface.
This commit is contained in:
Kristijan Ribarić 2024-10-05 14:47:52 +02:00
parent 3c095d47d8
commit a9ec46885c

View file

@ -348,7 +348,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
currentContainer.appendChild(currentWorkspace); currentContainer.appendChild(currentWorkspace);
if (activeWorkspace.themeColor) { if (activeWorkspace.themeColor) {
this.generateZenColorsComplementary(activeWorkspace.themeColor); this.generateZenGradientTheme(activeWorkspace.themeColor);
} else { } else {
// If no themeColor is set, reset to default colors // If no themeColor is set, reset to default colors
this.resetZenColors(); this.resetZenColors();
@ -933,117 +933,190 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
shiftHue = (h, shift) => (h + shift + 360) % 360; // Ensuring positive hue values shiftHue = (h, shift) => (h + shift + 360) % 360; // Ensuring positive hue values
generateZenColorsComplementary(baseHex) { generateZenGradientTheme(baseHex) {
const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches; const isDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
// Get HSL from base hex color // Get HSL from base hex color
let [h, s, l] = this.hexToHsl(baseHex); let [h, s, l] = this.hexToHsl(baseHex);
// Apply desaturation and adjust lightness for pastel effect // Adjust saturation based on user preference
s = Math.min(s, 40); // Cap saturation at 40% const desaturate = Services.prefs.getBoolPref('zen.workspaces.gradient.desaturate', false);
s = desaturate ? Math.min(s, 55) : Math.min(s, 80);
if (!isDarkTheme) { // Control lightness/intensity based on theme
l = Math.max(l, 70); // Ensure lightness is at least 70% in light mode let lightness, accentLightness;
if (isDarkTheme) {
lightness = 12; // Default lightness for dark theme
accentLightness = 40;
} else { } else {
l = 30; // Set base lightness to 30% in dark mode lightness = 80; // Default lightness for light theme
accentLightness = 58;
} }
baseHex = this.hslToHex(h, s, l);
let colors = {}; // Set CSS variables for saturation and lightness
document.documentElement.style.setProperty('--saturation', `${s}%`);
document.documentElement.style.setProperty('--lightness', `${lightness}%`);
document.documentElement.style.setProperty('--accent-color-lightness', `${accentLightness}%`);
if (s < 15) { // Determine the target hue based on the base hue category
// Neutral color selected (e.g., grey shade) function getColorCategory(hue) {
// Generate a primary color that pops if ((hue >= 0 && hue < 30) || (hue >= 330 && hue <= 360)) {
const popHues = [0, 30, 60, 120, 180, 240, 300]; // Array of hues for vibrant colors return 'red';
const popHue = popHues[Math.floor(Math.random() * popHues.length)]; // Randomly select a hue } else if (hue >= 30 && hue < 60) {
const popSaturation = 80; // High saturation for popping color return 'orange';
const popLightness = isDarkTheme ? 40 : 50; // Adjusted lightness based on theme } else if (hue >= 60 && hue < 90) {
return 'yellow';
const l_tertiary = isDarkTheme ? 15 : l; // Much darker in dark mode } else if (hue >= 90 && hue < 150) {
return 'green';
colors = { } else if (hue >= 150 && hue < 210) {
"--zen-colors-primary": this.hslToHex(popHue, popSaturation, popLightness), // Vibrant color return 'cyan';
"--zen-colors-secondary": this.hslToHex(h, s, Math.min(l_tertiary + 10, 100)), // Slightly lighter neutral } else if (hue >= 210 && hue < 270) {
"--zen-colors-tertiary": this.hslToHex(h, s, l_tertiary), // Darker neutral color (background) return 'blue';
"--zen-colors-border": this.hslToHex(h, s, Math.max(l_tertiary - 5, 0)), // Even darker neutral } else if (hue >= 270 && hue < 330) {
"--zen-dialog-background": this.hslToHex(h, s, Math.min(l_tertiary + 5, 100)), // Slightly lighter neutral return 'purple';
};
} else {
// Non-neutral color selected
const primaryH = this.shiftHue(h, isDarkTheme ? 0 : -10); // No hue shift in dark mode
const secondaryH = this.shiftHue(h, isDarkTheme ? 0 : 10);
if (!isDarkTheme) {
// Light mode
colors = {
"--zen-colors-primary": this.hslToHex(primaryH, s, Math.max(l - 10, 0)), // Slightly darker shade with shifted hue
"--zen-colors-secondary": this.hslToHex(secondaryH, s, Math.min(l + 10, 100)), // Slightly lighter shade with shifted hue
"--zen-colors-tertiary": baseHex, // Base color (background)
"--zen-colors-border": this.hslToHex(h, s, Math.max(l - 20, 0)), // Darker version for border
"--zen-dialog-background": this.hslToHex(h, s, Math.min(l + 5, 100)), // Slightly lighter
};
} else { } else {
// Dark mode return 'unknown';
const l_tertiary = 15; // Much darker for tertiary color
const l_primary = 40; // Lightness adjusted for white text
colors = {
"--zen-colors-primary": this.hslToHex(h, s, l_primary), // Similar to baseHex but dark enough for white text
"--zen-colors-secondary": this.hslToHex(h, s, Math.max(l_primary - 10, 0)), // Slightly darker than primary
"--zen-colors-tertiary": this.hslToHex(h, s, l_tertiary), // Much darker base color (background)
"--zen-colors-border": this.hslToHex(h, s, Math.max(l_tertiary - 5, 0)), // Even darker for border
"--zen-dialog-background": this.hslToHex(h, s, Math.min(l_tertiary + 5, 100)), // Slightly lighter
};
} }
} }
// Apply the colors to the document's root style function getTargetHue(category) {
Object.keys(colors).forEach(key => { switch (category) {
document.documentElement.style.setProperty(key, colors[key]); case 'red':
}); // Last color should be yellowish (60-90)
return 75; // Midpoint of yellow range
case 'orange':
// Last color should be blueish (210-270)
return 240; // Midpoint of blue range
case 'yellow':
// Last color should be purplish (270-330)
return 300; // Midpoint of purple range
case 'green':
// Last color should be reddish (0-30 or 330-360)
return 0; // Red hue
case 'cyan':
// Last color should be orangeish (30-60)
return 45; // Midpoint of orange range
case 'blue':
// Last color should be greenish (90-150)
return 120; // Midpoint of green range
case 'purple':
// Last color should be cyan (150-210)
return 180; // Midpoint of cyan range
default:
// If unknown, pick a complementary hue
return (h + 180) % 360;
}
}
return colors; function hueDifference(h1, h2) {
let diff = h2 - h1;
if (diff > 180) {
diff -= 360;
} else if (diff < -180) {
diff += 360;
}
return diff;
}
function interpolateHue(h1, h2, t) {
let diff = hueDifference(h1, h2);
return (h1 + diff * t + 360) % 360;
}
const baseCategory = getColorCategory(h);
const targetHue = getTargetHue(baseCategory);
const numberOfColors = 4; // Number of gradient colors
const gradientColors = [];
for (let i = 0; i < numberOfColors; i++) {
let t = i / (numberOfColors - 1); // Normalized position (0 to 1)
let newHue = interpolateHue(h, targetHue, t);
let colorVar = `--gradient-color${i + 1}`;
let colorValue = `hsl(${newHue}, var(--saturation), var(--lightness))`;
document.documentElement.style.setProperty(colorVar, colorValue);
gradientColors.push(colorValue);
}
// Set custom accent color using base hue
let accentColor = `hsla(${h}, 50%, var(--accent-color-lightness), 0.7)`;
document.documentElement.style.setProperty('--custom-accent-color', accentColor);
// Optionally switch colors if preference is set
const switchColors = Services.prefs.getBoolPref('zen.workspaces.gradient.switch-colors', true);
if (switchColors) {
gradientColors.forEach((color, index) => {
let switchedColorVar = `--switched-color${index + 1}`;
document.documentElement.style.setProperty(switchedColorVar, color);
});
// Update accent color if needed
document.documentElement.style.setProperty('--custom-accent-color', accentColor);
}
document.documentElement.setAttribute('zen-gradient-theme', 'true');
} }
resetZenColors() { resetZenColors() {
// Remove custom properties // Remove custom properties
const properties = [ const properties = [
"--zen-colors-primary", '--saturation',
"--zen-colors-secondary", '--lightness',
"--zen-colors-tertiary", '--accent-color-lightness',
"--zen-colors-border", '--gradient-color1',
"--zen-dialog-background" '--gradient-color2',
'--gradient-color3',
'--gradient-color4',
'--switched-color1',
'--switched-color2',
'--switched-color3',
'--switched-color4',
'--custom-accent-color',
'--gradient-start',
'--zen-main-browser-background',
'--zen-themed-toolbar-bg',
'--toolbarbutton-hover-background',
'--toolbarbutton-active-background',
'--panel-item-hover-bgcolor',
'--zen-workspaces-strip-background-color',
'--zen-webview-border-radius',
'--zen-border-radius',
]; ];
properties.forEach(prop => { properties.forEach(prop => {
document.documentElement.style.removeProperty(prop); document.documentElement.style.removeProperty(prop);
}); });
document.documentElement.removeAttribute('zen-gradient-theme');
} }
zenColorOptions = [ zenColorOptions = [
null, null,
"#FFD1DC", // Pastel Pink "#FF5733", // Vibrant Orange
"#FFB347", // Pastel Orange "#FFC300", // Vibrant Yellow
"#FFFF99", // Pastel Yellow "#DAF7A6", // Light Green
"#77DD77", // Pastel Green "#33FF57", // Vibrant Green
"#AEC6CF", // Pastel Blue "#33FFF6", // Aqua
"#D8BFD8", // Pastel Lilac "#3357FF", // Vibrant Blue
"#98FF98", // Pastel Mint "#9D33FF", // Vibrant Purple
"#FFDAB9", // Pastel Peach "#FF33A8", // Vibrant Pink
"#E6E6FA", // Pastel Lavender "#FF3333", // Vibrant Red
"#F5F5DC", // Pastel Beige "#FF8C33", // Vibrant Coral
"#F0E68C", // Khaki "#FFD133", // Vibrant Gold
"#E0FFFF", // Light Cyan "#8CFF33", // Lime Green
"#FFB6C1", // Light Pink "#33FF8C", // Mint Green
"#ADD8E6", // Light Blue "#33FFD1", // Turquoise
"#CD5C5C", // Darker Red "#338CFF", // Sky Blue
"#F08080", // Light Coral "#8C33FF", // Deep Purple
"#AFEEEE", // Pale Turquoise "#D133FF", // Magenta
"#20B2AA", // Light Sea Green "#FF33D1", // Hot Pink
"#8470FF", // Light Slate Blue "#FF338C", // Rose
"#FFA07A", // Light Salmon "#333333", // Dark Gray
"#000000" // Black "#FFFFFF" // White
]; ];
// Function to initialize the color picker // Function to initialize the color picker
initializeZenColorPicker(containerId, onColorSelected, initialColor = null) { initializeZenColorPicker(containerId, onColorSelected, initialColor = null) {
const container = document.getElementById(containerId); const container = document.getElementById(containerId);