diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 79211cbb..f12b7e02 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -571,9 +571,9 @@ "description": "Schreibt {filepath} und lädt neu", "description-missing": "Erfordert fuzzel Starter" }, - "vesktop": { - "description": "Schreibt {filepath}", - "description-missing": "Erfordert vesktop Discord-Client" + "discord": { + "description": "Schreibt {filepath} für {client}", + "description-missing": "Kein Discord-Client erkannt. Installieren Sie vesktop, webcord, armcord, equibop, lightcord oder dorion." }, "pywalfox": { "description": "Schreibt {filepath} und führt pywalfox update aus", diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index b2c0df34..cc216d79 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -577,9 +577,9 @@ "description": "Write {filepath} and reload", "description-missing": "Requires {app} to be installed" }, - "vesktop": { - "description": "Write {filepath}", - "description-missing": "Requires {app} to be installed" + "discord": { + "description": "Write {filepath} for {client}", + "description-missing": "No Discord client detected. Install vesktop, webcord, armcord, equibop, lightcord, or dorion." }, "pywalfox": { "description": "Write {filepath} and run pywalfox update", diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 53a69f7a..eb54a954 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -571,9 +571,9 @@ "description": "Escribir {filepath} y recargar", "description-missing": "Requiere que {app} esté instalado" }, - "vesktop": { - "description": "Escribir {filepath}", - "description-missing": "Requiere que {app} esté instalado" + "discord": { + "description": "Escribir {filepath} para {client}", + "description-missing": "No se detectó cliente de Discord. Instala vesktop, webcord, armcord, equibop, lightcord o dorion." }, "pywalfox": { "description": "Escribir {filepath} y ejecutar pywalfox update", diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 2a5b07f9..96fe5ca0 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -571,9 +571,9 @@ "description": "Écrire ~/.config/fuzzel/themes/noctalia et recharger", "description-missing": "Nécessite que le lanceur fuzzel soit installé" }, - "vesktop": { - "description": "Écrire ~/.config/vesktop/themes/noctalia.theme.css", - "description-missing": "Nécessite que le client Discord vesktop soit installé" + "discord": { + "description": "Écrire {filepath} pour {client}", + "description-missing": "Aucun client Discord détecté. Installez vesktop, webcord, armcord, equibop, lightcord ou dorion." }, "pywalfox": { "description": "Écrire ~/.cache/wal/colors.json et exécuter pywalfox update", diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 528ff799..f306a45c 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -537,9 +537,9 @@ "description": "Escrever {filepath} e recarregar", "description-missing": "Requer que o {app} esteja instalado" }, - "vesktop": { - "description": "Escrever {filepath}", - "description-missing": "Requer que o {app} esteja instalado" + "discord": { + "description": "Escrever {filepath} para {client}", + "description-missing": "Nenhum cliente Discord detectado. Instale vesktop, webcord, armcord, equibop, lightcord ou dorion." }, "pywalfox": { "description": "Escrever {filepath} e executar pywalfox update", diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 23af1975..87dd56a3 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -571,9 +571,9 @@ "description": "写入 {filepath} 并重新加载", "description-missing": "需要安装 {app}" }, - "vesktop": { - "description": "写入 {filepath}", - "description-missing": "需要安装 {app}" + "discord": { + "description": "为 {client} 写入 {filepath}", + "description-missing": "未检测到 Discord 客户端。请安装 vesktop、webcord、armcord、equibop、lightcord 或 dorion。" }, "pywalfox": { "description": "写入 {filepath} 并运行 pywalfox update", diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 34e4eb59..5c830d0f 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -193,7 +193,13 @@ "ghostty": false, "foot": false, "fuzzel": false, - "vesktop": false, + "discord": false, + "discord_vesktop": false, + "discord_webcord": false, + "discord_armcord": false, + "discord_equibop": false, + "discord_lightcord": false, + "discord_dorion": false, "pywalfox": false, "enableUserTemplates": false }, diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 2f686558..09a515de 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -340,7 +340,13 @@ Singleton { property bool ghostty: false property bool foot: false property bool fuzzel: false - property bool vesktop: false + property bool discord: false + property bool discord_vesktop: false + property bool discord_webcord: false + property bool discord_armcord: false + property bool discord_equibop: false + property bool discord_lightcord: false + property bool discord_dorion: false property bool pywalfox: false property bool enableUserTemplates: false } diff --git a/Modules/Settings/Tabs/ColorSchemeTab.qml b/Modules/Settings/Tabs/ColorSchemeTab.qml index 213ff4df..1c5ab5e8 100644 --- a/Modules/Settings/Tabs/ColorSchemeTab.qml +++ b/Modules/Settings/Tabs/ColorSchemeTab.qml @@ -494,22 +494,29 @@ ColumnLayout { } } - NCheckbox { - label: "Vesktop" - description: ProgramCheckerService.vesktopAvailable ? I18n.tr("settings.color-scheme.templates.programs.vesktop.description", { - "filepath": "~/.config/vesktop/themes/noctalia.theme.css" - }) : I18n.tr("settings.color-scheme.templates.programs.vesktop.description-missing", { - "app": "vesktop" - }) - checked: Settings.data.templates.vesktop - enabled: ProgramCheckerService.vesktopAvailable - opacity: ProgramCheckerService.vesktopAvailable ? 1.0 : 0.6 - onToggled: checked => { - if (ProgramCheckerService.vesktopAvailable) { - Settings.data.templates.vesktop = checked + // Show individual checkboxes for each detected Discord client + Repeater { + model: ProgramCheckerService.availableDiscordClients + delegate: NCheckbox { + label: modelData.name.charAt(0).toUpperCase() + modelData.name.slice(1) + description: I18n.tr("settings.color-scheme.templates.programs.discord.description", { + "client": modelData.name.charAt(0).toUpperCase() + modelData.name.slice(1), + "filepath": modelData.themePath + }) + checked: Settings.data.templates["discord_" + modelData.name] || false + onToggled: checked => { + Settings.data.templates["discord_" + modelData.name] = checked AppThemeService.generate() } - } + } + } + + // Show message if no Discord clients detected + NText { + visible: ProgramCheckerService.availableDiscordClients.length === 0 + text: I18n.tr("settings.color-scheme.templates.programs.discord.description-missing") + color: Color.mOnSurfaceVariant + pointSize: Style.fontSizeS * scaling } NCheckbox { diff --git a/Services/ColorSchemeService.qml b/Services/ColorSchemeService.qml index a211b4af..19d5fc05 100644 --- a/Services/ColorSchemeService.qml +++ b/Services/ColorSchemeService.qml @@ -161,7 +161,8 @@ Singleton { // Check if any Matugen templates are enabled function hasEnabledMatugenTemplates() { - return Settings.data.templates.gtk || Settings.data.templates.qt || Settings.data.templates.kitty || Settings.data.templates.ghostty || Settings.data.templates.foot || Settings.data.templates.fuzzel || Settings.data.templates.vesktop || Settings.data.templates.pywalfox + return Settings.data.templates.gtk || Settings.data.templates.qt || Settings.data.templates.kitty || Settings.data.templates.ghostty || Settings.data.templates.foot || Settings.data.templates.fuzzel || Settings.data.templates.discord || Settings.data.templates.discord_vesktop || Settings.data.templates.discord_webcord + || Settings.data.templates.discord_armcord || Settings.data.templates.discord_equibop || Settings.data.templates.discord_lightcord || Settings.data.templates.discord_dorion || Settings.data.templates.pywalfox } // Writer to colors.json using a JsonAdapter for safety diff --git a/Services/MatugenTemplates.qml b/Services/MatugenTemplates.qml index b50450bc..935871cf 100644 --- a/Services/MatugenTemplates.qml +++ b/Services/MatugenTemplates.qml @@ -69,65 +69,127 @@ Singleton { }) } + // Applications configuration + readonly property var applications: [{ + "name": "gtk", + "templates": [{ + "version": "gtk3", + "output": "~/.config/gtk-3.0/gtk.css" + }, { + "version": "gtk4", + "output": "~/.config/gtk-4.0/gtk.css" + }], + "input": "gtk.css", + "postHook": "gsettings set org.gnome.desktop.interface color-scheme prefer-{mode}" + }, { + "name": "qt", + "templates": [{ + "version": "qt5", + "output": "~/.config/qt5ct/colors/noctalia.conf" + }, { + "version": "qt6", + "output": "~/.config/qt6ct/colors/noctalia.conf" + }], + "input": "qtct.conf" + }, { + "name": "fuzzel", + "templates": [{ + "version": "fuzzel", + "output": "~/.config/fuzzel/themes/noctalia" + }], + "input": "fuzzel.conf", + "postHook": AppThemeService.colorsApplyScript + " fuzzel" + }, { + "name": "pywalfox", + "templates": [{ + "version": "pywalfox", + "output": "~/.cache/wal/colors.json" + }], + "input": "pywalfox.json", + "postHook": AppThemeService.colorsApplyScript + " pywalfox" + }, { + "name": "discord_vesktop", + "templates": [{ + "version": "discord_vesktop", + "output": "~/.config/vesktop/themes/noctalia.theme.css" + }], + "input": "vesktop.css" + }, { + "name": "discord_webcord", + "templates": [{ + "version": "discord_webcord", + "output": "~/.config/webcord/themes/noctalia.theme.css" + }], + "input": "vesktop.css" + }, { + "name": "discord_armcord", + "templates": [{ + "version": "discord_armcord", + "output": "~/.config/armcord/themes/noctalia.theme.css" + }], + "input": "vesktop.css" + }, { + "name": "discord_equibop", + "templates": [{ + "version": "discord_equibop", + "output": "~/.config/equibop/themes/noctalia.theme.css" + }], + "input": "vesktop.css" + }, { + "name": "discord_lightcord", + "templates": [{ + "version": "discord_lightcord", + "output": "~/.config/lightcord/themes/noctalia.theme.css" + }], + "input": "vesktop.css" + }, { + "name": "discord_dorion", + "templates": [{ + "version": "discord_dorion", + "output": "~/.config/dorion/themes/noctalia.theme.css" + }], + "input": "vesktop.css" + }] + // -------------------------------- function addApplicationTemplates(lines, mode) { - var applications = [{ - "name": "gtk", - "templates": [{ - "version": "gtk3", - "output": "~/.config/gtk-3.0/gtk.css" - }, { - "version": "gtk4", - "output": "~/.config/gtk-4.0/gtk.css" - }], - "input": "gtk.css", - "postHook": "gsettings set org.gnome.desktop.interface color-scheme prefer-" + mode - }, { - "name": "qt", - "templates": [{ - "version": "qt5", - "output": "~/.config/qt5ct/colors/noctalia.conf" - }, { - "version": "qt6", - "output": "~/.config/qt6ct/colors/noctalia.conf" - }], - "input": "qtct.conf" - }, { - "name": "fuzzel", - "templates": [{ - "version": "fuzzel", - "output": "~/.config/fuzzel/themes/noctalia" - }], - "input": "fuzzel.conf", - "postHook": AppThemeService.colorsApplyScript + " fuzzel" - }, { - "name": "pywalfox", - "templates": [{ - "version": "pywalfox", - "output": "~/.cache/wal/colors.json" - }], - "input": "pywalfox.json", - "postHook": AppThemeService.colorsApplyScript + " pywalfox" - }, { - "name": "vesktop", - "templates": [{ - "version": "vesktop", - "output": "~/.config/vesktop/themes/noctalia.theme.css" - }], - "input": "vesktop.css" - }] - applications.forEach(function (app) { - if (Settings.data.templates[app.name]) { + // Check if app has a condition and if it's met + var shouldInclude = true + if (app.condition !== undefined) { + shouldInclude = app.condition + } + + if (Settings.data.templates[app.name] && shouldInclude) { app.templates.forEach(function (template) { lines.push("\n[templates." + template.version + "]") lines.push('input_path = "' + Quickshell.shellDir + '/Assets/MatugenTemplates/' + app.input + '"') lines.push('output_path = "' + template.output + '"') if (app.postHook) { - lines.push('post_hook = "' + app.postHook + '"') + var postHook = app.postHook.replace("{mode}", mode) + lines.push('post_hook = "' + postHook + '"') } }) } }) } + + // Extract Discord clients from applications array + readonly property var discordClients: { + var clients = [] + for (var i = 0; i < applications.length; i++) { + var app = applications[i] + if (app.name && app.name.startsWith("discord_")) { + var clientName = app.name.replace("discord_", "") + var themePath = app.templates[0].output + var configPath = themePath.replace("/themes/noctalia.theme.css", "") + clients.push({ + "name": clientName, + "configPath": configPath, + "themePath": themePath + }) + } + } + return clients + } } diff --git a/Services/MediaService.qml b/Services/MediaService.qml index da4b1701..006285c9 100644 --- a/Services/MediaService.qml +++ b/Services/MediaService.qml @@ -61,7 +61,8 @@ Singleton { if (title1) { for (var j = 0; j < genericPlayers.length; j++) { - if (matchedGenericIndices[j]) continue + if (matchedGenericIndices[j]) + continue let genericPlayer = genericPlayers[j] let title2 = String(genericPlayer.trackTitle || "").trim() @@ -71,27 +72,29 @@ Singleton { let scoreSpecific = (specificPlayer.trackArtUrl ? 1 : 0) let scoreGeneric = (genericPlayer.trackArtUrl ? 1 : 0) - if(scoreSpecific > scoreGeneric){ dataPlayer = specificPlayer } + if (scoreSpecific > scoreGeneric) { + dataPlayer = specificPlayer + } let virtualPlayer = { - identity: identityPlayer.identity, - desktopEntry: identityPlayer.desktopEntry, - trackTitle: dataPlayer.trackTitle, - trackArtist: dataPlayer.trackArtist, - trackAlbum: dataPlayer.trackAlbum, - trackArtUrl: dataPlayer.trackArtUrl, - length: dataPlayer.length || 0, - position: dataPlayer.position || 0, - playbackState: dataPlayer.playbackState, - isPlaying: dataPlayer.isPlaying || false, - canPlay: dataPlayer.canPlay || false, - canPause: dataPlayer.canPause || false, - canGoNext: dataPlayer.canGoNext || false, - canGoPrevious: dataPlayer.canGoPrevious || false, - canSeek: dataPlayer.canSeek || false, - canControl: dataPlayer.canControl || false, - _stateSource: dataPlayer, - _controlTarget: identityPlayer + "identity": identityPlayer.identity, + "desktopEntry": identityPlayer.desktopEntry, + "trackTitle": dataPlayer.trackTitle, + "trackArtist": dataPlayer.trackArtist, + "trackAlbum": dataPlayer.trackAlbum, + "trackArtUrl": dataPlayer.trackArtUrl, + "length": dataPlayer.length || 0, + "position": dataPlayer.position || 0, + "playbackState": dataPlayer.playbackState, + "isPlaying": dataPlayer.isPlaying || false, + "canPlay": dataPlayer.canPlay || false, + "canPause": dataPlayer.canPause || false, + "canGoNext": dataPlayer.canGoNext || false, + "canGoPrevious": dataPlayer.canGoPrevious || false, + "canSeek": dataPlayer.canSeek || false, + "canControl": dataPlayer.canControl || false, + "_stateSource": dataPlayer, + "_controlTarget": identityPlayer } finalPlayers.push(virtualPlayer) matchedGenericIndices[j] = true diff --git a/Services/ProgramCheckerService.qml b/Services/ProgramCheckerService.qml index a7d5f436..268274e3 100644 --- a/Services/ProgramCheckerService.qml +++ b/Services/ProgramCheckerService.qml @@ -16,13 +16,67 @@ Singleton { property bool ghosttyAvailable: false property bool footAvailable: false property bool fuzzelAvailable: false - property bool vesktopAvailable: false property bool gpuScreenRecorderAvailable: false property bool wlsunsetAvailable: false + // Discord client auto-detection + property var availableDiscordClients: [] + // Signal emitted when all checks are complete signal checksCompleted + // Function to detect Discord client by checking config directories + function detectDiscordClient() { + // Build list of client names from MatugenTemplates + var clientNames = [] + for (var i = 0; i < MatugenTemplates.discordClients.length; i++) { + clientNames.push(MatugenTemplates.discordClients[i].name) + } + + // Use a Process to check directory existence for all clients + discordDetector.command = ["sh", "-c", "available_clients=\"\"; " + "for client in " + clientNames.join(" ") + "; do " + " if [ -d \"$HOME/.config/$client\" ]; then " + " available_clients=\"$available_clients $client\"; " + " fi; " + "done; " + "echo \"$available_clients\""] + discordDetector.running = true + } + + // Process to detect Discord client directories + Process { + id: discordDetector + running: false + + onExited: function (exitCode) { + availableDiscordClients = [] + + if (exitCode === 0) { + var detectedClients = stdout.text.trim().split(/\s+/).filter(function (client) { + return client.length > 0 + }) + + if (detectedClients.length > 0) { + // Build list of available clients + for (var i = 0; i < detectedClients.length; i++) { + var clientName = detectedClients[i] + for (var j = 0; j < MatugenTemplates.discordClients.length; j++) { + var client = MatugenTemplates.discordClients[j] + if (client.name === clientName) { + availableDiscordClients.push(client) + break + } + } + } + + Logger.log("ProgramChecker", "Detected Discord clients:", detectedClients.join(", ")) + } + } + + if (availableDiscordClients.length === 0) { + Logger.log("ProgramChecker", "No Discord clients detected") + } + } + + stdout: StdioCollector {} + stderr: StdioCollector {} + } + // Programs to check - maps property names to commands readonly property var programsToCheck: ({ "matugenAvailable": ["which", "matugen"], @@ -31,7 +85,6 @@ Singleton { "ghosttyAvailable": ["which", "ghostty"], "footAvailable": ["which", "foot"], "fuzzelAvailable": ["which", "fuzzel"], - "vesktopAvailable": ["which", "vesktop"], "gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"], "wlsunsetAvailable": ["which", "wlsunset"] }) @@ -59,6 +112,8 @@ Singleton { // Check next program or emit completion signal if (root.completedChecks >= root.totalChecks) { + // Run Discord client detection after all checks are complete + root.detectDiscordClient() root.checksCompleted() } else { root.checkNextProgram() @@ -113,6 +168,21 @@ Singleton { checker.running = true } + // Manual function to test Discord detection (for debugging) + function testDiscordDetection() { + Logger.log("ProgramChecker", "Testing Discord detection...") + Logger.log("ProgramChecker", "HOME:", Quickshell.env("HOME")) + + // Test each client directory + for (var i = 0; i < MatugenTemplates.discordClients.length; i++) { + var client = MatugenTemplates.discordClients[i] + var configDir = client.configPath.replace("~", Quickshell.env("HOME")) + Logger.log("ProgramChecker", "Checking:", configDir) + } + + detectDiscordClient() + } + // Initialize checks when service is created Component.onCompleted: { checkAllPrograms()