-
-
Notifications
You must be signed in to change notification settings - Fork 91
App Rules
App rules let Rift make decisions automatically when it discovers a window. A rule can send a window to a workspace, make it floating, give it an initial position or size, focus it, or tell Rift not to manage it at all.
Rules are configured in the [virtual_workspaces] section of ~/.config/rift/config.toml:
[virtual_workspaces]
enabled = true
auto_assign_windows = true
app_rules = []Tip
App rules are evaluated when windows are discovered(or opened). If a title-based rule needs to react after an application changes its title, enable reapply_app_rules_on_title_change as described below.
Each item in app_rules has two kinds of fields:
- Match fields identify which windows the rule applies to.
- Action fields describe what Rift should do with a matching window.
For example:
app_rules = [
{ app_id = "com.apple.Terminal", workspace = "coding" },
{ app_id = "com.apple.Calculator", floating = true, size = { w = 420.0, h = 680.0 } },
]The first rule sends Terminal windows to the workspace named coding. The second makes Calculator windows float and gives them an initial size.
Rules do not continuously force a window into a state after every manual change. Position and size are initial placement hints, and a floating window remains freely movable afterward.
All match fields supplied in one rule must match the same window. In other words, fields are combined with AND, not OR.
| Field | Match behavior | Example |
|---|---|---|
app_id |
Case-insensitive exact match against the application bundle identifier | "com.apple.Safari" |
app_name |
Case-insensitive fuzzy name match; the configured value and reported name may contain one another | "Safari" |
title_regex |
Case-insensitive regular expression matched against the window title | `"^(Inbox |
title_substring |
Case-insensitive literal substring matched against the window title | "Preferences" |
ax_role |
Case-sensitive exact match against the macOS Accessibility role | "AXWindow" |
ax_subrole |
Case-sensitive exact match against the macOS Accessibility subrole | "AXDialog" |
A rule with only app_id matches every discovered window belonging to that application. A rule with both app_id and title_substring matches only that app's windows whose titles contain the configured text.
Use mdls with the application bundle to inspect its identifier:
mdls -name kMDItemCFBundleIdentifier -r /Applications/Safari.appBundle identifiers are usually more stable than application names, so prefer app_id for rules that should survive renames or localization.
Use title_substring for straightforward title matching. Use title_regex when the title has a predictable pattern:
app_rules = [
{ app_id = "com.example.Editor", title_regex = "^project-[0-9]+$", workspace = "coding" },
]The regex is case-insensitive. Invalid regular expressions are ignored and Rift logs a warning; the rest of the configuration can still load. TOML strings use backslashes for escaping, so prefer single-quoted TOML strings when that makes a pattern easier to read:
app_rules = [
{ title_regex = '^Notes: \\d+$', floating = true },
]| Field | Behavior |
|---|---|
workspace |
Assigns the window to a workspace by 0-based index or by workspace name. If omitted, the active workspace is used. |
floating |
Makes the matching window floating when true. Set it to false in a more specific rule to keep an exception tiled. |
position |
Sets an initial normalized position for a floating window. x = 0.0, y = 0.0 is top-left; x = 1.0, y = 1.0 is bottom-right. |
size |
Sets an initial size in logical pixels. w and h are optional, so either dimension can be left out. |
focus |
Focuses the matching window after applying the rule. If necessary, Rift switches to the target workspace. |
manage |
Controls whether Rift manages the window. false makes Rift ignore it completely; the default is true. |
position and size are tables:
position = { x = 0.5, y = 0.25 }
size = { w = 900.0, h = 650.0 }For a floating window, Rift applies the requested size and then resolves the normalized position against the available screen area. For a tiled window, a requested size is applied once after insertion into the layout rather than turning the window into a floating window.
Rift evaluates every rule that matches a window, then chooses one rule to provide the actions. The selection process is:
- Count the non-empty match fields in each matching rule. This is its specificity.
- The rule with the highest specificity wins.
- If rules have equal specificity, the rule that appears earlier in
app_ruleswins.
Action fields such as workspace, floating, size, and focus do not make a rule more specific. Specificity is based only on the match fields.
This makes a general rule plus a more specific exception easy to express:
app_rules = [
# Two match fields: this wins for Terminal windows titled "ssh ...".
{ app_id = "com.apple.Terminal", title_regex = "^ssh ", workspace = "remote", floating = false },
# One match field: other Terminal windows go to coding.
{ app_id = "com.apple.Terminal", workspace = "coding" },
]The order of the rules matters when their specificity ties:
app_rules = [
# This wins because it appears first when both rules match equally.
{ app_name = "Browser", workspace = "web" },
{ app_name = "Browser", workspace = "research" },
]When designing exceptions, give the exception an additional match field whenever possible instead of relying on order alone.
Workspace indexes are zero-based. This sends an application to the third workspace:
app_rules = [
{ app_id = "com.apple.Xcode", workspace = 2 },
]Names are often easier to maintain. Define them in the same section and reference them from a rule:
[virtual_workspaces]
workspace_names = ["main", "web", "coding", "chat"]
app_rules = [
{ app_id = "com.microsoft.VSCode", workspace = "coding" },
{ app_id = "com.apple.Safari", workspace = "web" },
{ app_id = "com.hnc.Discord", workspace = "chat" },
]app_rules = [
{ app_id = "com.apple.Calculator", floating = true },
{ app_id = "com.raycast.macos", floating = true },
]Match the dialog using its Accessibility subrole when possible:
app_rules = [
{ app_id = "com.example.Editor", ax_subrole = "AXDialog", floating = true },
{ app_id = "com.example.Editor", floating = false },
]The dialog rule has two match fields and therefore wins over the general application rule for matching dialogs.
Normalized coordinates describe the available travel space after the window's size is applied. 0.5, 0.5 centers the window:
app_rules = [
{ app_id = "com.example.Preview", floating = true, position = { x = 0.5, y = 0.5 }, size = { w = 800.0, h = 600.0 } },
]app_rules = [
{ app_id = "com.apple.Spotlight", focus = true },
]Use focus = true carefully: applications that open helper windows frequently can cause Rift to switch workspaces unexpectedly.
app_rules = [
{ app_id = "com.example.Overlay", manage = false },
]An unmanaged window is invisible to Rift's tiling, floating, and workspace assignment logic. Use this for overlays, helper windows, or applications that should behave exactly as they do without a window manager.
[virtual_workspaces]
reapply_app_rules_on_title_change = true
app_rules = [
{ app_id = "com.example.Editor", title_regex = "^Build failed", workspace = "debug", focus = true },
]With this option enabled, Rift re-evaluates a known window when its title changes. It is disabled by default so ordinary title updates do not repeatedly reassign or focus windows.
- Confirm the application bundle identifier with
mdls. - Start with only
app_idorapp_name, then add title or Accessibility conditions one at a time. - Remember that all match fields are ANDed together.
- Check title spelling and capitalization when using
title_substring; matching is case-insensitive, but the text still needs to be present. - For dialogs and sheets, inspect the application's Accessibility attributes.
AXDialogandAXSystemDialogare common subroles, but applications can report different values.
- Check that workspace indexes are zero-based.
- Prefer a workspace name over a numeric index if the workspace list changes frequently.
- Look for another matching rule with more match fields.
- If two rules have equal specificity, remember that the earlier rule wins.
- Ensure
auto_assign_windowsis enabled.
Some applications create a window before its final title is available. Enable reapply_app_rules_on_title_change so Rift evaluates the rule again after title updates.
Make sure the file is at ~/.config/rift/config.toml, check that the TOML is valid, and reload the configuration:
rift-cli execute config reloadYou can also enable configuration hot reload in [settings]:
[settings]
hot_reload = trueUse rift-cli query windows to inspect the windows Rift currently knows about, including their application and title data. If a title_regex is invalid, Rift logs a warning and ignores that rule.