Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
558a6cf
feat(sketchybar): add AeroSpace bar core
CollieIsCute Sep 12, 2026
b41ee6d
feat(sketchybar): generate Matugen palette
CollieIsCute Sep 12, 2026
fba0090
ci: verify SketchyBar config on macOS
CollieIsCute Sep 12, 2026
48b33fd
docs: document SketchyBar rollout
CollieIsCute Sep 12, 2026
c9bac3a
fix(sketchybar): reload after menu apps start
CollieIsCute Sep 12, 2026
787e553
fix(ci): isolate SketchyBar palette test
CollieIsCute Sep 12, 2026
13885bb
fix(sketchybar): refine live macOS layout
CollieIsCute Sep 12, 2026
68a1238
fix(ci): avoid huge Arch external diff
CollieIsCute Sep 12, 2026
cc0ad94
revert: keep verbose Arch CI output
CollieIsCute Sep 12, 2026
80ad0da
fix(sketchybar): start bar after login when no process exists
CollieIsCute Sep 12, 2026
a1c1dd0
fix(sketchybar): keep clock and workspaces clear of the notch
CollieIsCute Sep 12, 2026
33370f4
fix(arch): install LocalSend dependency missing from AUR metadata
CollieIsCute Sep 12, 2026
7700422
fix(sketchybar): keep aliases stable and preserve chart colors
CollieIsCute Sep 12, 2026
2ccde66
ci: keep dotfiles checks focused on installation
CollieIsCute Sep 12, 2026
9879b55
fix(sketchybar): open native usage and control center menus
CollieIsCute Sep 12, 2026
e2fcf9c
feat(sketchybar): separate CodexBar hover and click actions
CollieIsCute Sep 12, 2026
8856b68
Merge branch 'master' into feature/sketchybar
CollieIsCute Sep 12, 2026
cba4ee9
fix(arch): drop redundant LocalSend dependency override
CollieIsCute Sep 12, 2026
1538335
feat(sketchybar): include Claude usage in shared hover summary
CollieIsCute Sep 12, 2026
7b79792
feat(sketchybar): show quota reset countdowns on hover
CollieIsCute Sep 13, 2026
2bdfd3f
feat(sketchybar): group hover quotas into bordered cells
CollieIsCute Sep 13, 2026
f75cb59
feat(sketchybar): alternate visibility with native menu bar
CollieIsCute Sep 13, 2026
449d7cd
fix(brew): trust formula entries without redundant tap installs
CollieIsCute Sep 13, 2026
20067b3
refactor: simplify SketchyBar plan and group Stats items
CollieIsCute Sep 13, 2026
1b4cff0
feat: complete compact SketchyBar widgets and dynamic workspaces
CollieIsCute Sep 13, 2026
93d5cce
fix: equalize capsule spacing and compact AI meters
CollieIsCute Sep 13, 2026
f3a7812
feat: format SketchyBar clock with uppercase Chinese months
CollieIsCute Sep 13, 2026
82c2cfc
docs: keep README focused on setup and usage
CollieIsCute Sep 13, 2026
c14a258
chore: keep SketchyBar plan local only
CollieIsCute Sep 13, 2026
2b769ec
refactor: share SketchyBar styling and mouse event handling
CollieIsCute Sep 13, 2026
4098364
refactor: simplify workspace visibility and volume updates
CollieIsCute Sep 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ tmux-client-*.log
.opencode/package-lock.json
.opencode/bun.lock
.opencode/opencode-loop/
/sketchybar-plan.html
256 changes: 76 additions & 180 deletions README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions home/.chezmoiignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
.config/brew/**
.config/karabiner
.config/karabiner/**
.config/sketchybar
.config/sketchybar/**
Library
Library/**
{{- end }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{- if eq .chezmoi.os "darwin" -}}
#!/bin/sh
set -eu
# Two upstream meters share one SketchyBar group and hover summary.
# CodexBar reads this setting on launch; no account preferences are changed.
defaults write com.steipete.codexbar mergeIcons -bool false
{{- end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{{- if eq .chezmoi.os "darwin" -}}
#!/bin/sh
set -eu
# helper: {{ include "dot_config/sketchybar/menu-bar-events.swift" | sha256sum }}
# status items: {{ include "dot_config/sketchybar/status-items.swift" | sha256sum }}

mkdir -p "$HOME/.local/bin" "$HOME/.cache/sketchybar"
menu_build=$(mktemp "$HOME/.local/bin/.sb-menu-events.XXXXXX")
trap 'rm -f "$menu_build"' EXIT HUP INT TERM
xcrun swiftc -O "$HOME/.config/sketchybar/menu-bar-events.swift" -o "$menu_build"
mv "$menu_build" "$HOME/.local/bin/sb-menu-events"
menu_build=$(mktemp "$HOME/.local/bin/.sb-status-items.XXXXXX")
xcrun swiftc -O "$HOME/.config/sketchybar/status-items.swift" -o "$menu_build"
mv "$menu_build" "$HOME/.local/bin/sb-status-items"

# Restart only our helper; a running bar reloads after its supervisor exits.
pkill -x -u "$(id -u)" sb-menu-events || :
lockf -kst 5 "$HOME/.cache/sketchybar/menu-events.lock" true
if pgrep -x -u "$(id -u)" sketchybar >/dev/null; then
sketchybar --reload
fi
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -eu

# wallpaper: {{ include "dot_config/wallpapers/xp_wallpaper.jpg" | sha256sum }}
# config: {{ include "dot_config/matugen/config.toml.tmpl" | sha256sum }}
# sketchybar: {{ include "dot_config/sketchybar/colors.sh.tera" | sha256sum }}
# inputs: {{ include ".chezmoiexternal.toml" | sha256sum }}
# dispatcher: {{ include "dot_local/bin/executable_chezmoi-theme.tmpl" | sha256sum }}

Expand Down
14 changes: 13 additions & 1 deletion home/dot_config/aerospace/aerospace.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ config-version = 2
start-at-login = true
# auto-reload-config = true # NOTE: requires AeroSpace > v0.20.2-Beta (not yet released)

after-startup-command = [
'exec-and-forget open -gja CodexBar',
'exec-and-forget open -gja Stats',
# ponytail: one delayed reload covers login-item startup; add an app event helper only if three seconds proves insufficient.
# --reload returns success even without a running bar; check the process first.
'exec-and-forget /bin/sh -c "sleep 3; if pgrep -x sketchybar >/dev/null; then sketchybar --reload; else sketchybar; fi"',
]

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

# Neutralize macOS "Hide Application" (Cmd+H) since we use Cmd+H for focus
# AeroSpace intercepts Cmd+H before macOS, but if somehow an app hides, this unhides it
automatically-unhide-macos-hidden-apps = true
Expand All @@ -41,7 +53,7 @@ on-focused-monitor-changed = ['move-mouse monitor-lazy-center']
inner.vertical = 0
outer.left = 0
outer.bottom = 0
outer.top = 0
outer.top = 36
outer.right = 0

# ────────────────────────────────────────
Expand Down
6 changes: 3 additions & 3 deletions home/dot_config/brew/Brewfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
tap "cxwx/cxbrew", trusted: { formula: "matugen" }

brew "bat"
brew "bear"
brew "btop"
Expand All @@ -15,14 +13,15 @@ brew "dos2unix"
brew "eza"
brew "fastfetch"
brew "fd"
brew "FelixKratz/formulae/sketchybar", trusted: true
brew "fish"
brew "gh"
brew "git"
brew "glab"
brew "gnu-tar"
brew "lazygit"
brew "llvm"
brew "cxwx/cxbrew/matugen"
brew "cxwx/cxbrew/matugen", trusted: true
brew "mold"
brew "neovim"
brew "ninja"
Expand All @@ -42,6 +41,7 @@ brew "zellij"
brew "zoxide"
cask "brave-browser"
cask "claude-code@latest"
cask "codexbar"
cask "flutter"
cask "font-jetbrains-mono-nerd-font"
cask "ghostty"
Expand Down
5 changes: 5 additions & 0 deletions home/dot_config/matugen/config.toml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ output_path = "~/.config/ghostty/themes/noctalia"
input_path = "~/.config/matugen/templates/tmux/tmux.conf"
output_path = "~/.config/tmux/themes/noctalia.conf"
post_hook = 'bash "$HOME/.config/matugen/templates/tmux/apply.sh"'

[templates.sketchybar]
input_path = "~/.config/sketchybar/colors.sh.tera"
output_path = "~/.config/sketchybar/colors.sh"
post_hook = 'command -v sketchybar >/dev/null 2>&1 && sketchybar --reload || true'
{{ else -}}
[templates.alacritty]
input_path = "~/.config/matugen/templates/matugen-themes/alacritty.toml"
Expand Down
6 changes: 6 additions & 0 deletions home/dot_config/sketchybar/colors.sh.tera
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated by Matugen. Edit colors.sh.tera, not colors.sh.
TRANSPARENT=0x00000000
CAPSULE=0x4d{{colors.surface_variant.default.hex_stripped}}
ON_SURFACE=0xff{{colors.on_surface.default.hex_stripped}}
PRIMARY=0xff{{colors.primary.default.hex_stripped}}
ON_PRIMARY=0xff{{colors.on_primary.default.hex_stripped}}
176 changes: 176 additions & 0 deletions home/dot_config/sketchybar/executable_sketchybarrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#!/bin/sh

CONFIG_DIR=${CONFIG_DIR:-"$HOME/.config/sketchybar"}
PLUGIN_DIR="$CONFIG_DIR/plugins"
# Brackets absorb member padding; normal capsules own the full gap on the left.
# https://github.com/FelixKratz/SketchyBar/blob/v2.24.0/src/group.c
CAPSULE_GAP=8

# Safe first-run palette; Matugen replaces it and reloads the bar.
TRANSPARENT=0x00000000
CAPSULE=0x4d303034
ON_SURFACE=0xfff2f0f4
PRIMARY=0xffd0bcff
ON_PRIMARY=0xff381e72
test -r "$CONFIG_DIR/colors.sh" && . "$CONFIG_DIR/colors.sh"

sketchybar --bar \
position=top \
height=36 \
notch_width=200 \
y_offset=0 \
topmost=off \
color="$TRANSPARENT" \
shadow=off \
corner_radius=0 \
margin=0 \
padding_left="$CAPSULE_GAP" \
padding_right="$CAPSULE_GAP" \
display=all

sketchybar --default \
icon.font="JetBrainsMono Nerd Font:Regular:14.0" \
label.font="JetBrainsMono Nerd Font:Regular:13.0" \
icon.color="$ON_SURFACE" \
label.color="$ON_SURFACE" \
icon.padding_left=6 \
icon.padding_right=4 \
label.padding_left=4 \
label.padding_right=6 \
background.height=27 \
background.corner_radius=9 \
background.shadow.drawing=off \
popup.height=28 \
popup.background.color="0xff${CAPSULE#????}" \
popup.background.border_width=1 \
popup.background.border_color="0x33${ON_SURFACE#????}" \
padding_left=2 \
padding_right=2

sketchybar --add event aerospace_workspace_change

# q/e anchor to either side of the notch instead of drawing through its center.
sketchybar --add item clock q \
--set clock update_freq=1 script="$PLUGIN_DIR/clock.sh"

for sid in 1 2 3 4 5 6 7 8 9 magic; do
label=$sid
test "$sid" = magic && label=M
sketchybar --add item "space.$sid" e \
--set "space.$sid" \
icon="$label" \
icon.highlight_color="$ON_PRIMARY" \
label.drawing=off \
background.color="$PRIMARY" \
background.drawing=off \
click_script="aerospace workspace $sid" \
drawing=off
done

sketchybar --add item workspace.observer e \
--set workspace.observer drawing=off updates=on update_freq=2 \
script="$PLUGIN_DIR/workspace.sh" \
--subscribe workspace.observer aerospace_workspace_change space_windows_change front_app_switched system_woke

sketchybar --add bracket workspaces '/space\..*/'

