macOS 用了差不多一年半,但是一直觉得不得劲,因为在Windows 下,我有一个基于 Autohotkey V1 的键盘映射方案 xlr-sapce 键盘映射方案 for Windows,让我在工作打字时更快的操作光标,移动、选择、编辑,以空格为修饰键,加字母区域的按键,手部不需要任何多余的移动,而在macOS 一直没办法实现
以前在使用 Linux 的时候也尝试实现这个映射方案,最接近的方案是使用 XModMap,但最终还差点,找资料实在太难了,而 AI 让我的想法更好的实现了,所以下面是在 macOS 下xlr-sapceFN 映射方案,虽然不够完美,但是也非常接近了
xlr-sapceFN 映射方案简介
xlr-spaceFN 映射方案与其它键盘映射方案最大的不同,以及最大的优点就是,使用空格作为修饰键,在Windows 下,Autohotkey让这个想法完美实现了,目前macOS 下虽然功能实现了,但还是有点遗憾
本配置将空格键改造为一个智能功能层激活键:
- 轻点空格 → 正常输出空格(无延迟感,一般100-150毫秒内,自己调)
- 按住空格 → 激活功能层,此时按下特定字母键可执行导航、编辑等快捷操作
快速参考卡片(按键位布局)
| q | w Bksp | e ↑ | r Del | t | y PgUp | u Sel← | i Sel↑ | o Sel→ | p Page Home |
| a Home | s ← | d ↓ | f → | g End | h ←选词 | j ←选择 | k ↓选择 | l →选择 | Enter 行尾 + 换行 |
| z Undo | x Cut | c Copy | v Paste | b PgDn | n →选词 | m | / Page End |
macOS xlr-sapceFN 映射方案实现核心思路:如何实现“空格当修饰键”
实现方式:使用 Karabiner-Elements+ 配置文件的方式实现
Karabiner 的规则不能直接写 GetKeyState,但可以通过 to_if_held_down 和 to_if_alone 组合实现:
- 当按下空格并稍后释放(没有按其他键)→ 发送
space(单独空格)。 - 当按下空格并按住不放时 → 进入“修饰键模式”,此时再按其他键触发自定义动作。
当前必须接受的缺点
Karabiner-Elements 无法实现和 AutoHotkey 完全一致的逻辑,这是两个平台键盘事件处理机制上的本质差异导致的:AutoHotkey 在 Windows 低层 (钩子) 驱动可以直接操控键盘状态,强制将其“角色”转换;而 Karabiner-Elements 在 macOS 受沙盒限制,只能通过“裁决”策略用延迟判断该输出字符还是激活功能,无法做到真正的零延迟使用方法
具体来说就是:在 macOS 下,必须为空格按下进程设置一个延迟时间,比如说 150 毫秒:
- 在这个时间内:按下并释放空格则正常输出空格 >>> 理想状态
- 在这个时间内:按下空格和其它键并释放,没有冲突的情况下,输出其它键 >>> 难受状态
- 在这个时间之后:按下并释放空格没有任何反应 >>> 难受状态
- 在这个时间之后:按下空格和设定的组合键,实现设定的映射 >>> 理想状态
在上面几种情况中,日常使用其实还是非常容易触发难受状态的,整个工作流程会受到一点影响,相对来说来还是能够接受的 ,最难受的其实是使用删除的时候,很多时候删除操作非常快,但按太快容易进入难受状态,以后再想办法吧
操作指引
- 下载并安装Karabiner-Elements后打开,进入Setup ,将所有功能打开,尤其是 Driver Extension
- 将下面的 JSON 代码保存为
~/.config/karabiner/assets/complex_modifications/xlr_spaceFN_modifier.json(如果目录不存在则手动创建)。 - 打开 Karabiner-Elements → Complex Modifications → Add predefined rule → xlr-SpaceFN → Enable All
- 如果某些快捷键与系统快捷键冲突(如
Ctrl+Space切换输入法),可以在 Karabiner 的 Simple Modifications 或系统设置中关闭对应组合。 - [Base] Hold Spacebar to activate layer (required) 是必须启用的 ,其它的已经分成了移动、选择、编辑三块,按需要开启



