feat: new config for sketchybar

This commit is contained in:
Ray Andrew 2025-11-29 17:39:02 -06:00
parent cc55237709
commit 4c8ccec1ae
Signed by: rayandrew
SSH key fingerprint: SHA256:XYrYrxF0Z3A72n8P/p6mqPRNQZT22F88XcLsG+kX4xw
47 changed files with 1792 additions and 154 deletions

21
bin/presentation-mode Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
# This script is used to change the top and bottom padding of the outer container in the Aerospace theme.
presentation_pixel_gaps=300
top_normal_pixel_gaps=82
bottom_normal_pixel_gaps=40
terminal_font_size=18
terminal_presentation_font_size=22
if [ $1 == "on" ]; then
sed -i '' "s/\(outer\.top = *\[.*\), $top_normal_pixel_gaps]/\1, $presentation_pixel_gaps]/" ~/.config/aerospace/aerospace.toml
sed -i '' "s/\(outer\.bottom = *\[.*\), $bottom_normal_pixel_gaps]/\1, $presentation_pixel_gaps]/" ~/.config/aerospace/aerospace.toml
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/stefan.pinter/Pictures/Wallpapers/black-gray.jpg"'
aerospace reload-config
elif [ $1 == "off" ]; then
sed -i '' "s/\(outer\.top = *\[.*\), $presentation_pixel_gaps]/\1, $top_normal_pixel_gaps]/" ~/.config/aerospace/aerospace.toml
sed -i '' "s/\(outer\.bottom = *\[.*\), $presentation_pixel_gaps]/\1, $bottom_normal_pixel_gaps]/" ~/.config/aerospace/aerospace.toml
osascript -e 'tell application "System Events" to set picture of every desktop to "/Users/rayandrew/Pictures/Wallpapers/bluering.png"'
aerospace reload-config
fi

View file

@ -2,7 +2,6 @@ config-version = 2
after-startup-command = [ after-startup-command = [
"workspace 10", "workspace 10",
"layout h_accordion",
"workspace-back-and-forth" "workspace-back-and-forth"
] ]
@ -11,15 +10,20 @@ enable-normalization-flatten-containers = false
enable-normalization-opposite-orientation-for-nested-containers = false enable-normalization-opposite-orientation-for-nested-containers = false
on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] on-focused-monitor-changed = ['move-mouse monitor-lazy-center']
exec-on-workspace-change = ['/bin/bash', '-c', exec-on-workspace-change = ['/bin/bash', '-c',
'/run/current-system/sw/bin/sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE PREV_WORKSPACE=$AEROSPACE_PREV_WORKSPACE && /etc/profiles/per-user/rayandrew/bin/aerospace-scratchpad hook pull-window $AEROSPACE_PREV_WORKSPACE $AEROSPACE_FOCUSED_WORKSPACE' '/run/current-system/sw/bin/sketchybar --trigger aerospace_workspace_changed FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE PREV_WORKSPACE=$AEROSPACE_PREV_WORKSPACE && /etc/profiles/per-user/rayandrew/bin/aerospace-scratchpad hook pull-window $AEROSPACE_PREV_WORKSPACE $AEROSPACE_FOCUSED_WORKSPACE'
]
on-focus-changed = [
'move-mouse window-lazy-center',
'exec-and-forget /bin/bash -c /run/current-system/sw/bin/sketchybar --trigger front_app_switched',
'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger update_windows'
] ]
[gaps] [gaps]
inner.horizontal = [{ monitor.'^built-in retina display$' = 10 }, 15] inner.horizontal = [{ monitor.'^built-in retina display$' = 15 }, 15]
inner.vertical = [{ monitor.'^built-in retina display$' = 10 }, 15] inner.vertical = [{ monitor.'^built-in retina display$' = 15 }, 15]
outer.left = [{ monitor.'^built-in retina display$' = 5 }, 15] outer.left = [{ monitor.'^built-in retina display$' = 15 }, 15]
outer.right = [{ monitor.'^built-in retina display$' = 5 }, 15] outer.right = [{ monitor.'^built-in retina display$' = 15 }, 15]
outer.bottom = [{ monitor.'^built-in retina display$' = 5 }, 15] outer.bottom = [{ monitor.'^built-in retina display$' = 15 }, 15]
outer.top = [{ monitor.'^built-in retina display$' = 15 }, 50] outer.top = [{ monitor.'^built-in retina display$' = 15 }, 50]
[mode.main.binding] [mode.main.binding]
@ -41,6 +45,7 @@ exec-on-workspace-change = ['/bin/bash', '-c',
alt-f = 'fullscreen' alt-f = 'fullscreen'
alt-s = 'layout v_accordion' alt-s = 'layout v_accordion'
alt-t = 'layout h_accordion'
alt-e = 'layout tiles horizontal vertical' alt-e = 'layout tiles horizontal vertical'
alt-space = 'layout floating tiling' alt-space = 'layout floating tiling'
@ -66,9 +71,14 @@ exec-on-workspace-change = ['/bin/bash', '-c',
alt-shift-9 = 'move-node-to-workspace 9' alt-shift-9 = 'move-node-to-workspace 9'
alt-shift-0 = 'move-node-to-workspace 10' alt-shift-0 = 'move-node-to-workspace 10'
alt-shift-semicolon = 'mode service' alt-shift-semicolon = [
alt-r = 'mode resize' 'mode service',
'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger send_message MESSAGE="SERVICE MODE" HOLD="true"'
]
alt-r = [
'mode resize',
'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger send_message MESSAGE="RESIZE MODE" HOLD="true"'
]
cmd-ctrl-1 = "exec-and-forget aerospace-scratchpad show Finder" cmd-ctrl-1 = "exec-and-forget aerospace-scratchpad show Finder"
@ -77,11 +87,22 @@ exec-on-workspace-change = ['/bin/bash', '-c',
j = 'resize height +50' j = 'resize height +50'
k = 'resize height -50' k = 'resize height -50'
l = 'resize width +50' l = 'resize width +50'
enter = 'mode main' esc = [
esc = 'mode main' 'mode main',
'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message'
]
enter = [
'mode main',
'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message'
]
[mode.service.binding] [mode.service.binding]
c = ['reload-config', 'mode main'] esc = [
'reload-config',
'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger hide_message',
'exec-and-forget /run/current-system/sw/bin/sketchybar --reload',
'mode main',
]
r = ['flatten-workspace-tree', 'mode main'] r = ['flatten-workspace-tree', 'mode main']
f = ['layout floating tiling', 'mode main'] f = ['layout floating tiling', 'mode main']
backspace = ['close-all-windows-but-current', 'mode main'] backspace = ['close-all-windows-but-current', 'mode main']
@ -102,3 +123,19 @@ exec-on-workspace-change = ['/bin/bash', '-c',
[[on-window-detected]] [[on-window-detected]]
if.app-id = 'com.1password.1password' if.app-id = 'com.1password.1password'
run = ['layout floating'] run = ['layout floating']
[[on-window-detected]]
if.app-id = 'com.spotify.client'
run = ['move-node-to-workspace 7']
[[on-window-detected]]
if.app-id = 'com.apple.mail'
run = ['move-node-to-workspace 8']
[[on-window-detected]]
if.app-id = 'com.microsoft.teams2'
run = ['move-node-to-workspace 9']
[[on-window-detected]]
if.app-id = 'com.tinyspeck.slackmacgap'
run = ['move-node-to-workspace 9']

View file

@ -0,0 +1,23 @@
#!/usr/bin/env lua
local colors = require 'colors'
local bar_height = 35
sbar.bar {
blur_radius = 20,
border_color = colors.bar.border,
border_width = 2,
color = colors.bar.bg,
corner_radius = 5,
height = bar_height,
margin = 10,
notch_width = 0,
padding_left = 10,
padding_right = 10,
position = 'top',
shadow = true,
sticky = true,
topmost = false,
y_offset = 2,
}

View file

@ -0,0 +1,56 @@
#!/usr/bin/env lua
local colors = require 'colors'
local settings = require 'settings'
sbar.default {
updates = 'when_shown',
icon = {
font = {
family = settings.font.text,
style = 'Bold',
size = 13.0,
},
color = colors.fg,
padding_left = settings.paddings,
padding_right = settings.paddings,
background = { image = { corner_radius = 9 } },
},
label = {
font = {
family = settings.font.text,
style = 'Semibold',
size = 13.0,
},
color = colors.fg,
padding_left = settings.paddings,
padding_right = settings.paddings,
},
background = {
height = 30,
corner_radius = 12,
border_width = 1,
-- border_color = colors.bar.border,
image = {
corner_radius = 8,
-- border_color = colors.grey,
border_width = 1,
},
},
popup = {
background = {
border_width = 1,
corner_radius = 8,
border_color = colors.popup.border,
color = colors.popup.bg,
shadow = { drawing = true },
},
blur_radius = 20,
},
padding_left = 5,
padding_right = 5,
scroll_texts = true,
}
require 'bar'
require 'items'

View file

@ -0,0 +1,37 @@
local colors = require 'colors'
local icons = require 'icons'
local settings = require 'settings'
-- Padding item required because of bracket
sbar.add('item', { width = 5 })
local apple = sbar.add('item', {
icon = {
font = { size = 16.0 },
string = icons.apple,
padding_right = settings.paddings,
padding_left = 0,
color = colors.fg,
},
label = { drawing = false },
background = {
color = colors.bar.bg,
-- border_color = colors.black,
-- border_width = 1
},
padding_left = 1,
padding_right = 1,
click_script = 'sk-menus -s 0',
})
-- Double border for apple using a single item bracket
-- sbar.add("bracket", { apple.name }, {
-- background = {
-- color = colors.transparent,
-- height = 30,
-- -- border_color = colors.grey,
-- }
-- })
-- Padding item required because of bracket
sbar.add('item', { width = 7 })

View file

@ -0,0 +1,34 @@
local function file_exists(path)
local f = io.open(path, 'r')
if f then
f:close()
return true
end
return false
end
local function load_module_if_program_exists(module_name, binary_path)
if file_exists(binary_path) then
print('Loading ' .. module_name .. ' (found at ' .. binary_path .. ')')
local ok, err = pcall(require, module_name)
if not ok then
print('ERROR loading ' .. module_name .. ' module: ' .. tostring(err))
else
print('Successfully loaded ' .. module_name)
end
else
print('Skipping ' .. module_name .. ' (binary not found at ' .. binary_path .. ')')
end
end
require 'items.apple'
-- require 'items.aerospace'
load_module_if_program_exists('items.aerospace', '/opt/homebrew/bin/aerospace')
load_module_if_program_exists('items.yabai', '/etc/profiles/per-user/rayandrew/bin/yabai')
require 'items.front_app'
require 'items.menu'
require 'items.battery'
require 'items.cal'
require 'items.volume'
require 'items.cpu'
require 'items.wifi'

View file

@ -0,0 +1 @@
Shamelessly copied from https://github.com/agenttank/dotfiles_macos/tree/1feca82518312cd8c4a784032471fedfa895989d

View file

@ -1,23 +1,15 @@
#!/usr/bin/env lua local settings = require 'config.settings'
local colors = require 'colors'
local bar_height = 35
sbar.bar { sbar.bar {
blur_radius = 20, topmost = 'window',
border_color = colors.bar.border, height = settings.dimens.graphics.bar.height,
border_width = 2, color = settings.colors.bar.transparent,
color = colors.bar.bg, padding_right = settings.dimens.padding.right,
corner_radius = 5, padding = settings.dimens.padding.bar,
height = bar_height, padding_left = settings.dimens.padding.left,
margin = 10, margin = settings.dimens.padding.bar,
notch_width = 0, corner_radius = settings.dimens.graphics.background.corner_radius,
padding_left = 10, y_offset = settings.dimens.graphics.bar.offset,
padding_right = 10, -- blur_radius = settings.dimens.graphics.blur_radius,
position = 'top', border_width = 0,
shadow = true,
sticky = true,
topmost = false,
y_offset = 2,
} }

View file

@ -0,0 +1,38 @@
local colors <const> = {
black = 0xff181819,
white = 0xfff8f8f2,
red = 0xf1FD6592,
green = 0xff007692,
blue = 0xff5199ba,
yellow = 0xffffff81,
orange = 0xfff4c07b,
magenta = 0xd3fc7ebd,
purple = 0xff796fa9,
other_purple = 0xff302c45,
cyan = 0xff7bf2de,
grey = 0xff7f8490,
dirty_white = 0xc8cad3f5,
dark_grey = 0xff2b2736,
transparent = 0x00000000,
bar = {
bg = 0xf1151320,
border = 0xff2c2e34,
},
popup = {
bg = 0xf1151320,
border = 0xff2c2e34,
},
slider = {
bg = 0xf1151320,
border = 0xff2c2e34,
},
bg1 = 0xd322212c,
bg2 = 0xff302c45,
with_alpha = function(color, alpha)
if alpha > 1.0 or alpha < 0.0 then return color end
return (color & 0x00ffffff) | (math.floor(alpha * 255.0) << 24)
end,
}
return colors

View file

@ -0,0 +1,40 @@
local padding <const> = {
background = 8,
icon = 10,
label = 8,
bar = 10,
left = 12,
right = 12,
item = 18,
popup = 8,
}
local graphics <const> = {
bar = {
height = 30,
offset = 8,
},
background = {
height = 24,
corner_radius = 9,
},
slider = {
height = 20,
},
popup = {
width = 200,
large_width = 300,
},
blur_radius = 30,
}
local text <const> = {
icon = 16.0,
label = 14.0,
}
return {
padding = padding,
graphics = graphics,
text = text,
}

View file

@ -0,0 +1,14 @@
local dimens <const> = require 'config.dimens'
return {
text = 'SpaceMono Nerd Font',
numbers = 'SpaceMono Nerd Font',
icons = function(size)
local font = 'sketchybar-app-font:Regular'
return size and font .. ':' .. size or font .. ':' .. dimens.text.icon
end,
styles = {
regular = 'Regular',
bold = 'Bold',
},
}

View file

@ -0,0 +1,331 @@
local apps <const> = {
['Live'] = ':ableton:',
['Adobe Bridge 2024'] = ':adobe_bridge:',
['Affinity Designer'] = ':affinity_designer:',
['Affinity Designer 2'] = ':affinity_designer_2:',
['Affinity Photo'] = ':affinity_photo:',
['Affinity Photo 2'] = ':affinity_photo_2:',
['Affinity Publisher'] = ':affinity_publisher:',
['Affinity Publisher 2'] = ':affinity_publisher_2:',
['Airmail'] = ':airmail:',
['Alacritty'] = ':alacritty:',
['Alfred Preferences'] = ':alfred:',
['Android Messages'] = ':android_messages:',
['Android Studio'] = ':android_studio:',
['Anytype'] = ':anytype:',
['App Eraser'] = ':app_eraser:',
['App Store'] = ':app_store:',
['Arc'] = ':arc:',
['Atom'] = ':atom:',
['Audacity'] = ':audacity:',
['Bambu Studio'] = ':bambu_studio:',
['MoneyMoney'] = ':bank:',
['Bear'] = ':bear:',
['BetterTouchTool'] = ':bettertouchtool:',
['Bilibili'] = ':bilibili:',
['哔哩哔哩'] = ':bilibili:',
['Bitwarden'] = ':bit_warden:',
['Blender'] = ':blender:',
['BluOS Controller'] = ':bluos_controller:',
['Calibre'] = ':book:',
['Brave Browser'] = ':brave_browser:',
['Calculator'] = ':calculator:',
['Soulver 3'] = ':calculator:',
['Calculette'] = ':calculator:',
['Calendar'] = ':calendar:',
['日历'] = ':calendar:',
['Fantastical'] = ':calendar:',
['Cron'] = ':calendar:',
['Amie'] = ':calendar:',
['Calendrier'] = ':calendar:',
['Notion Calendar'] = ':calendar:',
['Caprine'] = ':caprine:',
['Citrix Workspace'] = ':citrix:',
['Citrix Viewer'] = ':citrix:',
['ClickUp'] = ':click_up:',
['Code'] = ':code:',
['Code - Insiders'] = ':code:',
['Color Picker'] = ':color_picker:',
['数码测色计'] = ':color_picker:',
['CotEditor'] = ':coteditor:',
['Cypress'] = ':cypress:',
['DataGrip'] = ':datagrip:',
['DataSpell'] = ':dataspell:',
['DaVinci Resolve'] = ':davinciresolve:',
['Default'] = ':default:',
['CleanMyMac X'] = ':desktop:',
['DEVONthink 3'] = ':devonthink3:',
['DingTalk'] = ':dingtalk:',
['钉钉'] = ':dingtalk:',
['阿里钉'] = ':dingtalk:',
['Discord'] = ':discord:',
['Discord Canary'] = ':discord:',
['Discord PTB'] = ':discord:',
['Docker'] = ':docker:',
['Docker Desktop'] = ':docker:',
['GrandTotal'] = ':dollar:',
['Receipts'] = ':dollar:',
['Double Commander'] = ':doublecmd:',
['Drafts'] = ':drafts:',
['Dropbox'] = ':dropbox:',
['Element'] = ':element:',
['Emacs'] = ':emacs:',
['Evernote Legacy'] = ':evernote_legacy:',
['FaceTime'] = ':face_time:',
['FaceTime 通话'] = ':face_time:',
['Figma'] = ':figma:',
['Final Cut Pro'] = ':final_cut_pro:',
['Finder'] = ':finder:',
['访达'] = ':finder:',
['Firefox'] = ':firefox:',
['Firefox Developer Edition'] = ':firefox_developer_edition:',
['Firefox Nightly'] = ':firefox_developer_edition:',
['Folx'] = ':folx:',
['Fusion'] = ':fusion:',
['System Preferences'] = ':gear:',
['System Settings'] = ':gear:',
['Systemeinstellungen'] = ':gear:',
['系统设置'] = ':gear:',
['Réglages Système'] = ':gear:',
['GitHub Desktop'] = ':git_hub:',
['Godot'] = ':godot:',
['GoLand'] = ':goland:',
['Chromium'] = ':google_chrome:',
['Google Chrome'] = ':google_chrome:',
['Google Chrome Canary'] = ':google_chrome:',
['Grammarly Editor'] = ':grammarly:',
['Home Assistant'] = ':home_assistant:',
['Hyper'] = ':hyper:',
['IntelliJ IDEA'] = ':idea:',
['Inkdrop'] = ':inkdrop:',
['Inkscape'] = ':inkscape:',
['Insomnia'] = ':insomnia:',
['Iris'] = ':iris:',
['iTerm'] = ':iterm:',
['iTerm2'] = ':iterm:',
['Jellyfin Media Player'] = ':jellyfin:',
['Joplin'] = ':joplin:',
['카카오톡'] = ':kakaotalk:',
['KakaoTalk'] = ':kakaotalk:',
['Kakoune'] = ':kakoune:',
['KeePassXC'] = ':kee_pass_x_c:',
['Secrets'] = ':one_password:',
['Keyboard Maestro'] = ':keyboard_maestro:',
['Keynote'] = ':keynote:',
['Keynote 讲演'] = ':keynote:',
['kitty'] = ':kitty:',
['League of Legends'] = ':league_of_legends:',
['LibreWolf'] = ':libre_wolf:',
['Adobe Lightroom'] = ':lightroom:',
['Lightroom Classic'] = ':lightroomclassic:',
['LINE'] = ':line:',
['Linear'] = ':linear:',
['LM Studio'] = ':lm_studio:',
['LocalSend'] = ':localsend:',
['Logic Pro'] = ':logicpro:',
['Logseq'] = ':logseq:',
['Canary Mail'] = ':mail:',
['HEY'] = ':mail:',
['Mail'] = ':mail:',
['Mailspring'] = ':mail:',
['MailMate'] = ':mail:',
['Superhuman'] = ':mail:',
['邮件'] = ':mail:',
['MAMP'] = ':mamp:',
['MAMP PRO'] = ':mamp:',
['Maps'] = ':maps:',
['Google Maps'] = ':maps:',
['Matlab'] = ':matlab:',
['Mattermost'] = ':mattermost:',
['Messages'] = ':messages:',
['信息'] = ':messages:',
['Nachrichten'] = ':messages:',
['Messenger'] = ':messenger:',
['Microsoft Edge'] = ':microsoft_edge:',
['Microsoft Excel'] = ':microsoft_excel:',
['Microsoft Outlook'] = ':microsoft_outlook:',
['Microsoft PowerPoint'] = ':microsoft_power_point:',
['Microsoft Remote Desktop'] = ':microsoft_remote_desktop:',
['Microsoft Teams'] = ':microsoft_teams:',
['Microsoft Teams (work or school)'] = ':microsoft_teams:',
['Microsoft Word'] = ':microsoft_word:',
['Min'] = ':min_browser:',
['Miro'] = ':miro:',
['MongoDB Compass'] = ':mongodb:',
['mpv'] = ':mpv:',
['Mullvad Browser'] = ':mullvad_browser:',
['Music'] = ':music:',
['音乐'] = ':music:',
['Musique'] = ':music:',
['Neovide'] = ':neovide:',
['neovide'] = ':neovide:',
['Neovim'] = ':neovim:',
['neovim'] = ':neovim:',
['nvim'] = ':neovim:',
['网易云音乐'] = ':netease_music:',
['Noodl'] = ':noodl:',
['Noodl Editor'] = ':noodl:',
['NordVPN'] = ':nord_vpn:',
['Notability'] = ':notability:',
['Notes'] = ':notes:',
['Notizen'] = ':notes:',
['备忘录'] = ':notes:',
['Notion'] = ':notion:',
['Nova'] = ':nova:',
['Numbers'] = ':numbers:',
['Numbers 表格'] = ':numbers:',
['Obsidian'] = ':obsidian:',
['OBS'] = ':obsstudio:',
['OmniFocus'] = ':omni_focus:',
['1Password'] = ':one_password:',
['ChatGPT'] = ':openai:',
['OpenVPN Connect'] = ':openvpn_connect:',
['Opera'] = ':opera:',
['OrcaSlicer'] = ':orcaslicer:',
['Orion'] = ':orion:',
['Orion RC'] = ':orion:',
['Pages'] = ':pages:',
['Pages 文稿'] = ':pages:',
['Parallels Desktop'] = ':parallels:',
['Parsec'] = ':parsec:',
['Preview'] = ':pdf:',
['预览'] = ':pdf:',
['Skim'] = ':pdf:',
['zathura'] = ':pdf:',
['Aperçu'] = ':pdf:',
['PDF Expert'] = ':pdf_expert:',
['Adobe Photoshop'] = ':photoshop:',
['Pi-hole Remote'] = ':pihole:',
['Pine'] = ':pine:',
['Podcasts'] = ':podcasts:',
['播客'] = ':podcasts:',
['PomoDone App'] = ':pomodone:',
['Postman'] = ':postman:',
['PrusaSlicer'] = ':prusaslicer:',
['SuperSlicer'] = ':prusaslicer:',
['PyCharm'] = ':pycharm:',
['QQ'] = ':qq:',
['QQ音乐'] = ':qqmusic:',
['QQMusic'] = ':qqmusic:',
['Quantumult X'] = ':quantumult_x:',
['qutebrowser'] = ':qute_browser:',
['Raindrop.io'] = ':raindrop_io:',
['Reeder'] = ':reeder5:',
['Reminders'] = ':reminders:',
['提醒事项'] = ':reminders:',
['Rappels'] = ':reminders:',
['Replit'] = ':replit:',
['Rider'] = ':rider:',
['JetBrains Rider'] = ':rider:',
['Safari'] = ':safari:',
['Safari浏览器'] = ':safari:',
['Safari Technology Preview'] = ':safari:',
['Sequel Ace'] = ':sequel_ace:',
['Sequel Pro'] = ':sequel_pro:',
['Setapp'] = ':setapp:',
['SF Symbols'] = ':sf_symbols:',
['Signal'] = ':signal:',
['Sketch'] = ':sketch:',
['Skype'] = ':skype:',
['Slack'] = ':slack:',
['Spark'] = ':spark:',
['Spotify'] = ':spotify:',
['Spotlight'] = ':spotlight:',
['Sublime Text'] = ':sublime_text:',
['Tana'] = ':tana:',
['TeamSpeak 3'] = ':team_speak:',
['Telegram'] = ':telegram:',
['Terminal'] = ':terminal:',
['终端'] = ':terminal:',
['Typora'] = ':text:',
['Microsoft To Do'] = ':things:',
['Things'] = ':things:',
['Thunderbird'] = ':thunderbird:',
['TickTick'] = ':tick_tick:',
['TIDAL'] = ':tidal:',
['Tiny RDM'] = ':tinyrdm:',
['Todoist'] = ':todoist:',
['Toggl Track'] = ':toggl_track:',
['Tor Browser'] = ':tor_browser:',
['Tower'] = ':tower:',
['Transmit'] = ':transmit:',
['Trello'] = ':trello:',
['Tweetbot'] = ':twitter:',
['Twitter'] = ':twitter:',
['MacVim'] = ':vim:',
['Vim'] = ':vim:',
['VimR'] = ':vim:',
['Vivaldi'] = ':vivaldi:',
['VLC'] = ':vlc:',
['VMware Fusion'] = ':vmware_fusion:',
['VSCodium'] = ':vscodium:',
['Warp'] = ':warp:',
['WebStorm'] = ':web_storm:',
['微信'] = ':wechat:',
['WeChat'] = ':wechat:',
['企业微信'] = ':wecom:',
['WeCom'] = ':wecom:',
['WezTerm'] = ':wezterm:',
['WhatsApp'] = ':whats_app:',
['WhatsApp'] = ':whats_app:',
['Xcode'] = ':xcode:',
['Яндекс Музыка'] = ':yandex_music:',
['Yuque'] = ':yuque:',
['语雀'] = ':yuque:',
['Zed'] = ':zed:',
['Zeplin'] = ':zeplin:',
['zoom.us'] = ':zoom:',
['Zotero'] = ':zotero:',
['Zulip'] = ':zulip:',
['default'] = ':default:',
}
local text <const> = {
nerdfont = {
plus = '',
loading = '',
apple = '',
gear = '',
cpu = '',
clipboard = '󰅇',
switch = {
on = '󱨥',
off = '󱨦',
},
volume = {
_100 = '',
_66 = '',
_33 = '',
_10 = '',
_0 = '',
},
battery = {
_100 = '',
_75 = '',
_50 = '',
_25 = '',
_0 = '',
charging = '',
},
wifi = {
upload = '',
download = '',
connected = '󰖩',
disconnected = '󰖪',
router = '󰑩',
},
media = {
back = '',
forward = '',
play_pause = '',
},
slider = {
knob = '',
},
},
}
return {
text = text.nerdfont,
apps = apps,
}

View file

@ -0,0 +1 @@
return require 'config.settings'

View file

@ -0,0 +1,11 @@
local colors <const> = require 'config.colors'
local fonts <const> = require 'config.fonts'
local icons <const> = require 'config.icons'
local dimens <const> = require 'config.dimens'
return {
fonts = fonts,
dimens = dimens,
colors = colors,
icons = icons,
}

View file

@ -0,0 +1,37 @@
local events <const> = {
AEROSPACE_WORKSPACE_CHANGED = 'aerospace_workspace_changed',
AEROSPACE_SWITCH = 'aerospace_switch',
SWAP_MENU_AND_SPACES = 'swap_menu_and_spaces',
FRONT_APP_SWITCHED = 'front_app_switched',
UPDATE_WINDOWS = 'update_windows',
SEND_MESSAGE = 'send_message',
HIDE_MESSAGE = 'hide_message',
}
local items <const> = {
SPACES = 'workspaces',
MENU = 'menu',
MENU_TOGGLE = 'menu_toggle',
FRONT_APPS = 'front_apps',
MESSAGE = 'message',
VOLUME = 'widgets.volume',
WIFI = 'widgets.wifi',
BATTERY = 'widgets.battery',
CALENDAR = 'widgets.calendar',
}
local aerospace_cmd <const> = '/opt/homebrew/bin/aerospace'
local aerospace <const> = {
LIST_ALL_WORKSPACES = aerospace_cmd .. ' list-workspaces --all',
GET_CURRENT_WORKSPACE = aerospace_cmd .. ' list-workspaces --focused',
LIST_WINDOWS = aerospace_cmd .. ' list-windows --workspace focused --format "id=%{window-id}, name=%{app-name}"',
GET_CURRENT_WINDOW = aerospace_cmd .. ' list-windows --focused --format %{app-name}',
}
return {
items = items,
events = events,
aerospace_cmd = aerospace_cmd,
aerospace = aerospace,
}

View file

@ -0,0 +1,66 @@
local settings = require 'config.settings'
sbar.default {
updates = 'when_shown',
icon = {
font = {
family = settings.fonts.text,
style = settings.fonts.styles.regular,
size = settings.dimens.text.icon,
},
color = settings.colors.white,
padding_left = settings.dimens.padding.icon,
padding_right = settings.dimens.padding.icon,
},
label = {
font = {
family = settings.fonts.text,
style = settings.fonts.styles.regular,
size = settings.dimens.text.label,
},
color = settings.colors.white,
padding_left = settings.dimens.padding.label,
padding_right = settings.dimens.padding.label,
},
background = {
height = settings.dimens.graphics.background.height,
corner_radius = settings.dimens.graphics.background.corner_radius,
border_width = 0,
image = {
corner_radius = settings.dimens.graphics.background.corner_radius,
},
},
popup = {
y_offset = settings.dimens.padding.popup,
align = 'center',
background = {
border_width = 0,
corner_radius = settings.dimens.graphics.background.corner_radius,
color = settings.colors.popup.bg,
shadow = { drawing = true },
padding_left = settings.dimens.padding.icon,
padding_right = settings.dimens.padding.icon,
},
blur_radius = settings.dimens.graphics.blur_radius,
},
slider = {
highlight_color = settings.colors.orange,
background = {
height = settings.dimens.graphics.slider.height,
corner_radius = settings.dimens.graphics.background.corner_radius,
color = settings.colors.slider.bg,
border_color = settings.colors.slider.border,
border_width = 1,
},
knob = {
font = {
family = settings.fonts.text,
style = settings.fonts.styles.regular,
size = 32,
},
string = settings.icons.text.slider.knob,
drawing = false,
},
},
scroll_texts = true,
}

View file

@ -1,56 +1,13 @@
#!/usr/bin/env lua sbar = require 'sketchybar'
local colors = require 'colors' sbar.begin_config()
local settings = require 'settings' sbar.hotload(true)
sbar.default {
updates = 'when_shown',
icon = {
font = {
family = settings.font.text,
style = 'Bold',
size = 13.0,
},
color = colors.fg,
padding_left = settings.paddings,
padding_right = settings.paddings,
background = { image = { corner_radius = 9 } },
},
label = {
font = {
family = settings.font.text,
style = 'Semibold',
size = 13.0,
},
color = colors.fg,
padding_left = settings.paddings,
padding_right = settings.paddings,
},
background = {
height = 30,
corner_radius = 12,
border_width = 1,
-- border_color = colors.bar.border,
image = {
corner_radius = 8,
-- border_color = colors.grey,
border_width = 1,
},
},
popup = {
background = {
border_width = 1,
corner_radius = 8,
border_color = colors.popup.border,
color = colors.popup.bg,
shadow = { drawing = true },
},
blur_radius = 20,
},
padding_left = 5,
padding_right = 5,
scroll_texts = true,
}
require 'constants'
require 'config'
require 'bar' require 'bar'
require 'default'
require 'items' require 'items'
sbar.end_config()
sbar.event_loop()

View file

@ -1,37 +1,7 @@
local colors = require 'colors' local settings = require 'config.settings'
local icons = require 'icons'
local settings = require 'settings'
-- Padding item required because of bracket local apple = sbar.add('item', 'apple', {
sbar.add('item', { width = 5 }) icon = { string = settings.icons.text.apple },
local apple = sbar.add('item', {
icon = {
font = { size = 16.0 },
string = icons.apple,
padding_right = settings.paddings,
padding_left = 0,
color = colors.fg,
},
label = { drawing = false }, label = { drawing = false },
background = {
color = colors.bar.bg,
-- border_color = colors.black,
-- border_width = 1
},
padding_left = 1,
padding_right = 1,
click_script = 'sk-menus -s 0', click_script = 'sk-menus -s 0',
}) })
-- Double border for apple using a single item bracket
-- sbar.add("bracket", { apple.name }, {
-- background = {
-- color = colors.transparent,
-- height = 30,
-- -- border_color = colors.grey,
-- }
-- })
-- Padding item required because of bracket
sbar.add('item', { width = 7 })

View file

@ -0,0 +1,61 @@
local constants = require 'constants'
local settings = require 'config.settings'
local frontApps = {}
sbar.add('bracket', constants.items.FRONT_APPS, {}, { position = 'left' })
local frontAppWatcher = sbar.add('item', {
drawing = false,
updates = true,
})
local function selectFocusedWindow(frontAppName)
for appName, app in pairs(frontApps) do
local isSelected = appName == frontAppName
local color = isSelected and settings.colors.orange or settings.colors.white
app:set {
label = { color = color },
icon = { color = color },
}
end
end
local function updateWindows(windows)
sbar.remove('/' .. constants.items.FRONT_APPS .. '\\.*/')
frontApps = {}
local foundWindows = string.gmatch(windows, '[^\n]+')
for window in foundWindows do
local parsedWindow = {}
for key, value in string.gmatch(window, '(%w+)=([%w%s]+)') do
parsedWindow[key] = value
end
local windowId = parsedWindow['id']
local windowName = parsedWindow['name']
local icon = settings.icons.apps[windowName] or settings.icons.apps['default']
frontApps[windowName] = sbar.add('item', constants.items.FRONT_APPS .. '.' .. windowName, {
label = {
padding_left = 0,
string = windowName,
},
icon = {
string = icon,
font = settings.fonts.icons(),
},
click_script = constants.aerospace_cmd .. ' focus --window-id ' .. windowId,
})
frontApps[windowName]:subscribe(constants.events.FRONT_APP_SWITCHED, function(env) selectFocusedWindow(env.INFO) end)
end
sbar.exec(constants.aerospace.GET_CURRENT_WINDOW, function(frontAppName) selectFocusedWindow(frontAppName:gsub('[\n\r]', '')) end)
end
local function getWindows() sbar.exec(constants.aerospace.LIST_WINDOWS, updateWindows) end
frontAppWatcher:subscribe(constants.events.UPDATE_WINDOWS, function() getWindows() end)
getWindows()

View file

@ -1,34 +1,12 @@
local function file_exists(path) -- Left items
local f = io.open(path, 'r')
if f then
f:close()
return true
end
return false
end
local function load_module_if_program_exists(module_name, binary_path)
if file_exists(binary_path) then
print('Loading ' .. module_name .. ' (found at ' .. binary_path .. ')')
local ok, err = pcall(require, module_name)
if not ok then
print('ERROR loading ' .. module_name .. ' module: ' .. tostring(err))
else
print('Successfully loaded ' .. module_name)
end
else
print('Skipping ' .. module_name .. ' (binary not found at ' .. binary_path .. ')')
end
end
require 'items.apple' require 'items.apple'
-- require 'items.aerospace' require 'items.menu_spaces_toggle'
load_module_if_program_exists('items.aerospace', '/opt/homebrew/bin/aerospace') require 'items.menus'
load_module_if_program_exists('items.yabai', '/etc/profiles/per-user/rayandrew/bin/yabai') require 'items.spaces'
require 'items.front_app' require 'items.front_apps'
require 'items.menu'
require 'items.battery' -- Right items
require 'items.cal' require 'items.message'
require 'items.volume' require 'items.widgets'
require 'items.cpu'
require 'items.wifi' -- require("items.media")

View file

@ -0,0 +1,75 @@
local constants = require 'constants'
local settings = require 'config.settings'
sbar.add('event', constants.events.SWAP_MENU_AND_SPACES)
local function switchToggle(menuToggle)
local isShowingMenu = menuToggle:query().icon.value == settings.icons.text.switch.on
menuToggle:set {
icon = isShowingMenu and settings.icons.text.switch.off or settings.icons.text.switch.on,
label = isShowingMenu and 'Menus' or 'Spaces',
}
sbar.trigger(constants.events.SWAP_MENU_AND_SPACES, { isShowingMenu = isShowingMenu })
end
local function addToggle()
local menuToggle = sbar.add('item', constants.items.MENU_TOGGLE, {
icon = {
string = settings.icons.text.switch.on,
},
label = {
width = 0,
color = settings.colors.bg1,
string = 'Spaces',
},
background = {
color = settings.colors.with_alpha(settings.colors.dirty_white, 0.0),
},
})
sbar.add('item', constants.items.MENU_TOGGLE .. '.padding', {
width = settings.dimens.padding.label,
})
menuToggle:subscribe('mouse.entered', function(env)
sbar.animate(
'tanh',
30,
function()
menuToggle:set {
background = {
color = { alpha = 1.0 },
border_color = { alpha = 0.5 },
},
icon = { color = settings.colors.bg1 },
label = { width = 'dynamic' },
}
end
)
end)
menuToggle:subscribe('mouse.exited', function(env)
sbar.animate(
'tanh',
30,
function()
menuToggle:set {
background = {
color = { alpha = 0.0 },
border_color = { alpha = 0.0 },
},
icon = { color = settings.colors.white },
label = { width = 0 },
}
end
)
end)
menuToggle:subscribe('mouse.clicked', function(env) switchToggle(menuToggle) end)
menuToggle:subscribe(constants.events.AEROSPACE_SWITCH, function(env) switchToggle(menuToggle) end)
end
addToggle()

View file

@ -0,0 +1,72 @@
local constants = require 'constants'
local settings = require 'config.settings'
local maxItems <const> = 15
local menuItems = {}
local isShowingMenu = false
local frontAppWatcher = sbar.add('item', {
drawing = false,
updates = true,
})
local swapWatcher = sbar.add('item', {
drawing = false,
updates = true,
})
local function createPlaceholders()
for index = 1, maxItems, 1 do
local menu = sbar.add('item', constants.items.MENU .. '.' .. index, {
drawing = false,
icon = { drawing = false },
width = 'dynamic',
label = {
font = {
style = index == 1 and settings.fonts.styles.bold or settings.fonts.styles.regular,
},
},
click_script = 'sk-menus -s ' .. index,
})
menuItems[index] = menu
end
sbar.add('bracket', { '/' .. constants.items.MENU .. '\\..*/' }, {
background = {
color = settings.colors.bg1,
padding_left = settings.dimens.padding.item,
padding_right = settings.dimens.padding.item,
},
})
end
local function updateMenus()
sbar.set('/' .. constants.items.MENU .. '\\..*/', { drawing = false })
sbar.exec('sk-menus -l', function(menus)
local index = 1
for menu in string.gmatch(menus, '[^\r\n]+') do
if index < maxItems then
menuItems[index]:set {
width = 'dynamic',
label = menu,
drawing = isShowingMenu,
}
else
break
end
index = index + 1
end
end)
sbar.set(constants.items.MENU .. '.padding', { drawing = isShowingMenu })
end
frontAppWatcher:subscribe(constants.events.FRONT_APP_SWITCHED, updateMenus)
swapWatcher:subscribe(constants.events.SWAP_MENU_AND_SPACES, function(env)
isShowingMenu = env.isShowingMenu == 'on'
updateMenus()
end)
createPlaceholders()

View file

@ -0,0 +1,51 @@
local constants = require 'constants'
local settings = require 'config.settings'
local message = sbar.add('item', constants.items.MESSAGE, {
width = 0,
position = 'center',
popup = { align = 'center' },
label = {
padding_left = 0,
padding_right = 0,
},
background = {
padding_left = 0,
padding_right = 0,
},
})
local messagePopup = sbar.add('item', {
position = 'popup.' .. message.name,
width = 'dynamic',
label = {
padding_right = settings.dimens.padding.label,
padding_left = settings.dimens.padding.label,
},
icon = {
padding_left = 0,
padding_right = 0,
},
})
local function hideMessage() message:set { popup = { drawing = false } } end
local function showMessage(content, hold)
hideMessage()
message:set { popup = { drawing = true } }
messagePopup:set { label = { string = content } }
if hold == false then sbar.delay(5, function()
if hold then return end
hideMessage()
end) end
end
message:subscribe(constants.events.SEND_MESSAGE, function(env)
local content = env.MESSAGE
local hold = env.HOLD ~= nil and env.HOLD == 'true' or false
showMessage(content, hold)
end)
message:subscribe(constants.events.HIDE_MESSAGE, hideMessage)

View file

@ -0,0 +1,179 @@
local constants = require 'constants'
local settings = require 'config.settings'
local utils = require 'utils'
local spaces = {}
local paddings = {}
local focusedWorkspace = nil
local swapWatcher = sbar.add('item', {
drawing = false,
updates = true,
})
local currentWorkspaceWatcher = sbar.add('item', {
drawing = false,
updates = true,
})
-- Modify this file with Visual Studio Code - at least vim does have problems with the icons
-- copy "Icons" from the nerd fonts cheat sheet and replace icon and name accordingly below
-- https://www.nerdfonts.com/cheat-sheet
local spaceConfigs <const> = {
['1'] = { icon = '󱞁', name = 'Main' },
['2'] = { icon = '', name = '' },
['3'] = { icon = '', name = '' },
['4'] = { icon = '', name = '' },
['5'] = { icon = '', name = '' },
['6'] = { icon = '', name = '' },
['7'] = { icon = '󰎆', name = 'Music' },
['8'] = { icon = '󰇮', name = 'Mail' },
['9'] = { icon = '󰭹', name = 'Chat' },
['10'] = { icon = '󰖟', name = 'Browser' },
['t'] = { icon = '', name = 'Meeting' },
}
-- Track which workspaces have windows
local workspaceHasWindows = {}
local updateWorkspaceVisibility = utils.coro.async(function()
local output = utils.coro.await(utils.coro.exec(constants.aerospace_cmd .. " list-windows --all --format '%{workspace}'"))
-- Reset all
for ws, _ in pairs(workspaceHasWindows) do
workspaceHasWindows[ws] = false
end
-- Mark workspaces that have windows
if output then
for ws in output:gmatch '[^\r\n]+' do
workspaceHasWindows[ws] = true
end
end
-- Update visibility for each space
for workspaceName, _ in pairs(spaceConfigs) do
local spaceName = constants.items.SPACES .. '.' .. workspaceName
local space = spaces[spaceName]
local padding = paddings[spaceName]
if space then
local hasWindows = workspaceHasWindows[workspaceName] == true
local isFocused = focusedWorkspace == workspaceName
local shouldShow = hasWindows or isFocused
space:set { drawing = shouldShow }
if padding then padding:set { drawing = shouldShow } end
end
end
end)
local function selectCurrentWorkspace(focusedWorkspaceName)
focusedWorkspace = focusedWorkspaceName
for workspaceName, _ in pairs(spaceConfigs) do
local spaceName = constants.items.SPACES .. '.' .. workspaceName
local item = spaces[spaceName]
local padding = paddings[spaceName]
if item ~= nil then
local isSelected = workspaceName == focusedWorkspaceName
local hasWindows = workspaceHasWindows[workspaceName] == true
local shouldShow = hasWindows or isSelected
-- Show/hide based on windows or focus
item:set {
drawing = shouldShow,
icon = { color = isSelected and settings.colors.bg1 or settings.colors.white },
label = { color = isSelected and settings.colors.bg1 or settings.colors.white },
background = { color = isSelected and settings.colors.white or settings.colors.bg1 },
}
if padding then padding:set { drawing = shouldShow } end
end
end
updateWorkspaceVisibility()
sbar.trigger(constants.events.UPDATE_WINDOWS)
end
local findAndSelectCurrentWorkspace = utils.coro.async(function()
local focusedWorkspaceOutput = utils.coro.await(utils.coro.exec(constants.aerospace.GET_CURRENT_WORKSPACE))
if focusedWorkspaceOutput then
local focusedWorkspaceName = focusedWorkspaceOutput:match '[^\r\n]+'
selectCurrentWorkspace(focusedWorkspaceName)
end
end)
local function addWorkspaceItem(workspaceName)
local spaceName = constants.items.SPACES .. '.' .. workspaceName
local spaceConfig = spaceConfigs[workspaceName]
-- Skip workspaces without a config entry
if not spaceConfig then return end
local hasIcon = spaceConfig.icon and spaceConfig.icon ~= ''
local hasName = spaceConfig.name and spaceConfig.name ~= ''
local icon = hasIcon and spaceConfig.icon or workspaceName
local name = hasName and spaceConfig.name or workspaceName
local useBold = not hasIcon
local fontStyle = useBold and settings.fonts.styles.bold or settings.fonts.styles.regular
local iconFont = settings.fonts.text .. ':' .. fontStyle .. ':' .. settings.dimens.text.icon
spaces[spaceName] = sbar.add('item', spaceName, {
drawing = false, -- start hidden, will show if has windows or is focused
label = {
width = 0,
padding_left = 0,
string = name,
},
icon = {
string = icon,
font = iconFont,
color = settings.colors.white,
y_offset = useBold and 1 or 0,
},
background = {
color = settings.colors.bg1,
},
click_script = constants.aerospace_cmd .. ' workspace ' .. workspaceName,
})
spaces[spaceName]:subscribe('mouse.entered', function(env)
sbar.animate('tanh', 30, function() spaces[spaceName]:set { label = { width = 'dynamic' } } end)
end)
spaces[spaceName]:subscribe('mouse.exited', function(env)
sbar.animate('tanh', 30, function() spaces[spaceName]:set { label = { width = 0 } } end)
end)
paddings[spaceName] = sbar.add('item', spaceName .. '.padding', {
drawing = false,
width = settings.dimens.padding.label,
})
end
local createWorkspaces = utils.coro.async(function()
local workspacesOutput = utils.coro.await(utils.coro.exec(constants.aerospace.LIST_ALL_WORKSPACES))
if workspacesOutput then
for workspaceName in workspacesOutput:gmatch '[^\r\n]+' do
addWorkspaceItem(workspaceName)
end
end
findAndSelectCurrentWorkspace()
end)
swapWatcher:subscribe(constants.events.SWAP_MENU_AND_SPACES, function(env)
local isShowingSpaces = env.isShowingMenu == 'off' and true or false
if isShowingSpaces then
updateWorkspaceVisibility()
else
sbar.set('/' .. constants.items.SPACES .. '\\..*/', { drawing = false })
end
end)
currentWorkspaceWatcher:subscribe(constants.events.AEROSPACE_WORKSPACE_CHANGED, function(env) selectCurrentWorkspace(env.FOCUSED_WORKSPACE) end)
-- Also update on window changes
currentWorkspaceWatcher:subscribe('front_app_switched', function(env) updateWorkspaceVisibility() end)
createWorkspaces()

View file

@ -0,0 +1,89 @@
local constants = require 'constants'
local settings = require 'config.settings'
local isCharging = false
local battery = sbar.add('item', constants.items.battery, {
position = 'right',
update_freq = 60,
})
local batteryPopup = sbar.add('item', {
position = 'popup.' .. battery.name,
width = 'dynamic',
label = {
padding_right = settings.dimens.padding.label,
padding_left = settings.dimens.padding.label,
},
icon = {
padding_left = 0,
padding_right = 0,
},
})
battery:subscribe({ 'routine', 'power_source_change', 'system_woke' }, function()
sbar.exec('pmset -g batt', function(batteryInfo)
local icon = '!'
local label = '?'
local found, _, charge = batteryInfo:find '(%d+)%%'
if found then
charge = tonumber(charge)
label = charge .. '%'
end
local color = settings.colors.green
local charging, _, _ = batteryInfo:find 'AC Power'
isCharging = charging
if charging then
icon = settings.icons.text.battery.charging
else
if found and charge > 80 then
icon = settings.icons.text.battery._100
elseif found and charge > 60 then
icon = settings.icons.text.battery._75
elseif found and charge > 40 then
icon = settings.icons.text.battery._50
elseif found and charge > 30 then
icon = settings.icons.text.battery._50
color = settings.colors.yellow
elseif found and charge > 20 then
icon = settings.icons.text.battery._25
color = settings.colors.orange
else
icon = settings.icons.text.battery._0
color = settings.colors.red
end
end
local lead = ''
if found and charge < 10 then lead = '0' end
battery:set {
icon = {
string = icon,
color = color,
},
label = {
string = lead .. label,
padding_left = 0,
},
}
end)
end)
battery:subscribe('mouse.clicked', function(env)
local drawing = battery:query().popup.drawing
battery:set { popup = { drawing = 'toggle' } }
if drawing == 'off' then
sbar.exec('pmset -g batt', function(batteryInfo)
local found, _, remaining = batteryInfo:find '(%d+:%d+) remaining'
local label = found and ('Time remaining: ' .. remaining .. 'h') or (isCharging and 'Charging' or 'No estimate')
batteryPopup:set { label = label }
end)
end
end)

View file

@ -0,0 +1,15 @@
local constants = require 'constants'
local calendar = sbar.add('item', constants.items.CALENDAR, {
position = 'right',
update_freq = 1,
icon = { padding_left = 0, padding_right = 0 },
})
calendar:subscribe({ 'forced', 'routine', 'system_woke' }, function(env)
calendar:set {
label = os.date '%a %d %b, %H:%M',
}
end)
calendar:subscribe('mouse.clicked', function(env) sbar.exec "open -a 'Calendar'" end)

View file

@ -0,0 +1,4 @@
require 'items.widgets.calendar'
require 'items.widgets.battery'
require 'items.widgets.volume'
require 'items.widgets.wifi'

View file

@ -0,0 +1,127 @@
local constants = require 'constants'
local settings = require 'config.settings'
local currentAudioDevice = 'None'
local volumeValue = sbar.add('item', constants.items.VOLUME .. '.value', {
position = 'right',
label = {
string = '??%',
padding_left = 0,
},
})
local volumeBracket = sbar.add('bracket', constants.items.VOLUME .. '.bracket', { volumeValue.name }, {
popup = {
align = 'center',
},
})
local volumeSlider = sbar.add('slider', constants.items.VOLUME .. '.slider', settings.dimens.graphics.popup.width, {
position = 'popup.' .. volumeBracket.name,
click_script = 'osascript -e "set volume output volume $PERCENTAGE"',
})
volumeValue:subscribe('volume_change', function(env)
local icon = settings.icons.text.volume._0
local volume = tonumber(env.INFO)
sbar.exec('SwitchAudioSource -t output -c', function(result)
-- local currentOutputDevice = result:sub(1, -2)
-- if currentOutputDevice == "AirPods Max" then
-- icon = "􀺹"
-- elseif currentOutputDevice == "AirPods von Longdong Silver" or currentOutputDevice == "AirPods von Anna" then
-- icon = "􀟥"
-- elseif currentOutputDevice == "Arctis Nova Pro Wireless" then
-- icon = "􀑈"
-- elseif currentOutputDevice == "Ear (2)" then
-- icon = "􀪷"
-- elseif currentOutputDevice == "iD4" then
-- icon = "􀝎"
-- else
if volume > 60 then
icon = settings.icons.text.volume._100
elseif volume > 30 then
icon = settings.icons.text.volume._66
elseif volume > 10 then
icon = settings.icons.text.volume._33
elseif volume > 0 then
icon = settings.icons.text.volume._10
end
-- end
local lead = ''
if volume < 10 then lead = '0' end
-- volumeIcon:set({ label = icon })
volumeSlider:set { slider = { percentage = volume } }
local hasVolume = volume ~= 0
volumeValue:set {
icon = icon,
label = {
string = hasVolume and lead .. volume .. '%' or '',
padding_right = hasVolume and 8 or 0,
},
}
end)
end)
local function hideVolumeDetails()
local drawing = volumeBracket:query().popup.drawing == 'on'
if not drawing then return end
volumeBracket:set { popup = { drawing = false } }
sbar.remove('/' .. constants.items.VOLUME .. '.device\\.*/')
end
local function toggleVolumeDetails(env)
if env.BUTTON == 'right' then
sbar.exec 'open /System/Library/PreferencePanes/Sound.prefpane'
return
end
local shouldDraw = volumeBracket:query().popup.drawing == 'off'
if shouldDraw then
volumeBracket:set { popup = { drawing = true } }
sbar.exec('SwitchAudioSource -t output -c', function(result)
currentAudioDevice = result:sub(1, -2)
sbar.exec('SwitchAudioSource -a -t output', function(available)
local current = currentAudioDevice
local counter = 0
for device in string.gmatch(available, '[^\r\n]+') do
local color = settings.colors.grey
if current == device then color = settings.colors.white end
sbar.add('item', constants.items.VOLUME .. '.device.' .. counter, {
position = 'popup.' .. volumeBracket.name,
align = 'center',
label = { string = device, color = color },
click_script = 'SwitchAudioSource -s "'
.. device
.. '" && sketchybar --set /'
.. constants.items.VOLUME
.. '.device\\.*/ label.color='
.. settings.colors.grey
.. ' --set $NAME label.color='
.. settings.colors.white,
})
counter = counter + 1
end
end)
end)
else
hideVolumeDetails()
end
end
local function changeVolume(env)
local delta = env.SCROLL_DELTA
sbar.exec('osascript -e "set volume output volume (output volume of (get volume settings) + ' .. delta .. ')"')
end
volumeValue:subscribe('mouse.clicked', toggleVolumeDetails)
volumeValue:subscribe('mouse.scrolled', changeVolume)
-- volumeValue:subscribe("mouse.exited.global", hideVolumeDetails)

View file

@ -0,0 +1,247 @@
local constants = require 'constants'
local settings = require 'config.settings'
local popupWidth <const> = settings.dimens.graphics.popup.width + 20
sbar.exec 'killall sk-network-load >/dev/null; sk-network-load en0 network_update 2.0'
local wifiUp = sbar.add('item', constants.items.WIFI .. '.up', {
position = 'right',
width = 0,
icon = {
padding_left = 0,
padding_right = 0,
font = {
style = settings.fonts.styles.bold,
size = 10.0,
},
string = settings.icons.text.wifi.upload,
},
label = {
font = {
family = settings.fonts.numbers,
style = settings.fonts.styles.bold,
size = 10.0,
},
color = settings.colors.orange,
string = '??? Bps',
},
y_offset = 4,
})
local wifiDown = sbar.add('item', constants.items.WIFI .. '.down', {
position = 'right',
icon = {
padding_left = 0,
padding_right = 0,
font = {
style = settings.fonts.styles.bold,
size = 10.0,
},
string = settings.icons.text.wifi.download,
},
label = {
font = {
family = settings.fonts.numbers,
style = settings.fonts.styles.bold,
size = 10,
},
color = settings.colors.blue,
string = '??? Bps',
},
y_offset = -4,
})
local wifi = sbar.add('item', constants.items.WIFI .. '.padding', {
position = 'right',
label = { drawing = false },
padding_right = 0,
})
local wifiBracket = sbar.add('bracket', constants.items.WIFI .. '.bracket', {
wifi.name,
wifiUp.name,
wifiDown.name,
}, {
popup = { align = 'center' },
})
local ssid = sbar.add('item', {
align = 'center',
position = 'popup.' .. wifiBracket.name,
width = popupWidth,
height = 16,
icon = {
string = settings.icons.text.wifi.router,
font = {
style = settings.fonts.styles.bold,
},
},
label = {
font = {
style = settings.fonts.styles.bold,
size = settings.dimens.text.label,
},
max_chars = 18,
string = '????????????',
},
})
local hostname = sbar.add('item', {
position = 'popup.' .. wifiBracket.name,
background = {
height = 16,
},
icon = {
align = 'left',
string = 'Hostname:',
width = popupWidth / 2,
font = {
size = settings.dimens.text.label,
},
},
label = {
max_chars = 20,
string = '????????????',
width = popupWidth / 2,
align = 'right',
},
})
local ip = sbar.add('item', {
position = 'popup.' .. wifiBracket.name,
background = {
height = 16,
},
icon = {
align = 'left',
string = 'IP:',
width = popupWidth / 2,
font = {
size = settings.dimens.text.label,
},
},
label = {
align = 'right',
string = '???.???.???.???',
width = popupWidth / 2,
},
})
local router = sbar.add('item', {
position = 'popup.' .. wifiBracket.name,
background = {
height = 16,
},
icon = {
align = 'left',
string = 'Router:',
width = popupWidth / 2,
font = {
size = settings.dimens.text.label,
},
},
label = {
align = 'right',
string = '???.???.???.???',
width = popupWidth / 2,
},
})
sbar.add('item', { position = 'right', width = settings.dimens.padding.item })
wifiUp:subscribe('network_update', function(env)
local upColor = (env.upload == '000 Bps') and settings.colors.grey or settings.colors.orange
local downColor = (env.download == '000 Bps') and settings.colors.grey or settings.colors.blue
wifiUp:set {
icon = { color = upColor },
label = {
string = env.upload,
color = upColor,
},
}
wifiDown:set {
icon = { color = downColor },
label = {
string = env.download,
color = downColor,
},
}
end)
wifi:subscribe({ 'wifi_change', 'system_woke', 'forced' }, function(env)
wifi:set {
icon = {
string = settings.icons.text.wifi.disconnected,
color = settings.colors.magenta,
},
}
sbar.exec([[ipconfig getifaddr en0]], function(ip)
local ipConnected = not (ip == '')
local wifiIcon
local wifiColor
if ipConnected then
wifiIcon = settings.icons.text.wifi.connected
wifiColor = settings.colors.white
end
wifi:set {
icon = {
string = wifiIcon,
color = wifiColor,
},
}
sbar.exec([[sleep 2; scutil --nwi | grep -m1 'utun' | awk '{ print $1 }']], function(vpn)
local isVPNConnected = not (vpn == '')
if isVPNConnected then
wifiIcon = settings.icons.text.wifi.vpn
wifiColor = settings.colors.green
end
wifi:set {
icon = {
string = wifiIcon,
color = wifiColor,
},
}
end)
end)
end)
local function hideDetails() wifiBracket:set { popup = { drawing = false } } end
local function toggleDetails()
local shouldDrawDetails = wifiBracket:query().popup.drawing == 'off'
if shouldDrawDetails then
wifiBracket:set { popup = { drawing = true } }
sbar.exec('networksetup -getcomputername', function(result) hostname:set { label = result } end)
sbar.exec('ipconfig getifaddr en0', function(result) ip:set { label = result } end)
sbar.exec("ipconfig getsummary en0 | awk -F ' SSID : ' '/ SSID : / {print $2}'", function(result) ssid:set { label = result } end)
sbar.exec("networksetup -getinfo Wi-Fi | awk -F 'Router: ' '/^Router: / {print $2}'", function(result) router:set { label = result } end)
else
hideDetails()
end
end
local function copyLabelToClipboard(env)
local label = sbar.query(env.NAME).label.value
sbar.exec('echo "' .. label .. '" | pbcopy')
sbar.set(env.NAME, { label = { string = settings.icons.text.clipboard, align = 'center' } })
sbar.delay(1, function() sbar.set(env.NAME, { label = { string = label, align = 'right' } }) end)
end
wifiUp:subscribe('mouse.clicked', toggleDetails)
wifiDown:subscribe('mouse.clicked', toggleDetails)
wifi:subscribe('mouse.clicked', toggleDetails)
ssid:subscribe('mouse.clicked', copyLabelToClipboard)
hostname:subscribe('mouse.clicked', copyLabelToClipboard)
ip:subscribe('mouse.clicked', copyLabelToClipboard)
router:subscribe('mouse.clicked', copyLabelToClipboard)

View file

@ -0,0 +1,67 @@
local M = {}
-- Coroutine-based async/await helpers
function M.await(callback_fn)
local co = coroutine.running()
if not co then error 'await must be called inside async' end
local result, err
local done = false
callback_fn(function(res, e)
result = res
err = e
done = true
if coroutine.status(co) == 'suspended' then coroutine.resume(co) end
end)
if not done then coroutine.yield() end
if err then error(err) end
return result
end
function M.awaitAll(callback_fns)
local co = coroutine.running()
if not co then error 'awaitAll must be called inside async' end
local results = {}
local pending = #callback_fns
local err = nil
for i, fn in ipairs(callback_fns) do
fn(function(res, e)
if e then err = e end
results[i] = res
pending = pending - 1
if pending == 0 and coroutine.status(co) == 'suspended' then coroutine.resume(co) end
end)
end
if pending > 0 then coroutine.yield() end
if err then error(err) end
return results
end
function M.async(fn)
return function(...)
local args = { ... }
local co = coroutine.create(function() fn(table.unpack(args)) end)
local ok, err = coroutine.resume(co)
if not ok then print('Async error: ' .. tostring(err)) end
end
end
-- Execute a shell command, returns a callback function for use with await
function M.exec(cmd)
return function(resolve)
sbar.exec(cmd, function(result, exit_code)
if exit_code ~= 0 then
resolve(nil, string.format('Exit Code: %s Message: %s', tostring(exit_code), M.dump(result)))
else
resolve(result, nil)
end
end)
end
end
return M

View file

@ -0,0 +1,5 @@
local coro <const> = require 'utils.coro'
return {
coro = coro,
}

View file

@ -25,9 +25,11 @@ in
enable = true; enable = true;
# active_color = "0xFFA1EFE4"; # active_color = "0xFFA1EFE4";
# inactive_color = "0xFFd3b58d"; # inactive_color = "0xFFd3b58d";
active_color = "0xFFFFFFFF"; # active_color = "0xFFFFFFFF";
inactive_color = "0xFF000000"; # inactive_color = "0xFF000000";
width = 5.0; active_color = "0xFFFFBE69";
inactive_color = "0xFF007692";
width = 8.0;
}; };
}; };
} }