# rift config # Copy this file to ~/.config/rift/config.toml or $HOME~/.config/rift/config.toml [settings] # Animations # - animate: master switch for all window animations # - animation_duration: seconds per animation (>= 0.0, typical 0.15–0.35) # - animation_fps: frames per second (0.0 = display refresh rate). 60–120 recommended. # ease_in_circ, ease_out_circ, ease_in_out_circ animate = false animation_duration = 0.3 animation_fps = 100.0 # Space activation behavior # - If true, spaces start inactive (rift does not manage windows there) # and you must toggle activation via a keybind (toggle_space_activated). # - If false, spaces are managed by default and you can disable specific ones. # Default is true; uncomment to change. #default_disable = false # Mouse/Focus behavior # - focus_follows_mouse: moving the mouse into a window focuses it # - mouse_follows_focus: when focus changes, move the mouse to the focused window # - mouse_hides_on_focus: hide the mouse cursor after focusing a window focus_follows_mouse = true mouse_follows_focus = true mouse_hides_on_focus = true # focus_follows_mouse_disable_hotkey temporarily suspends focus-follows-mouse while held # e.g. "Fn"; omit or set to null to keep focus-follows-mouse always active #focus_follows_mouse_disable_hotkey = "Fn" # Prevent certain apps from stealing focus/causing workspace switches # Provide bundle identifiers (e.g., "com.apple.Spotlight") # Examples: # "com.apple.dock", "com.apple.systemuiserver", "com.apple.SecurityAgent", # "com.raycast.macos", "com.apple.Spotlight" auto_focus_blacklist = [] # Run commands on start # # Tip: You can subscribe to rift events and trigger your own scripts. The command will # receive environment variables with context: # RIFT_EVENT_TYPE # "workspace_changed" | "windows_changed" | "window_title_changed" # RIFT_WORKSPACE_ID # workspace id # RIFT_WORKSPACE_NAME # workspace name # RIFT_WORKSPACE_INDEX # workspace index (window_title_changed only) # RIFT_WINDOW_COUNT # number of windows (windows_changed only) # RIFT_WINDOWS # comma-separated window ids (windows_changed only) # RIFT_WINDOW_ID # window id (window_title_changed only) # RIFT_PREVIOUS_WINDOW_TITLE # previous title (window_title_changed only) # RIFT_WINDOW_TITLE # new title (window_title_changed only) # RIFT_EVENT_JSON # full JSON payload of the event # # Example (sketchybar integration): # run_on_start = [ # "rift-cli subscribe cli --event workspace_changed --command sh --args -c --args 'sketchybar --trigger rift_workspace_changed RIFT_WORKSPACE_NAME=\"$RIFT_WORKSPACE_NAME\" RIFT_WORKSPACE_ID=\"$RIFT_WORKSPACE_ID\"'", # "rift-cli subscribe cli --event windows_changed --command sh --args -c --args 'sketchybar --trigger rift_windows_changed RIFT_WORKSPACE_NAME=\"$RIFT_WORKSPACE_NAME\" RIFT_WINDOW_COUNT=\"$RIFT_WINDOW_COUNT\"'" # ] run_on_start = [] # Hot Reloading # - If true, rift will watch the config file for changes and reload automatically. # - If false, your config changes will only apply when restarting rift. hot_reload = true [settings.layout] # Layout Types: # - "traditional" (i3/sway-like containers) # - "bsp" (binary space partitioning), # - "stack" (single stacked container) # - "master_stack" (master area + stack area) # defaults to "traditional" if omitted # # This is the default layout mode if there is nothing else configured # in the `workspace_rules` section below. # You can set different layouts per workspace using `workspace_rules`. mode = "bsp" # these settings only apply when layout mode == "master_stack" [settings.layout.master_stack] # Fraction of space reserved for the master area (0.05..0.95) master_ratio = 0.6 # Number of windows kept in the master area (>= 1) master_count = 1 # Which side the master area occupies (left | right | top | bottom) master_side = "left" # Where new windows go when the master area is already full (master | stack | focused) new_window_placement = "master" # these settings only apply when layout mode == "scrolling" [settings.layout.scrolling] # width of the active column (0..1 of screen width) column_width_ratio = 0.7 # min_column_width_ratio / max_column_width_ratio: clamp bounds used by resize commands min_column_width_ratio = 0.3 max_column_width_ratio = 0.9 # Alignment for the focused column: "left", "center", or "right" alignment = "center" # Horizontal focus behavior: # - "niri": reveal columns only when needed based on navigation direction # (disrespects alignment setting above) # - "anchored": always align focused column according to `alignment` focus_navigation_style = "niri" # the scrolling layout tends to function better without animations, # so you can disable them if you like # animate = false [settings.layout.scrolling.gestures] # Enable horizontal scroll gestures to switch columns enabled = false # Invert horizontal direction (swap left/right) invert_horizontal = false # Number of fingers required for gesture fingers = 3 # If true, scrolling past the end of the strip will trigger a workspace switch propagate_to_workspace_swipe = false [settings.layout.stack] # How much of each stacked window sticks out (in pixels) # Set to 0.0 to have them directly on top of each other. # Recommended: 30-50 for clear edges while keeping focus visible. stack_offset = 40.0 # Default stacking orientation when creating a new stack. # Options: # - "perpendicular" (default): choose the perpendicular orientation to the parent layout # - "same": use the same orientation as the parent layout # - "horizontal"/"vertical": force a specific orientation default_orientation = "perpendicular" [settings.layout.gaps] # Gap configuration # - outer: space between windows and screen edges # - inner: space between tiled windows # - per_display: optional display-specific overrides keyed by display UUID. # When present, the values in a per-display override replace the defaults # (you may override only `outer`, only `inner`, or both for a display). # Use the display's UUID as the key (for example, from system profiler or via rift CLI). [settings.layout.gaps.outer] top = 0 left = 0 bottom = 0 right = 0 [settings.layout.gaps.inner] horizontal = 0 vertical = 0 # Example per-display overrides: # Replace the quoted key with your display's UUID. Only specify values you want to override. # [settings.layout.gaps.per_display."11111111-2222-3333-4444-555555555555".outer] # top = 20 # left = 20 # bottom = 20 # right = 20 # # [settings.layout.gaps.per_display."11111111-2222-3333-4444-555555555555".inner] # horizontal = 8 # vertical = 8 [settings.ui.menu_bar] # enable menu bar workspace indicators enabled = true # if enabled, it will show all workspaces including empty ones. disabled because this # tends to take up too much room in the menubar and then is auto hidden by macos show_empty = false # choose between drawing every workspace or only the active workspace mode = "all" # when showing workspaces as labels ("index" or "name"), choose which label to render active_label = "index" # when displaying workspaces, choose whether to draw the layout preview or a set of labels # options are "layout" or "label" display_style = "layout" [settings.ui.stack_line] # experimental stack line indicator (defaults to off) enabled = false # in a horizontal stack where the line will be (top/bottom) horiz_placement = "top" # in a vertical stack where the line will be (left/right) vert_placement = "left" # how thick the indicator will be (in px) thickness = 20.0 # spacing between the stack line and window edges (in px) spacing = 1.0 # experimental mission control [settings.ui.mission_control] enabled = false # enable fade animation when entering/exiting mission control fade_enabled = false # native macos mission control fade is about 180ms fade_duration_ms = 180.0 # Trackpad gestures [settings.gestures] # Enable horizontal swipes to switch virtual workspaces enabled = false # Invert horizontal direction (swap next/prev) invert_horizontal_swipe = false # Maximum vertical travel allowed for the horizontal swipe to register. # Accepts normalized fraction (0..1) or percent (0..100). Example: 0.4 or 40.0 swipe_vertical_tolerance = 0.4 # Skip empty workspaces when swiping skip_empty = true # Number of fingers required for swipe fingers = 3 # Normalized horizontal distance (0..1) to trigger swipe for NSTouch-based detection distance_pct = 0.08 # Haptic feedback on commit (generic | alignment | level_change) haptics_enabled = true haptic_pattern = "level_change" # Window snapping / drag-swap behavior [settings.window_snapping] # Fraction threshold (0.0..1.0) specifying how much of the dragged window # area must be overlapped by a candidate window in order to trigger a # swap while dragging. Typical values are around 0.2–0.4. # If you prefer more aggressive swapping, lower the value; increase it # to require greater overlap before a swap occurs. drag_swap_fraction = 0.3 [virtual_workspaces] # Virtual workspaces # - enabled: if false, rift behaves like a simple tiling WM with a single space # - default_workspace_count: number of workspaces to create on startup (1–32) # - auto_assign_windows: when true, new windows can be auto-assigned using app_rules # - preserve_focus_per_workspace: remember last focused window per workspace # - workspace_auto_back_and_forth: when enabled, if you try to switch to the same workspace # that's already active, it will switch to the last workspace instead # - reapply_app_rules_on_title_change: if true, app rules are re-evaluated when a window's title changes enabled = true default_workspace_count = 10 auto_assign_windows = true preserve_focus_per_workspace = true workspace_auto_back_and_forth = false reapply_app_rules_on_title_change = false # Workspace-specific rules # - workspace: target workspace by index (integer) or name (string) # - layout: layout mode to use ("traditional", "bsp", "stack", "master_stack", "scrolling") # workspace_rules = [ # { workspace = 1, layout = "bsp" }, # { workspace = "second", layout = "scrolling" } # ] workspace_rules = [] # Default workspace to activate on startup (0-based index). # If omitted, defaults to 0 (first workspace). Must be less than default_workspace_count. # Uncomment to change: # default_workspace = 0 # Workspace names (indexed order). If fewer than default_workspace_count are provided, # remaining workspaces are named "Workspace X". # Do not provide more names than default_workspace_count. workspace_names = [ "first", "second" ] # App rules (automatic assignment) # Define rules that match new windows and set properties (workspace, floating, etc). # # Matchable fields: # - app_id : application bundle identifier (exact) # - app_name : substring match against application name # - title_regex : regular expression matched against the window title # - title_substring: literal substring (case-sensitive) matched against the title # - ax_role / ax_subrole: exact match against macOS Accessibility (AX) role/subrole # # Rule options: # - workspace (integer, 0-based): target workspace index. If omitted or invalid, uses the active workspace. # Alternatively, `workspace` may be a workspace name string to target by name. # - floating (boolean): whether matched windows should float by default. # - manage (boolean): whether Rift should manage the matching window. Set to false to ignore the window completely (default = true). # # Matching behavior (summary): # 1. All rules that match a window are evaluated. # 2. If multiple matching rules share the same non-empty `app_id`, the rule # with the most specified conditions (the most non-empty fields) wins. # If there's a tie, the earlier rule in the file wins. # 3. If matching rules do NOT share the same `app_id`, the rule with the # highest specificity (most non-empty fields) is selected; ties are broken by order. # 4. Within a single rule, all specified fields are combined conjunctively: # the rule matches only if every non-empty field matches the window. # # Tips: # - To make exceptions for a specific app, place a more specific rule before # a general `app_id` rule. Example: # app_rules = [ # { app_id = "com.example.X", title_regex = "Dialog", floating = true }, # { app_id = "com.example.X", floating = false }, # ] # # - Use `title_substring` for simple, literal (case-sensitive) substring matches. # Use `title_regex` for advanced patterns. # # Accessibility (AX) matching: # - `ax_role` and `ax_subrole` match the exact AX values reported by macOS # (e.g., "AXWindow", "AXDialog", "AXSystemDialog", "AXToolbar"). # - Useful for distinguishing dialogs, sheets, toolbars, etc., when bundle/name/title aren't enough. # # Validation: # - `title_regex` must be a valid regular expression. Invalid regexes cause the rule # to be ignored and a warning to be logged. # # Examples: # - Float any window with "Preferences" in the title: # app_rules = [ # { title_substring = "Preferences", floating = true }, # ] # # - Match by app name substring and workspace: # app_rules = [ # { app_name = "Calendar", workspace = 2, floating = true }, # ] # # - Accessibility example: float dialog windows for a specific app: # app_rules = [ # { app_id = "com.example.X", ax_subrole = "AXDialog", floating = true }, # ] # # By default there are no app rules; add or uncomment rules below as needed. app_rules = [] # Modifier combinations that can be reused in key bindings # Define common modifier combinations to avoid repetition. # Example usage: with `comb1 = "Alt + Shift"`, you can write: # "comb1 + H" = { move_node = "left" } [modifier_combinations] comb1 = "Alt + Shift" [keys] # Key binding syntax # - Use quotes around the key: "Alt + Shift + H" # - Supported modifiers: Alt, Ctrl, Shift, Meta (Meta = Command ⌘ on macOS) # - Arrow keys can be written as ArrowUp/ArrowDown/ArrowLeft/ArrowRight, or just Up/Down/Left/Right # - Spacing is flexible: "Alt+Down" and "Alt + Down" are equivalent # - Key values map to commands (see list below) # Commands # - toggle_space_activated # - next_workspace / prev_workspace # - next_workspace = true|false (optional skip-empty override) # - prev_workspace = true|false (optional skip-empty override) # - switch_to_workspace = N # - move_window_to_workspace = N / move_window_to_workspace = { workspace = N, window_id = 123 } (optional window id) # - create_workspace # - switch_to_last_workspace # - set_workspace_layout = { mode = "traditional"|"bsp"|"stack"|"master_stack"|"scrolling" } (active workspace) # - set_workspace_layout = { workspace = N, mode = "traditional"|"bsp"|"stack"|"master_stack"|"scrolling" } # - next_window / prev_window (focus wraps when it reaches last window in current workspace) # - ascend / descend # - move_focus = "left"|"right"|"up"|"down" # - move_node = "left"|"right"|"up"|"down" # - join_window = "left"|"right"|"up"|"down" # - toggle_stack / toggle_orientation / unjoin_windows # - toggle_focus_floating / toggle_window_floating / toggle_fullscreen / toggle_fullscreen_within_gaps # - resize_window_grow / resize_window_shrink / resize_window_by = 0.05 # - swap_windows = [123, 456] # - exec = "command" | exec = ["cmd", "arg1", "..."] # - move_mouse_to_display = "left"|"right"|"up"|"down"|N|"" # - move_window_to_display = { selector = "left"|"right"|"up"|"down"|N|"", window_id = 123 } # - close_window = { window_server_id = 123 } # - focus_window = { window_id = 123, window_server_id = 456 } # - show_mission_control_all / show_mission_control_current / dismiss_mission_control (this is rift's own mission control, not macOS's) # the following commands *only* work when the master stack layout is active # - adjust_master_ratio = 0.05 / adjust_master_count = 1 # - promote_to_master / swap_master_stack # the following commands *only* work when the scrolling layout is active # - scroll_strip = { delta = 0.5 } # - snap_strip / center_selection "Alt + Z" = "toggle_space_activated" "Alt + H" = { move_focus = "left" } "Alt + J" = { move_focus = "down" } "Alt + K" = { move_focus = "up" } "Alt + L" = { move_focus = "right" } "comb1 + H" = { move_node = "left" } "comb1 + J" = { move_node = "down" } "comb1 + K" = { move_node = "up" } "comb1 + L" = { move_node = "right" } "Alt + 1" = { switch_to_workspace = 0 } "Alt + 2" = { switch_to_workspace = 1 } "Alt + 3" = { switch_to_workspace = 2 } "Alt + 4" = { switch_to_workspace = 3 } "Alt + 5" = { switch_to_workspace = 4 } "Alt + 6" = { switch_to_workspace = 5 } "Alt + 7" = { switch_to_workspace = 6 } "Alt + 8" = { switch_to_workspace = 7 } "Alt + 9" = { switch_to_workspace = 8 } "Alt + 0" = { switch_to_workspace = 9 } "comb1 + 1" = { move_window_to_workspace = 0 } "comb1 + 2" = { move_window_to_workspace = 1 } "comb1 + 3" = { move_window_to_workspace = 2 } "comb1 + 4" = { move_window_to_workspace = 3 } "comb1 + 5" = { move_window_to_workspace = 4 } "comb1 + 6" = { move_window_to_workspace = 5 } "comb1 + 7" = { move_window_to_workspace = 6 } "comb1 + 8" = { move_window_to_workspace = 7 } "comb1 + 9" = { move_window_to_workspace = 8 } "comb1 + 0" = { move_window_to_workspace = 9 } "Alt + Tab" = "switch_to_last_workspace" # commands to change the layout engine of the active workspace # "Alt + Ctrl + 1" = { set_workspace_layout = { mode = "traditional" } } # "Alt + Ctrl + 2" = { set_workspace_layout = { mode = "bsp" } } # "Alt + Ctrl + 3" = { set_workspace_layout = { mode = "master_stack" } } # "Alt + Ctrl + 4" = { set_workspace_layout = { mode = "scrolling" } } "Alt + Shift + Left" = { join_window = "left" } "Alt + Shift + Right" = { join_window = "right" } "Alt + Shift + Up" = { join_window = "up" } "Alt + Shift + Down" = { join_window = "down" } "Alt + Comma" = "toggle_stack" "Alt + Slash" = "toggle_orientation" "Alt + Ctrl + E" = "unjoin_windows" "Alt + Shift + Space" = "toggle_window_floating" "Alt + F" = "toggle_fullscreen" "Alt + Shift + F" = "toggle_fullscreen_within_gaps" "comb1 + Ctrl + Space" = "toggle_focus_floating" # briefly bring focus to floating window # smartly resize windows "Alt + Shift + Equal" = "resize_window_grow" "Alt + Shift + Minus" = "resize_window_shrink" # Move mouse cursor to display by index (0-based) or UUID # Examples: # "Alt + Shift + A" = { move_mouse_to_display = 0 } # "Alt + Shift + S" = { move_mouse_to_display = 1 } # "Alt + Shift + D" = { move_mouse_to_display = "e9a4f5a1-2f8b-45c1-9f4e-5a6b7c8d9e0f" } # Focus another display (direction/index/uuid) # "Alt + Shift + F" = { focus_display = "right" } # Move a window to a display (by index, UUID, or direction) # "Alt + Ctrl + Up" = { move_window_to_display = { selector = "up" } } # "Alt + Ctrl + Down" = { move_window_to_display = { selector = "down" } } # "Alt + Ctrl + Left" = { move_window_to_display = { selector = "left" } } # "Alt + Ctrl + Right" = { move_window_to_display = { selector = "right" } } # "Alt + Ctrl + A" = { move_window_to_display = { selector = 0 } } # if mission control is enabled # this will show an exploded view of the windows in the active workspace # "Alt + Ctrl + Shift + M" = "show_mission_control_current" # this will show the mission control view shown in the readme # "Alt + Ctrl + M" = "show_mission_control_all" "Alt + Enter" = { "exec" = ["/bin/bash", "-c", "open -a \"/System/Applications/Utilities/Terminal.app\""] } "Alt + Shift + D" = "debug" # prints layout tree "Alt + Ctrl + S" = "serialize" "Alt + Ctrl + Q" = "save_and_exit"