nikitabobko / AeroSpace

AeroSpace is an i3-like tiling window manager for macOS
https://nikitabobko.github.io/AeroSpace/guide
MIT License
7.16k stars 114 forks source link

Question: better integration with sketchybar #175

Open regisverdin opened 8 months ago

regisverdin commented 8 months ago

Hey there. I've been trying to build a proper dock-like menu bar for aerospace using sketchybar, and have run into some issues. I'm posting this in the hopes that someone else has tried this with more success, or has some suggestions. This is where I got:

Screenshot 2024-03-03 at 1 26 56 AM

It should support the following:

My impression is that there should be a better integration between aerospace and sketchybar, or else aerospace should just implement it's own menu bar. The communication between aerospace and sketchybar here is really messy (although I think this is mostly on sketchybar's side... it's very hard to manage state in sketchybar)

I think this would be a really useful tool to have, especially for users with many workspaces who want a quick way to keep track of what is where.

Here are my config files I hacked together. This is as far as I got before giving up.


aerospace.toml:

# 'start-at-login' needs to be 'true' for 'after-login-command' to work
# Available commands: https://nikitabobko.github.io/AeroSpace/commands.html
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.html
after-startup-command = [
    # JankyBorders has a built-in detection of already running process,
    # so it won't be run twice on AeroSpace restart
    'exec-and-forget borders',
    # 'exec-and-forget /opt/homebrew/opt/sketchybar/bin/sketchybar'
]

[[on-window-detected]]
run = ['/bin/bash -c /opt/homebrew/opt/sketchybar/bin/sketchybar --trigger new-window']

# Notify Sketchybar about workspace change
# exec-on-workspace-change = [
#     '/bin/bash',
#     '-c',
#     '/opt/homebrew/opt/sketchybar/bin/sketchybar --trigger change-focused-workspace FOCUSED=$AEROSPACE_FOCUSED_WORKSPACE PREV_FOCUSED=$AEROSPACE_PREV_WORKSPACE',
# ]

# Start AeroSpace at login
start-at-login = false

# Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide.html#normalization
enable-normalization-flatten-containers = true
enable-normalization-opposite-orientation-for-nested-containers = true

# See: https://nikitabobko.github.io/AeroSpace/guide.html#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'

# 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.html#assign-workspaces-to-monitors

gaps.inner.horizontal = [
    { monitor."built-in" = 10 },
    { monitor."LG Ultra HD" = 12 },
    10,
]
gaps.inner.vertical = [
    { monitor."built-in" = 10 },
    { monitor."LG Ultra HD" = 12 },
    10,
]
gaps.outer.left = [
    { monitor."built-in" = 30 },
    { monitor."LG Ultra HD" = 30 },
    10,
]
gaps.outer.bottom = [
    { monitor."built-in" = 10 },
    { monitor."LG Ultra HD" = 30 },
    10,
]
gaps.outer.top = [
    { monitor."built-in" = 10 },
    { monitor."LG Ultra HD" = 60 },
    30,
]
gaps.outer.right = [
    { monitor."built-in" = 10 },
    { monitor."LG Ultra HD" = 30 },
    10,
]

# Visual indent makes it easier to understand that containers of the same orientation are nested.
# If you have 'enable-normalization-opposite-orientation-for-nested-containers' enabled then
# there is no way to observe the indent
indent-for-nested-containers-with-the-same-orientation = 30

# 'main' binding mode declaration
# See: https://nikitabobko.github.io/AeroSpace/guide.html#binding-modes
# 'main' binding mode must be always presented
[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
# - 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.html

# See: https://nikitabobko.github.io/AeroSpace/commands.html#layout
alt-slash = 'layout tiles horizontal vertical'
alt-comma = 'layout accordion horizontal vertical'

# See: https://nikitabobko.github.io/AeroSpace/commands.html#focus
alt-h = ['focus left', 'exec-and-forget sketchybar --trigger change-focused-window']
alt-j = ['focus down', 'exec-and-forget sketchybar --trigger change-focused-window']
alt-k = ['focus up', 'exec-and-forget sketchybar --trigger change-focused-window']
alt-l = ['focus right', 'exec-and-forget sketchybar --trigger change-focused-window']

alt-ctrl-f = 'fullscreen'

# See: https://nikitabobko.github.io/AeroSpace/commands.html#move
alt-shift-h = ['move left', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-j = ['move down', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-k = ['move up', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-l = ['move right', 'exec-and-forget sketchybar --trigger move-window']

# See: https://nikitabobko.github.io/AeroSpace/commands.html#resize
alt-shift-minus = 'resize smart -50'
alt-shift-equal = 'resize smart +50'

# See: https://nikitabobko.github.io/AeroSpace/commands.html#workspace
alt-y = ['workspace 0', 'exec-and-forget sketchybar --trigger change-focused-workspace']
alt-u = ['workspace 1', 'exec-and-forget sketchybar --trigger change-focused-workspace']
alt-i = ['workspace 2', 'exec-and-forget sketchybar --trigger change-focused-workspace']
alt-o = ['workspace 3', 'exec-and-forget sketchybar --trigger change-focused-workspace']
alt-p = ['workspace 4', 'exec-and-forget sketchybar --trigger change-focused-workspace']

# See: https://nikitabobko.github.io/AeroSpace/commands.html#move-node-to-workspace
alt-shift-y = ['move-node-to-workspace 0', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-u = ['move-node-to-workspace 1', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-i = ['move-node-to-workspace 2', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-o = ['move-node-to-workspace 3', 'exec-and-forget sketchybar --trigger move-window']
alt-shift-p = ['move-node-to-workspace 4', 'exec-and-forget sketchybar --trigger move-window']

# See: https://nikitabobko.github.io/AeroSpace/commands.html#workspace-back-and-forth
alt-tab = 'workspace-back-and-forth'
# See: https://nikitabobko.github.io/AeroSpace/commands.html#move-workspace-to-monitor
alt-shift-tab = 'move-workspace-to-monitor --wrap-around next'

# See: https://nikitabobko.github.io/AeroSpace/commands.html#mode
alt-shift-semicolon = 'mode service'
alt-shift-slash = 'mode join'

# 'service' binding mode declaration.
# See: https://nikitabobko.github.io/AeroSpace/guide.html#binding-modes
[mode.service.binding]
r = ['flatten-workspace-tree', 'mode main'] # reset layout
#s = ['layout sticky tiling', 'mode main'] # sticky is not yet supported https://github.com/nikitabobko/AeroSpace/issues/2
f = [
    'layout floating tiling',
    'mode main',
] # Toggle between floating and tiling layout
backspace = ['close-all-windows-but-current', 'mode main']
esc = ['reload-config', 'mode main']

# 'join' binding mode declaration
# See: https://nikitabobko.github.io/AeroSpace/guide.html#binding-modes
[mode.join.binding]
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']
esc = 'mode main'

sketchybarrc:

#! /bin/bash

source ~/.config/sketchybar/sketchybar-app-font/dist/icon_map.sh

PLUGIN_DIR="$CONFIG_DIR/plugins"

BAR_COLOR=0xff22202b
MAIN_COLOR=0xffa17fa7
ACCENT_COLOR=0xffe19286

bar=(
  height=32
  # color=$BAR_COLOR
  color=0x000000
  # border_color=$MAIN_COLOR
  # border_width=1
  # corner_radius=10
  shadow=off
  position=top
  sticky=on
  padding_right=10
  padding_left=10
  y_offset=5
  margin=15
  notch_width=0
)

sketchybar --bar "${bar[@]}"

default=(
  padding_left=5
  padding_right=5
  label.font="Hack Nerd Font:Bold:14.0"
  icon.color=$MAIN_COLOR
  label.color=$MAIN_COLOR
  icon.padding_left=4
  icon.padding_right=4
  label.padding_left=4
  label.padding_right=4
)

sketchybar --default "${default[@]}"

sketchybar --add event aerospace-workspace-change
sketchybar --add event change-focused-workspace
sketchybar --add event change-focused-window
sketchybar --add event move-window-within-workspace
sketchybar --add event change-workspace

for sid in $(aerospace list-workspaces --all); do
  label=(
    label="$sid"
    label.color=$MAIN_COLOR
    label.padding_right=13
    script="$CONFIG_DIR/plugins/aerospace.sh change-focused-workspace $sid"
  )
  sketchybar --add item label.$sid left \
    --subscribe label.$sid change-focused-workspace \
    --set label.$sid "${label[@]}"

  icons=()
  while IFS= read -r line; do

    window_id=$(echo $line | awk -F ' \\| ' '{print $1}')
    icons+=("icon.$window_id")

    #this is to set the focused window a highlighted color
    # focused_window_info=$(aerospace list-windows --focused)
    focused_window_id=$(echo $focused_window_info | awk -F ' \\| ' '{print $1}')
    # echo $focused_window_id
    if [ "$window_id" = "$focused_window_id" ]; then
        icon_color=$ACCENT_COLOR
    else
        icon_color=$MAIN_COLOR
    fi
    # echo $icon_color

    app_name=$(echo $line | awk -F ' \\| ' '{print $2}')
    __icon_map "${app_name}"

    icon=(
      icon="${icon_result}"
      icon.font="sketchybar-app-font:Regular:20.0"
      icon.color="$icon_color"
      label.align=center
      click_script="aerospace workspace $sid"
      script="$CONFIG_DIR/plugins/aerospace.sh change-focused-window $window_id"
    )

    sketchybar --add item icon.$window_id left \
      --subscribe icon.$window_id change-focused-workspace change-focused-window \
      --set icon.$window_id "${icon[@]}"
  done < <(aerospace list-windows --workspace "$sid")

  echo ${icons[@]}
  space=(
    background.height=26
    background.corner_radius=4
    background.border_color=$MAIN_COLOR
    background.border_width=1
    background.color=0x00000000
    script="$CONFIG_DIR/plugins/aerospace.sh create-delete-window"
  )
  sketchybar --add bracket space.$sid label.$sid ${icons[@]} \
    --subscribe space.$sid space_windows_change \
    --set space.$sid "${space[@]}"

done

sketchybar --add item clock right \
           --set clock update_freq=10 icon=  script="$PLUGIN_DIR/clock.sh" \
           --add item volume right \
           --set volume script="$PLUGIN_DIR/volume.sh" \
           --subscribe volume volume_change \
           --add item battery right \
           --set battery update_freq=120 script="$PLUGIN_DIR/battery.sh" \
           --subscribe battery system_woke power_source_change

##### Force all scripts to run the first time (never do this in a script) #####
# sketchybar --update

aerospace.sh:

#!/usr/bin/env bash

MAIN_COLOR=0xffa17fa7
ACCENT_COLOR=0xffe19286

if [ "$1" = "change-focused-window" ]; then
    echo "change-focused-window"
    focused_window_info=$(aerospace list-windows --focused)
    focused_window_id=$(echo $focused_window_info | awk -F ' \\| ' '{print $1}')
    if [ "$2" = "$focused_window_id" ]; then
        sketchybar --set $NAME icon.color=$ACCENT_COLOR
    else
        sketchybar --set $NAME icon.color=$MAIN_COLOR
    fi
fi

if [ "$1" = "change-focused-workspace" ]; then
    echo "change-focused-workspace"
    focused_workspace=$(aerospace list-workspaces --focused)
    if [ "$2" = "$focused_workspace" ]; then
        sketchybar --set $NAME label.color=$ACCENT_COLOR
    else
        sketchybar --set $NAME label.color=$MAIN_COLOR
    fi
fi

if [ "$1" = "move-window-within-workspace" ]; then
    echo "move-window-within-workspace"
    focused_workspace=$(aerospace list-workspaces --focused)
    if [ "$2" = "$focused_workspace" ]; then
        sketchybar --set $NAME label.color=$ACCENT_COLOR
    else
        sketchybar --set $NAME label.color=$MAIN_COLOR
    fi
fi

if [ "$1" = "create-delete-window" ]; then
    echo "create-delete"
    apps_in_window=$(aerospace list-windows --workspace $(aerospace list-workspaces --focused))
    # echo $apps_in_window
    # echo $INFO

    #USE A REGEX IN SKETCHYBAR. Prefix all your icons with their window id.
    sketchybar --remove /icon./
    sketchybar --remove /label./
    sketchybar --remove /space./

    # #Re-render all app icons in all workspaces
    source ~/.config/sketchybar/sketchybar-app-font/dist/icon_map.sh

    PLUGIN_DIR="$CONFIG_DIR/plugins"

    BAR_COLOR=0xff22202b
    MAIN_COLOR=0xffa17fa7
    ACCENT_COLOR=0xffe19286
    default=(
    padding_left=5
    padding_right=5
    label.font="Hack Nerd Font:Bold:14.0"
    icon.color=$MAIN_COLOR
    label.color=$MAIN_COLOR
    icon.padding_left=4
    icon.padding_right=4
    label.padding_left=4
    label.padding_right=4
    )

    sketchybar --default "${default[@]}"

    for sid in $(aerospace list-workspaces --all); do
        label=(
            label="$sid"
            label.color=$MAIN_COLOR
            label.padding_right=13
            script="$CONFIG_DIR/plugins/aerospace.sh change-focused-workspace $sid"
        )
        sketchybar --add item label.$sid left \
            --subscribe label.$sid change-focused-workspace \
            --set label.$sid "${label[@]}"

        icons=()
        while IFS= read -r line; do
            window_id=$(echo $line | awk -F ' \\| ' '{print $1}')
            icons+=("icon.$window_id")

            app_name=$(echo $line | awk -F ' \\| ' '{print $2}')
            __icon_map "${app_name}"

            icon=(
            icon="${icon_result}"
            icon.font="sketchybar-app-font:Regular:20.0"
            label.align=center
            click_script="aerospace workspace $sid"
            script="$CONFIG_DIR/plugins/aerospace.sh change-focused-window $window_id"
            )

            sketchybar --add item icon.$window_id left \
            --subscribe icon.$window_id change-focused-workspace change-focused-window \
            --set icon.$window_id "${icon[@]}"
        done < <(aerospace list-windows --workspace "$sid")

        # echo ${icons[@]}
        space=(
            background.height=26
            background.corner_radius=4
            background.border_color=$MAIN_COLOR
            background.border_width=1
            background.color=0x00000000
            script="$CONFIG_DIR/plugins/aerospace.sh create-delete-window"
        )
        sketchybar --add bracket space.$sid label.$sid ${icons[@]} \
            --subscribe space.$sid space_windows_change \
            --set space.$sid "${space[@]}"

    done

fi

#also need MOVE window. get the order from aerospace list-windows --workspace. bind the "move" aerospace commands in aerospace.toml to exec 'sketchybar --trigger ...'.
nikitabobko commented 8 months ago

aerospace list-windows --workspace $(aerospace list-workspaces --focused)

This can be simplified to: aerospace list-windows --workspace focused


However, I can't help you with sketchybar integration, since I don't use any bars. Hopefully, somebody else can

jakufort commented 8 months ago

(piggybacking on this question, since this is in the same area :) )

I use exec-on-workspace-change to propagate "workspace-changed" event to sketchybar which let's me to change active workspace and change window title. Is there any event to detect when window title changes? For example, when I change tab. I haven't seen anything that would fit, but I might be missing something.

(I thought that it's something that could be solved by on-window-detected, but exec-and-forget command is not accepted in run)

nikitabobko commented 8 months ago

I thought that it's something that could be solved by on-window-detected, but exec-and-forget command is not accepted in run

on-window-detected is not invoked on the window's title changes, so allowing exec-and-forget in on-window-detected won't help here

Currently AeroSpace doesn't listen for "window changed title" events. I see that there is an API for it, so technically it should be possible. Tracking issue: #177

nohzafk commented 5 months ago

I've no idea why on my environment M3 Max Macbook, exec-on-workspace-change can't work, it just doesn't trigger the event.

I implemented a focused workspace indicator using SbarLua, used this sketchybar config as a starting point.

I post here for anyone who is interested.

items/aerospaces.lua

local sbar = require("sketchybar")
local colors = require("colors")
local settings = require("settings")

local workspace = sbar.add("item", "aerospace", {
    position = "right",
    label = {
        string = "",
        color = colors.white,
        highlight_color = colors.green,
        font = {
            family = settings.font.numbers,
            style = settings.font.style_map["Bold"],
            size = 20.0,
        },
        drawing = true,
    },
    padding_right = 12,
})

local function highlight_focused_workspace(env)
    sbar.exec("aerospace list-workspaces --focused", function(focused_workspace)
        for id in focused_workspace:gmatch("%S+") do
            local is_focused = tostring(id) == focused_workspace:match("%S+")
            sbar.animate("sin", 10, function()
                workspace:set({
                    icon = {
                        highlight = is_focused
                    },
                    label = {
                        highlight = is_focused,
                        string = id,
                    },
                })
            end)
        end
    end)
end

-- Subscribe to the front_app_switched event to highlight the focused workspace
workspace:subscribe("front_app_switched", highlight_focused_workspace)

-- Initially highlight the focused workspace
highlight_focused_workspace()

return workspace
agenttank commented 4 months ago

@nohzafk great, thank you! I installed sketchybar with the script from your second link (repo by FelixKratz) and then put in aerospaces.lua in the "items" folder and adding this to the init.lua in the same directory: require("items.aerospaces")

i now have sketchybar with a green indicator/number that tells me which workspace is active. do you also have a visible indication of workspaces that have applications on it but are not on a screen as well?

nohzafk commented 4 months ago

@agenttank I do have some customize scripts for indicators, but not exactly what you want. You can use this example as a starting point to utilize hammerspoon window events to trigger sketchybar event.

and at the sbarlua side, write functions to call aerospace commands to get the windows/apps information

agenttank commented 3 months ago

there's also this very nice sketchybar for AeroSpace configuration that actually works and the creator shared it here.

https://github.com/forteleaf/sketkchybar-with-aerospace/tree/main

vegetablesalad commented 2 months ago

there's also this very nice sketchybar for AeroSpace configuration that actually works and the creator shared it here.

https://github.com/forteleaf/sketkchybar-with-aerospace/tree/main

This is quite nice. Only problem is that it is slow to update, not sure if everyone has it, but I'm not the only one.

lucax88x commented 1 month ago

I've starting building a cli app to integrate sketchybar with aerospace, because I find sketchybarrc to be pretty limited & slow and bash honestly suck.

it's far far from complete, but the init & update are instant this way, i'll ping there when it's done.

https://github.com/lucax88x/wentsketchy

lucax88x commented 1 month ago

pic

Screenshot 2024-10-05 at 18 54 54

video: https://github.com/user-attachments/assets/4a238f9f-dd4a-4e7b-9a6f-e458564acca7

sneak peek of the current status.

feel free to fork and use it ;)

pszypowicz commented 1 month ago

I'm just missing the event to "redraw" applications available in the workspace. For this I would need to be notified when a window was opened or closed. Currently we only have an event when a workspace was changed which is not enough.

Let's say you just closed the last firefox windows in a last "F" workspace. Technically you are still in it, no "workspace" changed event was triggered, no way to remove "firefox" app from the "F" workspace.

Maybe aerospace could implement custom events via NSDistributedNotificationCenter: https://felixkratz.github.io/SketchyBar/config/events#creating-custom-events for a various of actions:

agenttank commented 1 month ago

I'm just missing the event to "redraw" applications available in the workspace. For this I would need to be notified when a window was opened or closed. Currently we only have an event when a workspace was changed which is not enough.

Let's say you just closed the last firefox windows in a last "F" workspace. Technically you are still in it, no "workspace" changed event was triggered, no way to remove "firefox" app from the "F" workspace.

Maybe aerospace could implement custom events via NSDistributedNotificationCenter: https://felixkratz.github.io/SketchyBar/config/events#creating-custom-events for a various of actions:

  • Windows added / removed from the workspace
  • Workspace added/removed

i just use "on-focus-changed" good enough for me

like so https://github.com/agenttank/dotfiles_macos/blob/main/aerospace/aerospace.toml

pszypowicz commented 1 month ago

i just use "on-focus-changed"

Actually this gave me a better responsiveness than the default "exec on workspace changed", thank you!

ipstone commented 2 days ago

hello, nice discussions and work to make the interface more reflective - I am wondering on the other direction, are there minimal configs that can enable aerospace's workspace more intuitive?

The situation I often encounter with aerospace is that I lost awareness of which workspace I am on, or what is in this workspace, currently I am trying to mitigate this issue by:

  1. Drag the aerospace menu item all the way to the right (command + drag)

    • Though it can not be moved further right passed the time/siri/apple intelligence icon, but it's helpful that I can know where to look.
    • I agree it is not as visible as sketchybar, but I have some issues with sketchybar runs on an intel mac etc...
  2. Use raycast to query what's in my current workspace by a raycast Script to call:

    • aerospace list-windows --workspace focused thanks @nikitabobko

Not sure what could be some setups that is minimal and provide some good visual 'feedback/reflection', that it can be known quickly/intuitively which workspace is on, and what is on this workspace.

Thanks!