"When all you have is a hammer, everything looks like a nail"
Hamr is an extensible launcher for Wayland compositors built with Quickshell. Extend it with plugins in any language using a simple JSON protocol.
Supported Compositors: Hyprland, Niri
Minimalist UI - Clean, modern, no visual clutter. Just a search bar and results. No sidebars, tabs, or menus unless absolutely necessary.
Zero Configuration - Works out of the box with sensible defaults. Settings exist but you should never need to touch them.
Minimum Interactions - Every feature optimized for fewest possible keystrokes. Search, Enter, done.
Learns Your Habits - Frecency ranking means frequently-used items rise to the top automatically. Type "ff" once to launch Firefox, and "ff" becomes your shortcut forever.
Keyboard-First - Full functionality without touching the mouse. Vim-style navigation (Ctrl+J/K), quick action shortcuts (Ctrl+1-6), and muscle-memory-friendly bindings.
Your Shortcuts, Your Way - First time, type "move to workspace 3". Next time, just "w3". Hamr learns your patterns and creates personal shortcuts automatically. No configuration, no aliases to maintain.
- Frecency-based ranking - Results sorted by frequency + recency (inspired by zoxide)
- Learned search affinity - System learns your search shortcuts (type "q" to find QuickLinks if that's how you found it before)
- Pattern-matched plugins - Plugins can auto-trigger on patterns (e.g., math expressions, URLs) without explicit prefixes
- Fuzzy matching - Fast, typo-tolerant search powered by fuzzysort, includes desktop entry keywords (e.g., "whatsapp" finds ZapZap)
- Extensible plugins - Language-agnostic handlers with simple JSON protocol (Python, Bash, Go, Rust, etc.)
- History tracking - Search, plugin actions, and shell command history
- Smart suggestions - Context-aware app suggestions based on time, workspace, and usage patterns
- Preview panel - Drawer-style side panel shows rich previews (images, markdown, metadata) on hover/selection; pin previews to screen
- Draggable & persistent position - Drag the launcher anywhere on screen; position remembered across sessions
- State restoration - Click outside to dismiss, reopen within 30s to resume where you left off (configurable)
- Live plugin updates - Plugins can emit real-time updates via daemon mode (no full list refresh, preserves focus)
- File watching - Plugins can watch files/directories for changes with native inotify
| Prefix | Function | Prefix | Function |
|---|---|---|---|
~ |
File search | ; |
Clipboard history |
/ |
Actions & plugins | ! |
Shell history |
= |
Calculator | : |
Emoji picker |
These shortcuts are fully customizable. See Customizing Prefix Shortcuts for details.
Type math expressions directly - no prefix needed. Examples: 2+2, sqrt(16), 10c (celsius), $50 to EUR, 20% of 32, 10ft to m
Powered by qalculate - supports 150+ currencies, 100+ units, percentages, and advanced math.
All plugins are indexed and searchable directly from the main bar - no prefix required. Just type what you want (e.g., "clipboard", "emoji", "power") and Hamr finds it. Prefix shortcuts like /, ~, ; are optional conveniences, not requirements.
| Plugin | Description |
|---|---|
apps |
App drawer with categories (like rofi/dmenu) |
bitwarden |
Password manager with keyring integration |
calculate |
Calculator with currency, units, and temperature |
clipboard |
Clipboard history with OCR search, filter by type |
create-plugin |
AI helper to create new plugins (requires OpenCode) |
dict |
Dictionary lookup with definitions |
emoji |
Emoji picker with search |
files |
File search with fd + fzf, thumbnails for images |
aur |
Search and install packages from AUR (yay/paru) |
flathub |
Search and install apps from Flathub |
notes |
Quick notes with multi-line content support |
pictures |
Browse images with thumbnails |
player |
Media player controls via playerctl (play/pause, next, prev, shuffle, loop) |
power |
System power and session controls (shutdown, reboot, suspend, logout) |
quicklinks |
Web search with customizable quicklinks |
screenrecord |
Screen recording with auto-trim (wf-recorder) |
screenshot |
Browse screenshots with OCR text search |
settings |
Configure Hamr launcher options |
sound |
System volume controls (volume up/down, mute, mic mute) |
shell |
Shell command history (zsh/bash/fish) |
snippet |
Text snippets for quick insertion |
theme |
Dark/light mode and accent color switching |
timer |
Countdown timers with presets, FAB display, and notifications |
todo |
Simple todo list manager (live updates via daemon) |
topcpu |
Process monitor sorted by CPU usage (live daemon refresh) |
topmem |
Process monitor sorted by memory usage (live daemon refresh) |
url |
Open URLs in browser (auto-detects domain patterns) |
wallpaper |
Wallpaper selector (swww, hyprpaper, swaybg, feh) |
webapp |
Install and manage web apps |
whats-that-word |
Find words from descriptions or fix misspellings |
zoxide |
Jump to frequently used directories |
hyprland |
Window management, dispatchers, and global shortcuts (Hyprland only) |
niri |
Window management and compositor actions (Niri only) |
Forgot which keybinding moves a window to workspace 3? Can't remember the shortcut for toggling floating mode? No problem. Just type what you want in plain English and Hamr handles the rest.
The hyprland plugin provides natural language access to Hyprland window management - no need to memorize keybindings or dig through config files.
Window Management:
toggle floating,fullscreen,maximize,pin,center windowclose window,focus left/right/up/downmove window left/right/up/down,swap left/right/up/down
Workspace Navigation:
workspace 3,go to 5,next workspace,previous workspacemove to 2,move to workspace 4 silentscratchpad,empty workspace
Window Groups (Tabs):
create group- Make current window a groupjoin group left/right- Add window to adjacent groupremove from group,next in group,prev in group
Global Shortcuts:
Every app that registers DBus global shortcuts becomes instantly searchable. That obscure "Toggle side panel" shortcut from your browser extension? Just type side panel. The screen recording hotkey you set up months ago? Type record. No need to remember Ctrl+Alt+Shift+whatever - describe what you want and Hamr finds it.
Monitor Control:
next monitor,prev monitormove workspace to monitor,swap workspaces
Commands are saved to history for quick access. Even better: after using "move to workspace 3" a few times, just type w3 or m3 - Hamr learns your shortcuts automatically. Type /hyprland to browse all available commands, or search directly from the main bar.
The niri plugin provides natural language access to Niri window management, optimized for Niri's scrollable tiling layout.
Window Management:
close window,fullscreen,toggle floating,center columnmaximize column,toggle tabbed(column tabbed display)
Column Operations:
consume window- Add window to the right into the focused columnexpel window- Move focused window out of columnexpand column- Expand column to available widthfocus column left/right,move column left/right
Window Movement:
focus window up/down,move window up/downswap window left/right
Workspace Navigation:
focus workspace up/down,focus workspace previousmove window to workspace up/downmove column to workspace up/down
Monitor Control:
focus monitor left/right/up/down/next/previousmove window to monitor left/rightmove column to monitor left/right
Utilities:
toggle overview,screenshot,screenshot window,screenshot screenshow hotkey overlay,switch keyboard layoutpower off monitors,power on monitors
Type /niri to browse all available actions, or search directly from the main bar.
A Rust-based rewrite is available in public alpha. It offers the same features with improved performance and a more streamlined codebase.
Try it out:
git clone https://github.com/stewart86/hamr.git
cd hamr
./install.shThis will install the Rust version instead of the QML version. The alpha version is stable for daily use but may have some rough edges.
Requirements: Linux with a supported Wayland compositor (Hyprland or Niri)
# Using paru (or yay, etc.)
paru -S hamrManual installation (from source)
# Clone the repository
git clone https://github.com/stewart86/hamr.git
cd hamr
# Run the install script (auto-installs dependencies)
./install.shThe install script will:
- Check for missing dependencies and offer to install them via your AUR helper (paru, yay, etc.)
- Create a symlink at
~/.config/quickshell/hamr - Set up the user plugins directory at
~/.config/hamr/plugins/
What gets installed
Required dependencies:
| Category | Packages |
|---|---|
| Core | quickshell >= 0.2.1 (or quickshell-git) |
| Python | python, python-click, python-loguru, python-tqdm, python-gobject, gnome-desktop-4 |
| Clipboard | wl-clipboard, cliphist |
| File search | fd, fzf |
| Desktop | xdg-utils, libnotify, gtk3, libpulse, jq |
| Compositor | hyprland or niri |
| Calculator | libqalculate |
| Fonts | ttf-material-symbols-variable, ttf-jetbrains-mono-nerd, ttf-readex-pro |
Optional dependencies:
matugen- Material You color generation from wallpaperstesseract- OCR for screenshot text searchimagemagick- Alternative thumbnail generationbitwarden-cli- Bitwarden password manager pluginpython-keyring- Secure session storage for Bitwarden pluginslurp- Screen region selectionwf-recorder- Screen recording
Manual dependency installation
If you prefer to install dependencies manually:
# Using paru (or yay, etc.)
paru -S quickshell python python-click python-loguru python-tqdm \
python-gobject gnome-desktop-4 wl-clipboard cliphist fd fzf \
xdg-utils libnotify gtk3 libpulse jq libqalculate \
ttf-material-symbols-variable ttf-jetbrains-mono-nerd ttf-readex-pro
# Plus your compositor (one of):
paru -S hyprland # or
paru -S niri
# Optional
paru -S matugen-bin tesseract imagemagick bitwarden-cli slurp wf-recorderUsing the Nix flake
# Try without installing
nix run github:Stewart86/hamr -- --help
# Install to your profile
nix profile install github:Stewart86/hamrNixOS / Home Manager integration
Add the flake input to your configuration:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
hamr.url = "github:Stewart86/hamr";
};
outputs = { self, nixpkgs, hamr, ... }: {
# NixOS configuration
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
{
nixpkgs.overlays = [ hamr.overlays.default ];
environment.systemPackages = [ pkgs.hamr ];
}
];
};
# Or with Home Manager
homeConfigurations.myuser = home-manager.lib.homeManagerConfiguration {
modules = [
{
nixpkgs.overlays = [ hamr.overlays.default ];
home.packages = [ pkgs.hamr ];
}
];
};
};
}Nix dependencies
The flake includes all required dependencies:
quickshell,qt5compat,qtmultimedia- Python with
click,loguru,tqdm,pygobject3 wl-clipboard,cliphist,fd,fzf,jq,libqalculate- Fonts:
material-symbols,nerd-fonts.jetbrains-mono
Optional: Add zoxide to your system for the zoxide plugin.
Fedora / Ubuntu / Debian
Hamr requires Quickshell which must be built from source on non-Arch distros. See the Quickshell documentation for build instructions.
Once Quickshell is installed, clone Hamr and install dependencies manually:
git clone https://github.com/stewart86/hamr.git
cd hamr
# Create config directories
mkdir -p ~/.config/quickshell ~/.config/hamr/plugins
ln -s "$(pwd)" ~/.config/quickshell/hamr
# Install Python dependencies
pip install click loguru tqdm PyGObject
# Install system packages (example for Fedora)
sudo dnf install fd-find fzf wl-clipboard jq qalculate
# Install fonts (see font section below)Font Installation (Non-Arch)
| Font | Purpose |
|---|---|
| Material Symbols Rounded | Icons throughout the UI |
| JetBrains Mono NF | Monospace text and Nerd Font icons |
| Readex Pro | Reading/content text |
Download from:
- JetBrains Mono Nerd Font - Download
JetBrainsMono.zip - Material Symbols
- Readex Pro
mkdir -p ~/.local/share/fonts
# Extract/copy downloaded fonts to ~/.local/share/fonts/
fc-cache -fvHamr starts hidden and listens for a toggle signal. Configure your compositor to toggle Hamr with a keybinding.
Hyprland
Add to ~/.config/hypr/hyprland.conf:
# Autostart hamr
exec-once = hamr
# Toggle Hamr with Super key (tap to toggle)
bind = SUPER, SUPER_L, global, quickshell:hamrToggle
bindr = SUPER, SUPER_L, global, quickshell:hamrToggleRelease
# Or toggle with Ctrl+Space
bind = Ctrl, Space, global, quickshell:hamrToggleReload config: hyprctl reload
Niri
1. Enable systemd service (recommended):
systemctl --user enable hamr.service
systemctl --user add-wants niri.service hamr.service
systemctl --user start hamr.service2. Add keybinding to ~/.config/niri/config.kdl:
binds {
// Toggle Hamr with Ctrl+Space
Ctrl+Space { spawn "hamr" "toggle"; }
// Or with Mod+Space (Super key)
Mod+Space { spawn "hamr" "toggle"; }
}Alternative: Manual autostart (without systemd)
If you prefer not to use systemd, add to ~/.config/niri/config.kdl:
spawn-at-startup "hamr"Start Hamr manually (for testing):
hamrAfter starting, press your keybind (e.g., Ctrl+Space) to open Hamr.
You can bind keys to open specific plugins directly:
hamr plugin <plugin_name>Hyprland
Add to ~/.config/hypr/hyprland.conf:
# Open clipboard directly with Mod+V
bind = SUPER, V, exec, hamr plugin clipboard
# Open emoji picker with Mod+Period
bind = SUPER, Period, exec, hamr plugin emoji
# Open file search with Mod+E
bind = SUPER, E, exec, hamr plugin filesNiri
Add to ~/.config/niri/config.kdl:
binds {
// Open clipboard directly with Mod+V
Mod+V { spawn "hamr" "plugin" "clipboard"; }
// Open emoji picker with Mod+Period
Mod+Period { spawn "hamr" "plugin" "emoji"; }
// Open file search with Mod+E
Mod+E { spawn "hamr" "plugin" "files"; }
}Available plugins: apps, bitwarden, calculate, clipboard, emoji, files, notes, pictures, power, quicklinks, screenshot, settings, shell, snippet, todo, wallpaper, hyprland, niri, etc.
AUR:
paru -Syu hamrManual installation:
cd /path/to/hamr
./install.sh --updateAUR:
sudo pacman -R hamr
# Optionally remove user data:
rm -rf ~/.config/hamrManual installation:
./install.sh --uninstallTroubleshooting
"I ran hamr but nothing appears"
This is expected. Hamr starts hidden and waits for a toggle signal. Make sure you:
- Added the keybinding to your compositor config (see Post-Installation Setup above)
- Reloaded your compositor config
- Press your keybind (e.g., Super key or Ctrl+Space)
Check dependencies
./install.sh --checkView logs
journalctl --user -u quickshell -fCrash with Qt version mismatch
WARN: Quickshell was built against Qt 6.10.0 but the system has updated to Qt 6.10.1...
This happens when Qt is updated but Quickshell wasn't rebuilt. Fix by rebuilding Quickshell:
paru -S quickshell --rebuild
# or for quickshell-git:
paru -S quickshell-git --rebuildWarning about missing colors.json
WARN: Read of colors.json failed: File does not exist
This is harmless. Hamr uses built-in default colors. For dynamic theming from your wallpaper, install matugen and use the wallpaper plugin.
Warning about missing quicklinks.json
WARN: Read of quicklinks.json failed: File does not exist
This is harmless. Quicklinks are optional. To add quicklinks, create ~/.config/hamr/quicklinks.json:
[
{"name": "GitHub", "url": "https://github.com", "icon": "code"}
]Clipboard Support (Optional)
# Install cliphist (Arch: pacman -S cliphist wl-clipboard)
# Add to Hyprland startup
exec-once = wl-paste --type text --watch cliphist store
exec-once = wl-paste --type image --watch cliphist storeTheming
Hamr uses Material Design colors for its UI. Colors can come from:
- matugen (recommended) - Auto-generates colors from your wallpaper. Install matugen and set a wallpaper via the wallpaper plugin
- Custom colors.json - Set path in config:
"paths": {"colorsJson": "~/.config/hamr/colors.json"} - Built-in defaults - Dark theme fallback when no colors.json found
Quick setup with matugen:
# Install matugen (Arch)
paru -S matugen-bin
# Set a wallpaper - colors are generated automatically
# Type /wallpaper in HamrFor detailed setup including matugen templates, integration with DankMaterialShell, and manual theming, see docs/THEMING.md.
Hamr loads plugins from two locations:
- Built-in plugins:
<hamr>/plugins/- Included with Hamr, read-only - User plugins:
~/.config/hamr/plugins/- Your custom plugins
User plugins with the same name as built-in plugins will override them.
Each plugin is a folder containing:
manifest.json- Plugin metadata and configurationhandler.py(optional) - Handler script for dynamic plugins
For simple actions, use staticIndex in the manifest - no handler needed.
Language agnostic: Plugins communicate via JSON over stdin/stdout. Use Python, Bash, Go, Rust, Node.js - any language that can read/write JSON.
Full documentation: See plugins/README.md for the complete protocol reference, response types, indexing, daemon mode, forms, and more.
| Capability | Description |
|---|---|
| Multi-step navigation | Show lists, let users drill down, navigate back |
| Rich cards | Display markdown content (definitions, previews, help) |
| Preview panel | Side panel with image/markdown/text preview, pinnable to screen |
| Multi-field forms | Forms with text, textarea, select, checkbox fields |
| Image thumbnails | Show image previews in result lists |
| Action buttons | Add context actions per item (copy, delete, open folder) |
| Plugin action bar | Toolbar buttons for plugin-level actions (Add, Wipe) with Ctrl+1-6 shortcuts |
| Confirmation dialogs | Inline confirmation for dangerous actions (e.g., "Wipe All") |
| Image browser | Full image browser UI with directory navigation |
| OCR text search | Search images by text content (requires tesseract) |
| Execute commands | Run any shell command, optionally save to history |
| Custom placeholders | Change search bar placeholder text per step |
| Live search | Filter results as user types |
| Submit mode | Wait for Enter before processing (for text input, chat) |
| Daemon mode | Persistent processes with live updates (async file watching, real-time data) |
| File watching | Plugins watch files/directories with inotify for instant updates |
| Incremental updates | update response type patches individual items without full refresh |
| Visual enhancements | Badges, chips, gauges, progress bars, graphs for rich result displays |
Quick Start: Hello World Plugin
mkdir -p ~/.config/hamr/plugins/hellomanifest.json:
{
"name": "Hello World",
"description": "A simple greeting plugin",
"icon": "waving_hand"
}handler.py:
#!/usr/bin/env python3
import json
import sys
def main():
input_data = json.load(sys.stdin)
step = input_data.get("step", "initial")
if step == "initial":
print(json.dumps({
"type": "results",
"results": [
{"id": "english", "name": "Hello!", "icon": "language"},
{"id": "spanish", "name": "Hola!", "icon": "language"},
]
}))
return
if step == "action":
selected_id = input_data.get("selected", {}).get("id", "")
print(json.dumps({
"type": "card",
"card": {
"title": "Greeting",
"content": f"You selected: {selected_id}",
"markdown": True
}
}))
if __name__ == "__main__":
main()chmod +x ~/.config/hamr/plugins/hello/handler.pyType /hello to try it!
JSON Protocol Reference
{
"step": "initial|search|action",
"query": "user's search text",
"selected": {"id": "selected-item-id"},
"action": "action-button-id",
"session": "unique-session-id"
}Show Results:
{
"type": "results",
"results": [
{
"id": "unique-id",
"name": "Display Name",
"description": "Optional subtitle",
"icon": "material_icon_name",
"thumbnail": "/path/to/image.png",
"actions": [{"id": "copy", "name": "Copy", "icon": "content_copy"}]
}
],
"placeholder": "Custom placeholder...",
"inputMode": "realtime",
"pluginActions": [
{"id": "add", "name": "Add", "icon": "add_circle"},
{"id": "wipe", "name": "Wipe", "icon": "delete_sweep", "confirm": "Are you sure?"}
]
}Update Individual Items (preserves selection/focus):
{
"type": "update",
"items": [
{
"id": "item-1",
"gauge": {"value": 75, "max": 100},
"badges": [{"text": "5"}]
}
]
}Show Card:
{
"type": "card",
"card": {"title": "Title", "content": "Markdown **content**", "markdown": true}
}Execute Action (Safe API):
{
"type": "execute",
"open": "/path/to/file",
"name": "Open file.pdf",
"close": true
}Available safe actions: launch (desktop file), copy (clipboard), typeText (type via ydotool), openUrl, open (xdg-open), notify, sound.
Open Image Browser:
{
"type": "imageBrowser",
"imageBrowser": {
"directory": "~/Pictures",
"title": "Select Image",
"enableOcr": true,
"actions": [{"id": "set_wallpaper", "name": "Set Wallpaper", "icon": "wallpaper"}]
}
}Set enableOcr: true to enable background OCR indexing for text search within images (requires tesseract).
Open Grid Browser (for emoji, icon selection, etc.):
{
"type": "gridBrowser",
"gridBrowser": {
"title": "Select Emoji",
"items": [
{"id": "😀", "name": "grinning face", "icon": "😀", "iconType": "text", "keywords": ["happy"]}
],
"columns": 10,
"cellAspectRatio": 1.0,
"actions": [{"id": "copy", "name": "Copy", "icon": "content_copy"}]
}
}Handler receives {"step": "action", "selected": {"id": "gridBrowser", "itemId": "😀", "action": "copy"}} on selection.
Show Form (multi-field input):
{
"type": "form",
"form": {
"title": "Add Note",
"submitLabel": "Save",
"fields": [
{"id": "title", "type": "text", "label": "Title", "required": true},
{"id": "content", "type": "textarea", "label": "Content", "rows": 6}
]
},
"context": "add_note"
}Handler receives {"step": "form", "formData": {"title": "...", "content": "..."}} on submit.
Field types: text, textarea, select, checkbox, password. Keyboard: Esc cancel, Ctrl+Enter submit.
| Mode | Behavior | Use Case |
|---|---|---|
realtime |
Search on every keystroke (default) | Fuzzy filtering, file search |
submit |
Search only on Enter | Text input, AI chat, adding items |
Set "inputMode": "submit" in results/card response to wait for Enter.
Add pluginActions to display toolbar buttons below the search bar. Useful for plugin-level operations like "Add", "Wipe All", "Refresh".
"pluginActions": [
{"id": "add", "name": "Add", "icon": "add_circle"},
{"id": "wipe", "name": "Wipe All", "icon": "delete_sweep", "confirm": "Wipe all? This cannot be undone."}
]- Keyboard shortcuts: Ctrl+1 through Ctrl+6
- Use
confirmfield for dangerous actions (shows inline confirmation dialog) - Handler receives
{"step": "action", "selected": {"id": "__plugin__"}, "action": "add"}
For plugins that need real-time updates or file watching, use daemon mode instead of polling:
{
"name": "My Plugin",
"daemon": {
"enabled": true,
"background": false
}
}background: false- Daemon runs only when plugin is active (e.g., live data displays)background: true- Daemon always runs (e.g., status badges, file watching)
Daemon handlers use an event loop to handle multiple requests and emit updates asynchronously. See plugins/README.md for full documentation with examples.
Benefits over polling:
- Single persistent process (more efficient)
- Real-time updates via inotify (instant, not scheduled)
- Bidirectional communication (hamr can control daemon behavior)
- Supports both foreground and background modes
- File watching with fallback to mtime polling
Example daemon response (live update):
# Emit update to individual items without full list refresh
emit({"type": "update", "items": [{"id": "volume", "gauge": {"value": 75, "max": 100}}]})
# Emit status update for plugin badge in main list
emit({"type": "status", "status": {"badges": [{"text": "5"}]}})
# Emit incremental index update (adds items to search)
emit({"type": "index", "mode": "incremental", "items": [{"id": "new-item", "name": "..."}]})Static Index (No Handler)
For simple actions that don't need dynamic logic, use staticIndex in the manifest:
{
"name": "Screenshot",
"description": "Capture screen region",
"icon": "screenshot",
"staticIndex": [
{
"id": "snip",
"name": "Screenshot Snip",
"description": "Capture and copy screen region",
"icon": "screenshot",
"keywords": ["screenshot", "capture", "snip"],
"execute": {
"command": ["bash", "-c", "grim -g \"$(slurp)\" - | wl-copy"],
"notify": "Screenshot copied",
"close": true
}
}
]
}Items appear directly in main search - no handler script needed.
Example plugins: accentcolor/, theme/, snip/
Hamr learns your search habits and creates automatic shortcuts. No configuration needed.
How it works:
- Type "ff", scroll to "Firefox", press Enter
- Next time you type "ff", Firefox appears at the top
- The system remembers the last 5 search terms for each item
Ranking algorithm:
- Learned shortcuts first - Items where you've used that exact search term before rank highest
- Frecency decides ties - Among learned shortcuts, most frequently/recently used wins
- Fuzzy matches last - Items that match but you haven't searched that way before
This means an item you use 10 times with "ff" will beat a command named "ff" that you've never executed.
What's tracked:
- Apps, actions, workflows, quicklinks
- URLs, workflow executions (wallpaper changes, file opens, etc.)
- Each item stores up to 5 recent search terms
Example:
Type "ff" → select Firefox → "ff" recorded
Type "ff" → select Firefox → frecency increased
Type "ff" → Firefox is now first (beats other "ff" matches you never use)
Type "wl" → select "Set wallpaper: sunset.jpg" → "wl" recorded
Type "wl" → wallpaper action appears first
Terms naturally age out based on frecency, so your shortcuts stay relevant as habits change.
When you open Hamr with an empty search, you may see suggested apps at the top marked with a sparkle icon. These combine your usage frequency with contextual predictions.
What triggers suggestions:
| Signal | Weight | Example |
|---|---|---|
| App sequences | High | VS Code suggested after opening Terminal |
| Session start | High | Email client suggested right after login |
| Time of day | Medium | Slack suggested at 9am if you always open it then |
| Workspace | Medium | Browser suggested on workspace 1 |
| Day of week | Low | Personal apps suggested on weekends |
How it works:
- Every app launch records context (time, workspace, monitor, previous app)
- Patterns are detected using Wilson score intervals (statistically sound for small samples)
- Context signals combine with frecency (frequency + recency) into a final score
- Apps above 25% confidence appear as suggestions (max 2)
No configuration needed. Suggestions appear automatically as patterns emerge. Use Hamr normally and it learns your habits within a few days.
Hamr is configured via ~/.config/hamr/config.json. Use the built-in settings plugin (/settings) to browse and modify options - no manual editing needed.
The action bar shortcuts are fully customizable. Edit ~/.config/hamr/config.json to change which prefixes appear and which plugins they trigger:
{
"search": {
"actionBarHints": [
{ "prefix": "~", "icon": "folder", "label": "Files", "plugin": "files" },
{ "prefix": ";", "icon": "content_paste", "label": "Clipboard", "plugin": "clipboard" },
{ "prefix": "/", "icon": "extension", "label": "Plugins", "plugin": "action" },
{ "prefix": "!", "icon": "terminal", "label": "Shell", "plugin": "shell" },
{ "prefix": "=", "icon": "calculate", "label": "Math", "plugin": "calculate" },
{ "prefix": ":", "icon": "emoji_emotions", "label": "Emoji", "plugin": "emoji" }
]
}
}Each hint has:
- prefix: The trigger character (e.g.,
~,;,:) - icon: Material Symbol name
- label: Display name shown in the action bar
- plugin: Plugin ID to launch (e.g.,
files,clipboard,emoji) oractionfor plugin search mode
You can reorder, remove, or add hints. For example, to replace emoji with notes:
{ "prefix": ":", "icon": "note", "label": "Notes", "plugin": "notes" }Configuration Reference
| Category | Option | Default | Description |
|---|---|---|---|
| Apps | terminal |
ghostty |
Terminal emulator for shell commands |
terminalArgs |
--class=floating.terminal |
Arguments passed to terminal | |
shell |
zsh |
Shell for command execution (zsh, bash, fish) | |
| Behavior | stateRestoreWindowMs |
30000 |
Time (ms) to preserve state after soft close (0 to disable) |
clickOutsideAction |
intuitive |
Click outside behavior: intuitive, close, or minimize |
|
| Search | maxDisplayedResults |
16 |
Maximum results shown in launcher |
maxRecentItems |
20 |
Recent history items on empty search | |
debounceMs |
50 |
Search input debounce (ms) | |
diversityDecay |
0.7 |
Decay for consecutive results from same plugin (0-1, lower = more diverse) | |
maxResultsPerPlugin |
0 |
Hard limit per plugin (0 = no limit) | |
| Appearance | backgroundTransparency |
0.2 |
Background transparency (0-1) |
launcherXRatio |
0.5 |
Horizontal position (0=left, 1=right) | |
launcherYRatio |
0.1 |
Vertical position (0=top, 1=bottom) | |
fontScale |
1 |
Font scaling factor (0.75=min, 1.5=max) | |
| Sizes | searchWidth |
580 |
Search bar width (px) |
maxResultsHeight |
600 |
Max results container height (px) | |
| Paths | wallpaperDir |
"" |
Custom wallpaper directory (empty = ~/Pictures/Wallpapers) |
colorsJson |
"" |
Custom colors.json path (empty = ~/.config/hamr/colors.json) |
File Structure
~/.config/quickshell/hamr/ # Symlink to cloned repo
├── shell.qml # Entry point
├── GlobalStates.qml # UI state
├── plugins/ # Built-in plugins (read-only)
├── modules/
│ ├── common/ # Appearance, Config, widgets
│ ├── launcher/ # Launcher UI components
│ └── imageBrowser/ # Image browser UI
├── services/ # LauncherSearch, PluginRunner, etc.
└── scripts/ # Thumbnail generation, OCR indexing
~/.config/hamr/
├── plugins/ # User plugins (override built-in)
├── config.json # User configuration
├── quicklinks.json # Custom quicklinks
└── plugin-indexes.json # Plugin data and frecency (auto-generated)
| Hamr | Vicinae | rofi/wofi | |
|---|---|---|---|
| Plugin Language | Any (Python, Bash, Go...) | TypeScript/React | Bash scripts |
| Plugin Protocol | Simple JSON stdin/stdout | Raycast-compatible SDK | Custom modes |
| Linux Native | Yes, built for Linux | Raycast shim layer | Yes |
| Rich UI | Cards, thumbnails, image browser | React components | Text-based |
| Dependencies | Quickshell only | Qt + Node.js runtime | Minimal |
| Learning Curve | ~10 min to first plugin | TypeScript + React knowledge | Script-based |
Launchers like Vicinae aim for Raycast extension compatibility, but this approach has limitations on Linux:
- macOS-hardcoded paths - Many Raycast extensions use paths like
~/Library/...that don't exist on Linux (example issue) - macOS APIs - Extensions often rely on macOS-specific APIs (AppleScript, Finder, Keychain)
- Heavy runtime - Requires Node.js + TypeScript toolchain for plugins
Hamr takes a different approach: plugins are native Linux scripts using a simple JSON protocol. Write in any language, use Linux-native tools (fd, fzf, wl-copy), and integrate with your existing dotfiles.
# A complete Hamr plugin - no SDK, no build step
#!/usr/bin/env python3
import json, sys
print(json.dumps({"type": "results", "results": [{"id": "1", "name": "Hello"}]}))This means browser bookmarks on Linux just read from ~/.config/google-chrome/Default/Bookmarks directly - no compatibility layer needed.
Want functionality from a Raycast extension? Use the built-in create-plugin workflow:
/create-plugin
> Replicate this Raycast extension: https://github.com/raycast/extensions/tree/main/extensions/browser-bookmarks
The AI will analyze the Raycast extension, translate the patterns to Hamr's protocol, and create a native Linux plugin. See plugins/AGENTS.md for the full conversion guide.
Hamr is fully local and offline. No data ever leaves your machine.
| Data | Location | Purpose |
|---|---|---|
| Plugin data | ~/.config/hamr/plugin-indexes.json |
Frecency, search terms, smart suggestions |
| Configuration | ~/.config/hamr/config.json |
User preferences |
| Clipboard history | Via cliphist (system) |
Clipboard search |
What's tracked for smart suggestions:
- App launch counts and timestamps
- Time of day / day of week patterns
- Workspace and monitor associations
- App sequence patterns (which app follows another)
What's NOT tracked:
- No network requests, analytics, or telemetry
- No file contents or document text
- No keystrokes or input outside Hamr
- No data shared with plugins (they only receive search queries)
To clear all history: rm ~/.config/hamr/plugin-indexes.json
Hamr was originally extracted and adapted from end-4's illogical-impulse. Major thanks to end-4 for the Material Design theming, fuzzy search, widget components, and overall architecture that inspired this project.
Hamr is now fully standalone and works out of the box on any supported Wayland compositor (Hyprland, Niri).
This project is licensed under the GNU General Public License v3.0 (GPL-3.0).
Hamr is a derivative work based on end-4/dots-hyprland, which is also licensed under GPL-3.0. Major thanks to end-4 for the Material Design theming, fuzzy search, widget components, and overall architecture that made this project possible.
See the LICENSE file for the full license text.
