1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
local IS_MAC = _REBIND.platform == "macos"
local WORD = if IS_MAC then "LAlt" else "LCtrl"
local LAYOUTS = {
hjkl = { H = "Left", J = "Down", K = "Up", L = "Right" },
ijkl = { J = "Left", K = "Down", I = "Up", L = "Right" },
}
local cfg = UI.Schema({
layerKey = UI.Keybind("CapsLock", { label = "Layer key", tab = "Layout" }),
layout = UI.Select(
"hjkl",
{ "hjkl", "ijkl" },
{ label = "Arrow keys", tab = "Layout" }
),
repeatDelay = UI.Slider(350, {
min = 150,
max = 800,
step = 10,
suffix = " ms",
label = "Repeat delay",
tab = "Layout",
}),
repeatRate = UI.Slider(30, {
min = 15,
max = 120,
step = 5,
suffix = " ms",
label = "Repeat interval",
tab = "Layout",
}),
exclude = UI.Text("", {
label = "Pass through in these apps",
placeholder = "process names, comma separated",
tab = "Apps",
}),
})
local layerHeld = false
local held = {}
local function target(key)
local arrows = LAYOUTS[cfg.layout] or LAYOUTS.hjkl
if arrows[key] then
return arrows[key]
end
if key == "U" then
return "Home"
elseif key == "O" then
return "End"
elseif key == "N" then
return WORD .. "+Left"
elseif key == "M" then
return WORD .. "+Right"
end
return nil
end
local function excluded()
local process = System.Window().process:lower()
for name in cfg.exclude:gmatch("[^,]+") do
name = name:gsub("^%s+", ""):gsub("%s+$", ""):lower()
if name ~= "" and process:find(name, 1, true) then
return true
end
end
return false
end
local function press(key, chord)
HID.Combo(chord)
held[key] = Timer.After(cfg.repeatDelay, function()
held[key] = Timer.Every(cfg.repeatRate, function()
HID.Combo(chord)
end)
end)
end
local function releaseAll()
for key, timer in held do
timer:Cancel()
held[key] = nil
end
end
function OnDown(key)
if key == cfg.layerKey then
layerHeld = true
return false
end
if held[key] then
return false
end
if not layerHeld then
return true
end
local chord = target(key)
if chord == nil or excluded() then
return true
end
press(key, chord)
return false
end
function OnUp(key)
if key == cfg.layerKey then
layerHeld = false
releaseAll()
return false
end
local timer = held[key]
if timer then
timer:Cancel()
held[key] = nil
return false
end
return true
end
function OnStart()
Log.Info(
`Hold {cfg.layerKey}: {cfg.layout} arrows, U Home, O End, N and M move by word.`
)
end
function OnStop()
releaseAll()
end
function OnError(message)
Log.Error(message)
releaseAll()
end