package providers import ( "os" "path/filepath" "strings" "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" "gopkg.in/yaml.v3" ) type MiracleConfig struct { Terminal string `yaml:"terminal"` ActionKey string `yaml:"action_key"` DefaultActionOverrides []MiracleActionOverride `yaml:"default_action_overrides"` CustomActions []MiracleCustomAction `yaml:"custom_actions"` } type MiracleActionOverride struct { Name string `yaml:"name"` Action string `yaml:"action"` Modifiers []string `yaml:"modifiers"` Key string `yaml:"key"` } type MiracleCustomAction struct { Command string `yaml:"command"` Action string `yaml:"action"` Modifiers []string `yaml:"modifiers"` Key string `yaml:"key"` } type MiracleKeyBinding struct { Mods []string Key string Action string Comment string } var miracleDefaultBinds = []MiracleKeyBinding{ {Mods: []string{"Super"}, Key: "Return", Action: "terminal", Comment: "Open terminal"}, {Mods: []string{"Super"}, Key: "v", Action: "request_vertical", Comment: "Layout windows vertically"}, {Mods: []string{"Super"}, Key: "h", Action: "request_horizontal", Comment: "Layout windows horizontally"}, {Mods: []string{"Super"}, Key: "Up", Action: "select_up", Comment: "Select window above"}, {Mods: []string{"Super"}, Key: "Down", Action: "select_down", Comment: "Select window below"}, {Mods: []string{"Super"}, Key: "Left", Action: "select_left", Comment: "Select window left"}, {Mods: []string{"Super"}, Key: "Right", Action: "select_right", Comment: "Select window right"}, {Mods: []string{"Super", "Shift"}, Key: "Up", Action: "move_up", Comment: "Move window up"}, {Mods: []string{"Super", "Shift"}, Key: "Down", Action: "move_down", Comment: "Move window down"}, {Mods: []string{"Super", "Shift"}, Key: "Left", Action: "move_left", Comment: "Move window left"}, {Mods: []string{"Super", "Shift"}, Key: "Right", Action: "move_right", Comment: "Move window right"}, {Mods: []string{"Super"}, Key: "r", Action: "toggle_resize", Comment: "Toggle resize mode"}, {Mods: []string{"Super"}, Key: "f", Action: "fullscreen", Comment: "Toggle fullscreen"}, {Mods: []string{"Super", "Shift"}, Key: "q", Action: "quit_active_window", Comment: "Close window"}, {Mods: []string{"Super", "Shift"}, Key: "e", Action: "quit_compositor", Comment: "Exit compositor"}, {Mods: []string{"Super"}, Key: "Space", Action: "toggle_floating", Comment: "Toggle floating"}, {Mods: []string{"Super", "Shift"}, Key: "p", Action: "toggle_pinned_to_workspace", Comment: "Toggle pinned to workspace"}, {Mods: []string{"Super"}, Key: "w", Action: "toggle_tabbing", Comment: "Toggle tabbing layout"}, {Mods: []string{"Super"}, Key: "s", Action: "toggle_stacking", Comment: "Toggle stacking layout"}, {Mods: []string{"Super"}, Key: "1", Action: "select_workspace_0", Comment: "Workspace 1"}, {Mods: []string{"Super"}, Key: "2", Action: "select_workspace_1", Comment: "Workspace 2"}, {Mods: []string{"Super"}, Key: "3", Action: "select_workspace_2", Comment: "Workspace 3"}, {Mods: []string{"Super"}, Key: "4", Action: "select_workspace_3", Comment: "Workspace 4"}, {Mods: []string{"Super"}, Key: "5", Action: "select_workspace_4", Comment: "Workspace 5"}, {Mods: []string{"Super"}, Key: "6", Action: "select_workspace_5", Comment: "Workspace 6"}, {Mods: []string{"Super"}, Key: "7", Action: "select_workspace_6", Comment: "Workspace 7"}, {Mods: []string{"Super"}, Key: "8", Action: "select_workspace_7", Comment: "Workspace 8"}, {Mods: []string{"Super"}, Key: "9", Action: "select_workspace_8", Comment: "Workspace 9"}, {Mods: []string{"Super"}, Key: "0", Action: "select_workspace_9", Comment: "Workspace 10"}, {Mods: []string{"Super", "Shift"}, Key: "1", Action: "move_to_workspace_0", Comment: "Move to workspace 1"}, {Mods: []string{"Super", "Shift"}, Key: "2", Action: "move_to_workspace_1", Comment: "Move to workspace 2"}, {Mods: []string{"Super", "Shift"}, Key: "3", Action: "move_to_workspace_2", Comment: "Move to workspace 3"}, {Mods: []string{"Super", "Shift"}, Key: "4", Action: "move_to_workspace_3", Comment: "Move to workspace 4"}, {Mods: []string{"Super", "Shift"}, Key: "5", Action: "move_to_workspace_4", Comment: "Move to workspace 5"}, {Mods: []string{"Super", "Shift"}, Key: "6", Action: "move_to_workspace_5", Comment: "Move to workspace 6"}, {Mods: []string{"Super", "Shift"}, Key: "7", Action: "move_to_workspace_6", Comment: "Move to workspace 7"}, {Mods: []string{"Super", "Shift"}, Key: "8", Action: "move_to_workspace_7", Comment: "Move to workspace 8"}, {Mods: []string{"Super", "Shift"}, Key: "9", Action: "move_to_workspace_8", Comment: "Move to workspace 9"}, {Mods: []string{"Super", "Shift"}, Key: "0", Action: "move_to_workspace_9", Comment: "Move to workspace 10"}, } func ParseMiracleConfig(configPath string) (*MiracleConfig, error) { expanded, err := utils.ExpandPath(configPath) if err != nil { return nil, err } info, err := os.Stat(expanded) if err != nil { return nil, err } var configFile string if info.IsDir() { configFile = filepath.Join(expanded, "config.yaml") } else { configFile = expanded } data, err := os.ReadFile(configFile) if err != nil { return nil, err } var config MiracleConfig if err := yaml.Unmarshal(data, &config); err != nil { return nil, err } if config.ActionKey == "" { config.ActionKey = "meta" } return &config, nil } func resolveMiracleModifier(mod, actionKey string) string { switch mod { case "primary": return resolveActionKey(actionKey) case "alt", "alt_left", "alt_right": return "Alt" case "shift", "shift_left", "shift_right": return "Shift" case "ctrl", "ctrl_left", "ctrl_right": return "Ctrl" case "meta", "meta_left", "meta_right": return "Super" default: return mod } } func resolveActionKey(actionKey string) string { switch actionKey { case "meta": return "Super" case "alt": return "Alt" case "ctrl": return "Ctrl" default: return "Super" } } func miracleKeyCodeToName(keyCode string) string { name := strings.TrimPrefix(keyCode, "KEY_") name = strings.ToLower(name) switch name { case "enter": return "Return" case "space": return "Space" case "up": return "Up" case "down": return "Down" case "left": return "Left" case "right": return "Right" case "tab": return "Tab" case "escape", "esc": return "Escape" case "delete": return "Delete" case "backspace": return "BackSpace" case "home": return "Home" case "end": return "End" case "pageup": return "Page_Up" case "pagedown": return "Page_Down" case "print": return "Print" case "pause": return "Pause" case "volumeup": return "XF86AudioRaiseVolume" case "volumedown": return "XF86AudioLowerVolume" case "mute": return "XF86AudioMute" case "micmute": return "XF86AudioMicMute" case "brightnessup": return "XF86MonBrightnessUp" case "brightnessdown": return "XF86MonBrightnessDown" case "kbdillumup": return "XF86KbdBrightnessUp" case "kbdillumdown": return "XF86KbdBrightnessDown" case "comma": return "comma" case "minus": return "minus" case "equal": return "equal" } if len(name) == 1 { return name } return name } func MiracleConfigToBindings(config *MiracleConfig) []MiracleKeyBinding { overridden := make(map[string]bool) var bindings []MiracleKeyBinding for _, override := range config.DefaultActionOverrides { mods := make([]string, 0, len(override.Modifiers)) for _, mod := range override.Modifiers { mods = append(mods, resolveMiracleModifier(mod, config.ActionKey)) } bindings = append(bindings, MiracleKeyBinding{ Mods: mods, Key: miracleKeyCodeToName(override.Key), Action: override.Name, Comment: miracleActionDescription(override.Name), }) overridden[override.Name] = true } for _, def := range miracleDefaultBinds { if overridden[def.Action] { continue } bindings = append(bindings, def) } for _, custom := range config.CustomActions { mods := make([]string, 0, len(custom.Modifiers)) for _, mod := range custom.Modifiers { mods = append(mods, resolveMiracleModifier(mod, config.ActionKey)) } bindings = append(bindings, MiracleKeyBinding{ Mods: mods, Key: miracleKeyCodeToName(custom.Key), Action: custom.Command, Comment: custom.Command, }) } return bindings } func miracleActionDescription(action string) string { switch action { case "terminal": return "Open terminal" case "request_vertical": return "Layout windows vertically" case "request_horizontal": return "Layout windows horizontally" case "select_up": return "Select window above" case "select_down": return "Select window below" case "select_left": return "Select window left" case "select_right": return "Select window right" case "move_up": return "Move window up" case "move_down": return "Move window down" case "move_left": return "Move window left" case "move_right": return "Move window right" case "toggle_resize": return "Toggle resize mode" case "fullscreen": return "Toggle fullscreen" case "quit_active_window": return "Close window" case "quit_compositor": return "Exit compositor" case "toggle_floating": return "Toggle floating" case "toggle_pinned_to_workspace": return "Toggle pinned to workspace" case "toggle_tabbing": return "Toggle tabbing layout" case "toggle_stacking": return "Toggle stacking layout" case "magnifier_on": return "Enable magnifier" case "magnifier_off": return "Disable magnifier" case "magnifier_increase_size": return "Increase magnifier area" case "magnifier_decrease_size": return "Decrease magnifier area" case "magnifier_increase_scale": return "Increase magnifier scale" case "magnifier_decrease_scale": return "Decrease magnifier scale" } if num, ok := strings.CutPrefix(action, "select_workspace_"); ok { return "Workspace " + num } if num, ok := strings.CutPrefix(action, "move_to_workspace_"); ok { return "Move to workspace " + num } return action }