Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dotfiles terminal preview

License Platform WM Python Shell

También disponible en Español


Personal system configuration for CachyOS + Omarchy (Hyprland on Wayland).

This repository contains the scripts, daemons, keybindings, and config files built on top of the Omarchy base. Everything solves a specific problem — nothing is here just because it looked interesting. The goal is to make a fresh install feel like home in under five minutes.


Contents

Component What it does
claude-copy-response Copies Claude Code's last response to the clipboard with one button press
claude-bypass Removes permission prompts from Claude Desktop
mouse-dpi-daemon Shows a desktop notification when the DPI button is pressed on a SteelSeries mouse
mouse-restore Restores DPI presets and polling rate after any hardware reset
bindings.lua Hyprland keybindings — apps, workspaces, scratchpad, mouse shortcuts
mako/config Notification styling with auto-dismiss

Preview

Notification previews

Left: DPI change notification — Right: Claude response copied to clipboard


Requirements

  • CachyOS with Omarchy installed
  • WezTerm — required by claude-copy-response for active session detection
  • Python 3.10+
  • wl-clipboard — Wayland clipboard utility
  • libnotify — desktop notification support (notify-send)
  • rivalcfg (optional) — only needed if you use a SteelSeries mouse
# Required
sudo pacman -S wl-clipboard libnotify

# Optional — SteelSeries mouse tools (AUR)
yay -S rivalcfg

Quick start

git clone https://github.com/YOUR_USERNAME/dotfiles.git ~/dotfiles
cd ~/dotfiles
bash install.sh

The installer creates symlinks from this repo to the correct system paths. Existing files are backed up with a timestamp before being replaced — nothing is silently overwritten.

# Preview what would change without touching anything
bash install.sh --dry-run

After installing:

hyprctl reload        # apply Hyprland keybindings
mouse-restore         # restore mouse config (SteelSeries only)
pkill mako && mako &disown  # reload notification daemon

How the installer works

install.sh uses symlinks rather than file copies. This means:

  • Editing any file in ~/dotfiles/ takes effect immediately — no re-run needed
  • git pull to update the system to the latest version
  • git diff to audit what changed between updates

Components

claude-copy-response

The problem: Claude Code runs in the terminal and has no built-in button to copy its last response. Claude Desktop has a copy icon — the CLI doesn't.

The solution: A Python script triggered by a mouse side button (or any keybinding) that locates the active Claude session, extracts the last assistant response, and copies it to the Wayland clipboard.

claude-copy-response flow

Why it's not trivial:

WezTerm runs as a single process for all tabs — you can't identify which tab is focused by PID alone. The script uses WezTerm's IPC (wezterm cli list) to match the active Hyprland window title against open panes and recover the working directory of the focused tab.

Claude's .jsonl transcript has a subtlety: text blocks and tool_use blocks (internal tool calls Claude makes) carry different message IDs. Searching for "the last assistant message by ID" always lands on a tool invocation — never on readable text. The correct approach is to find the last real user message (not a tool result) and collect every text block that follows it.

Fallback: If WezTerm IPC fails, the script falls back to the globally most-recently-modified .jsonl across all Claude sessions.

Dependencies: Python 3, wl-copy, notify-send, hyprctl, wezterm


claude-bypass

The problem: Claude Desktop prompts for confirmation before every file read, write, or shell command. In an active workflow this interrupts constantly.

The solution: A thin Bash wrapper that sits between the Claude .desktop launcher and the real binary. It intercepts session-start calls and injects --dangerously-skip-permissions --permission-mode bypassPermissions automatically, without touching non-session calls like claude --version.

Setup: Point CLAUDE_CODE_LOCAL_BINARY in Claude Desktop's .desktop entry to this wrapper instead of the real binary at /opt/claude-code/bin/claude.

Note: This disables all permission prompts. Only use it on a machine where you trust the agent's actions.


mouse-dpi-daemon

The problem: The SteelSeries Aerox 3 Wireless has a hardware button that cycles through DPI presets, but Linux provides no visual indicator of which preset is active.

The solution: A Python daemon that reads raw HID events from the mouse and fires a desktop notification when the DPI button is pressed.

The mouse sends a vendor-specific HID report 0xAD 0x02 <preset_index> on its if04 interface whenever the DPI button cycles. The daemon opens the device by its stable by-id symlink (which survives USB reconnects) and maps the index to a human-readable value from ~/.config/mouse-dpi/presets.

Why a daemon and not a one-shot script:

The mouse disconnects and reconnects on its own — wireless sleep, USB re-plug, port changes. A one-shot script dies on disconnect. The daemon wraps reads in an outer retry loop and simply waits for the device path to reappear. The systemd service adds a second layer: Restart=always with a 3-second backoff means the process always comes back, even on unexpected crashes.

DPI presets file (config/mouse-dpi/presets):

800 1400

Space-separated values, one per hardware preset slot. Update this file whenever you change presets with rivalcfg.


mouse-restore

The problem: rivalcfg -r performs a full firmware reset that wipes all DPI presets and resets the polling rate to its default. This is easy to trigger accidentally during debugging.

The solution: A single script that re-applies the complete mouse configuration in the correct order.

rivalcfg -s "800, 1400"                      # restore DPI presets
rivalcfg -p 1000                             # restore polling rate to 1000 Hz
systemctl --user restart mouse-dpi.service   # restart the notification daemon

