From 4811224a5763d9bfb08f925c3891d83ad26e210d Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sun, 5 Oct 2025 22:12:46 -0400 Subject: [PATCH] Test simpler matugen approach --- Assets/MatugenTemplates/vesktop.css | 24 +- Helpers/ColorVariants.js | 161 ----------- Helpers/ColorsConvert.js | 92 +++++++ Services/MatugenService.qml | 404 +++------------------------- 4 files changed, 148 insertions(+), 533 deletions(-) delete mode 100644 Helpers/ColorVariants.js create mode 100644 Helpers/ColorsConvert.js diff --git a/Assets/MatugenTemplates/vesktop.css b/Assets/MatugenTemplates/vesktop.css index 23defda0f..17472dd30 100644 --- a/Assets/MatugenTemplates/vesktop.css +++ b/Assets/MatugenTemplates/vesktop.css @@ -15,17 +15,17 @@ --colors: on; /* turn off to use discord default colors */ /* text colors */ - --text-0: {{colors.surface.default.hex}}; /* text on colored elements */ - --text-1: {{colors.surface.default.hex}}; /* default text on colored elements */ - --text-2: {{colors.on_surface_variant.default.hex}}; /* headings and important text */ - --text-3: {{colors.on_surface.default.hex}}; /* normal text */ - --text-4: {{colors.on_surface_variant.default.hex}}; /* icon buttons and channels */ - --text-5: {{colors.on_surface_variant.default.hex}}; /* muted channels/chats and timestamps */ + --text-0: {{colors.background.default.hex}}; /* text on colored elements */ + --text-1: {{colors.on_background.default.hex}}; /* default text on colored elements */ + --text-2: {{colors.on_surface.default.hex}}; /* headings and important text */ + --text-3: {{colors.on_surface_variant.default.hex}}; /* normal text */ + --text-4: {{colors.outline.default.hex}}; /* icon buttons and channels */ + --text-5: {{colors.outline_variant.default.hex}}; /* muted channels/chats and timestamps */ /* background and dark colors */ - --bg-1: {{colors.surface_container.default.hex}}; /* dark buttons when clicked */ - --bg-2: {{colors.surface_container.default.hex}}; /* dark buttons */ - --bg-3: {{colors.surface_container_highest.default.hex}}; /* spacing, secondary elements */ + --bg-1: {{colors.surface_container_lowest.default.hex}}; /* dark buttons when clicked */ + --bg-2: {{colors.surface_container_low.default.hex}}; /* dark buttons */ + --bg-3: {{colors.surface_container.default.hex}}; /* spacing, secondary elements */ --bg-4: {{colors.surface.default.hex}}; /* main background color */ --hover: {{colors.surface_container_high.default.hex}}; /* channels and buttons when hovered */ --active: {{colors.surface_container_highest.default.hex}}; /* channels and buttons when clicked or selected */ @@ -68,9 +68,9 @@ --offline: {{colors.outline.default.hex}}; /* change to #83838b for default offline color */ /* border colors */ - --border-light: {{colors.surface_container.default.hex}}; /* light border color */ - --border: {{colors.surface_container.default.hex}}; /* normal border color */ - --button-border: {{colors.surface_container.default.hex}}; /* neutral border color of buttons */ + --border-light: {{colors.surface_container_high.default.hex}}; /* light border color */ + --border: {{colors.surface_container_highest.default.hex}}; /* normal border color */ + --button-border: {{colors.surface_container_high.default.hex}}; /* neutral border color of buttons */ /* base colors */ --red-1: {{colors.error.default.hex}}; diff --git a/Helpers/ColorVariants.js b/Helpers/ColorVariants.js deleted file mode 100644 index 4d81eb68a..000000000 --- a/Helpers/ColorVariants.js +++ /dev/null @@ -1,161 +0,0 @@ -// Material 3 Color Generator Helpers - -/** - * Convert hex color to HSL - */ -function hexToHSL(hex) { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - if (!result) return null; - - let r = parseInt(result[1], 16) / 255; - let g = parseInt(result[2], 16) / 255; - let b = parseInt(result[3], 16) / 255; - - const max = Math.max(r, g, b); - const min = Math.min(r, g, b); - let h, - s, - l = (max + min) / 2; - - if (max === min) { - h = s = 0; - } else { - const d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch (max) { - case r: - h = ((g - b) / d + (g < b ? 6 : 0)) / 6; - break; - case g: - h = ((b - r) / d + 2) / 6; - break; - case b: - h = ((r - g) / d + 4) / 6; - break; - } - } - - return { h: h * 360, s: s * 100, l: l * 100 }; -} - -/** - * Convert HSL to hex color - */ -function hslToHex(h, s, l) { - s /= 100; - l /= 100; - - const c = (1 - Math.abs(2 * l - 1)) * s; - const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); - const m = l - c / 2; - let r = 0, - g = 0, - b = 0; - - if (0 <= h && h < 60) { - r = c; - g = x; - b = 0; - } else if (60 <= h && h < 120) { - r = x; - g = c; - b = 0; - } else if (120 <= h && h < 180) { - r = 0; - g = c; - b = x; - } else if (180 <= h && h < 240) { - r = 0; - g = x; - b = c; - } else if (240 <= h && h < 300) { - r = x; - g = 0; - b = c; - } else if (300 <= h && h < 360) { - r = c; - g = 0; - b = x; - } - - r = Math.round((r + m) * 255); - g = Math.round((g + m) * 255); - b = Math.round((b + m) * 255); - - return ( - "#" + - [r, g, b] - .map((x) => { - const hex = x.toString(16); - return hex.length === 1 ? "0" + hex : hex; - }) - .join("") - ); -} - -/** - * Generate fixed_dim variant (darker, muted version) - * Material 3 fixed_dim is typically 20-30% darker - */ -function generateFixedDim(hexColor, isDarkTheme = false) { - const hsl = hexToHSL(hexColor); - if (!hsl) return hexColor; - - let newL, newS; - - if (isDarkTheme) { - // Dark Theme: Reduce lightness by 25-30% and slightly reduce saturation - newL = Math.max(hsl.l * 0.7, 10); - newS = Math.max(hsl.s * 0.85, 5); - } else { - // Light Theme: Increase lightness by 20-25% and reduce saturation - newL = Math.min(hsl.l * 1.25, 90); - newS = Math.max(hsl.s * 0.85, 10); - } - - return hslToHex(hsl.h, newS, newL); -} - -/** - * Generate bright variant (lighter, more vibrant version) - * Material 3 bright is typically 15-25% lighter with boosted saturation - */ -function generateBright(hexColor, isDarkTheme = false) { - const hsl = hexToHSL(hexColor); - if (!hsl) return hexColor; - - let newL, newS; - if (isDarkTheme) { - // Increase lightness by 20-25% and boost saturation - newL = Math.min(hsl.l * 1.25, 90); - newS = Math.min(hsl.s * 1.1, 100); - } else { - // Light Theme: Decrease lightness by 15-20% and boost saturation - newL = Math.max(hsl.l * 0.8, 20); - newS = Math.min(hsl.s * 1.15, 100); - } - - return hslToHex(hsl.h, newS, newL); -} - -/** - * Generate container variant (much lighter, desaturated version) - * Material 3 container is typically used for backgrounds, much lighter with reduced saturation - */ -function generateContainer(hexColor, isDarkTheme = false) { - const hsl = hexToHSL(hexColor); - if (!hsl) return hexColor; - - let newL, newS; - if (isDarkTheme) { - // Dark theme: darken the color (aim for 10-20 lightness) - newL = Math.max(hsl.l - (hsl.l - 15) * 0.85, 10); - newS = Math.max(hsl.s * 0.4, 10); - } else { - // Light theme: lighten the color (aim for 85-90 lightness) - newL = Math.min(hsl.l + (90 - hsl.l) * 0.85, 90); - newS = Math.max(hsl.s * 0.4, 10); - } - - return hslToHex(hsl.h, newS, newL); -} \ No newline at end of file diff --git a/Helpers/ColorsConvert.js b/Helpers/ColorsConvert.js new file mode 100644 index 000000000..a29d922e0 --- /dev/null +++ b/Helpers/ColorsConvert.js @@ -0,0 +1,92 @@ +/** + * Convert hex color to HSL + */ +function hexToHSL(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (!result) return null; + + let r = parseInt(result[1], 16) / 255; + let g = parseInt(result[2], 16) / 255; + let b = parseInt(result[3], 16) / 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h, + s, + l = (max + min) / 2; + + if (max === min) { + h = s = 0; + } else { + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = ((g - b) / d + (g < b ? 6 : 0)) / 6; + break; + case g: + h = ((b - r) / d + 2) / 6; + break; + case b: + h = ((r - g) / d + 4) / 6; + break; + } + } + + return { h: h * 360, s: s * 100, l: l * 100 }; +} + +/** + * Convert HSL to hex color + */ +function hslToHex(h, s, l) { + s /= 100; + l /= 100; + + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + let r = 0, + g = 0, + b = 0; + + if (0 <= h && h < 60) { + r = c; + g = x; + b = 0; + } else if (60 <= h && h < 120) { + r = x; + g = c; + b = 0; + } else if (120 <= h && h < 180) { + r = 0; + g = c; + b = x; + } else if (180 <= h && h < 240) { + r = 0; + g = x; + b = c; + } else if (240 <= h && h < 300) { + r = x; + g = 0; + b = c; + } else if (300 <= h && h < 360) { + r = c; + g = 0; + b = x; + } + + r = Math.round((r + m) * 255); + g = Math.round((g + m) * 255); + b = Math.round((b + m) * 255); + + return ( + "#" + + [r, g, b] + .map((x) => { + const hex = x.toString(16); + return hex.length === 1 ? "0" + hex : hex; + }) + .join("") + ); +} diff --git a/Services/MatugenService.qml b/Services/MatugenService.qml index 0910f7ec3..35512a554 100644 --- a/Services/MatugenService.qml +++ b/Services/MatugenService.qml @@ -5,7 +5,7 @@ import Quickshell import Quickshell.Io import qs.Commons import qs.Services -import "../Helpers/ColorVariants.js" as ColorVariants +import "../Helpers/ColorsConvert.js" as ColorsConvert Singleton { id: root @@ -42,325 +42,6 @@ Singleton { Logger.log("Matugen", "Service started") } - // -------------------------------- - // Convert predefined color scheme to Matugen format - function convertPredefinedSchemeToMatugen(schemeData) { - var variant = schemeData - // If scheme provides dark/light variants, pick based on settings - if (schemeData && (schemeData.dark || schemeData.light)) { - if (Settings.data.colorSchemes.darkMode) { - variant = schemeData.dark || schemeData.light - } else { - variant = schemeData.light || schemeData.dark - } - } - - // Map predefined scheme colors to Matugen color structure - only core Material 3 colors - var matugenColors = { - "colors": { - "primary": { - "light": { - "color": variant.mPrimary - }, - "default": { - "color": variant.mPrimary - }, - "dark": { - "color": variant.mPrimary - } - }, - "on_primary": { - "light": { - "color": variant.mOnPrimary - }, - "default": { - "color": variant.mOnPrimary - }, - "dark": { - "color": variant.mOnPrimary - } - }, - "secondary": { - "light": { - "color": variant.mSecondary - }, - "default": { - "color": variant.mSecondary - }, - "dark": { - "color": variant.mSecondary - } - }, - "on_secondary": { - "light": { - "color": variant.mOnSecondary - }, - "default": { - "color": variant.mOnSecondary - }, - "dark": { - "color": variant.mOnSecondary - } - }, - "tertiary": { - "light": { - "color": variant.mTertiary - }, - "default": { - "color": variant.mTertiary - }, - "dark": { - "color": variant.mTertiary - } - }, - "on_tertiary": { - "light": { - "color": variant.mOnTertiary - }, - "default": { - "color": variant.mOnTertiary - }, - "dark": { - "color": variant.mOnTertiary - } - }, - "error": { - "light": { - "color": variant.mError - }, - "default": { - "color": variant.mError - }, - "dark": { - "color": variant.mError - } - }, - "on_error": { - "light": { - "color": variant.mOnError - }, - "default": { - "color": variant.mOnError - }, - "dark": { - "color": variant.mOnError - } - }, - "surface": { - "light": { - "color": variant.mSurface - }, - "default": { - "color": variant.mSurface - }, - "dark": { - "color": variant.mSurface - } - }, - "on_surface": { - "light": { - "color": variant.mOnSurface - }, - "default": { - "color": variant.mOnSurface - }, - "dark": { - "color": variant.mOnSurface - } - }, - "surface_variant": { - "light": { - "color": variant.mSurfaceVariant - }, - "default": { - "color": variant.mSurfaceVariant - }, - "dark": { - "color": variant.mSurfaceVariant - } - }, - "on_surface_variant": { - "light": { - "color": variant.mOnSurfaceVariant - }, - "default": { - "color": variant.mOnSurfaceVariant - }, - "dark": { - "color": variant.mOnSurfaceVariant - } - }, - "outline": { - "light": { - "color": variant.mOutline - }, - "default": { - "color": variant.mOutline - }, - "dark": { - "color": variant.mOutline - } - }, - "primary_fixed_dim": { - "light": { - "color": ColorVariants.generateFixedDim(variant.mPrimary) - }, - "default": { - "color": ColorVariants.generateFixedDim(variant.mPrimary) - }, - "dark": { - "color": ColorVariants.generateFixedDim(variant.mPrimary) - } - }, - "secondary_fixed_dim": { - "light": { - "color": ColorVariants.generateFixedDim(variant.mSecondary) - }, - "default": { - "color": ColorVariants.generateFixedDim(variant.mSecondary) - }, - "dark": { - "color": ColorVariants.generateFixedDim(variant.mSecondary) - } - }, - "tertiary_fixed_dim": { - "light": { - "color": ColorVariants.generateFixedDim(variant.mTertiary) - }, - "default": { - "color": ColorVariants.generateFixedDim(variant.mTertiary) - }, - "dark": { - "color": ColorVariants.generateFixedDim(variant.mTertiary) - } - }, - "surface_bright": { - "light": { - "color": ColorVariants.generateBright(variant.mSurface) - }, - "default": { - "color": ColorVariants.generateBright(variant.mSurface) - }, - "dark": { - "color": ColorVariants.generateBright(variant.mSurface) - } - }, - "surface_variant_bright": { - "light": { - "color": ColorVariants.generateBright(variant.mSurfaceVariant) - }, - "default": { - "color": ColorVariants.generateBright(variant.mSurfaceVariant) - }, - "dark": { - "color": ColorVariants.generateBright(variant.mSurfaceVariant) - } - }, - "primary_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mPrimary, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mPrimary, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mPrimary, true) - } - }, - "secondary_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mSecondary, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mSecondary, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mSecondary, true) - } - }, - "tertiary_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mTertiary, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mTertiary, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mTertiary, true) - } - }, - "on_primary_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mOnPrimary, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mOnPrimary, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mOnPrimary, true) - } - }, - "on_secondary_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mOnSecondary, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mOnSecondary, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mOnSecondary, true) - } - }, - "on_tertiary_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mOnTertiary, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mOnTertiary, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mOnTertiary, true) - } - }, - "surface_container": { - "light": { - "color": ColorVariants.generateContainer(variant.mSurface, false) - }, - "default": { - "color": ColorVariants.generateContainer(variant.mSurface, true) - }, - "dark": { - "color": ColorVariants.generateContainer(variant.mSurface, true) - } - }, - "surface_container_high": { - "light": { - "color": ColorVariants.generateBright(ColorVariants.generateContainer(variant.mSurface, false), false) - }, - "default": { - "color": ColorVariants.generateBright(ColorVariants.generateContainer(variant.mSurface, true), true) - }, - "dark": { - "color": ColorVariants.generateBright(ColorVariants.generateContainer(variant.mSurface, true), true) - } - }, - "surface_container_highest": { - "light": { - "color": ColorVariants.generateBright(ColorVariants.generateBright(ColorVariants.generateContainer(variant.mSurface, false), false), false) - }, - "default": { - "color": ColorVariants.generateBright(ColorVariants.generateBright(ColorVariants.generateContainer(variant.mSurface, true), true), true) - }, - "dark": { - "color": ColorVariants.generateBright(ColorVariants.generateBright(ColorVariants.generateContainer(variant.mSurface, true), true), true) - } - } - } - } - - return JSON.stringify(matugenColors) - } - // -------------------------------- // Generate colors using current wallpaper and settings function generateFromWallpaper() { @@ -394,6 +75,29 @@ Singleton { generateProcess.running = true } + // -------------------------------- + function selectVibrantColor(schemeData, mode) { + var colors = [] + colors.push(schemeData[mode]["mPrimary"]); + colors.push(schemeData[mode]["mSecondary"]); + colors.push(schemeData[mode]["mTertiary"]); + + + var bestScore = 0 + var bestScoreIndex = -1 + for (var i=0; i bestScore) { + bestScore = score + bestScoreIndex = i + } + } + + return colors[bestScoreIndex] + } + // -------------------------------- // Generate templates from predefined color scheme function generateFromPredefinedScheme(schemeData) { @@ -404,39 +108,33 @@ Singleton { var pathEsc = dynamicConfigPath.replace(/'/g, "'\\''") var extraRepo = (Quickshell.shellDir + "/Assets/Matugen/extra").replace(/'/g, "'\\''") var extraUser = (Settings.configDir + "matugen.d").replace(/'/g, "'\\''") - - // Convert predefined scheme to Matugen format - var matugenJson = convertPredefinedSchemeToMatugen(schemeData) - var jsonPath = Settings.cacheDir + "matugen.import.json" - var jsonPathEsc = jsonPath.replace(/'/g, "'\\''") + const color = selectVibrantColor(schemeData, mode) // Build the script var script = "" - script += "cat > '" + pathEsc + "' << 'EOF'\n" + content + "EOF\n" - script += "for d in '" + extraRepo + "' '" + extraUser + "'; do\n" - script += " if [ -d \"$d\" ]; then\n" - script += " for f in \"$d\"/*.toml; do\n" - script += " [ -f \"$f\" ] && { echo; echo \"# extra: $f\"; cat \"$f\"; } >> '" + pathEsc + "'\n" - script += " done\n" - script += " fi\n" - script += "done\n" - script += "matugen image --import-json '" + jsonPathEsc + "' --config '" + pathEsc + "' --mode " + mode + " '" + Quickshell.shellDir + "/Assets/Wallpaper/noctalia.png'" + script += "cat > '" + pathEsc + "' << 'EOF'\n" + content + "EOF\n\n" + // script += "for d in '" + extraRepo + "' '" + extraUser + "'; do\n" + // script += " if [ -d \"$d\" ]; then\n" + // script += " for f in \"$d\"/*.toml; do\n" + // script += " [ -f \"$f\" ] && { echo; echo \"# extra: $f\"; cat \"$f\"; } >> '" + pathEsc + "'\n" + // script += " done\n" + // script += " fi\n" + // script += "done\n\n" + script += "matugen color hex '" + color + "' --config '" + pathEsc + "' --mode " + mode - // Add user config execution if enabled - if (Settings.data.templates.enableUserTemplates) { - var userConfigDir = (Quickshell.env("HOME") + "/.config/matugen/").replace(/'/g, "'\\''") - script += "\n# Execute user config if it exists\nif [ -f '" + userConfigDir + "config.toml' ]; then\n" - script += " matugen image --import-json '" + jsonPathEsc + "' --config '" + userConfigDir + "config.toml' --mode " + mode + " '" + Quickshell.shellDir + "/Assets/Wallpaper/noctalia.png'\n" - script += "fi" - } + console.log(script) + + // // Add user config execution if enabled + // if (Settings.data.templates.enableUserTemplates) { + // var userConfigDir = (Quickshell.env("HOME") + "/.config/matugen/").replace(/'/g, "'\\''") + // script += "\n# Execute user config if it exists\nif [ -f '" + userConfigDir + "config.toml' ]; then\n" + // script += " matugen color hex " + color + " --config '" + userConfigDir + "config.toml' --mode " + mode + // script += "fi" + // } script += "\n" generateProcess.command = ["bash", "-lc", script] - - // Write JSON file with our custom colors - // once written matugen will be executed via 'generateProcess' - jsonWriter.path = jsonPath - jsonWriter.setText(matugenJson) + generateProcess.running = true // ----- // For terminals simply copy the full color from theme from iTerm2 so everything looks super nice! @@ -487,20 +185,6 @@ Singleton { return `${Quickshell.shellDir}/Assets/ColorScheme/${colorScheme}/terminal/${terminal}/${colorScheme}-${darkLight}` } - // -------------------------------- - // File writer for JSON import file - FileView { - id: jsonWriter - onSaved: { - Logger.log("Matugen", "JSON import file written successfully") - // Run matugen command after JSON file is written - generateProcess.running = true - } - onSaveFailed: { - Logger.error("Matugen", "Failed to write JSON import file:", error) - } - } - // -------------------------------- Process { id: generateProcess