sketchybar --add item volume right \
--set volume script="$PLUGIN_DIR/volume.sh" \
--subscribe volume volume_change mouse.scrolled mouse.clicked system_woke

add_alias() {
# Item-0 is shared by unrelated macOS 26 items: retain its resolved index.
case "$1" in
*,Item-0\(*|*,\(*) source=$1 ;;
*) source=$(printf '%s\n' "$1" | sed 's/([0-9][0-9]*)$//') ;;
esac
sketchybar --add alias "$source" "$3" || return 0
sketchybar --rename "$source" "$2" --set "$2" "click_script=${4-}"
}

# A zero-width anchor gives both provider icons one shared hover popup.
sketchybar --add item codexbar left \
--set codexbar width=0 padding_left=0 padding_right=0 \
icon.drawing=off label.drawing=off \
popup.align=left \
popup.background.corner_radius=10 \
update_freq=60 script="$PLUGIN_DIR/codexbar.sh" \
--subscribe codexbar system_woke
sketchybar --add item codexbar.status popup.codexbar \
--set codexbar.status width=330 icon.drawing=off \
label="CodexBar · 正在讀取…" label.color="$PRIMARY" \
label.padding_left=12 label.padding_right=12

# Only Stats, CodexBar and the input source are included; there is no App tray.
status_helper="$HOME/.local/bin/sb-status-items"
menu_items=$("$status_helper" list) || menu_items='[]'
printf '%s\n' "$menu_items" \
| /usr/bin/jq -r '.[] | [.bundle, .source, .window, .help] | @tsv' \
| (
while IFS="$(printf '\t')" read -r bundle source window help; do
click=$(/usr/bin/jq -nr --arg helper "$status_helper" --arg bundle "$bundle" --arg window "$window" \
'[$helper, "click", $bundle, $window] | @sh')
case "$bundle" in
com.steipete.codexbar)
case "$window" in codexbar-codex|codexbar-claude) ;; *) continue ;; esac
name="meter.${window#codexbar-}"
add_alias "$source" "$name" left ''
sketchybar --set "$name" \
script="$PLUGIN_DIR/native-menu.sh" \
--subscribe "$name" mouse.entered mouse.exited mouse.clicked
;;
eu.exelban.Stats)
add_alias "$source" "stats.$window" right "$click"
sketchybar --set "stats.$window" \
popup.align=right \
popup.background.corner_radius=6 \
script="$PLUGIN_DIR/native-menu.sh" \
--subscribe "stats.$window" mouse.entered mouse.exited mouse.clicked \
--add item "hint.$window" "popup.stats.$window" \
--set "hint.$window" icon.drawing=off \
label="${help:-$window}" label.padding_left=10 label.padding_right=10
;;
com.apple.TextInputMenuAgent)
add_alias "$source" input_source right "$click"
sketchybar --move input_source before volume
;;
esac
done
)

