Matugen/Code: add VSCodium support

This commit is contained in:
Ly-sec
2025-11-14 07:40:15 +01:00
parent 04fcb94ebc
commit 96ae2c0d6f
14 changed files with 206 additions and 23 deletions
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Schreibe {Dateipfad}. Das Hyprluna-Theme muss manuell installiert und aktiviert werden",
"description-missing": "Benötigt die Installation von {app}"
"description-missing": "Kein Code-Client erkannt. Installieren Sie VSCode oder VSCodium."
},
"description": "Anwendungsspezifisches Theming.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Write {filepath}. Hyprluna theme needs to be installed and activated manually.",
"description-missing": "Requires {app} to be installed"
"description-missing": "No Code client detected. Install VSCode or VSCodium."
},
"description": "Application-specific theming.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Escribe {filepath}. El tema Hyprluna debe ser instalado y activado manualmente.",
"description-missing": "Requiere que {app} esté instalado/a."
"description-missing": "No se detectó cliente de Code. Instala VSCode o VSCodium."
},
"description": "Tematización específica de aplicaciones.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Écrire {filepath}. Le thème Hyprluna doit être installé et activé manuellement.",
"description-missing": "Nécessite l'installation de {app}"
"description-missing": "Aucun client Code détecté. Installez VSCode ou VSCodium."
},
"description": "Thématisation spécifique aux applications.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Schrijf {filepath}. Het Hyprluna-thema moet handmatig worden geïnstalleerd en geactiveerd.",
"description-missing": "Vereist dat {app} is geïnstalleerd."
"description-missing": "Geen Code-client gedetecteerd. Installeer VSCode of VSCodium."
},
"description": "Toepassingsspecifieke theming.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Escreva em {filepath}. O tema Hyprluna precisa ser instalado e ativado manualmente.",
"description-missing": "Requer que o {app} esteja instalado."
"description-missing": "Nenhum cliente Code detectado. Instale VSCode ou VSCodium."
},
"description": "Tematização específica de aplicativos.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Записать {filepath}. Тему Hyprluna нужно установить и активировать вручную.",
"description-missing": "Требуется установка {app}"
"description-missing": "Клиент Code не обнаружен. Установите VSCode или VSCodium."
},
"description": "Тематика для конкретных приложений.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "{filepath} dosyasına yaz. Hyprluna temasının kurulu ve manuel olarak etkinleştirilmiş olması gerekir.",
"description-missing": "Kurulum için {app} gereklidir"
"description-missing": "Code istemcisi tespit edilmedi. VSCode veya VSCodium kurun."
},
"description": "Uygulamaya özel temalandırma.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "Записати {filepath}. Тему Hyprluna потрібно встановити та активувати вручну.",
"description-missing": "Потрібна установка {app}"
"description-missing": "Клієнт Code не виявлено. Встановіть VSCode або VSCodium."
},
"description": "Оформлення окремих програм.",
"discord": {
+1 -1
View File
@@ -880,7 +880,7 @@
"programs": {
"code": {
"description": "写入 {filepath}。Hyprluna 主题需要手动安装和激活。",
"description-missing": "需要安装 {app}"
"description-missing": "未检测到 Code 客户端。请安装 VSCode 或 VSCodium。"
},
"description": "应用程序特定主题。",
"discord": {
+39 -10
View File
@@ -740,19 +740,48 @@ ColumnLayout {
}
}
// Code clients - single toggle with dynamic description
NCheckbox {
id: codeToggle
label: "Code"
description: ProgramCheckerService.codeAvailable ? I18n.tr("settings.color-scheme.templates.programs.code.description", {
"filepath": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}) : I18n.tr("settings.color-scheme.templates.programs.code.description-missing", {
"app": "code"
})
checked: Settings.data.templates.code
enabled: ProgramCheckerService.codeAvailable
opacity: ProgramCheckerService.codeAvailable ? 1.0 : 0.6
description: {
if (ProgramCheckerService.availableCodeClients.length === 0) {
return I18n.tr("settings.color-scheme.templates.programs.code.description-missing")
} else {
// Show detected clients
var clientInfo = []
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
var client = ProgramCheckerService.availableCodeClients[i]
// Capitalize first letter and format nicely
var clientName = client.name === "code" ? "VSCode" : "VSCodium"
clientInfo.push(clientName)
}
return "Detected: " + clientInfo.join(", ")
}
}
Layout.fillWidth: true
Layout.preferredWidth: -1
checked: {
// Check if any Code client template is enabled
var anyEnabled = false
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
var client = ProgramCheckerService.availableCodeClients[i]
if (Settings.data.templates["code_" + client.name]) {
anyEnabled = true
break
}
}
return anyEnabled
}
enabled: ProgramCheckerService.availableCodeClients.length > 0
opacity: ProgramCheckerService.availableCodeClients.length > 0 ? 1.0 : 0.6
onToggled: checked => {
if (ProgramCheckerService.codeAvailable) {
Settings.data.templates.code = checked
// Enable/disable all detected Code clients
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
var client = ProgramCheckerService.availableCodeClients[i]
Settings.data.templates["code_" + client.name] = checked
}
if (ProgramCheckerService.availableCodeClients.length > 0) {
AppThemeService.generate()
}
}
+65 -1
View File
@@ -31,6 +31,9 @@ Singleton {
// Discord client auto-detection
property var availableDiscordClients: []
// Code client auto-detection
property var availableCodeClients: []
// Signal emitted when all checks are complete
signal checksCompleted
@@ -97,6 +100,66 @@ Singleton {
stderr: StdioCollector {}
}
// Function to detect Code client by checking config directories
function detectCodeClient() {
// Build shell script to check each client
var scriptParts = ["available_clients=\"\";"]
for (var i = 0; i < TemplateRegistry.codeClients.length; i++) {
var client = TemplateRegistry.codeClients[i]
var clientName = client.name
var configPath = client.configPath
// Check if the config directory exists
scriptParts.push("if [ -d \"$HOME" + configPath.substring(1) + "\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;")
}
scriptParts.push("echo \"$available_clients\"")
// Use a Process to check directory existence for all clients
codeDetector.command = ["sh", "-c", scriptParts.join(" ")]
codeDetector.running = true
}
// Process to detect Code client directories
Process {
id: codeDetector
running: false
onExited: function (exitCode) {
availableCodeClients = []
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 < TemplateRegistry.codeClients.length; j++) {
var client = TemplateRegistry.codeClients[j]
if (client.name === clientName) {
availableCodeClients.push(client)
break
}
}
}
Logger.i("ProgramChecker", "Detected Code clients:", detectedClients.join(", "))
}
}
if (availableCodeClients.length === 0) {
Logger.d("ProgramChecker", "No Code clients detected")
}
}
stdout: StdioCollector {}
stderr: StdioCollector {}
}
// Programs to check - maps property names to commands
readonly property var programsToCheck: ({
"matugenAvailable": ["which", "matugen"],
@@ -140,8 +203,9 @@ Singleton {
// Check next program or emit completion signal
if (root.completedChecks >= root.totalChecks) {
// Run Discord client detection after all checks are complete
// Run Discord and Code client detection after all checks are complete
root.detectDiscordClient()
root.detectCodeClient()
root.checksCompleted()
} else {
root.checkNextProgram()
+59
View File
@@ -114,6 +114,18 @@ Singleton {
}
})
}
} else if (app.id === "code") {
// Handle Code clients specially
if (Settings.data.templates.code) {
app.clients.forEach(client => {
// Check if this specific client is detected
if (isCodeClientEnabled(client.name)) {
lines.push(`\n[templates.code_${client.name}]`)
lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}"`)
lines.push(`output_path = "${client.path}"`)
}
})
}
} else {
// Handle regular apps
if (Settings.data.templates[app.id]) {
@@ -140,6 +152,16 @@ Singleton {
return false
}
function isCodeClientEnabled(clientName) {
// Check ProgramCheckerService to see if client is detected
for (var i = 0; i < ProgramCheckerService.availableCodeClients.length; i++) {
if (ProgramCheckerService.availableCodeClients[i].name === clientName) {
return true
}
}
return false
}
function buildMatugenScript(content, wallpaper, mode) {
const delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
const pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")
@@ -163,6 +185,10 @@ Singleton {
if (Settings.data.templates.discord) {
script += processDiscordClients(app, colors, mode, homeDir)
}
} else if (app.id === "code") {
if (Settings.data.templates.code) {
script += processCodeClients(app, colors, mode, homeDir)
}
} else {
if (Settings.data.templates[app.id]) {
script += processTemplate(app, colors, mode, homeDir)
@@ -198,6 +224,39 @@ Singleton {
return script
}
function processCodeClients(codeApp, colors, mode, homeDir) {
let script = ""
const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, false)
codeApp.clients.forEach(client => {
if (!isCodeClientEnabled(client.name))
return
const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${codeApp.input}`
const outputPath = client.path.replace("~", homeDir)
const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
// Extract base config directory for checking
var baseConfigDir = ""
if (client.name === "code") {
baseConfigDir = "~/.vscode".replace("~", homeDir)
} else if (client.name === "codium") {
baseConfigDir = "~/.vscode-oss".replace("~", homeDir)
}
script += `\n`
script += `if [ -d "${baseConfigDir}" ]; then\n`
script += ` mkdir -p ${outputDir}\n`
script += ` cp '${templatePath}' '${outputPath}'\n`
script += ` ${replaceColorsInFile(outputPath, palette)}`
script += `else\n`
script += ` echo "Code client ${client.name} not found at ${baseConfigDir}, skipping"\n`
script += `fi\n`
})
return script
}
function processTemplate(app, colors, mode, homeDir) {
const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, app.strict || false)
let script = ""
+33 -2
View File
@@ -142,14 +142,18 @@ Singleton {
"path": "~/.config/discord",
"requiresThemesFolder": true
}]
}, // VSCode with hardcoded path (requirement #5)
},
{
"id": "code",
"name": "VSCode",
"category": "applications",
"input": "code.json",
"outputs": [{
"clients": [{
"name": "code",
"path": "~/.vscode/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}, {
"name": "codium",
"path": "~/.vscode-oss/extensions/hyprluna.hyprluna-theme-1.0.2/themes/hyprluna.json"
}]
}, {
"id": "spicetify",
@@ -179,6 +183,33 @@ Singleton {
return clients
}
// Extract Code clients for ProgramCheckerService compatibility
readonly property var codeClients: {
var clients = []
var codeApp = applications.find(app => app.id === "code")
if (codeApp && codeApp.clients) {
codeApp.clients.forEach(client => {
// Extract base config directory from theme path
var themePath = client.path
var baseConfigDir = ""
if (client.name === "code") {
// For VSCode: ~/.vscode/extensions/... -> ~/.vscode
baseConfigDir = "~/.vscode"
} else if (client.name === "codium") {
// For VSCodium: ~/.vscode-oss/extensions/... -> ~/.vscode-oss
baseConfigDir = "~/.vscode-oss"
}
clients.push({
"name": client.name,
"configPath": baseConfigDir,
"themePath": themePath,
"requiresThemesFolder": false
})
})
}
return clients
}
// Build user templates TOML content
function buildUserTemplatesToml() {
var lines = []