feat: add sketchybar and hammerspoon configs

This commit is contained in:
Ray Andrew 2025-11-27 20:56:22 -06:00
parent c1d49a8954
commit d3fb4fa158
Signed by: rayandrew
SSH key fingerprint: SHA256:XYrYrxF0Z3A72n8P/p6mqPRNQZT22F88XcLsG+kX4xw
56 changed files with 2025 additions and 1548 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ result
/.pre-commit-config.yaml
home/emacs/config/var
.direnv
.claude

7
.stylua.toml Normal file
View file

@ -0,0 +1,7 @@
column_width = 160
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"
call_parentheses = "None"
collapse_simple_statement = "Always"

40
bin/yabai-create-space Executable file
View file

@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Ensure exactly 10 spaces exist on the main display (display 1)
# These can then be moved to other displays as needed
DESIRED_SPACES=10
MAIN_DISPLAY=1
# Get current number of spaces on main display
CURRENT_SPACE_COUNT=$(yabai -m query --spaces --display "$MAIN_DISPLAY" | jq 'length')
echo "Current spaces on display $MAIN_DISPLAY: $CURRENT_SPACE_COUNT"
echo "Desired spaces: $DESIRED_SPACES"
# Focus main display first
yabai -m display --focus "$MAIN_DISPLAY"
if [ "$CURRENT_SPACE_COUNT" -lt "$DESIRED_SPACES" ]; then
MISSING_SPACES=$((DESIRED_SPACES - CURRENT_SPACE_COUNT))
echo "Creating $MISSING_SPACES spaces on display $MAIN_DISPLAY..."
for i in $(seq 1 $MISSING_SPACES); do
yabai -m space --create
echo "Created space $((CURRENT_SPACE_COUNT + i))"
done
elif [ "$CURRENT_SPACE_COUNT" -gt "$DESIRED_SPACES" ]; then
EXTRA_SPACES=$((CURRENT_SPACE_COUNT - DESIRED_SPACES))
echo "Removing $EXTRA_SPACES extra spaces from display $MAIN_DISPLAY..."
# Get the last space on main display and destroy it
for i in $(seq 1 $EXTRA_SPACES); do
LAST_SPACE=$(yabai -m query --spaces --display "$MAIN_DISPLAY" | jq 'map(select(."is-native-fullscreen" == false))[-1].index')
yabai -m space --destroy "$LAST_SPACE"
echo "Destroyed space $LAST_SPACE"
done
fi
echo "Total spaces on display $MAIN_DISPLAY: $(yabai -m query --spaces --display $MAIN_DISPLAY | jq 'length')"
sketchybar --trigger space_change --trigger windows_on_spaces

View file

@ -49,3 +49,5 @@ if functions -q fish_vi_key_bindings
else
bind \et sesh-list
end
alias vim "nvim"

View file

@ -0,0 +1,3 @@
require 'keyboard'
hs.alert.show 'Hammerspoon config loaded'

View file

@ -0,0 +1,12 @@
local keymap = require 'keyboard.keymaps'
require 'keyboard.yabai'
-- General keymap
-- reload config
keymap.super {
key = 'c',
mods = { 'shift' },
message = 'Reload Hammerspoon config',
fn = function() hs.reload() end,
}

View file

@ -0,0 +1,18 @@
local M = {}
M.super_key = 'alt'
M.super = function(opts)
local mods = { M.super_key }
if opts.mods then
for _, mod in ipairs(opts.mods) do
table.insert(mods, mod)
end
end
if opts.message then return hs.hotkey.bind(mods, opts.key, opts.message, opts.fn, opts.release, opts.repeat_fn) end
return hs.hotkey.bind(mods, opts.key, opts.fn, opts.release, opts.repeat_fn)
end
return M

View file

@ -0,0 +1,137 @@
local keymap = require 'keyboard.keymaps'
local yabai_cmd = '/etc/profiles/per-user/rayandrew/bin/yabai'
local jq_cmd = '/etc/profiles/per-user/rayandrew/bin/jq'
local function yabai_query(query, jq_filter)
local cmd = yabai_cmd .. ' -m query ' .. query
if jq_filter then cmd = cmd .. ' | ' .. jq_cmd .. ' ' .. jq_filter end
local handle = io.popen(cmd)
local result = handle:read('*a'):gsub('%s+', '')
handle:close()
return result
end
local function yabai(commands, logic)
local cmd_string = ''
for i, cmd in ipairs(commands) do
if i == 1 then
cmd_string = yabai_cmd .. ' -m ' .. cmd
else
if logic == 'and' then
cmd_string = cmd_string .. ' && ' .. yabai_cmd .. ' -m ' .. cmd
elseif logic == 'or' then
cmd_string = cmd_string .. ' || ' .. yabai_cmd .. ' -m ' .. cmd
else
cmd_string = cmd_string .. ' ; ' .. yabai_cmd .. ' -m ' .. cmd
end
end
end
os.execute(cmd_string)
end
local function yabai_key(opts)
keymap.super {
key = opts.key,
mods = opts.mods,
message = opts.message,
fn = function() yabai(opts.commands, opts.logic) end,
}
end
keymap.super {
key = 'return',
-- message = "Spawn Ghostty",
fn = function()
-- hs.application.launchOrFocus("Ghostty")
-- hs.timer.doAfter(0.1, function()
-- hs.eventtap.keyStroke({ "cmd" }, "n")
-- end)
os.execute 'open -na Ghostty'
end,
}
yabai_key { key = 'f', commands = { 'window --toggle zoom-fullscreen' } }
yabai_key { key = 'l', commands = { 'space --focus recent' } }
yabai_key { key = 'm', commands = { 'space --toggle mission-control' } }
yabai_key { key = 'p', commands = { 'window --toggle pip' } }
yabai_key { key = 'g', commands = { 'space --toggle padding', 'space --toggle gap' } }
yabai_key { key = 'r', commands = { 'space --rotate 90' } }
yabai_key { key = 't', commands = { 'window --toggle float', 'window --grid 4:4:1:1:2:2' } }
yabai_key { key = "'", commands = { 'space --layout stack' } }
yabai_key { key = ';', commands = { 'space --layout bsp' } }
yabai_key { key = 'tab', commands = { 'space --focus recent' } }
-- navigation
yabai_key { key = 'h', commands = { 'window --focus west', 'display --focus west' }, logic = 'or' }
yabai_key { key = 'j', commands = { 'window --focus south', 'display --focus south' }, logic = 'or' }
yabai_key { key = 'k', commands = { 'window --focus north', 'display --focus north' }, logic = 'or' }
yabai_key { key = 'l', commands = { 'window --focus east', 'display --focus east' }, logic = 'or' }
-- move windows (swap within space, or move to adjacent display if at edge)
yabai_key { mods = { 'shift' }, key = 'h', commands = { 'window --swap west', 'window --display west', 'display --focus west' }, logic = 'or' }
yabai_key { mods = { 'shift' }, key = 'j', commands = { 'window --swap south', 'window --display south', 'display --focus south' }, logic = 'or' }
yabai_key { mods = { 'shift' }, key = 'k', commands = { 'window --swap north', 'window --display north', 'display --focus north' }, logic = 'or' }
yabai_key { mods = { 'shift' }, key = 'l', commands = { 'window --swap east', 'window --display east', 'display --focus east' }, logic = 'or' }
-- spaces - focus space and its display
local function focus_space(space_num)
local display = yabai_query('--spaces', "'.[] | select(.index == " .. space_num .. ") | .display'")
if display and display ~= '' then
os.execute(yabai_cmd .. ' -m display --focus ' .. display)
hs.timer.usleep(50000) -- 50ms delay
end
os.execute(yabai_cmd .. ' -m space --focus ' .. space_num)
end
keymap.super { key = '1', fn = function() focus_space(1) end }
keymap.super { key = '2', fn = function() focus_space(2) end }
keymap.super { key = '3', fn = function() focus_space(3) end }
keymap.super { key = '4', fn = function() focus_space(4) end }
keymap.super { key = '5', fn = function() focus_space(5) end }
keymap.super { key = '6', fn = function() focus_space(6) end }
keymap.super { key = '7', fn = function() focus_space(7) end }
keymap.super { key = '8', fn = function() focus_space(8) end }
keymap.super { key = '9', fn = function() focus_space(9) end }
keymap.super { key = '0', fn = function() focus_space(10) end }
-- move window to space and follow
yabai_key { mods = { 'shift' }, key = '1', commands = { 'window --space 1', 'space --focus 1' } }
yabai_key { mods = { 'shift' }, key = '2', commands = { 'window --space 2', 'space --focus 2' } }
yabai_key { mods = { 'shift' }, key = '3', commands = { 'window --space 3', 'space --focus 3' } }
yabai_key { mods = { 'shift' }, key = '4', commands = { 'window --space 4', 'space --focus 4' } }
yabai_key { mods = { 'shift' }, key = '5', commands = { 'window --space 5', 'space --focus 5' } }
yabai_key { mods = { 'shift' }, key = '6', commands = { 'window --space 6', 'space --focus 6' } }
yabai_key { mods = { 'shift' }, key = '7', commands = { 'window --space 7', 'space --focus 7' } }
yabai_key { mods = { 'shift' }, key = '8', commands = { 'window --space 8', 'space --focus 8' } }
yabai_key { mods = { 'shift' }, key = '9', commands = { 'window --space 9', 'space --focus 9' } }
yabai_key { mods = { 'shift' }, key = '0', commands = { 'window --space 10', 'space --focus 10' } }
-- move current space between displays
yabai_key { mods = { 'ctrl' }, key = 'h', message = 'Move space to left display', commands = { 'space --display west' } }
yabai_key { mods = { 'ctrl' }, key = 'l', message = 'Move space to right display', commands = { 'space --display east' } }
-- create new space and move window to it
keymap.super {
key = 'n',
mods = { 'shift' },
message = 'Create new space and move window',
fn = function()
os.execute(yabai_cmd .. ' -m space --create')
hs.timer.usleep(100000) -- 100ms
local index = yabai_query('--spaces --display', '\'map(select(."is-native-fullscreen" == false))[-1].index\'')
if index and index ~= '' then
os.execute(yabai_cmd .. ' -m window --space ' .. index)
os.execute(yabai_cmd .. ' -m space --focus ' .. index)
end
end,
}
-- split type - use insert direction to control next window placement
yabai_key { key = 'v', commands = { 'window --insert east' } }
yabai_key { key = 'v', mods = { 'shift' }, commands = { 'window --insert south' } }

View file