# Style aliases before adding their brackets, which absorb member padding.
# Preserve raster colors: tinting an opaque chart makes it a solid rectangle.
for aliases in '/^meter\./' '/^stats\./' '/^input_source$/'; do
sketchybar --set "$aliases" icon.drawing=off label.drawing=off \
padding_left=1 padding_right=1 alias.scale=0.82 alias.update_freq=2
done
sketchybar --set '/^meter\./' alias.scale=0.85 padding_left=0 padding_right=0 \
--set '/^input_source$/' alias.scale=1
for group in meter stats; do
sketchybar --add bracket "$group.group" "/$group\\..*/"
done
for capsule in clock volume '/^input_source$/' workspaces '/\.group$/'; do
sketchybar --set "$capsule" background.color="$CAPSULE" background.drawing=on
done
for capsule in clock volume '/^input_source$/'; do
sketchybar --set "$capsule" padding_left="$CAPSULE_GAP" padding_right=0
done

sketchybar --update

# One event listener survives reloads; the lock covers its fail-open supervisor.
menu_helper="$HOME/.local/bin/sb-menu-events"
# macOS pgrep excludes ancestors unless -a is set; the bar launches this rc.
# Clients share its name, so select the oldest instance including our parent.
bar_pid=$(pgrep -a -o -x -u "$(id -u)" sketchybar)
if test -x "$menu_helper" && test -n "$bar_pid"; then
mkdir -p "$HOME/.cache/sketchybar"
lockf -kst 0 "$HOME/.cache/sketchybar/menu-events.lock" /bin/sh -c '
"$1" watch "$2" "$3"
"$2" --bar hidden=off
' sh "$menu_helper" "$(command -v sketchybar)" "$bar_pid" &
"$menu_helper" sync "$(command -v sketchybar)" "$bar_pid"
else
sketchybar --bar hidden=off
printf '%s\n' 'SketchyBar menu coordination is unavailable; run chezmoi apply.' >&2
fi
79 changes: 79 additions & 0 deletions home/dot_config/sketchybar/menu-bar-events.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import AppKit
import Carbon
import CoreGraphics

