I tried few approaches to manage my windows on MacOS, starting from using package to arrange the windows by hotkeys manually.
But pretty soon I switched to a tiling manager Amethyst
. The app’s convenient and simple window manager with GUI interface for settings.
The only problem I faced there were a lot of glitches related to re-drawing and positioning the window to the right space.
I tried Aerospace
which much simpler and suddenly it became the winner.
Although it does not have a GUI to set it up, it has simple and clear configuration file which featured below with author’s comments.
Place a copy of presented configuration file to ~/.config/aerospace/aerospace.toml
.
After that, you can edit ~/.config/aerospace/aerospace.toml
to your liking
# You can use it to add commands that run after login to macOS user session. # 'start-at-login' needs to be 'true' for 'after-login-command' to work # Available commands: https://nikitabobko.github.io/AeroSpace/commands after-login-command = [] # You can use it to add commands that run after AeroSpace startup. # 'after-startup-command' is run after 'after-login-command' # Available commands : https://nikitabobko.github.io/AeroSpace/commands after-startup-command = [] # Start AeroSpace at login start-at-login = true # Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization enable-normalization-flatten-containers = true enable-normalization-opposite-orientation-for-nested-containers = true # See: https://nikitabobko.github.io/AeroSpace/guide#layouts # The 'accordion-padding' specifies the size of accordion padding # You can set 0 to disable the padding feature accordion-padding = 30 # Possible values: tiles|accordion default-root-container-layout = 'tiles' # Possible values: horizontal|vertical|auto # 'auto' means: wide monitor (anything wider than high) gets horizontal orientation, # tall monitor (anything higher than wide) gets vertical orientation default-root-container-orientation = 'auto' # Mouse follows focus when focused monitor changes # Drop it from your config, if you don't like this behavior # See https://nikitabobko.github.io/AeroSpace/guide#on-focus-changed-callbacks # See https://nikitabobko.github.io/AeroSpace/commands#move-mouse # Fallback value (if you omit the key): on-focused-monitor-changed = [] on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] # on-focus-changed = "move-mouse window-lazy-center" # You can effectively turn off macOS "Hide application" (cmd-h) feature by toggling this flag # Useful if you don't use this macOS feature, but accidentally hit cmd-h or cmd-alt-h key # Also see: https://nikitabobko.github.io/AeroSpace/goodies#disable-hide-app automatically-unhide-macos-hidden-apps = false # Possible values: (qwerty|dvorak|colemak) # See https://nikitabobko.github.io/AeroSpace/guide#key-mapping [key-mapping] preset = 'qwerty' # Gaps between windows (inner-*) and between monitor edges (outer-*). # Possible values: # - Constant: gaps.outer.top = 8 # - Per monitor: gaps.outer.top = [{ monitor.main = 16 }, { monitor."some-pattern" = 32 }, 24] # In this example, 24 is a default value when there is no match. # Monitor pattern is the same as for 'workspace-to-monitor-force-assignment'. # See: # https://nikitabobko.github.io/AeroSpace/guide#assign-workspaces-to-monitors [gaps] inner.horizontal = 10 inner.vertical = 10 outer.left = 10 outer.bottom = 10 outer.top = 10 outer.right = 10 # 'main' binding mode declaration # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes # 'main' binding mode must be always presented # Fallback value (if you omit the key): mode.main.binding = {} [mode.main.binding] # All possible keys: # - Letters. a, b, c, ..., z # - Numbers. 0, 1, 2, ..., 9 # - Keypad numbers. keypad0, keypad1, keypad2, ..., keypad9 # - F-keys. f1, f2, ..., f20 # - Special keys. minus, equal, period, comma, slash, backslash, quote, semicolon, # backtick, leftSquareBracket, rightSquareBracket, space, enter, esc, # backspace, tab, pageUp, pageDown, home, end, forwardDelete, # sectionSign (ISO keyboards only, european keyboards only) # - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual, # keypadMinus, keypadMultiply, keypadPlus # - Arrows. left, down, up, right # All possible modifiers: cmd, alt, ctrl, shift # All possible commands: https://nikitabobko.github.io/AeroSpace/commands # See: https://nikitabobko.github.io/AeroSpace/commands#exec-and-forget # You can uncomment the following lines to open up terminal with alt + enter shortcut # (like in i3) # alt-enter = '''exec-and-forget osascript -e ' # tell application "Terminal" # do script # activate # end tell' # ''' # See: https://nikitabobko.github.io/AeroSpace/commands#layout cmd-ctrl-slash = 'layout tiles horizontal vertical' cmd-ctrl-comma = 'layout accordion horizontal vertical' # See: https://nikitabobko.github.io/AeroSpace/commands#focus cmd-ctrl-h = 'focus left' cmd-ctrl-j = 'focus down' cmd-ctrl-k = 'focus up' cmd-ctrl-l = 'focus right' # See: https://nikitabobko.github.io/AeroSpace/commands#move cmd-ctrl-shift-h = 'move left' cmd-ctrl-shift-j = 'move down' cmd-ctrl-shift-k = 'move up' cmd-ctrl-shift-l = 'move right' # See: https://nikitabobko.github.io/AeroSpace/commands#resize alt-minus = 'resize smart -50' alt-equal = 'resize smart +50' # See: https://nikitabobko.github.io/AeroSpace/commands#workspace cmd-ctrl-1 = 'workspace 1' cmd-ctrl-2 = 'workspace 2' cmd-ctrl-3 = 'workspace 3' cmd-ctrl-4 = 'workspace 4' cmd-ctrl-5 = 'workspace 5' cmd-ctrl-6 = 'workspace 6' cmd-ctrl-7 = 'workspace 7' cmd-ctrl-8 = 'workspace 8' cmd-ctrl-9 = 'workspace 9' cmd-ctrl-0 = 'workspace 10' # Note! In your config, you can drop workspace bindings that you don't needs cmd-ctrl-b = 'workspace B' # Browser cmd-ctrl-e = 'workspace E' # Emacs cmd-ctrl-t = 'workspace T' # Terminal cmd-ctrl-o = 'workspace O' # Others # See: https://nikitabobko.github.io/AeroSpace/commands#move-node-to-workspace cmd-ctrl-shift-1 = 'move-node-to-workspace 1' cmd-ctrl-shift-2 = 'move-node-to-workspace 2' cmd-ctrl-shift-3 = 'move-node-to-workspace 3' cmd-ctrl-shift-4 = 'move-node-to-workspace 4' cmd-ctrl-shift-5 = 'move-node-to-workspace 5' cmd-ctrl-shift-6 = 'move-node-to-workspace 6' cmd-ctrl-shift-7 = 'move-node-to-workspace 7' cmd-ctrl-shift-8 = 'move-node-to-workspace 8' cmd-ctrl-shift-9 = 'move-node-to-workspace 9' cmd-ctrl-shift-0 = 'move-node-to-workspace 10' cmd-ctrl-shift-b = 'move-node-to-workspace B' cmd-ctrl-shift-e = 'move-node-to-workspace E' cmd-ctrl-shift-t = 'move-node-to-workspace T' cmd-ctrl-shift-o = 'move-node-to-workspace O' # See: https://nikitabobko.github.io/AeroSpace/commands#workspace-back-and-forth alt-tab = 'workspace-back-and-forth' # See: https://nikitabobko.github.io/AeroSpace/commands#move-workspace-to-monitor alt-shift-tab = 'move-workspace-to-monitor --wrap-around next' # See: https://nikitabobko.github.io/AeroSpace/commands#mode cmd-ctrl-shift-semicolon = 'mode service' [workspace-to-monitor-force-assignment] 1 = 'main' 2 = 'main' 3 = 'main' 4 = 'main' 5 = 'main' 6 = ['secondary', 'main'] 7 = ['secondary', 'main'] 8 = ['secondary', 'main'] 9 = ['secondary', 'main'] 10 = ['secondary', 'main'] B = ['secondary', 'main'] E = ['secondary', 'main'] T = ['secondary', 'main'] O = ['secondary', 'main'] # 'service' binding mode declaration. # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes [mode.service.binding] esc = ['reload-config', 'mode main'] r = ['flatten-workspace-tree', 'mode main'] # reset layout f = ['layout floating tiling', 'mode main'] # Toggle between floating and tiling layout backspace = ['close-all-windows-but-current', 'mode main'] # sticky is not yet supported https://github.com/nikitabobko/AeroSpace/issues/2 #s = ['layout sticky tiling', 'mode main'] alt-shift-h = ['join-with left', 'mode main'] alt-shift-j = ['join-with down', 'mode main'] alt-shift-k = ['join-with up', 'mode main'] alt-shift-l = ['join-with right', 'mode main'] # I dont need the following cuase keyboard have it, but just in case down = 'volume down' up = 'volume up' shift-down = ['volume set 0', 'mode main'] [[on-window-detected]] if.app-id = 'org.alacritty' run = "move-node-to-workspace T" [[on-window-detected]] if.app-id = 'org.gnu.Emacs' run = "move-node-to-workspace E" [[on-window-detected]] if.app-id = 'org.mozilla.firefox' run = "move-node-to-workspace B"
Important to note for the above configuration file the following:
- Aerospace implements
virtual spaces
which assigned tophysical displays
. cmd-ctrl+[0-9beto]
switches between spaces.cmd-ctrl-shift+[0-9beto]
moves move active app window to the respective space.cmd-ctrl-,
switches between alignments.cmd-ctrl-/
changes between vertical and horizontal style of alignment.
and that’s basically it! For most cases this simple navigation is enough. Of course you can configure it as you wish cause I changed default bindings for my needs.
But the App also provides the way to manage windows more precisely.
For example, by pressing cmd-ctrl-shift-;
you can enter service mode and the following hotkeys will do some other things:
Esc
reloads configuration.f
toggles between floating and tiling layout for the active window.alt-shift-[hjkl]
groups active window or joins it with the respective left, down, up or right one (I don’t use it often but anyway in particular situations may be useful).r
flatten apps windows structure created with previous commands, i.e. removes subgroups.
and the last part of the configuration contains conditions to move new windows of some apps to the specified workspaces.
In my case it is Alacrity
terminal which goes to workspace T
, firefox
goes to workspace B
and Emacs
goes to workspace E
.
To get ids of all currently running apps you can execute the following command:
aerospace list-apps
Good luck with your own configuration and I hope Aerospace make things work for you!