Karabiner-Elements
Karabiner-Elements copied to clipboard
Allow customizable and extendable complex modification settings
Issue
Writing complex modification rules for Karabiner is an all but manual process. There are external GUIs out there that format the JSON for you, but they don't have autocomplete, nor do they work for specific, environment-specific conditions, variables, etc. As such, they offer no benefit other than formatting JSON.
Furthermore, as stated here and reiterated here, Karabiner is currently very limited in that it ignores rules that are defined after other rules even if they technically have different conditions.
These issues include, but are not limited to:
Less specific rules overriding more specific rules
I can't write the below config and have both rules activated because the first overrides the second. This doesn't appear terrible at first until you have many rules, either within one file or in separate files, and they end up not working as expected because an earlier rule conflicts with a later rule.
Then you're forced to rewrite all previous rules to include condition_unless
or to dissect how all imported rules work to find the issue all BY HAND.
Example: Fails without `frontmost_application_unless` in first rule
{
"title": "Ctrl --> Command except in IDE, but I don't work!",
"rules": [
{
"description": "Ctrl --> Command",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "left_control",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_command"
}
]
},
{
"type": "basic",
"from": {
"key_code": "left_control",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_control"
}
],
"conditions": [
{
"type": "frontmost_application_if",
"bundle_identifiers": [
"com.jetbrains.*"
]
}
]
}
]
}
]
}
Related rules/manipulators all require possibly infinite amounts of duplicated code
If there exists a rule with many related manipulators, I have to duplicate the conditions for that rule in every single entry, resulting in a config that is both difficult to read and less configurable. Then, If I decide I want to add a new application in frontmost_application_if
, I have to do it multiple times even though it's all for the same rule.
Example: Fails without `device_if` in every rule/manipulator
{
"title": "Make Mac keys work like PC (Linux/Windows) on external keyboard",
"rules": [
{
"description": "Ctrl/Command to behave like PC (PC: Ctrl=Ctrl, Super=Command, Alt=Option)",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "left_control",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_command"
}
],
"conditions": [
"Oh no, I forgot to add device_if here :'c ",
"Ugh, after hours of debugging, finally tracked down that bug.",
"[some time later]",
"Wait, now I want to add a new external keyboard!",
"Looks like I have to edit every single rule I have D:< "
]
},
{
"type": "basic",
"from": {
"key_code": "left_command",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_option"
}
],
"conditions": [
{
"type": "device_if",
"identifiers": [
{
"vendor_id": 1234
}
]
}
]
},
{
"type": "basic",
"from": {
"key_code": "left_option",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_control"
}
],
"conditions": [
{
"type": "device_if",
"identifiers": [
{
"vendor_id": 1234
}
]
}
]
}
]
}
]
}
TL;DR
The current rule format has many issues and needs revisiting. Changing them would solve some of the most common issues users experience.
{
"title": "My key combo with lots of duplicated code that isn't user friendly",
"rules": [
{
"description": "I'm not following the DRY principle :(",
"manipulators": [
{
"type": "basic",
"from": {},
"to": [],
"conditions": [
"duplicate_condition_objects",
"e.g. frontmost_application_unless"
]
},
{
"type": "basic",
"from": {},
"to": [],
"conditions": [
"duplicate_condition_objects",
"e.g. frontmost_application_unless"
]
},
{
"type": "basic",
"from": {},
"to": [],
"conditions": [
"man, so much repeated code just so I can use",
"e.g. frontmost_application_if",
"And those are required b/c I can't be above them",
"due to a dependency, other import, or something else"
]
}
]
}
]
}
Yes, some of the examples (especially the jetbrains
one) above could be solved by just reordering them, but that was just a minimally-reproducible example. In a real life scenario that's not always possible depending on what rules a user imported or certain rule dependencies (or at least not reasonable, even if possible, because you'd have to e.g. dissect rules downloaded from the internet).
Priority: HIGH
It seems that most reported issues are related to this.
Proposal
Make Karabiner complex modifications extendable. Allow:
- Global conditions.
- Applied by default to every rule in
rules
.
- Applied by default to every rule in
- Rule-level conditions.
- Applied by default to every manipulator in
manipulators
. - Overrides any global conditions if they have the same
type
.
- Applied by default to every manipulator in
- Manipulator-level conditions.
- Applied only to a single manipulator.
- Overrides both rule-level and global conditions if they have the same
type
.
- Since lower-level conditions override higher-level conditions, there is no more need for
frontmost_application_unless
, etc. rules since the rules that apply only to those applications are overridden only in that application's rule/manipulators. - Alternatively, if it's easier for development, add new optional
condition.id
andcondition.overrides[ids]
fields so users can specify which conditions they want to override manually.
This would be an incredibly better way to write rules/conditions, especially since the entries' contents have to be written manually. It also means that similar manipulators can be grouped into one rule instead of spreading their logic across multiple rules (e.g. frontmost_application_unless
) and/or breaking because the user accidentally imported them in the wrong order (e.g. frontmost_application_if
not working due to a previous/global rule using the same key combo).
Also, this idea already has support from other users, so it's clearly a highly desired feature.
This would result in solving a majority of the issues users post and probably attract new users!
Examples
Simple
{
"title": "My key combo that is user friendly",
"rules": [
{
"description": "Yay, I'm following the DRY principle!",
"conditions": [
"condition_objects for all my manipulators"
],
"manipulators": [
{
"type": "basic",
"from": {},
"to": []
},
{
"type": "basic",
"from": {},
"to": []
}
]
}
]
}
Better application-only rules
{
"title": "Ctrl --> Command except in IDE, except NOW I WORK!",
"rules": [
{
"description": "Ctrl --> Command",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "left_control",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_command"
}
]
},
{
"type": "basic",
"from": {
"key_code": "left_control",
"modifiers": {
"optional": [
"any"
]
}
},
"to": [
{
"key_code": "left_control"
}
],
"conditions": [
{
"type": "frontmost_application_if",
"bundle_identifiers": [
"com.jetbrains.*"
]
}
]
}
]
}
]
}
All levels of conditions
{
"title": "My key combo that is SUPER user friendly",
"conditions": [
"default conditions to apply to all rules",
"e.g. device_if"
],
"rules": [
{
"description": "Wow, this is both DRY and way easier to read!",
"manipulators": [
"manipulators without unnecessary code duplication from global conditions"
]
},
{
"description": "Oh cool, I can keep the global rules I need and delete the ones I don't!",
"conditions": [
"Conditions to only apply to this rule's manipulators.",
"Maintains global, root-level rules",
"or overrides them if applicable."
],
"manipulators": [
{
"yay I'm not duplicating the global rules"
},
{
"nor do I have to add superfluous e.g. frontmost_application_unless",
"conditions to every single entry I contain!"
},
{
"type": "Even my custom manipulator is easier to write",
"from": {},
"to": [],
"conditions": [
"Extra condition to add only for me.",
"Alternatively, override the global rule(s) I don't want",
"while keeping the rest of them.",
"Alternatively, override the rule-level rules",
"so the other rules don't have to add",
"_unless entries."
]
}
]
}
]
}
+1
+2
+3
+4
+5