Run it after: rivalcfg -r, a USB port change, or any time DPI notifications stop appearing.


Keybindings

config/hypr/bindings.lua is written in Lua using Omarchy's binding layer on top of Hyprland. Organized in sections — application launchers, web apps, workspace navigation, and utility shortcuts.

App toggles on function keys

o.bind("F4", "Discord",  "discord-toggle")   -- open if closed, hide if open
o.bind("F5", "Spotify",  "spotify-toggle")
o.bind("F8", "WhatsApp", "whatsapp-toggle")

To change an app, replace the toggle name or use a launch + focus pair:

o.bind("F4", "Slack", { launch = "slack", focus = "^slack$" })

-- Find the window class of any running app:
-- hyprctl clients | grep class

App launchers on SUPER + SHIFT

o.bind("SUPER + SHIFT + G", "Signal",   { launch = "signal-desktop", focus = "^signal$" })
o.bind("SUPER + SHIFT + O", "Obsidian", { launch = "obsidian",        focus = "^obsidian$" })
o.bind("SUPER + SHIFT + D", "Docker",   { tui = "lazydocker" })

To add your own:

o.bind("SUPER + SHIFT + T", "App Name", { launch = "binary-name", focus = "^window-class$" })

Web apps

Any URL opened as a webapp gets a focused browser window:

o.bind("SUPER + SHIFT + A", "ChatGPT", { webapp = "https://chatgpt.com" })
o.bind("SUPER + SHIFT + Y", "YouTube", { webapp = "https://youtube.com/" })

To add more:

o.bind("SUPER + SHIFT + L", "Linear", { webapp = "https://linear.app" })

Numpad workspace switching

SUPER + KP_1..9,0          → Switch to workspace 1–10
SUPER + SHIFT + KP_1..9,0  → Move focused window to workspace 1–10

Keycodes are used instead of key names for reliable Wayland compatibility.

Scratchpad

ALT + S   → Send focused window to scratchpad (hide it globally)
ALT + D   → Pull window back into the current workspace

Utility shortcuts

F1          → Toggle keybindings cheatsheet
SUPER + W   → Close window (handles Discord/Vesktop separately)
ALT + KP_+  → Screenshot selected region → clipboard
mouse:275   → Copy last Claude Code response → clipboard

Notifications

Mako is Omarchy's notification daemon. The config layers over Omarchy's base styles with two changes:

default-timeout=3000    # auto-dismiss after 3 seconds

[urgency=critical]
default-timeout=0       # critical alerts stay until manually dismissed

Apply after editing:

pkill mako && mako &disown

Note: Omarchy symlinks ~/.config/mako/config to the current theme's file. The installer replaces that symlink with one pointing to this repo, so the timeout persists across theme changes.


Customization

Swapping an app on a function key

Open config/hypr/bindings.lua, change the app name/binary, save. Hyprland reloads automatically. Verify with hyprctl configerrors.

Changing DPI presets

# 1. Apply to hardware
rivalcfg -s "800, 1400, 3200"

# 2. Update the presets file to match
echo "800 1400 3200" > config/mouse-dpi/presets

# 3. Restart the daemon
systemctl --user restart mouse-dpi.service

Adapting the daemon to a different mouse

The daemon watches for a specific HID path and byte pattern. To find the right values for a different mouse:

# List available HID devices
ls /dev/input/by-id/ | grep -i your-mouse

# Capture raw HID output while pressing the DPI button
sudo cat /dev/input/by-id/YOUR-DEVICE-hidraw | xxd | head -20

Update HIDRAW and the 0xAD 0x02 byte check in bin/mouse-dpi-daemon accordingly.


Troubleshooting

Left click stops responding (SteelSeries Aerox 3)

This mouse has an intermittent hardware issue where BTN_LEFT gets stuck in a "pressed" state at the firmware level. The OS stops generating new click events because it believes the button is still held down.

# Confirm the stuck state
evtest /dev/input/by-id/usb-SteelSeries_*-event-mouse

Fix: Press the left button firmly several times. This mechanically resets the contact. There is no software workaround.

DPI notifications stopped appearing

The mouse was likely reset. Run:

mouse-restore

If that doesn't help:

systemctl --user status mouse-dpi.service
journalctl --user -u mouse-dpi.service -n 30

Notifications don't auto-dismiss

A stale mako process is blocking the new config:

pkill mako && mako &disown

Keybindings not active after editing bindings.lua

hyprctl reload && hyprctl configerrors

claude-copy-response picks the wrong session

Debug mode prints which pane was matched and which .jsonl was selected:

claude-copy-response --debug

Roadmap

claude-copy-response

  • Multi-terminal support — extend session detection beyond WezTerm to Kitty, Alacritty, Ghostty
  • --markdown flag — preserve code blocks and headers when pasting into rich-text contexts
  • --code flag — extract only the last code block from the response

Mouse daemon

  • Auto-detect the connected SteelSeries model instead of requiring a hardcoded device path
  • Support multiple simultaneously connected devices

System

OS CachyOS (Arch Linux, BORE-scheduled kernel)
Window Manager Hyprland via Omarchy
GPU NVIDIA (proprietary drivers)
Terminal WezTerm
Mouse SteelSeries Aerox 3 Wireless

License

MIT — use it, fork it, adapt it freely.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages