Yabai: Automatically destroy "empty" spaces

Created on 15 Aug 2019  路  13Comments  路  Source: koekeishiya/yabai

How can I update my yabairc to automatically destroy un-used spaces ? I am thinking of something like in i3, where I can close all windows in a given space, and jump to another space, and have the no longer used space automatically close. Currently, I have to manually destroy all my unused spaces. Any help would be appreciated!

Most helpful comment

@Juzov this is what I use nowadays:

#! /usr/bin/env sh

read -r -d '' action <<- 'EOF'
  recent_space_index="$(yabai -m query --spaces | 
    jq -er 'map(select(.id | tostring == env.YABAI_RECENT_SPACE_ID))[0].index')"
  if yabai -m query --windows --space "${recent_space_index}" |
    jq -er 'length == 0'
  then
    yabai -m space "${recent_space_index}" --destroy
  fi
EOF

yabai -m signal --add event='space_changed' action="${action}"

All 13 comments

# if there are at least 2 non-fullscreen spaces on the focused display,
# destroy the focused space
yabai -m query --spaces --display \
    | jq -re 'map(select(."native-fullscreen" == 0)) | length > 1' \
    && yabai -m space --destroy

The above code can then be used as an action from a signal, e.g. so it runs whenever a window is destroyed.

I added the following line to my yabairc

yabai -m signal --add event=window_destroyed action="yabai -m query --spaces --display \
| jq -re 'map(select(."native-fullscreen" == 0)) | length > 1' \
&& yabai -m        space --destroy"

but it does not seem to work. Am I doing something wrong here ?

You need to escape the inner double quotes. Also you can put it all on a single line鈥攖he backslash at the end of the line in my above example is just for splitting the code up into multiple lines for readability.

Yeah sorry for the naive copy-paste, but this is what I had

yabai -m signal --add event=window_destroyed action="yabai -m query --spaces --display | jq -re 'map(select(.\"native-fullscreen\" == 0)) | length > 1' && yabai -m space --destroy"

i.e. I had them inner quotes escaped as above. It still has no effect though!

Okay I think I have a working solution.

I have the following in my yabairc

yabai -m signal --add event=space_changed action="bash ~/.config/yabai/autodestroy.sh"

where autodestroy.sh is a script that contains

# autodestroy.sh
bash <(yabai -m query --spaces | \
     jq -re 'map(select(."windows" == [] and ."focused" == 0)) | \
     .[]| "yabai -m space \(.index|@sh) --destroy"')

i.e. I had them inner quotes escaped as above. It still has no effect though!

Ah sorry, that was on me. You hit the max length for commands so yabai cut it off somewhere in the middle. This was discussed briefly in another issue and is supposed to be removed.

~In your script, the bash <(...) is unnecessary.~ You can speed this up ever so slightly by using xargs/parallel over spawning a subshell for every space to be deleted.

Also your script is crashing the Dock in some situations, because yabai does not check whether you want to destroy the last non-fullscreen space on any display. Doing so crashes Dock.app.

Right, I added the snippet you mentioned above to check if there are more than 1 non-fullscreen spaces on the display, i.e. autodestroy.sh looks like

yabai -m query --spaces --display | \
     jq -re 'map(select(."native-fullscreen" == 0)) | length > 1' \
     && bash <(yabai -m query --spaces | \
          jq -re 'map(select(."windows" == [] and ."focused" == 0)) | .[]| \
          "yabai -m space \(.index|@sh) --destroy"')

This hasn't caused any crashes with Dock.app as far as I can tell. Xargs would indeed be a slightly optimized solution (I just wanted to get something quick and dirty to get it to work)!

I have another approach worth sharing.
Remove current space when focus is removed from it and the space is empty.

#$1 space to focus
# 2. delete if empty space!
windows="$(yabai -m query --spaces | \
    jq -r 'map(select(.focused == 1 )) | map(.windows[]) | length')"
if [ "$windows" -eq 0 ]
then
    yabai -m space --label deletelabel
    yabai -m space --focus "$1"
    yabai -m space deletelabel --destroy
else
    yabai -m space --focus "$1"
fi

and in skhdrc e.g.
lalt - 1 : sh ~/.config/yabai/localtion_of_script.sh 1

this is a snippet of a longer script that also creates a space if you try to focus on a non-existing space.

@Juzov this is what I use nowadays:

#! /usr/bin/env sh

read -r -d '' action <<- 'EOF'
  recent_space_index="$(yabai -m query --spaces | 
    jq -er 'map(select(.id | tostring == env.YABAI_RECENT_SPACE_ID))[0].index')"
  if yabai -m query --windows --space "${recent_space_index}" |
    jq -er 'length == 0'
  then
    yabai -m space "${recent_space_index}" --destroy
  fi
EOF

yabai -m signal --add event='space_changed' action="${action}"

@Juzov this is what I use nowadays:

#! /usr/bin/env sh

read -r -d '' action <<- 'EOF'
  recent_space_index="$(yabai -m query --spaces | 
    jq -er 'map(select(.id | tostring == env.YABAI_RECENT_SPACE_ID))[0].index')"
  if yabai -m query --windows --space "${recent_space_index}" |
    jq -er 'length == 0'
  then
    yabai -m space "${recent_space_index}" --destroy
  fi
EOF

yabai -m signal --add event='space_changed' action="${action}"

Is it possible to get this working while you are in that space? I.e., I've removed the last app from a space, and it automatically sends me back to the previous space.

@rublev am wondering this too... It would be nice

Use the same kind of logic presented in the script above, but hook it up to the signal application_terminated. Instead of checking for number of windows on the most recent space, check the number of windows on the active space.

For anyone having issues with the above scripts I managed to get a slightly modified version working.

#!/usr/bin/env bash
yabai -m query --spaces --display | \
     jq -re 'map(select(."native-fullscreen" == 0)) | length > 1' \
     && yabai -m query --spaces | \
          jq -re 'map(select(."windows" == [] and ."focused" == 0).index) | reverse | .[] ' | \
          xargs -I % sh -c 'yabai -m space % --destroy'

and then in yabairc

# Clean up empty spaces with no windows on them.
yabai -m signal --add event=space_changed action="sh ~/dotfiles/.config/yabai/scripts/cleanEmptySpaces.sh"

Hope that might help someone 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fuckbitchesgitmoney picture fuckbitchesgitmoney  路  4Comments

eraserhd picture eraserhd  路  4Comments

tsujp picture tsujp  路  3Comments

zoenglinghou picture zoenglinghou  路  3Comments

imjma picture imjma  路  3Comments