texpand

command module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2026 License: MIT Imports: 18 Imported by: 0

README

texpand

Lightweight Wayland text expander. Reads raw keyboard events via evdev, types replacements directly via uinput. Works on any Wayland compositor (KDE, GNOME, Hyprland, Sway, etc.).

Single static binary. YAML config (espanso-compatible format). Zero runtime dependencies (optional: wtype for Unicode, wl-clipboard as last-resort fallback).

Warning: This was vibe coded. It works, but don't expect anything from it xD.

How it works

[Keyboard] ──evdev──→ texpand ──uinput──→ [Any App]
  1. Monitors /dev/input/event* devices via evdev (non-exclusive)
  2. Maintains a rolling buffer of recent keystrokes
  3. On match: grabs keyboards, backspaces the trigger, types the replacement via uinput (falls back to wtype for Unicode, then clipboard paste as last resort)

Two trigger modes (set globally in config.yml):

  • Space (default): fires when space is pressed after the trigger
  • Immediate: fires as soon as the trigger is typed

Config changes are picked up automatically — no restart needed.

Install

go install github.com/andresousadotpt/texpand@latest
Initialize config
texpand init

Creates ~/.config/texpand/match/ with default YAML trigger files.

Set up permissions

texpand reads from /dev/input/ and writes to /dev/uinput.

# Add your user to the input group
sudo usermod -aG input $USER

# Ensure the uinput module loads at boot
echo uinput | sudo tee /etc/modules-load.d/uinput.conf
sudo modprobe uinput

# Allow input group to write to /dev/uinput
sudo cp 99-uinput.rules /etc/udev/rules.d/99-uinput.rules
sudo udevadm control --reload-rules && sudo udevadm trigger

# Log out and back in for group change to take effect
Systemd service
cp texpand.service ~/.config/systemd/user/texpand.service
systemctl --user daemon-reload
systemctl --user enable --now texpand.service

Update

go install github.com/andresousadotpt/texpand@latest
systemctl --user restart texpand.service

After updating, run the migration command to update your config files to the latest format:

texpand migrate

This safely removes deprecated fields, creates .bak backups of modified files, and is idempotent (safe to run multiple times).

To pick up new default config files (without overwriting your existing ones):

texpand init

Config format

