mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
@@ -514,5 +514,6 @@ func getCommonCommands() []*cobra.Command {
|
|||||||
matugenCmd,
|
matugenCmd,
|
||||||
clipboardCmd,
|
clipboardCmd,
|
||||||
doctorCmd,
|
doctorCmd,
|
||||||
|
configCmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
318
core/cmd/dms/commands_config.go
Normal file
318
core/cmd/dms/commands_config.go
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||||
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var configCmd = &cobra.Command{
|
||||||
|
Use: "config",
|
||||||
|
Short: "Configuration utilities",
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolveIncludeCmd = &cobra.Command{
|
||||||
|
Use: "resolve-include <compositor> <filename>",
|
||||||
|
Short: "Check if a file is included in compositor config",
|
||||||
|
Long: "Recursively check if a file is included/sourced in compositor configuration. Returns JSON with exists and included status.",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
return []string{"hyprland", "niri", "mangowc"}, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
case 1:
|
||||||
|
return []string{"cursor.kdl", "cursor.conf", "outputs.kdl", "outputs.conf", "binds.kdl", "binds.conf"}, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
},
|
||||||
|
Run: runResolveInclude,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
configCmd.AddCommand(resolveIncludeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IncludeResult struct {
|
||||||
|
Exists bool `json:"exists"`
|
||||||
|
Included bool `json:"included"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func runResolveInclude(cmd *cobra.Command, args []string) {
|
||||||
|
compositor := strings.ToLower(args[0])
|
||||||
|
filename := args[1]
|
||||||
|
|
||||||
|
var result IncludeResult
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch compositor {
|
||||||
|
case "hyprland":
|
||||||
|
result, err = checkHyprlandInclude(filename)
|
||||||
|
case "niri":
|
||||||
|
result, err = checkNiriInclude(filename)
|
||||||
|
case "mangowc", "dwl", "mango":
|
||||||
|
result, err = checkMangoWCInclude(filename)
|
||||||
|
default:
|
||||||
|
log.Fatalf("Unknown compositor: %s", compositor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error checking include: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, _ := json.Marshal(result)
|
||||||
|
fmt.Fprintln(os.Stdout, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHyprlandInclude(filename string) (IncludeResult, error) {
|
||||||
|
configDir, err := utils.ExpandPath("$HOME/.config/hypr")
|
||||||
|
if err != nil {
|
||||||
|
return IncludeResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPath := filepath.Join(configDir, "dms", filename)
|
||||||
|
result := IncludeResult{}
|
||||||
|
|
||||||
|
if _, err := os.Stat(targetPath); err == nil {
|
||||||
|
result.Exists = true
|
||||||
|
}
|
||||||
|
|
||||||
|
mainConfig := filepath.Join(configDir, "hyprland.conf")
|
||||||
|
if _, err := os.Stat(mainConfig); os.IsNotExist(err) {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
processed := make(map[string]bool)
|
||||||
|
result.Included = hyprlandFindInclude(mainConfig, "dms/"+filename, processed)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hyprlandFindInclude(filePath, target string, processed map[string]bool) bool {
|
||||||
|
absPath, err := filepath.Abs(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if processed[absPath] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
processed[absPath] = true
|
||||||
|
|
||||||
|
data, err := os.ReadFile(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDir := filepath.Dir(absPath)
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(trimmed, "#") || trimmed == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(trimmed, "source") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(trimmed, "=", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sourcePath := strings.TrimSpace(parts[1])
|
||||||
|
if matchesTarget(sourcePath, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := sourcePath
|
||||||
|
if !filepath.IsAbs(sourcePath) {
|
||||||
|
fullPath = filepath.Join(baseDir, sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
expanded, err := utils.ExpandPath(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if hyprlandFindInclude(expanded, target, processed) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNiriInclude(filename string) (IncludeResult, error) {
|
||||||
|
configDir, err := utils.ExpandPath("$HOME/.config/niri")
|
||||||
|
if err != nil {
|
||||||
|
return IncludeResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPath := filepath.Join(configDir, "dms", filename)
|
||||||
|
result := IncludeResult{}
|
||||||
|
|
||||||
|
if _, err := os.Stat(targetPath); err == nil {
|
||||||
|
result.Exists = true
|
||||||
|
}
|
||||||
|
|
||||||
|
mainConfig := filepath.Join(configDir, "config.kdl")
|
||||||
|
if _, err := os.Stat(mainConfig); os.IsNotExist(err) {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
processed := make(map[string]bool)
|
||||||
|
result.Included = niriFindInclude(mainConfig, "dms/"+filename, processed)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func niriFindInclude(filePath, target string, processed map[string]bool) bool {
|
||||||
|
absPath, err := filepath.Abs(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if processed[absPath] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
processed[absPath] = true
|
||||||
|
|
||||||
|
data, err := os.ReadFile(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDir := filepath.Dir(absPath)
|
||||||
|
content := string(data)
|
||||||
|
|
||||||
|
for _, line := range strings.Split(content, "\n") {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(trimmed, "//") || trimmed == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(trimmed, "include") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
startQuote := strings.Index(trimmed, "\"")
|
||||||
|
if startQuote == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
endQuote := strings.LastIndex(trimmed, "\"")
|
||||||
|
if endQuote <= startQuote {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
includePath := trimmed[startQuote+1 : endQuote]
|
||||||
|
if matchesTarget(includePath, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := includePath
|
||||||
|
if !filepath.IsAbs(includePath) {
|
||||||
|
fullPath = filepath.Join(baseDir, includePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if niriFindInclude(fullPath, target, processed) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkMangoWCInclude(filename string) (IncludeResult, error) {
|
||||||
|
configDir, err := utils.ExpandPath("$HOME/.config/mango")
|
||||||
|
if err != nil {
|
||||||
|
return IncludeResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPath := filepath.Join(configDir, "dms", filename)
|
||||||
|
result := IncludeResult{}
|
||||||
|
|
||||||
|
if _, err := os.Stat(targetPath); err == nil {
|
||||||
|
result.Exists = true
|
||||||
|
}
|
||||||
|
|
||||||
|
mainConfig := filepath.Join(configDir, "config.conf")
|
||||||
|
if _, err := os.Stat(mainConfig); os.IsNotExist(err) {
|
||||||
|
mainConfig = filepath.Join(configDir, "mango.conf")
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(mainConfig); os.IsNotExist(err) {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
processed := make(map[string]bool)
|
||||||
|
result.Included = mangowcFindInclude(mainConfig, "dms/"+filename, processed)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mangowcFindInclude(filePath, target string, processed map[string]bool) bool {
|
||||||
|
absPath, err := filepath.Abs(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if processed[absPath] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
processed[absPath] = true
|
||||||
|
|
||||||
|
data, err := os.ReadFile(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDir := filepath.Dir(absPath)
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(trimmed, "#") || trimmed == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(trimmed, "source") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(trimmed, "=", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sourcePath := strings.TrimSpace(parts[1])
|
||||||
|
if matchesTarget(sourcePath, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := sourcePath
|
||||||
|
if !filepath.IsAbs(sourcePath) {
|
||||||
|
fullPath = filepath.Join(baseDir, sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
expanded, err := utils.ExpandPath(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if mangowcFindInclude(expanded, target, processed) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesTarget(path, target string) bool {
|
||||||
|
path = strings.TrimPrefix(path, "./")
|
||||||
|
target = strings.TrimPrefix(target, "./")
|
||||||
|
return path == target || strings.HasSuffix(path, "/"+target)
|
||||||
|
}
|
||||||
@@ -401,8 +401,8 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkIncludeStatus() {
|
function checkIncludeStatus() {
|
||||||
const paths = getConfigPaths();
|
const compositor = CompositorService.compositor;
|
||||||
if (!paths) {
|
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "dwl") {
|
||||||
includeStatus = {
|
includeStatus = {
|
||||||
"exists": false,
|
"exists": false,
|
||||||
"included": false
|
"included": false
|
||||||
@@ -410,14 +410,27 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filename = (compositor === "niri") ? "outputs.kdl" : "outputs.conf";
|
||||||
|
const compositorArg = (compositor === "dwl") ? "mangowc" : compositor;
|
||||||
|
|
||||||
checkingInclude = true;
|
checkingInclude = true;
|
||||||
Proc.runCommand("check-outputs-include", ["sh", "-c", `exists=false; included=false; ` + `[ -f "${paths.outputsFile}" ] && exists=true; ` + `[ -f "${paths.configFile}" ] && grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" | grep -q '${paths.grepPattern}' && included=true; ` + `echo "$exists $included"`], (output, exitCode) => {
|
Proc.runCommand("check-outputs-include", ["dms", "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
|
||||||
checkingInclude = false;
|
checkingInclude = false;
|
||||||
const parts = output.trim().split(" ");
|
if (exitCode !== 0) {
|
||||||
includeStatus = {
|
includeStatus = {
|
||||||
"exists": parts[0] === "true",
|
"exists": false,
|
||||||
"included": parts[1] === "true"
|
"included": false
|
||||||
};
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
includeStatus = JSON.parse(output.trim());
|
||||||
|
} catch (e) {
|
||||||
|
includeStatus = {
|
||||||
|
"exists": false,
|
||||||
|
"included": false
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,22 +55,36 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkCursorIncludeStatus() {
|
function checkCursorIncludeStatus() {
|
||||||
const paths = getCursorConfigPaths();
|
const compositor = CompositorService.compositor;
|
||||||
if (!paths) {
|
if (compositor !== "niri" && compositor !== "hyprland" && compositor !== "dwl") {
|
||||||
cursorIncludeStatus = {
|
cursorIncludeStatus = {
|
||||||
"exists": false,
|
"exists": false,
|
||||||
"included": false
|
"included": false
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filename = (compositor === "niri") ? "cursor.kdl" : "cursor.conf";
|
||||||
|
const compositorArg = (compositor === "dwl") ? "mangowc" : compositor;
|
||||||
|
|
||||||
checkingCursorInclude = true;
|
checkingCursorInclude = true;
|
||||||
Proc.runCommand("check-cursor-include", ["sh", "-c", `exists=false; included=false; ` + `[ -f "${paths.cursorFile}" ] && exists=true; ` + `[ -f "${paths.configFile}" ] && grep -v '^[[:space:]]*\\(//\\|#\\)' "${paths.configFile}" | grep -q '${paths.grepPattern}' && included=true; ` + `echo "$exists $included"`], (output, exitCode) => {
|
Proc.runCommand("check-cursor-include", ["dms", "config", "resolve-include", compositorArg, filename], (output, exitCode) => {
|
||||||
checkingCursorInclude = false;
|
checkingCursorInclude = false;
|
||||||
const parts = output.trim().split(" ");
|
if (exitCode !== 0) {
|
||||||
cursorIncludeStatus = {
|
cursorIncludeStatus = {
|
||||||
"exists": parts[0] === "true",
|
"exists": false,
|
||||||
"included": parts[1] === "true"
|
"included": false
|
||||||
};
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cursorIncludeStatus = JSON.parse(output.trim());
|
||||||
|
} catch (e) {
|
||||||
|
cursorIncludeStatus = {
|
||||||
|
"exists": false,
|
||||||
|
"included": false
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user