方案代码
- 基础层(必需) – 按住空格激活功能层(设置变量)
- 光标移动层 – 方向键、行首行尾、翻页
- 文本选择层 – Shift + 方向、按词选择、选至行首尾
- 编辑操作层 – 删除、复制粘贴、撤销、行尾换行
基础层必须启用,其他层可以任意组合启用/禁用。
所有规则共用同一个变量space_fn_layer,互不冲突。
将下面代码按指引中的第二步保存为 josn 文件
{
"title": "xlr-SpaceFN-v2",
"rules": [
{
"description": "[Base] Hold Spacebar to activate layer (required)",
"manipulators": [
{
"description": "Hold Spacebar for Layer (with delay)",
"from": {
"key_code": "spacebar",
"modifiers": { "optional": ["any"] }
},
"to_if_alone": [{ "key_code": "spacebar" }],
"to_if_held_down": [{ "set_variable": { "name": "space_fn_layer", "value": 1 } }],
"to_after_key_up": [{ "set_variable": { "name": "space_fn_layer", "value": 0 } }],
"parameters": {
"basic.to_if_alone_timeout_milliseconds": 110,
"basic.to_if_held_down_threshold_milliseconds": 111
},
"type": "basic"
}
]
},
{
"description": "[Block 1] Cursor Movement (e,s,d,f | a,g | y,b)",
"manipulators": [
{
"from": { "key_code": "e" },
"to": [{ "key_code": "up_arrow" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "s" },
"to": [{ "key_code": "left_arrow" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "d" },
"to": [{ "key_code": "down_arrow" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "f" },
"to": [{ "key_code": "right_arrow" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "a" },
"to": [{ "key_code": "left_arrow", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "g" },
"to": [{ "key_code": "right_arrow", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "y" },
"to": [{ "key_code": "page_up" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "b" },
"to": [{ "key_code": "page_down" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
}
]
},
{
"description": "[Block 2] Text Selection (i,j,k,l | h,n | u,o)",
"manipulators": [
{
"from": { "key_code": "i" },
"to": [{ "key_code": "up_arrow", "modifiers": ["left_shift"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "j" },
"to": [{ "key_code": "left_arrow", "modifiers": ["left_shift"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "k" },
"to": [{ "key_code": "down_arrow", "modifiers": ["left_shift"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "l" },
"to": [{ "key_code": "right_arrow", "modifiers": ["left_shift"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "h" },
"to": [{ "key_code": "left_arrow", "modifiers": ["left_shift", "left_option"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "n" },
"to": [{ "key_code": "right_arrow", "modifiers": ["left_shift", "left_option"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "u" },
"to": [{ "key_code": "left_arrow", "modifiers": ["left_command", "left_shift"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "o" },
"to": [{ "key_code": "right_arrow", "modifiers": ["left_command", "left_shift"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
}
]
},
{
"description": "[Block 3] Editing (w,r | c,x,v,z | p,/, Enter)",
"manipulators": [
{
"from": { "key_code": "w" },
"to": [{ "key_code": "delete_or_backspace" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "r" },
"to": [{ "key_code": "delete_forward" }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "c" },
"to": [{ "key_code": "c", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "x" },
"to": [{ "key_code": "x", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "v" },
"to": [{ "key_code": "v", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "z" },
"to": [{ "key_code": "z", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "p" },
"to": [{ "key_code": "up_arrow", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "slash" },
"to": [{ "key_code": "down_arrow", "modifiers": ["left_command"] }],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
},
{
"from": { "key_code": "return_or_enter" },
"to": [
{ "key_code": "right_arrow", "modifiers": ["left_command"] },
{ "key_code": "return_or_enter" }
],
"conditions": [{ "name": "space_fn_layer", "type": "variable_if", "value": 1 }],
"type": "basic"
}
]
}
]
}
