AeroSpace icon indicating copy to clipboard operation
AeroSpace copied to clipboard

Question: better integration with sketchybar

Open regisverdin opened this issue 1 year ago • 10 comments

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:

  • highlight active app.
  • highlight active workspace.
  • order apps in a somewhat sensible way (l-r, top-bottom)
  • update when creating/closing a window
  • update when moving windows left/right within a workspace.
  • update when moving windows between workspaces.

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 ...'.

regisverdin avatar Mar 03 '24 09:03 regisverdin

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

nikitabobko avatar Mar 03 '24 10:03 nikitabobko

(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)

jakufort avatar Mar 04 '24 11:03 jakufort

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

nikitabobko avatar Mar 04 '24 13:03 nikitabobko

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

nohzafk avatar Jun 11 '24 08:06 nohzafk

@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?

agenttank avatar Jun 18 '24 09:06 agenttank

@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

nohzafk avatar Jun 18 '24 12:06 nohzafk

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

agenttank avatar Aug 06 '24 11:08 agenttank

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.

vegetablesalad avatar Aug 30 '24 19:08 vegetablesalad

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 avatar Sep 11 '24 07:09 lucax88x

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 ;)

lucax88x avatar Oct 05 '24 17:10 lucax88x

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
  • Workspace were rebalanced after new display was connected and workspace-to-monitor-force-assignment taken place
  • focus lost
  • binding mode change

pszypowicz avatar Oct 09 '24 06:10 pszypowicz

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

agenttank avatar Oct 09 '24 06:10 agenttank

i just use "on-focus-changed"

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

pszypowicz avatar Oct 09 '24 06:10 pszypowicz

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!

ipstone avatar Nov 07 '24 17:11 ipstone

@ipstone just a side-note for your point 2. I've created a pull-request to the raycast extension to show and select apps in the current focused workspace and all workspaces. Just waiting for it to be accepted

thewinger avatar Nov 11 '24 10:11 thewinger

this is awesome @thewinger thanks!

ipstone avatar Nov 20 '24 04:11 ipstone

@nikitabobko is it possible to extend the query to query current split/layout/mode, it will make it much easier to add things to sketchybar and scripting in general.

Thanks!

gldtn avatar Nov 24 '24 02:11 gldtn

it seems that a bug has been fixed, now exec-on-workspace-change works, I share my sketchybar lua config that replace show the apps in each aerospace workspace

support multiple monitors

Untitled Untitled

config-aerospace.toml

exec-on-workspace-change = [
  '/bin/bash', '-c', '/opt/homebrew/bin/sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE'
]

items/workspaces.lua

local colors = require("colors")
local settings = require("settings")
local app_icons = require("helpers.app_icons")
local sbar = require("sketchybar")

sbar.add("event", "aerospace_workspace_change")

local workspaces = {}

local function add_workspace(space_id, workspace_id)
  local i = workspace_id
  local space = sbar.add("space", "space." .. i, {
    space = space_id,
    icon = {
      font = { family = settings.font.numbers, size = 18 },
      string = workspace_id,
      padding_left = 15,
      padding_right = 8,
      color = colors.white,
      highlight_color = colors.green,
    },
    label = {
      padding_right = 20,
      color = colors.grey,
      highlight_color = colors.white,
      font = "sketchybar-app-font:Regular:16.0",
      y_offset = -1,
    },
    padding_right = 1,
    padding_left = 1,
    background = {
      color = colors.bg1,
      border_width = 1,
      height = 26,
      border_color = colors.black,
    },
    popup = { background = { border_width = 5, border_color = colors.black } },
  })

  workspaces[i] = space

  -- Single item bracket for space items to achieve double border on highlight
  local space_bracket = sbar.add("bracket", { space.name }, {
    background = {
      color = colors.transparent,
      border_color = colors.bg2,
      height = 28,
      border_width = 2,
    },
  })

  -- Padding space
  sbar.add("space", "space.padding." .. i, {
    space = i,
    script = "",
    width = settings.group_paddings,
  })

  space:subscribe("space_change", function(env)
    local selected = env.SELECTED == "true"
    space:set({
      icon = { highlight = selected },
      label = { highlight = selected },
      background = { border_color = selected and colors.black or colors.bg2 },
    })
    space_bracket:set({
      background = { border_color = selected and colors.grey or colors.bg2 },
    })
  end)
end

-- Function to execute shell commands and return the output
local function execute_command(command)
  local handle = io.popen(command)
  local result = handle:read("*a")
  handle:close()
  return result
end

-- Get monitor information
local monitors = {}
local monitor_output = execute_command("aerospace list-monitors")
for line in monitor_output:gmatch("[^\r\n]+") do
  local id, name = line:match("(%d+) | (.+)")
  if id and name then
    monitors[tonumber(id)] = name
  end
end

-- Get workspaces for each monitor
for monitor_id, _ in pairs(monitors) do
  local workspace_output = execute_command("aerospace list-workspaces --monitor " .. monitor_id)
  for workspace_id in workspace_output:gmatch("[^\r\n]+") do
    add_workspace(monitor_id, tonumber(workspace_id))
  end
end

local space_window_observer = sbar.add("item", {
  drawing = false,
  updates = true,
})

space_window_observer:subscribe({ "aerospace_workspace_change",}, function(env)
  sbar.exec([[aerospace list-windows --workspace ]] .. tostring(env.FOCUSED_WORKSPACE) .. [[ | awk -F '|' '{print $2}']], function(appNames)
    local appCounts = {}
    -- Split the input string by newline into individual app names
    for appName in string.gmatch(appNames, "[^\r\n]+") do
      -- Trim leading and trailing whitespace
      appName = appName:match("^%s*(.-)%s*$")
      if appCounts[appName] then
        appCounts[appName] = appCounts[appName] + 1
      else
        appCounts[appName] = 1
      end
    end

    local icon_line = ""
    local no_app = true
    for app, count in pairs(appCounts) do
      no_app = false
      local lookup = app_icons[app]
      local icon = ((lookup == nil) and app_icons["Default"] or lookup)
      icon_line = icon_line .. icon
    end
  
    if (no_app) then
      icon_line = " —"
    end

    sbar.animate("tanh", 10, function()
      workspaces[tonumber(env.FOCUSED_WORKSPACE)]:set({ label = icon_line })
    end)
  end)
end)

nohzafk avatar Nov 25 '24 00:11 nohzafk