// CarbonEvents.h documents these public events as broadcasts to all processes:
// kEventMenuBarShown / kEventMenuBarHidden. No event tap or private SkyLight API.
let arguments = CommandLine.arguments
guard arguments.count == 4, let barPID = Int32(arguments[3]), barPID > 0 else {
fputs("usage: sb-menu-events watch|sync SKETCHYBAR_PATH BAR_PID\n", stderr)
exit(64)
}
let executable = URL(fileURLWithPath: arguments[2])
var lastHidden: Bool?

func nativeBarVisible() -> Bool {
// NSMenu.menuBarVisible() describes this app, not the foreground app.
// https://developer.apple.com/documentation/coregraphics/cgwindowlistcopywindowinfo(_:_:)
let windows = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], 0)
as? [[String: Any]] ?? []
return windows.contains {
$0[kCGWindowOwnerName as String] as? String == "Window Server"
&& $0[kCGWindowName as String] as? String == "Menubar"
}
}

func setHidden(_ hidden: Bool) {
guard lastHidden != hidden else { return }
let command = Process()
command.executableURL = executable
command.arguments = ["--bar", "hidden=\(hidden ? "on" : "off")"]
do {
try command.run()
command.waitUntilExit()
guard command.terminationStatus == 0 else { exit(1) }
lastHidden = hidden
} catch {
fputs("sb-menu-events: \(error)\n", stderr)
exit(1)
}
}

