feat(signals): add ruleAdd action and UI wiring for no-code rule creation
This commit is contained in:
parent
7694d965c9
commit
935c662e1d
@ -361,6 +361,7 @@ struct AddActionSheet: View {
|
|||||||
case .windowMoveToSpace: return "square.grid.2x2"
|
case .windowMoveToSpace: return "square.grid.2x2"
|
||||||
case .windowMoveToDisplay: return "display"
|
case .windowMoveToDisplay: return "display"
|
||||||
case .configWindowOpacity, .configWindowOpacityActive, .configWindowOpacityNormal: return "circle.opacity"
|
case .configWindowOpacity, .configWindowOpacityActive, .configWindowOpacityNormal: return "circle.opacity"
|
||||||
|
case .ruleAdd: return "list.bullet.rectangle"
|
||||||
case .shellCommand: return "terminal"
|
case .shellCommand: return "terminal"
|
||||||
default: return "gear"
|
default: return "gear"
|
||||||
}
|
}
|
||||||
@ -374,6 +375,7 @@ struct AddActionSheet: View {
|
|||||||
case .windowToggleFullscreen: return .green
|
case .windowToggleFullscreen: return .green
|
||||||
case .windowMoveAbsolute, .windowMoveRelative, .windowResizeAbsolute, .windowResizeRelative: return .orange
|
case .windowMoveAbsolute, .windowMoveRelative, .windowResizeAbsolute, .windowResizeRelative: return .orange
|
||||||
case .configWindowOpacity, .configWindowOpacityActive, .configWindowOpacityNormal: return .pink
|
case .configWindowOpacity, .configWindowOpacityActive, .configWindowOpacityNormal: return .pink
|
||||||
|
case .ruleAdd: return .teal
|
||||||
case .shellCommand: return .gray
|
case .shellCommand: return .gray
|
||||||
default: return .secondary
|
default: return .secondary
|
||||||
}
|
}
|
||||||
@ -434,6 +436,7 @@ extension SignalAction.YabaiCommand {
|
|||||||
case .configWindowShadow: return "Set Window Shadow"
|
case .configWindowShadow: return "Set Window Shadow"
|
||||||
case .configMouseFollowsFocus: return "Set Mouse Follows Focus"
|
case .configMouseFollowsFocus: return "Set Mouse Follows Focus"
|
||||||
case .configFocusFollowsMouse: return "Set Focus Follows Mouse"
|
case .configFocusFollowsMouse: return "Set Focus Follows Mouse"
|
||||||
|
case .ruleAdd: return "Add Yabai Rule"
|
||||||
case .shellCommand: return "Run Shell Command"
|
case .shellCommand: return "Run Shell Command"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,6 +75,8 @@ struct SignalAction: Codable, Identifiable, Equatable {
|
|||||||
|
|
||||||
// Custom shell command
|
// Custom shell command
|
||||||
case shellCommand = "shell_command"
|
case shellCommand = "shell_command"
|
||||||
|
// Rule creation (add a yabai rule)
|
||||||
|
case ruleAdd = "rule_add"
|
||||||
}
|
}
|
||||||
|
|
||||||
var displayName: String {
|
var displayName: String {
|
||||||
@ -130,6 +132,7 @@ struct SignalAction: Codable, Identifiable, Equatable {
|
|||||||
case .configMouseFollowsFocus: return "Set Mouse Follows Focus"
|
case .configMouseFollowsFocus: return "Set Mouse Follows Focus"
|
||||||
case .configFocusFollowsMouse: return "Set Focus Follows Mouse"
|
case .configFocusFollowsMouse: return "Set Focus Follows Mouse"
|
||||||
case .shellCommand: return "Run Shell Command"
|
case .shellCommand: return "Run Shell Command"
|
||||||
|
case .ruleAdd: return "Add Yabai Rule"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,6 +313,36 @@ struct SignalAction: Codable, Identifiable, Equatable {
|
|||||||
|
|
||||||
case .shellCommand:
|
case .shellCommand:
|
||||||
return parameters["command"] ?? ""
|
return parameters["command"] ?? ""
|
||||||
|
case .ruleAdd:
|
||||||
|
// Build a yabai rule add command from provided parameters
|
||||||
|
var parts: [String] = ["yabai", "-m", "rule", "--add"]
|
||||||
|
|
||||||
|
func addArg(_ key: String, _ fmt: ((String) -> String)? = nil) {
|
||||||
|
if let val = parameters[key], !val.isEmpty {
|
||||||
|
let v = fmt?(val) ?? val
|
||||||
|
parts.append("\(key)=\(v)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// app/title/role/subrole - wrap in quotes if they contain spaces
|
||||||
|
addArg("app", { v in "\"\(v.replacingOccurrences(of: "\"", with: "\\\""))\"" })
|
||||||
|
addArg("title", { v in "\"\(v.replacingOccurrences(of: "\"", with: "\\\""))\"" })
|
||||||
|
addArg("role", nil)
|
||||||
|
addArg("subrole", nil)
|
||||||
|
|
||||||
|
// Boolean flags: manage, sticky, mouse_follows_focus -> on/off
|
||||||
|
addArg("manage", nil)
|
||||||
|
addArg("sticky", nil)
|
||||||
|
addArg("mouse_follows_focus", nil)
|
||||||
|
|
||||||
|
// Opacity / sub-layer / label
|
||||||
|
if let opacity = parameters["opacity"], !opacity.isEmpty {
|
||||||
|
parts.append("opacity=\(opacity)")
|
||||||
|
}
|
||||||
|
addArg("sub-layer", nil)
|
||||||
|
addArg("label", { v in "\"\(v.replacingOccurrences(of: "\"", with: "\\\""))\"" })
|
||||||
|
|
||||||
|
return parts.joined(separator: " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return "# Invalid command configuration"
|
return "# Invalid command configuration"
|
||||||
@ -443,6 +476,20 @@ struct CommandSchema {
|
|||||||
ParameterSchema(key: "opacity", label: "Opacity", type: .slider(min: 0.0, max: 1.0, step: 0.1), required: true, defaultValue: "1.0")
|
ParameterSchema(key: "opacity", label: "Opacity", type: .slider(min: 0.0, max: 1.0, step: 0.1), required: true, defaultValue: "1.0")
|
||||||
])
|
])
|
||||||
|
|
||||||
|
case .ruleAdd:
|
||||||
|
return CommandSchema(command: command, parameters: [
|
||||||
|
ParameterSchema(key: "app", label: "Application (bundle/display name)", type: .text, required: false, defaultValue: ""),
|
||||||
|
ParameterSchema(key: "title", label: "Window Title (optional)", type: .text, required: false, defaultValue: ""),
|
||||||
|
ParameterSchema(key: "role", label: "Role", type: .picker, required: false, defaultValue: "", options: ["AXWindow","AXDialog","AXSheet"]),
|
||||||
|
ParameterSchema(key: "subrole", label: "Subrole", type: .picker, required: false, defaultValue: "", options: ["AXStandardWindow","AXDialog","AXSystemDialog"]),
|
||||||
|
ParameterSchema(key: "manage", label: "Manage Window", type: .picker, required: false, defaultValue: "on", options: ["on","off"]),
|
||||||
|
ParameterSchema(key: "sticky", label: "Sticky", type: .picker, required: false, defaultValue: "off", options: ["on","off"]),
|
||||||
|
ParameterSchema(key: "mouse_follows_focus", label: "Mouse Follows Focus", type: .picker, required: false, defaultValue: "off", options: ["on","off"]),
|
||||||
|
ParameterSchema(key: "opacity", label: "Opacity (0.0-1.0)", type: .slider(min: 0.0, max: 1.0, step: 0.1), required: false, defaultValue: "1.0"),
|
||||||
|
ParameterSchema(key: "sub-layer", label: "Sub Layer", type: .picker, required: false, defaultValue: "normal", options: ["below","normal","above"]),
|
||||||
|
ParameterSchema(key: "label", label: "Label (optional)", type: .text, required: false, defaultValue: "")
|
||||||
|
])
|
||||||
|
|
||||||
case .shellCommand:
|
case .shellCommand:
|
||||||
return CommandSchema(command: command, parameters: [
|
return CommandSchema(command: command, parameters: [
|
||||||
ParameterSchema(key: "command", label: "Shell Command", type: .text, required: true, defaultValue: "")
|
ParameterSchema(key: "command", label: "Shell Command", type: .text, required: true, defaultValue: "")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user