A lightweight Windows-only hotkey + hotstring engine built in Nim on top of a low-level keyboard hook (WH_KEYBOARD_LL) via winim.
It’s designed to feel like the “core primitives” you’d want when building AHK-like behavior in Nim:
- register hotkeys like
Ctrl+K,Alt+Shift+T,Win+C, etc. - register hotstrings like
btw→by the way - choose whether to swallow the hotkey or let it pass through
- trigger hotstrings on end characters (space/enter/tab/punctuation) or immediately
- replace text via backspacing (simple) or select + paste (fast, best for long/multiline)
- Any combination of Ctrl / Alt / Shift / Win
- Action is just a
proc() - Optional swallow mode (block it from reaching other apps)
- Built-in helpers for sending keys / typing text / running commands
trigger→replacementortrigger→ custom actionproc()- Case-sensitive or case-insensitive
- Trigger modes:
htEndChar(fires when you type an end char like space/enter/tab/etc.)htImmediate(fires as soon as the buffer ends with the trigger)
- Replace modes:
rmBackspace(delete chars one-by-one then type replacement)rmPaste(select backwards then paste replacement — much faster)
- Windows
- Nim
- Dependencies:
winimlibclip(for clipboard-based paste mode)
Install deps (example):
nimble install winim libclipA lightweight Windows-only hotkey + hotstring engine built in Nim on top of a low-level keyboard hook (WH_KEYBOARD_LL) via winim.
It’s designed to feel like the “core primitives” you’d want when building AHK-like behavior in Nim:
- register hotkeys like
Ctrl+K,Alt+Shift+T,Win+C, etc. - register hotstrings like
btw→by the way - choose whether to swallow the hotkey or let it pass through
- trigger hotstrings on end characters (space/enter/tab/punctuation) or immediately
- replace text via backspacing (simple) or select + paste (fast, best for long/multiline)
- Any combination of Ctrl / Alt / Shift / Win
- Action is just a
proc() - Optional swallow mode (block it from reaching other apps)
- Built-in helpers for sending keys / typing text / running commands
trigger→replacementortrigger→ custom actionproc()- Case-sensitive or case-insensitive
- Trigger modes:
htEndChar(fires when you type an end char like space/enter/tab/etc.)htImmediate(fires as soon as the buffer ends with the trigger)
- Replace modes:
rmBackspace(delete chars one-by-one then type replacement)rmPaste(select backwards then paste replacement — much faster)
- Windows
- Nim
- Dependencies:
winimlibclip(for clipboard-based paste mode)
Install deps (example):
nimble install winim libclipCreate a file like main.nim:
import hk
import times
let sys = newHotkeySystem()
# Hotkey: Ctrl+K
sys.addHotkey('K', {mkCtrl}, proc() =
echo "Ctrl+K pressed!"
, desc = "Print message")
# Hotkey: Alt+N (types current time)
sys.addHotkey('N', {mkAlt}, proc() =
sendString(now().format("HH:mm:ss"))
, desc = "Type current time")
# Hotstring: btw -> by the way
sys.addHotstring("btw", "by the way", desc = "Expand btw")
# Hotstring (paste mode): signature snippet
sys.addHotstring("sig", "Best regards,\nJohn Doe\njohn@example.com",
replaceMode = rmPaste,
desc = "Insert signature")
# Immediate hotstring action: ::date
sys.addHotstringAction("::date", proc() =
pasteText(now().format("yyyy-MM-dd"))
, triggerMode = htImmediate,
replaceMode = rmPaste,
desc = "Insert date immediately")
sys.listHotkeys()
sys.listHotstrings()
echo "Running... Press Ctrl+C to quit."
sys.run()Run:
nim c -r main.nim-
ModifierKeymkCtrl,mkAlt,mkShift,mkWin -
HotstringTriggerhtEndCharhtImmediate
-
ReplaceModermBackspacermPaste
let sys = newHotkeySystem()sys.addHotkey('K', {mkCtrl}, proc() =
echo "Pressed!"
, swallow = false,
desc = "Print message")You can also use a VK directly:
sys.addHotkey(VK_F1, {}, proc() =
sys.listHotkeys()
sys.listHotstrings()
, desc = "Show help")sys.addHotstring("omw", "On my way!", desc = "Expand omw")Paste mode (recommended for long / multiline):
sys.addHotstring("addr", "123 Main Street...\nUSA",
replaceMode = rmPaste,
desc = "Insert address")sys.addHotstringAction(";calc", proc() =
runCommand("calc.exe")
, desc = "Open calculator")start()installs the hookrun()starts a Windows message loop (blocking)stop()removes the hook
discard sys.start()
# ...
sys.stop()
# Or just:
sys.run()These are handy for building actions:
sendKey(vk)sendBackspace(count)sendString(text)(SendInput unicode typing)pasteText(text)(clipboard + Ctrl+V via libclip)selectBackward(charCount)selectAndPaste(selectCount, text)runCommand(cmd)(ShellExecuteW "open")
Virtual-key helpers:
charToVK(ch): int32vkToChar(vk, shifted): char
The system keeps a rolling inputBuffer (default max length 32) and updates it on keydown for “normal typing” keys.
When you type an end character (space/tab/enter/punctuation), the buffer looks like:
"btw "
It compares the buffer minus the last char to the trigger:
"btw "→ compare"btw"→ match → replace
Triggers as soon as the buffer ends with the trigger:
- typing
::datetriggers instantly (no space required)
rmBackspace: deletes each character using backspace, then types replacementrmPaste: selects backwards withShift+Left, then pastes via clipboard (fast)
- Windows only (uses
SetWindowsHookExlow-level keyboard hook) - The hook skips keys flagged as injected (
LLKHF_INJECTED) to avoid re-trigger loops fromSendInput - Hotstring detection only runs when modifiers are
{}or{mkShift}(so typing while holding Ctrl/Alt/Win doesn’t corrupt buffer) - Some keys / layouts can be tricky (IME, non-US keyboard layouts, dead keys).
vkToCharcurrently covers common ASCII + some punctuation.
This library installs a global keyboard hook. That means it can see keystrokes system-wide while running. Use responsibly and avoid distributing binaries that capture sensitive input without explicit user consent.
Choose whatever fits your project (MIT is common). Add a LICENSE file if you plan to publish this as a nimble package.
This repo includes an isMainModule demo in hk.nim:
- hotkeys (Ctrl+K, Ctrl+Shift+T, Alt+N, F1)
- hotstrings (btw/omw/ty/np + paste-mode snippets + immediate
::dateetc.)
Run it directly:
nim c -r hk.nim