@ -0,0 +1,321 @@
-- these seem to get disentangled to https://github.com/kvndrsslr/sketchybar-app-font/tree/main/mappings
return {
['Live'] = ':ableton:',
['Activity Monitor'] = ':numbers:', -- no pre-existing, sadly
['Adobe Bridge'] = ':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'] = ':alfred:',
['Android Messages'] = ':android_messages:',
['Android Studio'] = ':android_studio:',
['Anki'] = ':anki:',
['Anytype'] = ':anytype:',
['App Eraser'] = ':app_eraser:',
['App Store'] = ':app_store:',
['Arc'] = ':arc:',
['Arduino'] = ':arduino:',
['Arduino IDE'] = ':arduino:',
['Atom'] = ':atom:',
['Audacity'] = ':audacity:',
['Bambu Studio'] = ':bambu_studio:',
['MoneyMoney'] = ':bank:',
['Battle.net'] = ':battle_net:',
['Bear'] = ':bear:',
['BetterTouchTool'] = ':bettertouchtool:',
['Bilibili'] = ':bilibili:',
['哔哩哔哩'] = ':bilibili:',
['Bitwarden'] = ':bit_warden:',
['Blender'] = ':blender:',
['BluOS Controller'] = ':bluos_controller:',
['Calibre'] = ':book:',
['Brave Browser'] = ':brave_browser:',
['BusyCal'] = ':busycal:',
['Calculator'] = ':calculator:',
['Calculette'] = ':calculator:',
['Calendar'] = ':calendar:',
['日历'] = ':calendar:',
['Fantastical'] = ':calendar:',
['Cron'] = ':calendar:',
['Amie'] = ':calendar:',
['Calendrier'] = ':calendar:',
['Notion Calendar'] = ':calendar:',
['Caprine'] = ':caprine:',
['Amazon Chime'] = ':chime:',
['Citrix Workspace'] = ':citrix:',
['Citrix Viewer'] = ':citrix:',
['Claude'] = ':claude:',
['ClickUp'] = ':click_up:',
['Code'] = ':code:',
['Code - Insiders'] = ':code:',
['Cold Turkey Blocker'] = ':cold_turkey_blocker:',
['Color Picker'] = ':color_picker:',
['数码测色计'] = ':color_picker:',
['Copilot'] = ':copilot:',
['CotEditor'] = ':coteditor:',
['Creative Cloud'] = ':creative_cloud:',
['Cursor'] = ':cursor:',
['Cypress'] = ':cypress:',
['DataGrip'] = ':datagrip:',
['DataSpell'] = ':dataspell:',
['DaVinci Resolve'] = ':davinciresolve:',
['Deezer'] = ':deezer:',
['Default'] = ':default:',
['CleanMyMac X'] = ':desktop:',
['DEVONthink 3'] = ':devonthink3:',
['DingTalk'] = ':dingtalk:',
['钉钉'] = ':dingtalk:',
['阿里钉'] = ':dingtalk:',
['Discord'] = ':discord:',
['Discord Canary'] = ':discord:',
['Discord PTB'] = ':discord:',
['Vesktop'] = ':discord:',
['Docker'] = ':docker:',
['Docker Desktop'] = ':docker:',
['GrandTotal'] = ':dollar:',
['Receipts'] = ':dollar:',
['Double Commander'] = ':doublecmd:',
['Drafts'] = ':drafts:',
['draw.io'] = ':draw_io:',
['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:',
['Fork'] = ':fork:',
['FreeTube'] = ':freetube:',
['Fusion'] = ':fusion:',
['System Preferences'] = ':gear:',
['System Settings'] = ':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:',
['IINA'] = ':iina:',
['Adobe Illustrator'] = ':illustrator:',
['Illustrator'] = ':illustrator:',
['Adobe InDesign'] = ':indesign:',
['InDesign'] = ':indesign:',
['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:',
['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:',
['Spark'] = ':mail:',
['邮件'] = ':mail:',
['MAMP'] = ':mamp:',
['MAMP PRO'] = ':mamp:',
['Maps'] = ':maps:',
['News'] = ':sioyek:',
['Ghostty'] = ':terminal:',
['Google Maps'] = ':maps:',
['Marta'] = ':marta:',
['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:',
['PWNeovide'] = ':neovide:',
['Neovide'] = ':neovide:',
['neovide'] = ':neovide:',
['Neovim'] = ':neovim:',
['neovim'] = ':neovim:',
['nvim'] = ':neovim:',
['网易云音乐'] = ':netease_music:',
['Noodl'] = ':noodl:',
['Noodl Editor'] = ':noodl:',
['NordVPN'] = ':nord_vpn:',
['Notability'] = ':notability:',
['Notes'] = ':notes:',
['备忘录'] = ':notes:',
['Notion'] = ':notion:',
['Nova'] = ':nova:',
['Numbers'] = ':numbers:',
['Numbers 表格'] = ':numbers:',
['Obsidian'] = ':obsidian:',
['OBS'] = ':obsstudio:',
['OmniFocus'] = ':omni_focus:',
['1Password'] = ':one_password:',
['Open Video Downloader'] = ':open_video_downloader:',
['ChatGPT'] = ':openai:',
['OpenVPN Connect'] = ':openvpn_connect:',
['Opera'] = ':opera:',
['OrbStack'] = ':orbstack:',
['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:',
['Pearcleaner'] = ':pearcleaner:',
['Phoenix Slides'] = ':phoenix_slides:',
['Adobe Photoshop'] = ':photoshop:',
['PhpStorm'] = ':php_storm:',
['Pi-hole Remote'] = ':pihole:',
['Pine'] = ':pine:',
['Plex'] = ':plex:',
['Plexamp'] = ':plexamp:',
['Podcasts'] = ':podcasts:',
['播客'] = ':podcasts:',
['PomoDone App'] = ':pomodone:',
['Postman'] = ':postman:',
['Proton Mail'] = ':proton_mail:',
['Proton Mail Bridge'] = ':proton_mail:',
['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:',
['Rio'] = ':rio:',
['Royal TSX'] = ':royaltsx:',
['Safari'] = ':safari:',
['Safari浏览器'] = ':safari:',
['Safari Technology Preview'] = ':safari:',
['Sequel Ace'] = ':sequel_ace:',
['Sequel Pro'] = ':sequel_pro:',
['Setapp'] = ':setapp:',
['SF Symbols'] = ':sf_symbols:',
['Signal'] = ':signal:',
['sioyek'] = ':sioyek:',
['Sketch'] = ':sketch:',
['Skype'] = ':skype:',
['Slack'] = ':slack:',
['Spark Desktop'] = ':spark:',
['Spotify'] = ':spotify:',
['Spotlight'] = ':spotlight:',
['Sublime Text'] = ':sublime_text:',
['Strongbox'] = ':one_password:',
['superProductivity'] = ':superproductivity:',
['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:',
['UTM'] = ':utm:',
['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'] = ':yandex_music:',
['Yuque'] = ':yuque:',
['语雀'] = ':yuque:',
['Zed'] = ':zed:',
['Zen Browser'] = ':zen_browser:',
['Zeplin'] = ':zeplin:',
['zoom.us'] = ':zoom:',
['Zotero'] = ':zotero:',
['Zulip'] = ':zulip:',
['Zen'] = ':zen_browser:',
}

23
config/sketchybar/bar.lua Normal file
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,49 @@
local function with_alpha(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 {
fg = 0xffd3b58d,
black = 0xff181819,
white = 0xffd3b58d,
red = 0xffF92672,
blue = 0xff66D9EF,
grey = 0xff7f8490,
transparent = 0x00000000,
bg1 = 0xff072626,
bg2 = 0xff1a1b1c,
bar = {
bg = 0xff072626,
border = 0xffFD971F,
},
popup = {
bg = with_alpha(0xff072626, 0.6),
border = 0xffFD971F,
},
with_alpha = with_alpha,
}
-- return {
-- fg = 0xff181819,
-- black = 0xff181819,
-- white = 0xffffffff,
-- red = 0xffff0000,
-- blue = 0xff0000ff,
-- grey = 0xff7f8490,
-- transparent = 0x00000000,
--
-- bar = {
-- bg = 0xffffffff,
-- border = 0xff000000,
-- },
-- popup = {
-- bg = with_alpha(0xffffffff, 0.6),
-- border = 0xff000000,
-- },
--
-- with_alpha = with_alpha,
-- }

View file

@ -0,0 +1,92 @@
local settings = require 'settings'
local icons = {
sf_symbols = {
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 = '􀊈',
},
},
-- Alternative NerdFont icons
nerdfont = {
plus = '',
loading = '',
apple = '',
gear = '',
cpu = '',
clipboard = 'Missing Icon',
switch = {
on = '󱨥',
off = '󱨦',
},
volume = {
_100 = '',
_66 = '',
_33 = '',
_10 = '',
_0 = '',
},
battery = {
_100 = '',
_75 = '',
_50 = '',
_25 = '',
_0 = '',
charging = '',
},
wifi = {
upload = '',
download = '',
connected = '󰖩',
disconnected = '󰖪',
router = 'Missing Icon',
},
media = {
back = '',
forward = '',
play_pause = '',
},
},
}
if not (settings.icons == 'NerdFont') then
return icons.sf_symbols
else
return icons.nerdfont
end

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,242 @@
-- https://github.com/Tnixc/nix-config/blob/main/home/programs/aerospace-sketchybar/sbar-config-libs/items/aerospaces.lua
local Promise = require 'promise'
local colors = require 'colors'
local utils = require 'utils'
local settings = require 'settings'
local app_icons = require 'app_icons'
local function getAllWorkspaces()
return utils.sbarExecP "aerospace list-workspaces --all --format '%{workspace}%{monitor-appkit-nsscreen-screens-id}%{monitor-id}%{monitor-name}' --json"
end
local function getVisibleWorkspaces()
return utils.sbarExecP "aerospace list-workspaces --visible --monitor all --format '%{workspace}%{monitor-appkit-nsscreen-screens-id}%{monitor-id}%{monitor-name}' --json"
end
local function getAllWindows()
return utils.sbarExecP "aerospace list-windows --all --format '%{app-name}%{window-title}%{workspace}%{monitor-id}%{monitor-appkit-nsscreen-screens-id}%{monitor-name}' --json"
end
local function getMonitorId(obj)
if obj['monitor-name'] then
if obj['monitor-name'] == 'ZOWIE XL LCD' then
return '2'
elseif obj['monitor-name'] == 'LG ULTRAWIDE' then
return '1'
end
end
if obj['monitor-appkit-nsscreen-screens-id'] then return obj['monitor-appkit-nsscreen-screens-id'] end
return obj['monitor-id']
end
local spaces = {}
local space_paddings = {}
local brackets = {}
local state = {
workspaces = {},
updating = false,
}
function getState()
local newstate = {
workspaces = {},
}
for workspaceid, space in pairs(spaces) do
newstate.workspaces[workspaceid] = {
monitor = 0,
active = false,
empty = true,
apps = {},
appicons = '',
}
end
return Promise.all({ getAllWorkspaces(), getVisibleWorkspaces(), getAllWindows() }):thenCall(function(values)
local all, visible, apps = values[1], values[2], values[3]
for _, workspace in ipairs(all) do
local workspaceid = workspace['workspace']
newstate.workspaces[workspaceid]['id'] = workspaceid
newstate.workspaces[workspaceid]['monitor'] = getMonitorId(workspace)
end
for _, workspace in ipairs(visible) do
local workspaceid = workspace['workspace']
newstate.workspaces[workspaceid]['active'] = true
end
for _, window in ipairs(apps) do
local workspaceid = window['workspace']
local appname = window['app-name']
newstate.workspaces[workspaceid]['apps'][appname] = true
newstate.workspaces[workspaceid]['empty'] = false
end
for workspaceid, workspacestate in pairs(newstate.workspaces) do
local appkeys = {}
for app in pairs(workspacestate['apps']) do
table.insert(appkeys, app)
end
table.sort(appkeys)
if #appkeys > 0 then
for _, app in ipairs(appkeys) do
local lookup = app_icons[app]
local icon = ((lookup == nil) and app_icons['Default'] or lookup)
workspacestate['appicons'] = workspacestate['appicons'] .. ' ' .. icon
end
else
workspacestate['appicons'] = ''
end
-- print(utils.dump(workspacestate))
end
return newstate
end)
end
local function updateState()
if not state.updating then
state.updating = true
return getState():thenCall(function(newstate)
state.workspaces = newstate.workspaces
state.updating = false
end)
end
return Promise.reject 'State is already updating'
end
local function highlightSpace(space, space_padding, space_bracket, selected)
space:set {
drawing = true,
icon = { highlight = selected },
label = { highlight = selected },
-- background = { border_color = selected and colors.white or colors.bg2 }
}
space_padding:set {
drawing = true,
}
if space_bracket then
space_bracket:set {
-- background = { border_color = selected and colors.grey or colors.bg2 },
}
end
end
local function onActiveSpaceChange(env)
local focused_workspace = env.FOCUSED_WORKSPACE
local last_workspace = env.PREV_WORKSPACE
-- print("aerospace_workspace_change from " .. last_workspace .. " to " .. focused_workspace)
local space = spaces[focused_workspace]
local space_padding = space_paddings[focused_workspace]
local prev_space = spaces[last_workspace]
local prev_space_padding = space_paddings[last_workspace]
sbar.animate('tanh', 10, function()
highlightSpace(space, space_padding, nil, true)
if state.workspaces[last_workspace]['monitor'] == state.workspaces[focused_workspace]['monitor'] then
highlightSpace(prev_space, prev_space_padding, nil, false)
end
end)
updateState()
end
local function syncState()
sbar.animate('tanh', 10, function()
for workspaceid, workspacestate in pairs(state.workspaces) do
if not workspacestate['empty'] then
spaces[workspaceid]:set {
drawing = true,
display = workspacestate['monitor'],
-- label = {
-- string = workspaceid,
-- highlight = workspacestate["active"],
-- },
-- icon = {
-- string = workspaceid,
-- color = colors.white,
-- highlight = workspacestate["active"],
-- },
label = {
string = workspacestate['appicons'],
highlight = workspacestate['active'],
},
icon = {
highlight = workspacestate['active'],
},
}
space_paddings[workspaceid]:set { drawing = true }
else
-- These should be hidden
spaces[workspaceid]:set {
drawing = false,
display = workspacestate['monitor'],
label = workspacestate['appicons'],
}
space_paddings[workspaceid]:set { drawing = false }
end
end
end)
end
local function updateStateAndSync() return updateState():thenCall(syncState) end
function setup()
getAllWorkspaces()
:thenCall(function(workspaces)
for _, workspace in ipairs(workspaces) do
local workspaceid = workspace['workspace']
local display = getMonitorId(workspace)
local space = sbar.add('item', 'space.' .. workspaceid, {
drawing = false, -- default to not showing the space -- we'll show if it has windows or is activated
updates = 'when_shown',
display = display,
icon = {
string = workspaceid,
color = colors.fg,
highlight_color = colors.red,
},
label = {
padding_right = 12,
color = colors.fg,
highlight_color = colors.blue,
font = 'sketchybar-app-font:Regular:14.0',
y_offset = -1,
-- drawing = false
},
padding_left = 1,
padding_right = 1,
click_script = 'aerospace workspace ' .. workspaceid,
})
spaces[workspaceid] = space
local padding = sbar.add('space', 'space.padding.' .. space.name, {
drawing = false,
updates = 'when_shown',
display = display,
script = '',
width = settings.space_paddings,
})
space_paddings[workspaceid] = padding
end
end)
:thenCall(function()
local space_window_observer = sbar.add('item', {
drawing = false,
updates = true,
})
space_window_observer:subscribe('aerospace_workspace_change', onActiveSpaceChange)
space_window_observer:subscribe('space_windows_change', updateStateAndSync)
space_window_observer:subscribe('system_woke', updateStateAndSync)
space_window_observer:subscribe('front_app_switched', updateStateAndSync)
end)
:thenCall(updateStateAndSync)
end
setup()

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,42 @@
local icons = require 'icons'
local battery = sbar.add('item', {
position = 'right',
icon = {
font = {
style = 'Regular',
size = 19.0,
},
},
label = { drawing = false },
update_freq = 120,
})
local function battery_update()
sbar.exec('pmset -g batt', function(batt_info)
local icon = '!'
if string.find(batt_info, 'AC Power') then
icon = icons.battery.charging
else
local found, _, charge = batt_info:find '(%d+)%%'
if found then charge = tonumber(charge) end
if found and charge > 80 then
icon = icons.battery._100
elseif found and charge > 60 then
icon = icons.battery._75
elseif found and charge > 40 then
icon = icons.battery._50
elseif found and charge > 20 then
icon = icons.battery._25
else
icon = icons.battery._0
end
end
battery:set { icon = icon }
end)
end
battery:subscribe({ 'routine', 'power_source_change', 'system_woke' }, battery_update)

View file

@ -0,0 +1,50 @@
local colors = require 'colors'
local settings = require 'settings'
-- Padding item required because of bracket
sbar.add('item', { position = 'right', width = settings.group_paddings })
local cal = sbar.add('item', {
icon = {
color = colors.fg,
padding_left = 0,
font = { size = 13 },
},
label = {
color = colors.fg,
padding_right = 0,
align = 'right',
},
position = 'right',
update_freq = 30,
padding_left = 0,
padding_right = 0,
click_script = 'open -n -a Calendar',
-- background = {
-- color = colors.bg1,
-- border_color = colors.bar.border,
-- border_width = 1,
-- },
})
-- Double border for calendar using a single item bracket
-- sbar.add("bracket", { cal.name }, {
-- background = {
-- color = colors.transparent,
-- height = 30,
-- border_color = colors.bar.bg,
-- },
-- })
-- Padding item required because of bracket
sbar.add('item', { position = 'right', width = settings.group_paddings })
-- cal:subscribe({ "forced", "routine", "system_woke" }, function(env)
-- cal:set({ icon = os.date("􀉉 %B %d %a"), label = os.date("􀐫 %H:%M") })
-- end)
cal:subscribe({ 'forced', 'routine', 'system_woke' }, function(env)
cal:set {
icon = os.date '􀉉 %B %d %a',
label = os.date '􀐫 %I:%M %p', -- 12-hour format with AM/PM
}
end)

View file

@ -0,0 +1,67 @@
local icons = require 'icons'
local colors = require 'colors'
local settings = require 'settings'
-- Execute the event provider binary which provides the event "cpu_update" for
-- the cpu load data, which is fired every 2.0 seconds.
sbar.exec 'killall sk-cpu-load >/dev/null; sk-cpu-load cpu_update 2.0'
local cpu = sbar.add('graph', 'widgets.cpu', 42, {
position = 'right',
graph = { color = colors.blue },
background = {
height = 22,
color = { alpha = 0 },
border_color = { alpha = 0 },
drawing = true,
},
icon = { string = icons.cpu },
label = {
string = 'cpu ??%',
font = {
family = settings.font.numbers,
style = settings.font.style_map['Bold'],
size = 9.0,
},
align = 'right',
padding_right = 0,
width = 0,
y_offset = 4,
},
padding_right = settings.paddings + 6,
})
cpu:subscribe('cpu_update', function(env)
-- Also available: env.user_load, env.sys_load
local load = tonumber(env.total_load)
cpu:push { load / 100. }
local color = colors.blue
if load > 30 then
if load < 60 then
color = colors.yellow
elseif load < 80 then
color = colors.orange
else
color = colors.red
end
end
cpu:set {
graph = { color = color },
label = 'cpu ' .. env.total_load .. '%',
}
end)
cpu:subscribe('mouse.clicked', function(env) sbar.exec "open -a 'Activity Monitor'" end)
-- Background around the cpu item
sbar.add('bracket', 'widgets.cpu.bracket', { cpu.name }, {
background = { color = colors.bg1 },
})
-- Background around the cpu item
sbar.add('item', 'widgets.cpu.padding', {
position = 'right',
width = settings.group_paddings,
})

View file

@ -0,0 +1,31 @@
local settings = require 'settings'
local app_icons = require 'app_icons'
local front_app = sbar.add('item', 'front_app', {
icon = {
font = 'sketchybar-app-font:Regular:16.0',
padding_left = 8,
padding_right = 8,
},
label = {
font = {
style = settings.font.style_map['Bold'],
size = 12.0,
},
padding_right = 8,
},
})
front_app:subscribe('front_app_switched', function(env)
local app_name = env.INFO
local icon = app_icons[app_name] or app_icons['Default']
front_app:set {
icon = {
string = icon,
},
label = {
string = app_name,
},
}
end)

View file

@ -0,0 +1,33 @@
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'
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,78 @@
local colors = require 'colors'
local icons = require 'icons'
local settings = require 'settings'
local menu_watcher = sbar.add('item', {
drawing = false,
updates = false,
})
local space_menu_swap = sbar.add('item', {
drawing = false,
updates = true,
})
sbar.add('event', 'swap_menus_and_spaces')
local max_items = 15
local menu_items = {}
for i = 1, max_items, 1 do
local menu = sbar.add('item', 'menu.' .. i, {
padding_left = settings.paddings,
padding_right = settings.paddings,
drawing = false,
icon = { drawing = false },
label = {
font = {
style = settings.font.style_map[i == 1 and 'Heavy' or 'Semibold'],
},
padding_left = 6,
padding_right = 6,
},
click_script = 'sk-menus -s ' .. i,
})
menu_items[i] = menu
end
sbar.add('bracket', { '/menu\\..*/' }, {
background = { color = colors.bg1 },
})
local menu_padding = sbar.add('item', 'menu.padding', {
drawing = false,
width = 5,
})
local function update_menus(env)
sbar.exec('sk-menus -l', function(menus)
sbar.set('/menu\\..*/', { drawing = false })
menu_padding:set { drawing = true }
id = 1
for menu in string.gmatch(menus, '[^\r\n]+') do
if id < max_items then
menu_items[id]:set { label = menu, drawing = true }
else
break
end
id = id + 1
end
end)
end
menu_watcher:subscribe('front_app_switched', update_menus)
space_menu_swap:subscribe('swap_menus_and_spaces', function(env)
local drawing = menu_items[1]:query().geometry.drawing == 'on'
if drawing then
menu_watcher:set { updates = false }
sbar.set('/menu\\..*/', { drawing = false })
sbar.set('/space\\..*/', { drawing = true })
sbar.set('front_app', { drawing = true })
else
menu_watcher:set { updates = true }
sbar.set('/space\\..*/', { drawing = false })
sbar.set('front_app', { drawing = false })
update_menus()
end
end)
return menu_watcher

View file

@ -0,0 +1,75 @@
local colors = require 'colors'
local icons = require 'icons'
local volume_slider = sbar.add('slider', 100, {
position = 'right',
updates = true,
label = { drawing = false },
icon = { drawing = false },
slider = {
highlight_color = colors.blue,
width = 0,
background = {
height = 6,
corner_radius = 3,
color = colors.bg2,
},
knob = {
string = '􀀁',
drawing = false,
},
},
})
local volume_icon = sbar.add('item', {
position = 'right',
icon = {
string = icons.volume._100,
width = 0,
align = 'left',
color = colors.grey,
font = {
style = 'Regular',
size = 14.0,
},
},
label = {
width = 25,
align = 'left',
font = {
style = 'Regular',
size = 14.0,
},
},
})
volume_slider:subscribe('mouse.clicked', function(env) sbar.exec("osascript -e 'set volume output volume " .. env['PERCENTAGE'] .. "'") end)
volume_slider:subscribe('volume_change', function(env)
local volume = tonumber(env.INFO)
local icon = icons.volume._0
if volume > 60 then
icon = icons.volume._100
elseif volume > 30 then
icon = icons.volume._66
elseif volume > 10 then
icon = icons.volume._33
elseif volume > 0 then
icon = icons.volume._10
end
volume_icon:set { label = icon }
volume_slider:set { slider = { percentage = volume } }
end)
local function animate_slider_width(width)
sbar.animate('tanh', 30.0, function() volume_slider:set { slider = { width = width } } end)
end
volume_icon:subscribe('mouse.clicked', function()
if tonumber(volume_slider:query().slider.width) > 0 then
animate_slider_width(0)
else
animate_slider_width(100)
end
end)

View file

@ -0,0 +1,219 @@
local icons = require 'icons'
local colors = require 'colors'
local settings = require 'settings'
-- Execute the event provider binary which provides the event "network_update"
-- for the network interface "en0", which is fired every 2.0 seconds.
sbar.exec 'killall sk-network-load >/dev/null; sk-network-load en0 network_update 2.0'
local popup_width = 250
local wifi_up = sbar.add('item', 'widgets.wifi1', {
position = 'right',
padding_left = -5,
width = 0,
icon = {
padding_right = 0,
font = {
style = settings.font.style_map['Bold'],
size = 9.0,
},
string = icons.wifi.upload,
},
label = {
font = {
family = settings.font.numbers,
style = settings.font.style_map['Bold'],
size = 9.0,
},
color = colors.red,
string = '??? Bps',
},
y_offset = 4,
})
local wifi_down = sbar.add('item', 'widgets.wifi2', {
position = 'right',
padding_left = -5,
icon = {
padding_right = 0,
font = {
style = settings.font.style_map['Bold'],
size = 9.0,
},
string = icons.wifi.download,
},
label = {
font = {
family = settings.font.numbers,
style = settings.font.style_map['Bold'],
size = 9.0,
},
color = colors.blue,
string = '??? Bps',
},
y_offset = -4,
})
local wifi = sbar.add('item', 'widgets.wifi.padding', {
position = 'right',
label = { drawing = false },
})
-- Background around the item
local wifi_bracket = sbar.add('bracket', 'widgets.wifi.bracket', {
wifi.name,
wifi_up.name,
wifi_down.name,
}, {
-- background = { color = colors.bar.bg },
popup = { align = 'center', height = 30 },
})
local ssid = sbar.add('item', {
position = 'popup.' .. wifi_bracket.name,
icon = {
font = {
style = settings.font.style_map['Bold'],
},
string = icons.wifi.router,
},
width = popup_width,
align = 'center',
label = {
font = {
size = 15,
style = settings.font.style_map['Bold'],
},
max_chars = 18,
string = '????????????',
},
background = {
height = 2,
color = colors.fg,
y_offset = -15,
},
})
local hostname = sbar.add('item', {
position = 'popup.' .. wifi_bracket.name,
icon = {
align = 'left',
string = 'Hostname:',
width = popup_width / 2,
},
label = {
max_chars = 20,
string = '????????????',
width = popup_width / 2,
align = 'right',
},
})
local ip = sbar.add('item', {
position = 'popup.' .. wifi_bracket.name,
icon = {
align = 'left',
string = 'IP:',
width = popup_width / 2,
},
label = {
string = '???.???.???.???',
width = popup_width / 2,
align = 'right',
},
})
local mask = sbar.add('item', {
position = 'popup.' .. wifi_bracket.name,
icon = {
align = 'left',
string = 'Subnet mask:',
width = popup_width / 2,
},
label = {
string = '???.???.???.???',
width = popup_width / 2,
align = 'right',
},
})
local router = sbar.add('item', {
position = 'popup.' .. wifi_bracket.name,
icon = {
align = 'left',
string = 'Router:',
width = popup_width / 2,
},
label = {
string = '???.???.???.???',
width = popup_width / 2,
align = 'right',
},
})
sbar.add('item', { position = 'right', width = settings.group_paddings })
wifi_up:subscribe('network_update', function(env)
local up_color = (env.upload == '000 Bps') and colors.fg or colors.red
local down_color = (env.download == '000 Bps') and colors.fg or colors.blue
wifi_up:set {
icon = { color = up_color },
label = {
string = env.upload,
color = up_color,
},
}
wifi_down:set {
icon = { color = down_color },
label = {
string = env.download,
color = down_color,
},
}
end)
wifi:subscribe({ 'wifi_change', 'system_woke' }, function(env)
sbar.exec('ipconfig getifaddr en0', function(ip)
local connected = not (ip == '')
wifi:set {
icon = {
string = connected and icons.wifi.connected or icons.wifi.disconnected,
color = connected and colors.fg or colors.red,
},
}
end)
end)
local function hide_details() wifi_bracket:set { popup = { drawing = false } } end
local function toggle_details()
local should_draw = wifi_bracket:query().popup.drawing == 'off'
if should_draw then
wifi_bracket: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 'Subnet mask: ' '/^Subnet mask: / {print $2}'", function(result) mask:set { label = result } end)
sbar.exec("networksetup -getinfo Wi-Fi | awk -F 'Router: ' '/^Router: / {print $2}'", function(result) router:set { label = result } end)
else
hide_details()
end
end
wifi_up:subscribe('mouse.clicked', toggle_details)
wifi_down:subscribe('mouse.clicked', toggle_details)
wifi:subscribe('mouse.clicked', toggle_details)
wifi:subscribe('mouse.exited.global', hide_details)
local function copy_label_to_clipboard(env)
local label = sbar.query(env.NAME).label.value
sbar.exec('echo "' .. label .. '" | pbcopy')
sbar.set(env.NAME, { label = { string = icons.clipboard, align = 'center' } })
sbar.delay(1, function() sbar.set(env.NAME, { label = { string = label, align = 'right' } }) end)
end
ssid:subscribe('mouse.clicked', copy_label_to_clipboard)
hostname:subscribe('mouse.clicked', copy_label_to_clipboard)
ip:subscribe('mouse.clicked', copy_label_to_clipboard)
mask:subscribe('mouse.clicked', copy_label_to_clipboard)
router:subscribe('mouse.clicked', copy_label_to_clipboard)

View file

@ -0,0 +1,190 @@
local colors = require 'colors'
-- local icons = require("icons")
local settings = require 'settings'
local app_icons = require 'app_icons'
local spaces = {}
local space_has_windows = {}
local yabai_cmd = '/etc/profiles/per-user/rayandrew/bin/yabai'
for i = 1, 10, 1 do
local space = sbar.add('space', 'space.' .. i, {
space = i,
icon = {
font = { family = settings.font.numbers },
string = i,
padding_left = 15,
padding_right = 8,
color = colors.white,
highlight_color = colors.red,
},
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 } },
})
spaces[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,
})
local space_popup = sbar.add('item', 'space.popup.' .. i, {
position = 'popup.space.' .. i,
padding_left = 5,
padding_right = 0,
background = {
drawing = true,
image = {
corner_radius = 9,
scale = 0.2,
},
},
})
space:subscribe('space_change', function(env)
local selected = env.SELECTED == 'true'
local color = selected and colors.grey or colors.bg2
local has_windows = space_has_windows[i] or false
space:set {
icon = { highlight = selected },
label = { highlight = selected },
background = { border_color = selected and colors.black or colors.bg2 },
drawing = selected or has_windows,
}
space_bracket:set {
background = { border_color = selected and colors.grey or colors.bg2 },
}
end)
space:subscribe('mouse.clicked', function(env)
if env.BUTTON == 'other' then
space_popup:set { background = { image = 'space.' .. env.SID } }
space:set { popup = { drawing = 'toggle' } }
else
local op = (env.BUTTON == 'right') and '--destroy' or '--focus'
sbar.exec(yabai_cmd .. ' -m space ' .. op .. ' ' .. env.SID)
end
end)
space:subscribe('mouse.exited', function(_) space:set { popup = { drawing = false } } end)
end
local space_window_observer = sbar.add('item', {
drawing = false,
updates = true,
})
-- local spaces_indicator = sbar.add("item", {
-- padding_left = -3,
-- padding_right = 0,
-- icon = {
-- padding_left = 8,
-- padding_right = 9,
-- color = colors.grey,
-- string = icons.switch.on,
-- },
-- label = {
-- width = 0,
-- padding_left = 0,
-- padding_right = 8,
-- string = "Spaces",
-- color = colors.bg1,
-- },
-- background = {
-- color = colors.with_alpha(colors.grey, 0.0),
-- border_color = colors.with_alpha(colors.bg1, 0.0),
-- }
-- })
space_window_observer:subscribe('space_windows_change', function(env)
local icon_line = ''
local no_app = true
for app, count in pairs(env.INFO.apps) 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
-- Track window state
space_has_windows[env.INFO.space] = not no_app
-- Query if this space is currently selected
local space_info = spaces[env.INFO.space]:query()
local is_selected = space_info and space_info.icon and space_info.icon.highlight or false
sbar.animate('tanh', 10, function()
spaces[env.INFO.space]:set {
label = icon_line,
drawing = is_selected or not no_app,
}
end)
end)
-- spaces_indicator:subscribe("swap_menus_and_spaces", function(env)
-- local currently_on = spaces_indicator:query().icon.value == icons.switch.on
-- spaces_indicator:set({
-- icon = currently_on and icons.switch.off or icons.switch.on
-- })
-- end)
--
-- spaces_indicator:subscribe("mouse.entered", function(env)
-- sbar.animate("tanh", 30, function()
-- spaces_indicator:set({
-- background = {
-- color = { alpha = 1.0 },
-- border_color = { alpha = 1.0 },
-- },
-- icon = { color = colors.bg1 },
-- label = { width = "dynamic" }
-- })
-- end)
-- end)
--
-- spaces_indicator:subscribe("mouse.exited", function(env)
-- sbar.animate("tanh", 30, function()
-- spaces_indicator:set({
-- background = {
-- color = { alpha = 0.0 },
-- border_color = { alpha = 0.0 },
-- },
-- icon = { color = colors.grey },
-- label = { width = 0, }
-- })
-- end)
-- end)
--
-- spaces_indicator:subscribe("mouse.clicked", function(env)
-- sbar.trigger("swap_menus_and_spaces")
-- end)

View file

@ -0,0 +1,19 @@
#!/usr/bin/env lua
return {
icons = 'sf-symbols',
font = {
text = 'SF Pro',
numbers = 'SF Pro',
style_map = {
['Regular'] = 'Regular',
['Semibold'] = 'Semibold',
['Bold'] = 'Bold',
['Heavy'] = 'Heavy',
['Black'] = 'Black',
},
},
paddings = 3,
group_paddings = 0,
space_paddings = 5,
}

View file

@ -0,0 +1,33 @@
local Promise = require 'promise'
local M = {}
function M.dump(o)
if type(o) == 'table' then
local s = '{'
for k, v in pairs(o) do
if type(k) ~= 'number' then k = '"' .. k .. '"' end
s = s .. ' [' .. k .. '] = ' .. M.dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
local function onErrorP(reason) print('Error found: ' .. (reason and M.dump(reason) or 'unknown')) end
-- https://github.com/Tnixc/nix-config/blob/main/home/programs/aerospace-sketchybar/sbar-config-libs/items/aerospaces.lua
function M.sbarExecP(cmd)
return Promise.new(function(resolve, failfunc)
sbar.exec(cmd, function(result, exit_code)
if exit_code ~= 0 then
if failfunc ~= nil then failfunc(string.format('Exit Code: %s Message: %s', tostring(exit_code), M.dump(result))) end
else
if resolve ~= nil then resolve(result) end
end
end)
end):catch(onErrorP)
end
return M

49
config/yabai/yabairc Normal file
View file

@ -0,0 +1,49 @@
sudo $(readlink -f $(which yabai)) --load-sa
yabai -m signal --add event=dock_did_restart action="sudo $(readlink -f $(which yabai)) --load-sa"
yabai -m config layout bsp
yabai -m config top_padding 5
yabai -m config bottom_padding 5
yabai -m config left_padding 15
yabai -m config right_padding 15
yabai -m config window_gap 15
yabai -m config auto_balance off
yabai -m config mouse_follows_focus off
yabai -m config focus_follows_mouse autofocus
yabai -m config mouse_modifier fn
yabai -m config mouse_action1 move
yabai -m config mouse_action2 resize
yabai -m config mouse_drop_action swap
yabai -m config external_bar all:45:0
yabai -m config window_shadow float
yabai -m config split_ratio 0.50
yabai -m config window_placement second_child
yabai -m config window_opacity on
yabai -m config active_window_opacity 1.0
yabai -m config normal_window_opacity 0.9
yabai -m config window_border off
yabai -m config window_border_width 6
yabai -m config active_window_border_color 0xff775759
yabai -m config normal_window_border_color 0xff555555
yabai -m config insert_feedback_color 0xffd75f5f
yabai -m rule --add app="^System Settings$" manage=off
yabai -m rule --add app="^Archive Utility$" manage=off
yabai -m rule --add app="^Wally$" manage=off
yabai -m rule --add app="^Pika$" manage=off
yabai -m rule --add app="^balenaEtcher$" manage=off
yabai -m rule --add app="^Creative Cloud$" manage=off
yabai -m rule --add app="^Logi Options$" manage=off
yabai -m rule --add app="^Alfred Preferences$" manage=off
yabai -m rule --add app="Raycast" manage=off
yabai -m rule --add app="^Music$" manage=off
yabai-create-space
echo "yabai configuration loaded..."

View file

@ -7,10 +7,10 @@
}:
let
cfg = config.custom.aerospace;
cfg = config.custom.gui.aerospace;
in
{
options.custom = with lib; {
options.custom.gui = with lib; {
aerospace = {
enable = mkEnableOption "Enable aerospace";
logFile = mkOption {

View file

@ -13,6 +13,7 @@
./keyboard.nix
./sketchybar
./nix.nix
./yabai.nix
];
options.custom = with lib; {

View file

@ -64,6 +64,7 @@
clang-format = mkEnableOption "Enable clang-format";
marta = mkEnableOption "Enable marta";
valgrind = mkEnableOption "Enable valgrind";
hammerspoon = mkEnableOption "Enable hammerspoon";
};
config = lib.mkMerge [
@ -215,7 +216,7 @@
})
(lib.mkIf config.custom.brew.flux {
homebrew.casks = [
"flux"
"flux-app"
];
})
(lib.mkIf config.custom.brew.cleanshot {
@ -376,5 +377,10 @@
# }
# ];
})
(lib.mkIf config.custom.brew.hammerspoon {
homebrew.casks = [
"hammerspoon"
];
})
];
}

View file

@ -1,17 +1,15 @@
{
pkgs,
lib,
config,
dots,
home-dir,
...
}:
let
cfg = config.custom.jankyborders;
cfg = config.custom.gui.jankyborders;
in
{
options.custom = with lib; {
options.custom.gui = with lib; {
jankyborders = {
enable = mkEnableOption "Enable jankyborders";
logFile = mkOption {

View file

@ -1,4 +1,4 @@
{ config, ... }:
{ ... }:
{
system.keyboard = {
enableKeyMapping = true;

View file

@ -1,321 +0,0 @@
-- these seem to get disentangled to https://github.com/kvndrsslr/sketchybar-app-font/tree/main/mappings
return {
["Live"] = ":ableton:",
["Activity Monitor"] = ":numbers:", -- no pre-existing, sadly
["Adobe Bridge"] = ":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"] = ":alfred:",
["Android Messages"] = ":android_messages:",
["Android Studio"] = ":android_studio:",
["Anki"] = ":anki:",
["Anytype"] = ":anytype:",
["App Eraser"] = ":app_eraser:",
["App Store"] = ":app_store:",
["Arc"] = ":arc:",
["Arduino"] = ":arduino:",
["Arduino IDE"] = ":arduino:",
["Atom"] = ":atom:",
["Audacity"] = ":audacity:",
["Bambu Studio"] = ":bambu_studio:",
["MoneyMoney"] = ":bank:",
["Battle.net"] = ":battle_net:",
["Bear"] = ":bear:",
["BetterTouchTool"] = ":bettertouchtool:",
["Bilibili"] = ":bilibili:",
["哔哩哔哩"] = ":bilibili:",
["Bitwarden"] = ":bit_warden:",
["Blender"] = ":blender:",
["BluOS Controller"] = ":bluos_controller:",
["Calibre"] = ":book:",
["Brave Browser"] = ":brave_browser:",
["BusyCal"] = ":busycal:",
["Calculator"] = ":calculator:",
["Calculette"] = ":calculator:",
["Calendar"] = ":calendar:",
["日历"] = ":calendar:",
["Fantastical"] = ":calendar:",
["Cron"] = ":calendar:",
["Amie"] = ":calendar:",
["Calendrier"] = ":calendar:",
["Notion Calendar"] = ":calendar:",
["Caprine"] = ":caprine:",
["Amazon Chime"] = ":chime:",
["Citrix Workspace"] = ":citrix:",
["Citrix Viewer"] = ":citrix:",
["Claude"] = ":claude:",
["ClickUp"] = ":click_up:",
["Code"] = ":code:",
["Code - Insiders"] = ":code:",
["Cold Turkey Blocker"] = ":cold_turkey_blocker:",
["Color Picker"] = ":color_picker:",
["数码测色计"] = ":color_picker:",
["Copilot"] = ":copilot:",
["CotEditor"] = ":coteditor:",
["Creative Cloud"] = ":creative_cloud:",
["Cursor"] = ":cursor:",
["Cypress"] = ":cypress:",
["DataGrip"] = ":datagrip:",
["DataSpell"] = ":dataspell:",
["DaVinci Resolve"] = ":davinciresolve:",
["Deezer"] = ":deezer:",
["Default"] = ":default:",
["CleanMyMac X"] = ":desktop:",
["DEVONthink 3"] = ":devonthink3:",
["DingTalk"] = ":dingtalk:",
["钉钉"] = ":dingtalk:",
["阿里钉"] = ":dingtalk:",
["Discord"] = ":discord:",
["Discord Canary"] = ":discord:",
["Discord PTB"] = ":discord:",
["Vesktop"] = ":discord:",
["Docker"] = ":docker:",
["Docker Desktop"] = ":docker:",
["GrandTotal"] = ":dollar:",
["Receipts"] = ":dollar:",
["Double Commander"] = ":doublecmd:",
["Drafts"] = ":drafts:",
["draw.io"] = ":draw_io:",
["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:",
["Fork"] = ":fork:",
["FreeTube"] = ":freetube:",
["Fusion"] = ":fusion:",
["System Preferences"] = ":gear:",
["System Settings"] = ":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:",
["IINA"] = ":iina:",
["Adobe Illustrator"] = ":illustrator:",
["Illustrator"] = ":illustrator:",
["Adobe InDesign"] = ":indesign:",
["InDesign"] = ":indesign:",
["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:",
["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:",
["Spark"] = ":mail:",
["邮件"] = ":mail:",
["MAMP"] = ":mamp:",
["MAMP PRO"] = ":mamp:",
["Maps"] = ":maps:",
["News"] = ":sioyek:",
["Ghostty"] = ":terminal:",
["Google Maps"] = ":maps:",
["Marta"] = ":marta:",
["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:",
["PWNeovide"] = ":neovide:",
["Neovide"] = ":neovide:",
["neovide"] = ":neovide:",
["Neovim"] = ":neovim:",
["neovim"] = ":neovim:",
["nvim"] = ":neovim:",
["网易云音乐"] = ":netease_music:",
["Noodl"] = ":noodl:",
["Noodl Editor"] = ":noodl:",
["NordVPN"] = ":nord_vpn:",
["Notability"] = ":notability:",
["Notes"] = ":notes:",
["备忘录"] = ":notes:",
["Notion"] = ":notion:",
["Nova"] = ":nova:",
["Numbers"] = ":numbers:",
["Numbers 表格"] = ":numbers:",
["Obsidian"] = ":obsidian:",
["OBS"] = ":obsstudio:",
["OmniFocus"] = ":omni_focus:",
["1Password"] = ":one_password:",
["Open Video Downloader"] = ":open_video_downloader:",
["ChatGPT"] = ":openai:",
["OpenVPN Connect"] = ":openvpn_connect:",
["Opera"] = ":opera:",
["OrbStack"] = ":orbstack:",
["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:",
["Pearcleaner"] = ":pearcleaner:",
["Phoenix Slides"] = ":phoenix_slides:",
["Adobe Photoshop"] = ":photoshop:",
["PhpStorm"] = ":php_storm:",
["Pi-hole Remote"] = ":pihole:",
["Pine"] = ":pine:",
["Plex"] = ":plex:",
["Plexamp"] = ":plexamp:",
["Podcasts"] = ":podcasts:",
["播客"] = ":podcasts:",
["PomoDone App"] = ":pomodone:",
["Postman"] = ":postman:",
["Proton Mail"] = ":proton_mail:",
["Proton Mail Bridge"] = ":proton_mail:",
["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:",
["Rio"] = ":rio:",
["Royal TSX"] = ":royaltsx:",
["Safari"] = ":safari:",
["Safari浏览器"] = ":safari:",
["Safari Technology Preview"] = ":safari:",
["Sequel Ace"] = ":sequel_ace:",
["Sequel Pro"] = ":sequel_pro:",
["Setapp"] = ":setapp:",
["SF Symbols"] = ":sf_symbols:",
["Signal"] = ":signal:",
["sioyek"] = ":sioyek:",
["Sketch"] = ":sketch:",
["Skype"] = ":skype:",
["Slack"] = ":slack:",
["Spark Desktop"] = ":spark:",
["Spotify"] = ":spotify:",
["Spotlight"] = ":spotlight:",
["Sublime Text"] = ":sublime_text:",
["Strongbox"] = ":one_password:",
["superProductivity"] = ":superproductivity:",
["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:",
["UTM"] = ":utm:",
["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"] = ":yandex_music:",
["Yuque"] = ":yuque:",
["语雀"] = ":yuque:",
["Zed"] = ":zed:",
["Zen Browser"] = ":zen_browser:",
["Zeplin"] = ":zeplin:",
["zoom.us"] = ":zoom:",
["Zotero"] = ":zotero:",
["Zulip"] = ":zulip:",
["Zen"] = ":zen_browser:",
}

View file

@ -1,23 +0,0 @@
#!/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

@ -1,48 +0,0 @@
local function with_alpha(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 {
fg = 0xffd3b58d,
black = 0xff181819,
white = 0xffd3b58d,
red = 0xffF92672,
blue = 0xff66D9EF,
grey = 0xff7f8490,
transparent = 0x00000000,
bar = {
bg = 0xff072626,
border = 0xffFD971F,
},
popup = {
bg = with_alpha(0xff072626, 0.6),
border = 0xffFD971F,
},
with_alpha = with_alpha,
}
-- return {
-- fg = 0xff181819,
-- black = 0xff181819,
-- white = 0xffffffff,
-- red = 0xffff0000,
-- blue = 0xff0000ff,
-- grey = 0xff7f8490,
-- transparent = 0x00000000,
--
-- bar = {
-- bg = 0xffffffff,
-- border = 0xff000000,
-- },
-- popup = {
-- bg = with_alpha(0xffffffff, 0.6),
-- border = 0xff000000,
-- },
--
-- with_alpha = with_alpha,
-- }

View file

@ -1,92 +0,0 @@
local settings = require("settings")
local icons = {
sf_symbols = {
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 = "􀊈",
},
},
-- Alternative NerdFont icons
nerdfont = {
plus = "",
loading = "",
apple = "",
gear = "",
cpu = "",
clipboard = "Missing Icon",
switch = {
on = "󱨥",
off = "󱨦",
},
volume = {
_100 = "",
_66 = "",
_33 = "",
_10 = "",
_0 = "",
},
battery = {
_100 = "",
_75 = "",
_50 = "",
_25 = "",
_0 = "",
charging = "",
},
wifi = {
upload = "",
download = "",
connected = "󰖩",
disconnected = "󰖪",
router = "Missing Icon",
},
media = {
back = "",
forward = "",
play_pause = "",
},
},
}
if not (settings.icons == "NerdFont") then
return icons.sf_symbols
else
return icons.nerdfont
end

View file

@ -1,56 +0,0 @@
#!/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

@ -1,252 +0,0 @@
-- https://github.com/Tnixc/nix-config/blob/main/home/programs/aerospace-sketchybar/sbar-config-libs/items/aerospaces.lua
local Promise = require("promise")
local colors = require("colors")
local utils = require("utils")
local settings = require("settings")
local app_icons = require("app_icons")
local function getAllWorkspaces()
return utils.sbarExecP(
"aerospace list-workspaces --all --format '%{workspace}%{monitor-appkit-nsscreen-screens-id}%{monitor-id}%{monitor-name}' --json"
)
end
local function getVisibleWorkspaces()
return utils.sbarExecP(
"aerospace list-workspaces --visible --monitor all --format '%{workspace}%{monitor-appkit-nsscreen-screens-id}%{monitor-id}%{monitor-name}' --json"
)
end
local function getAllWindows()
return utils.sbarExecP(
"aerospace list-windows --all --format '%{app-name}%{window-title}%{workspace}%{monitor-id}%{monitor-appkit-nsscreen-screens-id}%{monitor-name}' --json"
)
end
local function getMonitorId(obj)
if obj["monitor-name"] then
if obj["monitor-name"] == "ZOWIE XL LCD" then
return "2"
elseif obj["monitor-name"] == "LG ULTRAWIDE" then
return "1"
end
end
if obj["monitor-appkit-nsscreen-screens-id"] then
return obj["monitor-appkit-nsscreen-screens-id"]
end
return obj["monitor-id"]
end
local spaces = {}
local space_paddings = {}
local brackets = {}
local state = {
workspaces = {},
updating = false,
}
function getState()
local newstate = {
workspaces = {},
}
for workspaceid, space in pairs(spaces) do
newstate.workspaces[workspaceid] = {
monitor = 0,
active = false,
empty = true,
apps = {},
appicons = "",
}
end
return Promise.all({ getAllWorkspaces(), getVisibleWorkspaces(), getAllWindows() }):thenCall(function(values)
local all, visible, apps = values[1], values[2], values[3]
for _, workspace in ipairs(all) do
local workspaceid = workspace["workspace"]
newstate.workspaces[workspaceid]["id"] = workspaceid
newstate.workspaces[workspaceid]["monitor"] = getMonitorId(workspace)
end
for _, workspace in ipairs(visible) do
local workspaceid = workspace["workspace"]
newstate.workspaces[workspaceid]["active"] = true
end
for _, window in ipairs(apps) do
local workspaceid = window["workspace"]
local appname = window["app-name"]
newstate.workspaces[workspaceid]["apps"][appname] = true
newstate.workspaces[workspaceid]["empty"] = false
end
for workspaceid, workspacestate in pairs(newstate.workspaces) do
local appkeys = {}
for app in pairs(workspacestate["apps"]) do
table.insert(appkeys, app)
end
table.sort(appkeys)
if #appkeys > 0 then
for _, app in ipairs(appkeys) do
local lookup = app_icons[app]
local icon = ((lookup == nil) and app_icons["Default"] or lookup)
workspacestate["appicons"] = workspacestate["appicons"] .. " " .. icon
end
else
workspacestate["appicons"] = ""
end
-- print(utils.dump(workspacestate))
end
return newstate
end)
end
local function updateState()
if not state.updating then
state.updating = true
return getState():thenCall(function(newstate)
state.workspaces = newstate.workspaces
state.updating = false
end)
end
return Promise.reject("State is already updating")
end
local function highlightSpace(space, space_padding, space_bracket, selected)
space:set({
drawing = true,
icon = { highlight = selected },
label = { highlight = selected },
-- background = { border_color = selected and colors.white or colors.bg2 }
})
space_padding:set({
drawing = true,
})
if space_bracket then
space_bracket:set({
-- background = { border_color = selected and colors.grey or colors.bg2 },
})
end
end
local function onActiveSpaceChange(env)
local focused_workspace = env.FOCUSED_WORKSPACE
local last_workspace = env.PREV_WORKSPACE
-- print("aerospace_workspace_change from " .. last_workspace .. " to " .. focused_workspace)
local space = spaces[focused_workspace]
local space_padding = space_paddings[focused_workspace]
local prev_space = spaces[last_workspace]
local prev_space_padding = space_paddings[last_workspace]
sbar.animate("tanh", 10, function()
highlightSpace(space, space_padding, nil, true)
if state.workspaces[last_workspace]["monitor"] == state.workspaces[focused_workspace]["monitor"] then
highlightSpace(prev_space, prev_space_padding, nil, false)
end
end)
updateState()
end
local function syncState()
sbar.animate("tanh", 10, function()
for workspaceid, workspacestate in pairs(state.workspaces) do
if not workspacestate["empty"] then
spaces[workspaceid]:set({
drawing = true,
display = workspacestate["monitor"],
-- label = {
-- string = workspaceid,
-- highlight = workspacestate["active"],
-- },
-- icon = {
-- string = workspaceid,
-- color = colors.white,
-- highlight = workspacestate["active"],
-- },
label = {
string = workspacestate["appicons"],
highlight = workspacestate["active"],
},
icon = {
highlight = workspacestate["active"],
},
})
space_paddings[workspaceid]:set({ drawing = true })
else
-- These should be hidden
spaces[workspaceid]:set({
drawing = false,
display = workspacestate["monitor"],
label = workspacestate["appicons"],
})
space_paddings[workspaceid]:set({ drawing = false })
end
end
end)
end
local function updateStateAndSync()
return updateState():thenCall(syncState)
end
function setup()
getAllWorkspaces()
:thenCall(function(workspaces)
for _, workspace in ipairs(workspaces) do
local workspaceid = workspace["workspace"]
local display = getMonitorId(workspace)
local space = sbar.add("item", "space." .. workspaceid, {
drawing = false, -- default to not showing the space -- we'll show if it has windows or is activated
updates = "when_shown",
display = display,
icon = {
string = workspaceid,
color = colors.fg,
highlight_color = colors.red,
},
label = {
padding_right = 12,
color = colors.fg,
highlight_color = colors.blue,
font = "sketchybar-app-font:Regular:14.0",
y_offset = -1,
-- drawing = false
},
padding_left = 1,
padding_right = 1,
click_script = "aerospace workspace " .. workspaceid,
})
spaces[workspaceid] = space
local padding = sbar.add("space", "space.padding." .. space.name, {
drawing = false,
updates = "when_shown",
display = display,
script = "",
width = settings.space_paddings,
})
space_paddings[workspaceid] = padding
end
end)
:thenCall(function()
local space_window_observer = sbar.add("item", {
drawing = false,
updates = true,
})
space_window_observer:subscribe("aerospace_workspace_change", onActiveSpaceChange)
space_window_observer:subscribe("space_windows_change", updateStateAndSync)
space_window_observer:subscribe("system_woke", updateStateAndSync)
space_window_observer:subscribe("front_app_switched", updateStateAndSync)
end)
:thenCall(updateStateAndSync)
end
setup()

View file

@ -1,37 +0,0 @@
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

@ -1,44 +0,0 @@
local icons = require("icons")
local battery = sbar.add("item", {
position = "right",
icon = {
font = {
style = "Regular",
size = 19.0,
},
},
label = { drawing = false },
update_freq = 120,
})
local function battery_update()
sbar.exec("pmset -g batt", function(batt_info)
local icon = "!"
if string.find(batt_info, "AC Power") then
icon = icons.battery.charging
else
local found, _, charge = batt_info:find("(%d+)%%")
if found then
charge = tonumber(charge)
end
if found and charge > 80 then
icon = icons.battery._100
elseif found and charge > 60 then
icon = icons.battery._75
elseif found and charge > 40 then
icon = icons.battery._50
elseif found and charge > 20 then
icon = icons.battery._25
else
icon = icons.battery._0
end
end
battery:set({ icon = icon })
end)
end
battery:subscribe({ "routine", "power_source_change", "system_woke" }, battery_update)

View file

@ -1,50 +0,0 @@
local colors = require("colors")
local settings = require("settings")
-- Padding item required because of bracket
sbar.add("item", { position = "right", width = settings.group_paddings })
local cal = sbar.add("item", {
icon = {
color = colors.fg,
padding_left = 0,
font = { size = 13 },
},
label = {
color = colors.fg,
padding_right = 0,
align = "right",
},
position = "right",
update_freq = 30,
padding_left = 0,
padding_right = 0,
click_script = "open -n -a Calendar",
-- background = {
-- color = colors.bg1,
-- border_color = colors.bar.border,
-- border_width = 1,
-- },
})
-- Double border for calendar using a single item bracket
-- sbar.add("bracket", { cal.name }, {
-- background = {
-- color = colors.transparent,
-- height = 30,
-- border_color = colors.bar.bg,
-- },
-- })
-- Padding item required because of bracket
sbar.add("item", { position = "right", width = settings.group_paddings })
-- cal:subscribe({ "forced", "routine", "system_woke" }, function(env)
-- cal:set({ icon = os.date("􀉉 %B %d %a"), label = os.date("􀐫 %H:%M") })
-- end)
cal:subscribe({ "forced", "routine", "system_woke" }, function(env)
cal:set({
icon = os.date("􀉉 %B %d %a"),
label = os.date("􀐫 %I:%M %p"), -- 12-hour format with AM/PM
})
end)

View file

@ -1,69 +0,0 @@
local icons = require("icons")
local colors = require("colors")
local settings = require("settings")
-- Execute the event provider binary which provides the event "cpu_update" for
-- the cpu load data, which is fired every 2.0 seconds.
sbar.exec("killall sk-cpu-load >/dev/null; sk-cpu-load cpu_update 2.0")
local cpu = sbar.add("graph", "widgets.cpu", 42, {
position = "right",
graph = { color = colors.blue },
background = {
height = 22,
color = { alpha = 0 },
border_color = { alpha = 0 },
drawing = true,
},
icon = { string = icons.cpu },
label = {
string = "cpu ??%",
font = {
family = settings.font.numbers,
style = settings.font.style_map["Bold"],
size = 9.0,
},
align = "right",
padding_right = 0,
width = 0,
y_offset = 4,
},
padding_right = settings.paddings + 6,
})
cpu:subscribe("cpu_update", function(env)
-- Also available: env.user_load, env.sys_load
local load = tonumber(env.total_load)
cpu:push({ load / 100. })
local color = colors.blue
if load > 30 then
if load < 60 then
color = colors.yellow
elseif load < 80 then
color = colors.orange
else
color = colors.red
end
end
cpu:set({
graph = { color = color },
label = "cpu " .. env.total_load .. "%",
})
end)
cpu:subscribe("mouse.clicked", function(env)
sbar.exec("open -a 'Activity Monitor'")
end)
-- Background around the cpu item
sbar.add("bracket", "widgets.cpu.bracket", { cpu.name }, {
background = { color = colors.bg1 },
})
-- Background around the cpu item
sbar.add("item", "widgets.cpu.padding", {
position = "right",
width = settings.group_paddings,
})

View file

@ -1,28 +0,0 @@
local settings = require("settings")
local front_app = sbar.add("item", {
icon = {
drawing = false,
},
label = {
font = {
style = settings.font.style_map["Bold"],
size = 12.0,
},
},
})
front_app:subscribe("front_app_switched", function(env)
front_app:set({
label = {
string = env.INFO:upper(),
},
})
-- Or equivalently:
-- sbar.set(env.NAME, {
-- label = {
-- string = env.INFO
-- }
-- })
end)

View file

@ -1,9 +0,0 @@
require("items.aerospace")
require("items.apple")
require("items.menu")
require("items.front_app")
require("items.battery")
require("items.cal")
require("items.volume")
require("items.cpu")
require("items.wifi")

View file

@ -1,78 +0,0 @@
local colors = require("colors")
local icons = require("icons")
local settings = require("settings")
local menu_watcher = sbar.add("item", {
drawing = false,
updates = false,
})
local space_menu_swap = sbar.add("item", {
drawing = false,
updates = true,
})
sbar.add("event", "swap_menus_and_spaces")
local max_items = 15
local menu_items = {}
for i = 1, max_items, 1 do
local menu = sbar.add("item", "menu." .. i, {
padding_left = settings.paddings,
padding_right = settings.paddings,
drawing = false,
icon = { drawing = false },
label = {
font = {
style = settings.font.style_map[i == 1 and "Heavy" or "Semibold"],
},
padding_left = 6,
padding_right = 6,
},
click_script = "sk-menus -s " .. i,
})
menu_items[i] = menu
end
sbar.add("bracket", { "/menu\\..*/" }, {
background = { color = colors.bg1 },
})
local menu_padding = sbar.add("item", "menu.padding", {
drawing = false,
width = 5,
})
local function update_menus(env)
sbar.exec("sk-menus -l", function(menus)
sbar.set("/menu\\..*/", { drawing = false })
menu_padding:set({ drawing = true })
id = 1
for menu in string.gmatch(menus, "[^\r\n]+") do
if id < max_items then
menu_items[id]:set({ label = menu, drawing = true })
else
break
end
id = id + 1
end
end)
end
menu_watcher:subscribe("front_app_switched", update_menus)
space_menu_swap:subscribe("swap_menus_and_spaces", function(env)
local drawing = menu_items[1]:query().geometry.drawing == "on"
if drawing then
menu_watcher:set({ updates = false })
sbar.set("/menu\\..*/", { drawing = false })
sbar.set("/space\\..*/", { drawing = true })
sbar.set("front_app", { drawing = true })
else
menu_watcher:set({ updates = true })
sbar.set("/space\\..*/", { drawing = false })
sbar.set("front_app", { drawing = false })
update_menus()
end
end)
return menu_watcher

View file

@ -1,79 +0,0 @@
local colors = require("colors")
local icons = require("icons")
local volume_slider = sbar.add("slider", 100, {
position = "right",
updates = true,
label = { drawing = false },
icon = { drawing = false },
slider = {
highlight_color = colors.blue,
width = 0,
background = {
height = 6,
corner_radius = 3,
color = colors.bg2,
},
knob = {
string = "􀀁",
drawing = false,
},
},
})
local volume_icon = sbar.add("item", {
position = "right",
icon = {
string = icons.volume._100,
width = 0,
align = "left",
color = colors.grey,
font = {
style = "Regular",
size = 14.0,
},
},
label = {
width = 25,
align = "left",
font = {
style = "Regular",
size = 14.0,
},
},
})
volume_slider:subscribe("mouse.clicked", function(env)
sbar.exec("osascript -e 'set volume output volume " .. env["PERCENTAGE"] .. "'")
end)
volume_slider:subscribe("volume_change", function(env)
local volume = tonumber(env.INFO)
local icon = icons.volume._0
if volume > 60 then
icon = icons.volume._100
elseif volume > 30 then
icon = icons.volume._66
elseif volume > 10 then
icon = icons.volume._33
elseif volume > 0 then
icon = icons.volume._10
end
volume_icon:set({ label = icon })
volume_slider:set({ slider = { percentage = volume } })
end)
local function animate_slider_width(width)
sbar.animate("tanh", 30.0, function()
volume_slider:set({ slider = { width = width } })
end)
end
volume_icon:subscribe("mouse.clicked", function()
if tonumber(volume_slider:query().slider.width) > 0 then
animate_slider_width(0)
else
animate_slider_width(100)
end
end)

View file

@ -1,233 +0,0 @@
local icons = require("icons")
local colors = require("colors")
local settings = require("settings")
-- Execute the event provider binary which provides the event "network_update"
-- for the network interface "en0", which is fired every 2.0 seconds.
sbar.exec("killall sk-network-load >/dev/null; sk-network-load en0 network_update 2.0")
local popup_width = 250
local wifi_up = sbar.add("item", "widgets.wifi1", {
position = "right",
padding_left = -5,
width = 0,
icon = {
padding_right = 0,
font = {
style = settings.font.style_map["Bold"],
size = 9.0,
},
string = icons.wifi.upload,
},
label = {
font = {
family = settings.font.numbers,
style = settings.font.style_map["Bold"],
size = 9.0,
},
color = colors.red,
string = "??? Bps",
},
y_offset = 4,
})
local wifi_down = sbar.add("item", "widgets.wifi2", {
position = "right",
padding_left = -5,
icon = {
padding_right = 0,
font = {
style = settings.font.style_map["Bold"],
size = 9.0,
},
string = icons.wifi.download,
},
label = {
font = {
family = settings.font.numbers,
style = settings.font.style_map["Bold"],
size = 9.0,
},
color = colors.blue,
string = "??? Bps",
},
y_offset = -4,
})
local wifi = sbar.add("item", "widgets.wifi.padding", {
position = "right",
label = { drawing = false },
})
-- Background around the item
local wifi_bracket = sbar.add("bracket", "widgets.wifi.bracket", {
wifi.name,
wifi_up.name,
wifi_down.name,
}, {
-- background = { color = colors.bar.bg },
popup = { align = "center", height = 30 },
})
local ssid = sbar.add("item", {
position = "popup." .. wifi_bracket.name,
icon = {
font = {
style = settings.font.style_map["Bold"],
},
string = icons.wifi.router,
},
width = popup_width,
align = "center",
label = {
font = {
size = 15,
style = settings.font.style_map["Bold"],
},
max_chars = 18,
string = "????????????",
},
background = {
height = 2,
color = colors.fg,
y_offset = -15,
},
})
local hostname = sbar.add("item", {
position = "popup." .. wifi_bracket.name,
icon = {
align = "left",
string = "Hostname:",
width = popup_width / 2,
},
label = {
max_chars = 20,
string = "????????????",
width = popup_width / 2,
align = "right",
},
})
local ip = sbar.add("item", {
position = "popup." .. wifi_bracket.name,
icon = {
align = "left",
string = "IP:",
width = popup_width / 2,
},
label = {
string = "???.???.???.???",
width = popup_width / 2,
align = "right",
},
})
local mask = sbar.add("item", {
position = "popup." .. wifi_bracket.name,
icon = {
align = "left",
string = "Subnet mask:",
width = popup_width / 2,
},
label = {
string = "???.???.???.???",
width = popup_width / 2,
align = "right",
},
})
local router = sbar.add("item", {
position = "popup." .. wifi_bracket.name,
icon = {
align = "left",
string = "Router:",
width = popup_width / 2,
},
label = {
string = "???.???.???.???",
width = popup_width / 2,
align = "right",
},
})
sbar.add("item", { position = "right", width = settings.group_paddings })
wifi_up:subscribe("network_update", function(env)
local up_color = (env.upload == "000 Bps") and colors.fg or colors.red
local down_color = (env.download == "000 Bps") and colors.fg or colors.blue
wifi_up:set({
icon = { color = up_color },
label = {
string = env.upload,
color = up_color,
},
})
wifi_down:set({
icon = { color = down_color },
label = {
string = env.download,
color = down_color,
},
})
end)
wifi:subscribe({ "wifi_change", "system_woke" }, function(env)
sbar.exec("ipconfig getifaddr en0", function(ip)
local connected = not (ip == "")
wifi:set({
icon = {
string = connected and icons.wifi.connected or icons.wifi.disconnected,
color = connected and colors.fg or colors.red,
},
})
end)
end)
local function hide_details()
wifi_bracket:set({ popup = { drawing = false } })
end
local function toggle_details()
local should_draw = wifi_bracket:query().popup.drawing == "off"
if should_draw then
wifi_bracket: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 'Subnet mask: ' '/^Subnet mask: / {print $2}'", function(result)
mask:set({ label = result })
end)
sbar.exec("networksetup -getinfo Wi-Fi | awk -F 'Router: ' '/^Router: / {print $2}'", function(result)
router:set({ label = result })
end)
else
hide_details()
end
end
wifi_up:subscribe("mouse.clicked", toggle_details)
wifi_down:subscribe("mouse.clicked", toggle_details)
wifi:subscribe("mouse.clicked", toggle_details)
wifi:subscribe("mouse.exited.global", hide_details)
local function copy_label_to_clipboard(env)
local label = sbar.query(env.NAME).label.value
sbar.exec('echo "' .. label .. '" | pbcopy')
sbar.set(env.NAME, { label = { string = icons.clipboard, align = "center" } })
sbar.delay(1, function()
sbar.set(env.NAME, { label = { string = label, align = "right" } })
end)
end
ssid:subscribe("mouse.clicked", copy_label_to_clipboard)
hostname:subscribe("mouse.clicked", copy_label_to_clipboard)
ip:subscribe("mouse.clicked", copy_label_to_clipboard)
mask:subscribe("mouse.clicked", copy_label_to_clipboard)
router:subscribe("mouse.clicked", copy_label_to_clipboard)

View file

@ -1,19 +0,0 @@
#!/usr/bin/env lua
return {
icons = "sf-symbols",
font = {
text = "SF Pro",
numbers = "SF Pro",
style_map = {
["Regular"] = "Regular",
["Semibold"] = "Semibold",
["Bold"] = "Bold",
["Heavy"] = "Heavy",
["Black"] = "Black",
},
},
paddings = 3,
group_paddings = 0,
space_paddings = 5,
}

View file

@ -1,41 +0,0 @@
local Promise = require("promise")
local M = {}
function M.dump(o)
if type(o) == "table" then
local s = "{"
for k, v in pairs(o) do
if type(k) ~= "number" then
k = '"' .. k .. '"'
end
s = s .. " [" .. k .. "] = " .. M.dump(v) .. ","
end
return s .. "} "
else
return tostring(o)
end
end
local function onErrorP(reason)
print("Error found: " .. (reason and M.dump(reason) or "unknown"))
end
-- https://github.com/Tnixc/nix-config/blob/main/home/programs/aerospace-sketchybar/sbar-config-libs/items/aerospaces.lua
function M.sbarExecP(cmd)
return Promise.new(function(resolve, failfunc)
sbar.exec(cmd, function(result, exit_code)
if exit_code ~= 0 then
if failfunc ~= nil then
failfunc(string.format("Exit Code: %s Message: %s", tostring(exit_code), M.dump(result)))
end
else
if resolve ~= nil then
resolve(result)
end
end
end)
end):catch(onErrorP)
end
return M

View file

@ -8,7 +8,7 @@
}:
let
cfg = config.custom.sketchybar;
cfg = config.custom.gui.sketchybar;
sketchybar = lib.getExe pkgs.sketchybar;
lua = pkgs.lua5_4.withPackages (
ps: with ps; [
@ -19,7 +19,7 @@ let
);
in
{
options.custom = with lib; {
options.custom.gui = with lib; {
sketchybar = {
enable = mkEnableOption "Enable sketchybar";
logFile = mkOption {
@ -56,7 +56,7 @@ in
-- Add the sketchybar module to the package cpath (the module could be
-- installed into the default search path then this would not be needed)
package.path = "${dots}/darwin/sketchybar/config/?.lua;${dots}/darwin/sketchybar/config/?/?.lua;${dots}/darwin/sketchybar/config/?/init.lua;" .. package.path
package.path = "${dots}/config/sketchybar/?.lua;${dots}/config/sketchybar/?/?.lua;${dots}/config/sketchybar/?/init.lua;" .. package.path
sbar = require("sketchybar")
sbar.exec("killall sketchyhelper || sketchyhelper git.felix.sketchyhelper >/dev/null 2>&1 &")

52
darwin/yabai.nix Normal file
View file

@ -0,0 +1,52 @@
{
config,
pkgs,
lib,
user,
dots,
...
}:
{
options.custom.gui = with lib; {
yabai = {
enable = mkEnableOption "Enable yabai";
};
};
config = lib.mkIf (config.custom.gui.yabai.enable && pkgs.stdenv.isDarwin) {
# csrutil enable --without fs --without debug --without nvram
# nvram boot-args=-arm64e_preview_abi
# Generate sudoers file with SHA256 hash computed at activation time
system.activationScripts.yabaiSudoers.text = ''
echo "setting up yabai sudoers..." >&2
YABAI_BIN="${pkgs.yabai}/bin/yabai"
YABAI_HASH=$(shasum -a 256 "$YABAI_BIN" | ${pkgs.gawk}/bin/awk '{print $1}')
echo "${user} ALL = (root) NOPASSWD: sha256:$YABAI_HASH $YABAI_BIN --load-sa" > /etc/sudoers.d/yabai
chmod 0440 /etc/sudoers.d/yabai
'';
system.activationScripts.yabaiScriptingAddition.text = ''
echo "loading yabai scripting addition..." >&2
sudo ${pkgs.yabai}/bin/yabai --load-sa
'';
hm.home.packages = with pkgs; [
yabai
];
hm.xdg.configFile."yabai".source = config.hm.lib.file.mkOutOfStoreSymlink "${dots}/config/yabai";
hm.home.activation.yabaiService = config.hm.lib.dag.entryAfter [ "writeBoundary" ] ''
echo "Restarting yabai service..." >&2
$DRY_RUN_CMD ${pkgs.yabai}/bin/yabai --uninstall-service || true
$DRY_RUN_CMD ${pkgs.yabai}/bin/yabai --install-service
$DRY_RUN_CMD ${pkgs.yabai}/bin/yabai --start-service
'';
hm.home.shellAliases = {
restart-yabai = ''${pkgs.yabai}/bin/yabai --uninstall-service || ${pkgs.yabai}/bin/yabai --install-service || ${pkgs.yabai}/bin/yabai --start-service'';
};
};
}

View file

@ -9,6 +9,7 @@
{
imports = [
./i3
./hammerspoon.nix
./kitty.nix
./ghostty.nix
];

19
home/hammerspoon.nix Normal file
View file

@ -0,0 +1,19 @@
{
lib,
config,
dots,
...
}:
{
options.custom.gui = with lib; {
hammerspoon = {
enable = mkEnableOption "Enable hammerspoon";
};
};
config = lib.mkIf config.custom.gui.hammerspoon.enable {
home.file.".hammerspoon".source =
config.lib.file.mkOutOfStoreSymlink "${dots}/config/home/.hammerspoon";
};
}

View file

@ -4,9 +4,12 @@
}:
{
custom = {
aerospace.enable = false;
sketchybar.enable = false;
jankyborders.enable = false;
gui = {
aerospace.enable = false;
sketchybar.enable = true;
jankyborders.enable = false;
yabai.enable = true;
};
brew = {
zen-browser = false;
webex = true;
@ -60,6 +63,7 @@
clang-format = true;
marta = true;
valgrind = true;
hammerspoon = true;
};
};
@ -83,6 +87,7 @@
default.enable = true;
darwin.enable = true;
ghostty.enable = true;
hammerspoon.enable = true;
};
};
}

View file

@ -1,16 +1,8 @@
# https://github.com/Tnixc/nix-config/blob/main/home/programs/aerospace-sketchybar/flake.nix
{
clang,
fetchFromGitHub,
fetchurl,
gcc,
readline,
lua5_4,
lua54Packages,
stdenv,
darwin,
lib,
luaPackages,
}:
lua54Packages.buildLuarocksPackage rec {
pname = "promise-lua";

View file

@ -1,47 +1,3 @@
# https://github.com/khaneliman/khanelinix/blob/7703e485a2c7431f63004321da9e02ff7e06eb0b/packages/sbarlua/default.nix
# {
# clang,
# fetchFromGitHub,
# gcc,
# readline,
# lua,
# }:
# lua.stdenv.mkDerivation rec {
# pname = "SBarLua";
# version = "0-unstable-2024-08-12";
#
# name = "lua${lua.luaversion}-" + pname + "-" + version;
#
# src = fetchFromGitHub {
# owner = "FelixKratz";
# repo = "SbarLua";
# rev = "437bd2031da38ccda75827cb7548e7baa4aa9978";
# hash = "sha256-F0UfNxHM389GhiPQ6/GFbeKQq5EvpiqQdvyf7ygzkPg=";
# };
#
# nativeBuildInputs = [
# clang
# gcc
# ];
#
# buildInputs = [ readline ];
#
# propagatedBuildInputs = [ lua ];
#
# makeFlags = [
# "PREFIX=$(out)"
# "LUA_INC=-I${lua}/include"
# "LUA_LIBDIR=$(out)/lib/lua/${lua.luaversion}"
# "LUA_VERSION=${lua.luaversion}"
# ];
#
# installPhase = ''
# mkdir -p $out/lib/lua/${lua.luaversion}/
# cp -r bin/* "$out/lib/lua/${lua.luaversion}/"
# '';
# }
# https://github.com/Tnixc/nix-config/blob/main/home/programs/aerospace-sketchybar/flake.nix
{
clang,
@ -51,9 +7,8 @@
lua5_4,
lua54Packages,
stdenv,
darwin,
lib,
luaPackages,
apple-sdk_12,
}:
lua54Packages.buildLuaPackage rec {
pname = "sbar";
@ -74,5 +29,6 @@ lua54Packages.buildLuaPackage rec {
readline
clang
stdenv
] ++ lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [ CoreFoundation ]);
]
++ lib.optionals stdenv.isDarwin [ apple-sdk_12 ];
}