Skip to content

Apps and segmentation

Samy Lemcelli edited this page Aug 24, 2026 · 1 revision

Apps and segmentation

An app belongs to one space. That does two things: its notifications are routed to that space, and its windows open on that space's workspaces wherever you launch it from.

"apps": ["Signal", "Spotify"]

Launch Steam while work is active and it still lands on a personal workspace, silently, without pulling you there.

How placement works

omarchy-spaces keybinds install writes Hyprland window rules alongside the bindings, generated from every space's apps list:

o.window("^(steam)$", { workspace = "4 silent" })
o.window("^(Slack)$", { workspace = "11 silent" })

Static rules rather than a daemon watching window-open events. Hyprland places the window correctly the first time, so there is no visible jump, and there is nothing left running.

Preview before applying:

omarchy-spaces windowrules show     # which app lands where
omarchy-spaces windowrules print    # the generated Lua

The long form

A bare string covers the common case where the notification name and the window class are the same text. When they are not, or you want more control:

"apps": [
  "Signal",
  { "name": "Steam", "class": "steam", "slot": 4 },
  { "name": "Brave-browser", "windowRule": false },
  { "name": "YouTube Music", "class": "ytmusic", "shared": true }
]
Field Meaning
name Matches the app_name a notification carries
class Matches the Hyprland window class. Defaults to name
slot Which workspace of the owning space its windows open on. Defaults to 1
windowRule Set false to route notifications but never move windows
shared Belongs to no space. See below

Browsers need windowRule: false

Every profile of a browser shares one window class, so a placement rule on Brave-browser drags your work profile's windows into your personal space. The browser is already per space through browser.profile, so it does not need a window rule at all.

omarchy-spaces validate catches this:

warn: personal: 'Brave-browser' is a browser used by a space; a window rule on
      it would pull every profile into personal. Set "windowRule": false on that
      entry to keep notification routing without the placement.

Shared apps

Some apps belong to no space. A music player is the obvious one: you want it in both spaces, you want its notifications either way, and you do not want its window yanked around when you switch.

"defaults": {
  "apps": [ { "name": "YouTube Music", "class": "ytmusic", "shared": true } ]
}

A shared app is owned by nobody, so spaceForApp returns nothing for it, its notifications always get through, and no window rule is generated.

Shared entries are read from defaults.apps directly, not through inheritance. Arrays replace when inherited, so a space with its own apps list would otherwise never see a shared entry in defaults, which is exactly where people will put one. This is the one place inheritance is deliberately bypassed.

Finding the right names

The notification name and the window class are different things and are often different strings.

# what an app calls itself in notifications
ls -t ~/.local/state/omarchy/notifications/history/*.json | head -1 | \
  xargs python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['app'])"

# window classes of what is open now
hyprctl clients -j | python3 -c "
import json,sys
for c in json.load(sys.stdin): print(c['class'])"

The config app's app picker lists both, labelled by source, with names seen in notifications ranked first.

Clone this wiki locally