mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-08-15 18:43:59 +00:00
615 lines
18 KiB
QML
615 lines
18 KiB
QML
pragma Singleton
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Commons
|
|
import qs.Services
|
|
import "../Helpers/ColorsConvert.js" as ColorsConvert
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
readonly property string colorsApplyScript: Quickshell.shellDir + '/Bin/colors-apply.sh'
|
|
|
|
property string dynamicConfigPath: Settings.cacheDir + "matugen.dynamic.toml"
|
|
|
|
// External state management
|
|
Connections {
|
|
target: WallpaperService
|
|
function onWallpaperChanged(screenName, path) {
|
|
// Only detect changes on main screen
|
|
if (screenName === Screen.name && Settings.data.colorSchemes.useWallpaperColors) {
|
|
generateFromWallpaper()
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Settings.data.colorSchemes
|
|
function onDarkModeChanged() {
|
|
Logger.log("Matugen", "Detected dark mode change")
|
|
if (Settings.data.colorSchemes.useWallpaperColors) {
|
|
MatugenService.generateFromWallpaper()
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------
|
|
function init() {
|
|
// does nothing but ensure the singleton is created
|
|
// do not remove
|
|
Logger.log("Matugen", "Service started")
|
|
}
|
|
|
|
// --------------------------------
|
|
// Generate colors using current wallpaper and settings
|
|
function generateFromWallpaper() {
|
|
Logger.log("Matugen", "Generating from wallpaper on screen:", Screen.name)
|
|
|
|
var wp = WallpaperService.getWallpaper(Screen.name).replace(/'/g, "'\\''")
|
|
if (wp === "") {
|
|
Logger.error("Matugen", "No wallpaper was found")
|
|
return
|
|
}
|
|
|
|
var content = MatugenTemplates.buildConfigToml()
|
|
if (content === "") {
|
|
return
|
|
}
|
|
|
|
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
|
var pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")
|
|
var script = buildMatugenScript(content, pathEsc, wp, mode)
|
|
generateProcess.command = ["bash", "-lc", script]
|
|
generateProcess.running = true
|
|
}
|
|
|
|
// --------------------------------
|
|
function buildMatugenScript(content, pathEsc, wallpaper, mode) {
|
|
// Use a unique delimiter to avoid conflicts with config content
|
|
var delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
|
|
var script = "cat > '" + pathEsc + "' << '" + delimiter + "'\n" + content + "\n" + delimiter + "\n"
|
|
|
|
// Main matugen command
|
|
script += "matugen image '" + wallpaper + "' --config '" + pathEsc + "' --mode " + mode + " --type " + Settings.data.colorSchemes.matugenSchemeType
|
|
|
|
// Add user template execution if enabled
|
|
script += addUserTemplateExecution(wallpaper, mode)
|
|
|
|
return script + "\n"
|
|
}
|
|
|
|
// --------------------------------
|
|
function addUserTemplateExecution(input, mode) {
|
|
if (!Settings.data.templates.enableUserTemplates) {
|
|
return ""
|
|
}
|
|
|
|
var userConfigPath = getUserConfigPath()
|
|
var script = "\n# Execute user config if it exists\n"
|
|
script += "if [ -f '" + userConfigPath + "' ]; then\n"
|
|
script += " matugen image '" + input + "' --config '" + userConfigPath + "' --mode " + mode + " --type " + Settings.data.colorSchemes.matugenSchemeType + "\n"
|
|
script += "fi"
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function getUserConfigPath() {
|
|
return (Quickshell.env("HOME") + "/.config/matugen/config.toml").replace(/'/g, "'\\''")
|
|
}
|
|
|
|
// --------------------------------
|
|
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 < colors.length; i++) {
|
|
var hsl = ColorsConvert.hexToHSL(colors[i])
|
|
|
|
var score = hsl['s']
|
|
if (score > bestScore) {
|
|
bestScore = score
|
|
bestScoreIndex = i
|
|
}
|
|
}
|
|
|
|
return colors[bestScoreIndex]
|
|
}
|
|
|
|
// --------------------------------
|
|
// Generate templates from predefined color scheme
|
|
function generateFromPredefinedScheme(schemeData) {
|
|
Logger.log("Matugen", "Generating templates from predefined color scheme")
|
|
|
|
// Handle terminal theme copying for predefined schemes
|
|
handleTerminalThemes()
|
|
|
|
var content = MatugenTemplates.buildConfigToml()
|
|
if (content === "") {
|
|
return
|
|
}
|
|
|
|
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"
|
|
var pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")
|
|
|
|
// Use the full predefined color scheme instead of generating from a single color
|
|
var script = buildPredefinedSchemeScriptWithFullColors(content, pathEsc, schemeData, mode)
|
|
generateProcess.command = ["bash", "-lc", script]
|
|
generateProcess.running = true
|
|
}
|
|
|
|
// --------------------------------
|
|
function buildPredefinedSchemeScriptWithFullColors(content, pathEsc, schemeData, mode) {
|
|
// Instead of using matugen color generation, directly process templates with predefined colors
|
|
var colors = schemeData[mode] || schemeData.dark || schemeData.light
|
|
var script = ""
|
|
|
|
// Process each enabled template directly with predefined colors
|
|
script += processTemplatesWithPredefinedColors(colors, mode)
|
|
|
|
// Add user template execution if enabled
|
|
script += addUserTemplateExecutionForPredefinedColors(colors, mode)
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processTemplatesWithPredefinedColors(colors, mode) {
|
|
var script = ""
|
|
|
|
// Create a colors object that matches matugen's expected format
|
|
var matugenColors = {
|
|
"primary": {
|
|
"default": {
|
|
"hex": colors.mPrimary
|
|
}
|
|
},
|
|
"on_primary": {
|
|
"default": {
|
|
"hex": colors.mOnPrimary
|
|
}
|
|
},
|
|
"primary_container": {
|
|
"default": {
|
|
"hex": colors.mPrimary
|
|
}
|
|
},
|
|
"on_primary_container": {
|
|
"default": {
|
|
"hex": colors.mOnPrimary
|
|
}
|
|
},
|
|
"secondary": {
|
|
"default": {
|
|
"hex": colors.mSecondary
|
|
}
|
|
},
|
|
"on_secondary": {
|
|
"default": {
|
|
"hex": colors.mOnSecondary
|
|
}
|
|
},
|
|
"secondary_container": {
|
|
"default": {
|
|
"hex": colors.mSecondary
|
|
}
|
|
},
|
|
"on_secondary_container": {
|
|
"default": {
|
|
"hex": colors.mOnSecondary
|
|
}
|
|
},
|
|
"tertiary": {
|
|
"default": {
|
|
"hex": colors.mTertiary
|
|
}
|
|
},
|
|
"on_tertiary": {
|
|
"default": {
|
|
"hex": colors.mOnTertiary
|
|
}
|
|
},
|
|
"tertiary_container": {
|
|
"default": {
|
|
"hex": colors.mTertiary
|
|
}
|
|
},
|
|
"on_tertiary_container": {
|
|
"default": {
|
|
"hex": colors.mOnTertiary
|
|
}
|
|
},
|
|
"error": {
|
|
"default": {
|
|
"hex": colors.mError
|
|
}
|
|
},
|
|
"on_error": {
|
|
"default": {
|
|
"hex": colors.mOnError
|
|
}
|
|
},
|
|
"error_container": {
|
|
"default": {
|
|
"hex": colors.mError
|
|
}
|
|
},
|
|
"on_error_container": {
|
|
"default": {
|
|
"hex": colors.mOnError
|
|
}
|
|
},
|
|
"background": {
|
|
"default": {
|
|
"hex": colors.mSurface
|
|
}
|
|
},
|
|
"on_background": {
|
|
"default": {
|
|
"hex": colors.mOnSurface
|
|
}
|
|
},
|
|
"surface": {
|
|
"default": {
|
|
"hex": colors.mSurface
|
|
}
|
|
},
|
|
"on_surface": {
|
|
"default": {
|
|
"hex": colors.mOnSurface
|
|
}
|
|
},
|
|
"surface_variant": {
|
|
"default": {
|
|
"hex": colors.mSurfaceVariant
|
|
}
|
|
},
|
|
"on_surface_variant": {
|
|
"default": {
|
|
"hex": colors.mOnSurfaceVariant
|
|
}
|
|
},
|
|
"outline": {
|
|
"default": {
|
|
"hex": colors.mOutline
|
|
}
|
|
},
|
|
"outline_variant": {
|
|
"default": {
|
|
"hex": colors.mOutline
|
|
}
|
|
},
|
|
"shadow": {
|
|
"default": {
|
|
"hex": colors.mShadow
|
|
}
|
|
},
|
|
"surface_container": {
|
|
"default": {
|
|
"hex": colors.mSurfaceVariant
|
|
}
|
|
},
|
|
"surface_container_low": {
|
|
"default": {
|
|
"hex": colors.mSurface
|
|
}
|
|
},
|
|
"surface_container_lowest": {
|
|
"default": {
|
|
"hex": colors.mSurface
|
|
}
|
|
},
|
|
"surface_container_high": {
|
|
"default": {
|
|
"hex": colors.mSurfaceVariant
|
|
}
|
|
},
|
|
"surface_container_highest": {
|
|
"default": {
|
|
"hex": colors.mOutline
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process each enabled template using the same structure as MatugenTemplates
|
|
var applications = [{
|
|
"name": "gtk",
|
|
"templates": [{
|
|
"version": "gtk3",
|
|
"output": "gtk3"
|
|
}, {
|
|
"version": "gtk4",
|
|
"output": "gtk4"
|
|
}],
|
|
"input": "gtk.css"
|
|
}, {
|
|
"name": "qt",
|
|
"templates": [{
|
|
"version": "qt5",
|
|
"output": "qt5"
|
|
}, {
|
|
"version": "qt6",
|
|
"output": "qt6"
|
|
}],
|
|
"input": "qtct.conf"
|
|
}, {
|
|
"name": "fuzzel",
|
|
"templates": [{
|
|
"version": "fuzzel",
|
|
"output": "fuzzel"
|
|
}],
|
|
"input": "fuzzel.conf"
|
|
}, {
|
|
"name": "pywalfox",
|
|
"templates": [{
|
|
"version": "pywalfox",
|
|
"output": "pywalfox"
|
|
}],
|
|
"input": "pywalfox.json"
|
|
}, {
|
|
"name": "vesktop",
|
|
"templates": [{
|
|
"version": "vesktop",
|
|
"output": "vesktop"
|
|
}],
|
|
"input": "vesktop.css"
|
|
}]
|
|
|
|
applications.forEach(function (app) {
|
|
if (Settings.data.templates[app.name]) {
|
|
script += processTemplateForApp(app.name, matugenColors, mode)
|
|
}
|
|
})
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processTemplateForApp(appName, colors, mode) {
|
|
var script = ""
|
|
|
|
switch (appName) {
|
|
case "gtk":
|
|
script += processGtkTemplate(colors, mode)
|
|
break
|
|
case "qt":
|
|
script += processQtTemplate(colors, mode)
|
|
break
|
|
case "fuzzel":
|
|
script += processFuzzelTemplate(colors, mode)
|
|
break
|
|
case "pywalfox":
|
|
script += processPywalfoxTemplate(colors, mode)
|
|
break
|
|
case "vesktop":
|
|
script += processVesktopTemplate(colors, mode)
|
|
break
|
|
}
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processGtkTemplate(colors, mode) {
|
|
var script = ""
|
|
var templatePath = Quickshell.shellDir + "/Assets/MatugenTemplates/gtk.css"
|
|
var homeDir = Quickshell.env("HOME")
|
|
var outputPath3 = homeDir + "/.config/gtk-3.0/gtk.css"
|
|
var outputPath4 = homeDir + "/.config/gtk-4.0/gtk.css"
|
|
|
|
// Process GTK3 template
|
|
script += "mkdir -p " + homeDir + "/.config/gtk-3.0\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath3 + "'\n"
|
|
script += replaceColorsInFile(outputPath3, colors)
|
|
|
|
// Process GTK4 template
|
|
script += "mkdir -p " + homeDir + "/.config/gtk-4.0\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath4 + "'\n"
|
|
script += replaceColorsInFile(outputPath4, colors)
|
|
|
|
script += "gsettings set org.gnome.desktop.interface color-scheme prefer-" + mode + "\n"
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processQtTemplate(colors, mode) {
|
|
var script = ""
|
|
var templatePath = Quickshell.shellDir + "/Assets/MatugenTemplates/qtct.conf"
|
|
var homeDir = Quickshell.env("HOME")
|
|
var outputPath5 = homeDir + "/.config/qt5ct/colors/noctalia.conf"
|
|
var outputPath6 = homeDir + "/.config/qt6ct/colors/noctalia.conf"
|
|
|
|
// Process Qt5 template
|
|
script += "mkdir -p " + homeDir + "/.config/qt5ct/colors\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath5 + "'\n"
|
|
script += replaceColorsInFile(outputPath5, colors)
|
|
|
|
// Process Qt6 template
|
|
script += "mkdir -p " + homeDir + "/.config/qt6ct/colors\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath6 + "'\n"
|
|
script += replaceColorsInFile(outputPath6, colors)
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processFuzzelTemplate(colors, mode) {
|
|
var script = ""
|
|
var templatePath = Quickshell.shellDir + "/Assets/MatugenTemplates/fuzzel.conf"
|
|
var homeDir = Quickshell.env("HOME")
|
|
var outputPath = homeDir + "/.config/fuzzel/themes/noctalia"
|
|
|
|
script += "mkdir -p " + homeDir + "/.config/fuzzel/themes\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath + "'\n"
|
|
script += replaceColorsInFile(outputPath, colors)
|
|
script += MatugenService.colorsApplyScript + " fuzzel\n"
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processPywalfoxTemplate(colors, mode) {
|
|
var script = ""
|
|
var templatePath = Quickshell.shellDir + "/Assets/MatugenTemplates/pywalfox.json"
|
|
var homeDir = Quickshell.env("HOME")
|
|
var outputPath = homeDir + "/.cache/wal/colors.json"
|
|
|
|
script += "mkdir -p " + homeDir + "/.cache/wal\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath + "'\n"
|
|
script += replaceColorsInFile(outputPath, colors)
|
|
script += MatugenService.colorsApplyScript + " pywalfox\n"
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function processVesktopTemplate(colors, mode) {
|
|
var script = ""
|
|
var templatePath = Quickshell.shellDir + "/Assets/MatugenTemplates/vesktop.css"
|
|
var homeDir = Quickshell.env("HOME")
|
|
var outputPath = homeDir + "/.config/vesktop/themes/noctalia.theme.css"
|
|
|
|
script += "mkdir -p " + homeDir + "/.config/vesktop/themes\n"
|
|
script += "cp '" + templatePath + "' '" + outputPath + "'\n"
|
|
script += replaceColorsInFile(outputPath, colors)
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function replaceColorsInFile(filePath, colors) {
|
|
var script = ""
|
|
|
|
// Replace all color placeholders with actual colors
|
|
Object.keys(colors).forEach(function (colorKey) {
|
|
var colorValue = colors[colorKey].default.hex
|
|
// Escape special characters in the color value for sed
|
|
var escapedColor = colorValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
script += "sed -i 's/{{colors\\." + colorKey + "\\.default\\.hex}}/" + escapedColor + "/g' '" + filePath + "'\n"
|
|
})
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function addUserTemplateExecutionForPredefinedColors(colors, mode) {
|
|
if (!Settings.data.templates.enableUserTemplates) {
|
|
return ""
|
|
}
|
|
|
|
var userConfigPath = getUserConfigPath()
|
|
var script = "\n# Execute user config if it exists\n"
|
|
script += "if [ -f '" + userConfigPath + "' ]; then\n"
|
|
script += " # Process user templates with predefined colors\n"
|
|
script += " echo 'User templates processing not implemented for predefined colors yet'\n"
|
|
script += "fi"
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function buildPredefinedSchemeScript(content, pathEsc, color, mode) {
|
|
// Use a unique delimiter to avoid conflicts with config content
|
|
var delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
|
|
var script = "cat > '" + pathEsc + "' << '" + delimiter + "'\n" + content + "\n" + delimiter + "\n\n"
|
|
script += "matugen color hex '" + color + "' --config '" + pathEsc + "' --mode " + mode + "\n"
|
|
|
|
// Add user template execution if enabled
|
|
script += addUserTemplateExecutionForColor(color, mode)
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function addUserTemplateExecutionForColor(color, mode) {
|
|
if (!Settings.data.templates.enableUserTemplates) {
|
|
return ""
|
|
}
|
|
|
|
var userConfigPath = getUserConfigPath()
|
|
var script = "\n# Execute user config if it exists\n"
|
|
script += "if [ -f '" + userConfigPath + "' ]; then\n"
|
|
script += " matugen color hex '" + color + "' --config '" + userConfigPath + "' --mode " + mode + "\n"
|
|
script += "fi"
|
|
|
|
return script
|
|
}
|
|
|
|
// --------------------------------
|
|
function handleTerminalThemes() {
|
|
var terminals = {
|
|
"foot": "~/.config/foot/themes/noctalia",
|
|
"ghostty": "~/.config/ghostty/themes/noctalia",
|
|
"kitty": "~/.config/kitty/themes/noctalia.conf"
|
|
}
|
|
|
|
var copyCmd = Object.keys(terminals).filter(function (terminal) {
|
|
return Settings.data.templates[terminal]
|
|
}).map(function (terminal) {
|
|
var colorsPath = terminals[terminal]
|
|
var colorsPathParent = colorsPath.replace(/[^\/]*$/, "")
|
|
var terminalColorsTemplate = getTerminalColorsTemplate(terminal)
|
|
return ['mkdir -p ' + colorsPathParent, 'cp -f ' + terminalColorsTemplate + ' ' + colorsPath, colorsApplyScript + ' ' + terminal]
|
|
}).reduce(function (arr1, arr2) {
|
|
return arr1.concat(arr2)
|
|
}, []).join('; ')
|
|
|
|
if (copyCmd !== "") {
|
|
copyProcess.command = ["bash", "-lc", copyCmd]
|
|
copyProcess.running = true
|
|
}
|
|
}
|
|
|
|
// --------------------------------
|
|
function getTerminalColorsTemplate(terminal) {
|
|
var colorScheme = Settings.data.colorSchemes.predefinedScheme
|
|
const darkLight = Settings.data.colorSchemes.darkMode ? 'dark' : 'light'
|
|
|
|
// Convert display names back to folder names
|
|
var schemeMap = {
|
|
"Noctalia (default)": "Noctalia-default",
|
|
"Noctalia (legacy)": "Noctalia-legacy",
|
|
"Tokyo Night": "Tokyo-Night"
|
|
}
|
|
|
|
colorScheme = schemeMap[colorScheme] || colorScheme
|
|
var extension = terminal === 'kitty' ? ".conf" : ""
|
|
|
|
return `${Quickshell.shellDir}/Assets/ColorScheme/${colorScheme}/terminal/${terminal}/${colorScheme}-${darkLight}${extension}`
|
|
}
|
|
|
|
// --------------------------------
|
|
Process {
|
|
id: generateProcess
|
|
workingDirectory: Quickshell.shellDir
|
|
running: false
|
|
stderr: StdioCollector {
|
|
onStreamFinished: {
|
|
if (this.text !== "") {
|
|
Logger.warn("MatugenService", "GenerateProcess stderr:", this.text)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------
|
|
Process {
|
|
id: copyProcess
|
|
running: false
|
|
stderr: StdioCollector {
|
|
onStreamFinished: {
|
|
if (this.text !== "") {
|
|
Logger.warn("MatugenService", "CopyProcess stderr:", this.text)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|