Workspace identity for an Omarchy 4 desktop that runs tmux in every terminal. The idea is one name for one piece of work: the tmux session is named after the Hyprland workspace it lives on, the name is hashed to a colour with colorhash, and the same pill shows up in the top bar, in the tmux status line, and (with operator) on a macropad's LEDs. Rename in any one place and the others follow. Links opened from inside a session land in a Firefox tab group named and coloured after that session.
Three parts, all in this repo:
- a bar widget (
Workspaces.qmland friends) — the host capsule doubles as the Omarchy menu button, then one entry per workspace asid·name, the focused one a filled pill in its colorhash colour, urgent ones with a bell. Right-click a workspace for its window menu; SUPER+ALT+N opens a rename dialog with a live preview of the pill you are about to get. bin/— the scripts the widget, the keybinds, and tmux hooks call.lib/— the shared name/colour contract they all source.
Built for one desk. Shared in case it's useful on yours. No warranty, no promises, no roadmap — but if it breaks in an interesting way, an issue is welcome.
- Omarchy 4 (the QML uses Omarchy's own shell components; it will not run on
Omarchy 3 or bare Quickshell), Hyprland,
jq - tmux, with the status bar reading the
@wsid_*options this writes (ours is in the author's dotfiles; the option names are documented inbin/wsid-tmux-colors) - colorhash installed per
its README, so
~/.config/colorhash/palette.jsonexists. Without it every script still works; the bar falls back to the theme foreground and tmux to ANSI colours. - For
ws-open: Firefox and omarchy-firefox-bridge for the tab-group routing. Without the bridge, links fall through to plain Firefox.
omarchy plugin add https://github.com/interslice-systems/workspaces --enable
R=~/.config/omarchy/plugins/interslice.workspaces
mkdir -p ~/.local/bin ~/.local/lib
for f in "$R"/bin/*; do ln -sfn "$f" ~/.local/bin/; done
ln -sfn "$R"/lib/workspace-identity-lib ~/.local/lib/
ln -sfn "$R"/lib/ws-browser-lib.bash ~/.local/lib/The scripts source the lib from ~/.local/lib and read
~/.config/colorhash/palette.json. tmux picks up the colours if your status
bar calls ~/.local/bin/wsid-tmux-colors — ours does, guarded so a box
without it keeps its plain colours:
if-shell -b "test -x ~/.local/bin/wsid-tmux-colors" {
run-shell -b ~/.local/bin/wsid-tmux-colors
set-hook -g window-renamed 'run-shell -b "~/.local/bin/wsid-tmux-colors -w \"#{hook_window}\" -n \"#{hook_window_name}\""'
set-hook -g window-linked 'run-shell -b "~/.local/bin/wsid-tmux-colors -w \"#{hook_window}\" -n \"#{hook_window_name}\""'
set-hook -ag session-renamed 'run-shell -b ~/.local/bin/wsid-tmux-colors'
}
if-shell -b "test -x ~/.local/bin/ws-rename-workspace" {
set-hook -g session-renamed 'run-shell -b "~/.local/bin/ws-rename-workspace \"#{hook_session_name}\""'
}Keybinds, in ~/.config/hypr/bindings.lua:
o.bind("SUPER + ALT + RETURN", "Tmux (ws-attach)", os.getenv("HOME") .. "/.local/bin/ws-attach")
o.bind("SUPER + ALT + N", "Rename workspace", os.getenv("HOME") .. "/.local/bin/ws-rename")URL routing is opt-in: install a desktop entry whose Exec is
~/.local/bin/ws-browser %u with MimeType=x-scheme-handler/http;x-scheme-handler/https;,
and point ~/.config/mimeapps.list at it. Nothing runs ws-open unless a
machine's mimeapps says so.
bin/ws-blackbox records every tmux window, its panes, and the Claude Code session in each
pane once a minute (a systemd user timer; nothing stays resident). What disappears stays in
the bar, with ↺ to bring it back and ✕ to forget it:
| What disappeared | Shown as | ↺ |
|---|---|---|
| a Claude exited, its window is still open | caption claude <name> · exited |
types its resume line at that pane's prompt |
| a tmux window (closed, crashed, killed) | dimmed row at its old index | rebuilds it: index, name, splits, layout, each pane's directory, and a resume line wherever a Claude was |
| a whole workspace (server died, OOM, reboot) | outlined pill | opens a terminal on a free workspace, restarts the session under its name, rebuilds each window |
↺ types cd <dir> && claude --resume <id> without pressing Enter: Enter resumes, Ctrl+U
declines. It always takes you to the pane, and pressing it again never types a second copy.
A rebuilt window keeps its Claude record, so it goes on offering the resume until a Claude is
actually running there. Running processes other than Claude -- dev servers, editors, ssh --
are listed in the row's caption as a reminder, never replayed; scrollback and shell state are
lost with the server.
ln -sfn "$R"/bin/ws-blackbox ~/.local/bin/
for u in "$R"/systemd/*; do ln -sfn "$u" ~/.config/systemd/user/; done
systemctl --user daemon-reload && systemctl --user enable --now ws-blackbox.timer
ws-blackbox status # is it recording
ws-blackbox what # what is gone, why (journald), and the resume linesState lives in ~/.local/state/ws-blackbox/ (0700). It stores window names, working
directories, Claude session ids and short command names -- never full command lines. Nothing
is pruned automatically: an entry leaves only when you dismiss it (a restored one moves to its
rebuilt window). Without the
timer installed the widget looks exactly as it did.
Workspace restore asks Hyprland to launch the terminal, so the terminal (and any tmux server
it starts) is Hyprland's child whoever ran the restore. ws-blackbox itself only ever talks
to tmux with -N, never renames or switches sessions, and never sends Enter.
| Command | What it does |
|---|---|
ws-attach |
Open or focus the terminal+tmux pair for the active workspace. The session name is derived from the workspace name; an existing local terminal is found by tmux client ancestry |
ws-rename |
Rename the active workspace through its tmux session when one is attached here. With no args it summons the bar's rename dialog and falls back to omarchy-menu-input when the shell is down; --apply <raw> is the dialog's callback |
ws-rename-workspace <session> |
Mirror a tmux session rename onto the Hyprland workspace it lives on. Wired to tmux's session-renamed hook |
ws-name-suggest |
Print a candidate name for the active workspace (the git repo containing the focused pane's cwd), for the dialog to prefill |
ws-open <url> |
Route an http(s) URL to the Firefox window on the workspace of the tmux session it came from, into a tab group named and coloured after that session. No session, plain Firefox |
ws-browser |
The browser-facing adapter behind the desktop entry: normal or private launch, or hand off to ws-open |
wsid-tmux-colors |
Stamp colorhash colours into tmux user options (@wsid_ok, host, per-session, per-window) |
tmux-local-clients |
JSON of tmux sessions attached from local terminal windows, resolved by process ancestry (kitty, foot, ghostty) |
tmux-rename [window|session] |
Rename the current tmux window or session in a popup |
lib/workspace-identity-lib owns wsid_session_name (the one sanitizer every
path uses), wsid_reject_name, and the colorhash accessors. Names are stored
exactly; NFC normalization is only for the hash.
| Action | Result |
|---|---|
| Left click on the host capsule | Omarchy menu |
| Right click on the host capsule | a terminal |
| Left click on a workspace | focus it |
| Right click on a workspace | its window menu (WsMenu.qml) |
omarchy-shell shell summon interslice.workspaces |
the rename dialog (what ws-rename calls) |
The widget reads ~/.config/colorhash/palette.json through a FileView, so
swapping the palette recolours the bar live. Urgency comes straight from
Hyprland's per-window urgent hint (a BEL in any terminal on the workspace),
and clears when you focus the workspace.
for t in test/test-*; do bash "$t"; done
/usr/lib/qt6/bin/qmltestrunner -input tests/tst_WorkspaceMenuModel.qmlEvery suite is hermetic — hyprctl, tmux, the prompt and the notifier are all
stubbed — except that the two colour suites read the installed palette and
skip when it is absent. test/test-ws-blackbox runs a real tmux, but on a private
server (TMUX_TMPDIR points at a temp dir, and the suite checks the socket path
before touching anything). tests/tst_WorkspaceMenuModel.qml is a Qt Quick Test
for the window-menu model.
- Editing through a symlinked plugin folder needs
omarchy restart shell; the shell's file watcher does not follow the symlink and a first-compile error sticks in Qt's cache until a restart. - Hyprland permits two workspaces with the same name.
ws-renamerefuses that, because two workspaces would then fight over one tmux session.
MIT. See LICENSE.