guard arguments[1] == "watch" || arguments[1] == "sync" else { exit(64) }
let app = NSApplication.shared
app.setActivationPolicy(.prohibited)
if arguments[1] == "sync" {
setHidden(nativeBarVisible())
exit(0)
}

var eventTypes = [
EventTypeSpec(eventClass: OSType(kEventClassMenu), eventKind: UInt32(kEventMenuBarShown)),
EventTypeSpec(eventClass: OSType(kEventClassMenu), eventKind: UInt32(kEventMenuBarHidden)),
]
let status = InstallEventHandler(GetApplicationEventTarget(), { _, event, _ in
// ponytail: hide all displays together; use per-display bar IDs if needed.
setHidden(GetEventKind(event) == kEventMenuBarShown || nativeBarVisible())
return OSStatus(eventNotHandledErr)
}, eventTypes.count, &eventTypes, nil, nil)
guard status == noErr else {
fputs("sb-menu-events: cannot subscribe to menu events (\(status))\n", stderr)
exit(1)
}

let workspace = NSWorkspace.shared.notificationCenter
for name in [NSWorkspace.didWakeNotification, NSWorkspace.activeSpaceDidChangeNotification] {
workspace.addObserver(forName: name, object: nil, queue: .main) { _ in
setHidden(nativeBarVisible())
}
}
NotificationCenter.default.addObserver(
forName: NSApplication.didChangeScreenParametersNotification, object: nil, queue: .main
) { _ in setHidden(nativeBarVisible()) }

// The launching shell restores the bar even after SIGKILL or a helper crash.
let barExit = DispatchSource.makeProcessSource(identifier: barPID, eventMask: .exit, queue: .main)
barExit.setEventHandler { exit(0) }
barExit.resume()
setHidden(nativeBarVisible())
app.run()
8 changes: 8 additions & 0 deletions home/dot_config/sketchybar/plugins/executable_clock.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh
set -eu

label=$(date '+%m %d %H:%M:%S' | awk '{
split("壹 貳 參 肆 伍 陸 柒 捌 玖 拾 拾壹 拾貳", months)
printf "%s月 %d %s", months[$1 + 0], $2, $3
}')
sketchybar --set "$NAME" label="$label"
Loading
Loading