YAML files in ~/.config/texpand/match/*.yml. Espanso-compatible subset.

Global settings (config.yml)

~/.config/texpand/config.yml controls global behavior:

# "space" (default) - triggers fire on space
# "immediate" - triggers fire as soon as typed
trigger_mode: space
Simple trigger
matches:
    - trigger: "'date"
      replace: "{{_date}}"
Multiple triggers for same replacement
matches:
    - triggers: ["'binsh", "'#!"]
      replace: "#!/bin/sh"
Date variables
global_vars:
    - name: _date
      type: date
      params:
          format: "%d/%m/%Y"

matches:
    - trigger: "'date"
      replace: "{{_date}}"
Date with offset (tomorrow/yesterday)
matches:
    - trigger: "'tdate"
      replace: "{{tomorrow}}"
      vars:
          - name: tomorrow
            type: date
            params:
                format: "%a %m/%d/%Y"
                offset: 86400
Cursor positioning

Use $|$ to mark where the cursor should land after expansion:

matches:
    - trigger: "'11"
      replace: "{{time_with_ampm}} - 1:1 with [$|$]"
Supported strftime tokens
Token Meaning Example
%Y 4-digit year 2026
%m Month (zero-padded) 02
%d Day (zero-padded) 23
%H Hour 24h 14
%I Hour 12h 02
%M Minute 30
%S Second 05
%p AM/PM PM
%a Short weekday Mon
%A Full weekday Monday
%b Short month Jan
%B Full month January

All default triggers

Accented characters (fire immediately)
Trigger Output Trigger Output
]a á ]A Á
}a à }A Á
~a ã ~o õ
]e é ]E É
}e è }E È
]i í ]I Í
}i ì }I Ì
]o ó ]O Ó
}o ò }O Ò
]u ú ]U Ú
}u ù }U Ù
'c, ç
Symbols (fire on space)
Trigger Output
'deg º
'... ...
euros
Coding shortcuts (fire on space)
Trigger Output
'binsh / '#! #!/bin/sh
'gsm git switch main && git pull origin main
'gpomr git pull origin main --rebase
Date & time (fire on space)
Usage
texpand [--debug] [init|version|migrate]
Command Description
(none) Run texpand (monitor keyboards, expand triggers)
init Create default config in ~/.config/texpand/
version Print version
migrate Migrate config files to the latest format
Trigger Example output
'n 10:56 AM -
'date 23/02/2026
'ddate Mon 23/02/2026
'nn Mon 23/02/2026 - 10:56 AM -
'st Mon 23/02/2026 - 10:56 AM - meeting start
'end Mon 23/02/2026 - 10:56 AM - meeting end
'11 10:56 AM - 1:1 with [cursor]
'tdate Tomorrow's date
'ydate Yesterday's date

Adding triggers

Edit or create YAML files in ~/.config/texpand/match/. Changes are picked up automatically — no restart needed.

Managing the service

systemctl --user status texpand.service    # Check status
journalctl --user -u texpand.service -f    # View logs
systemctl --user restart texpand.service   # Restart after config changes
systemctl --user stop texpand.service      # Stop
systemctl --user disable texpand.service   # Disable auto-start

Debugging

Run texpand directly in a terminal (not via systemd) to see diagnostic output:

# Stop the service first to avoid conflicts
systemctl --user stop texpand.service

# Run in foreground — shows detected keyboards and trigger count
./texpand

You'll see output like:

texpand: monitoring 2 keyboard(s) — 35 triggers loaded
  AT Translated Set 2 keyboard
  Logitech USB Receiver
Debug mode

Use --debug (or -d) for verbose output on stderr — shows config loading, trigger mode, loaded triggers, buffer state, and match decisions:

./texpand --debug
Checking what config was loaded

Run texpand init to see the config directory, then inspect the YAML files:

texpand init    # Shows config path, skips existing files
ls ~/.config/texpand/match/
Watching events in real time

To see raw kernel input events (useful for verifying your keyboard is detected):

# List all input devices
ls -la /dev/input/event*

# Watch events from a specific device (Ctrl+C to stop)
# Requires: sudo pacman -S evtest
sudo evtest /dev/input/event0
Checking clipboard operations

If triggers fire but paste wrong text, verify wl-clipboard works:

echo "test" | wl-copy
wl-paste -n    # Should print "test"
Systemd logs
# Live logs
journalctl --user -u texpand.service -f

# Last 50 lines
journalctl --user -u texpand.service -n 50

# Since last boot
journalctl --user -u texpand.service -b

Troubleshooting

"No keyboard devices found"
groups  # Should include 'input'
sudo usermod -aG input $USER
# Log out and back in
"/dev/uinput" permission denied

The most common cause is the uinput kernel module not being loaded at boot:

# Ensure the module loads at boot and load it now
echo uinput | sudo tee /etc/modules-load.d/uinput.conf
sudo modprobe uinput

# Install the udev rule and reload
sudo cp 99-uinput.rules /etc/udev/rules.d/99-uinput.rules
sudo udevadm control --reload-rules && sudo udevadm trigger

# If udevadm trigger fails with "No such device", fix permissions manually:
sudo chgrp input /dev/uinput && sudo chmod 0660 /dev/uinput
ls -la /dev/uinput  # Should show crw-rw---- root input
WAYLAND_DISPLAY not set

texpand auto-detects the Wayland socket at startup. If it fails:

systemctl --user import-environment WAYLAND_DISPLAY
systemctl --user restart texpand.service
Wrong characters

The keymap assumes US/International layout. Letters and numbers work across layouts, but symbol keys (], }, ~, ') may differ.

License

MIT

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL