From 558a6cf19b3b3ab9c336692b99d7bee5e55aacb6 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sat, 12 Sep 2026 23:57:28 +0800 Subject: [PATCH 01/30] feat(sketchybar): add AeroSpace bar core Add the macOS-only package and shell configuration for workspace state, clock, 3% volume controls, CodexBar, Stats, and tray aliases. Keep aliases native and optional so missing permissions or applications do not block startup.\n\nValidation: tests/sketchybar.sh; Brewfile formula/cask parsing; AeroSpace TOML parsing; git diff --check. --- home/.chezmoiignore | 2 + home/dot_config/aerospace/aerospace.toml | 12 +- home/dot_config/brew/Brewfile | 3 + .../sketchybar/executable_sketchybarrc | 137 ++++++++++++++++++ .../sketchybar/plugins/executable_clock.sh | 4 + .../sketchybar/plugins/executable_volume.sh | 46 ++++++ .../plugins/executable_workspace.sh | 14 ++ tests/sketchybar.sh | 92 ++++++++++++ 8 files changed, 309 insertions(+), 1 deletion(-) create mode 100755 home/dot_config/sketchybar/executable_sketchybarrc create mode 100755 home/dot_config/sketchybar/plugins/executable_clock.sh create mode 100755 home/dot_config/sketchybar/plugins/executable_volume.sh create mode 100755 home/dot_config/sketchybar/plugins/executable_workspace.sh create mode 100755 tests/sketchybar.sh diff --git a/home/.chezmoiignore b/home/.chezmoiignore index 17b8c3d..309d584 100644 --- a/home/.chezmoiignore +++ b/home/.chezmoiignore @@ -6,6 +6,8 @@ .config/brew/** .config/karabiner .config/karabiner/** +.config/sketchybar +.config/sketchybar/** Library Library/** {{- end }} diff --git a/home/dot_config/aerospace/aerospace.toml b/home/dot_config/aerospace/aerospace.toml index 0f81206..c526beb 100644 --- a/home/dot_config/aerospace/aerospace.toml +++ b/home/dot_config/aerospace/aerospace.toml @@ -17,6 +17,16 @@ 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', + 'exec-and-forget sketchybar', +] + +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 @@ -41,7 +51,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 # ──────────────────────────────────────── diff --git a/home/dot_config/brew/Brewfile b/home/dot_config/brew/Brewfile index 802c53b..828e1d6 100644 --- a/home/dot_config/brew/Brewfile +++ b/home/dot_config/brew/Brewfile @@ -1,3 +1,4 @@ +tap "FelixKratz/formulae", trusted: { formula: "sketchybar" } tap "cxwx/cxbrew", trusted: { formula: "matugen" } brew "bat" @@ -15,6 +16,7 @@ brew "dos2unix" brew "eza" brew "fastfetch" brew "fd" +brew "FelixKratz/formulae/sketchybar" brew "fish" brew "gh" brew "git" @@ -42,6 +44,7 @@ brew "zellij" brew "zoxide" cask "brave-browser" cask "claude-code@latest" +cask "codexbar" cask "flutter" cask "font-jetbrains-mono-nerd-font" cask "ghostty" diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc new file mode 100755 index 0000000..6a5df60 --- /dev/null +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -0,0 +1,137 @@ +#!/bin/sh + +CONFIG_DIR=${CONFIG_DIR:-"$HOME/.config/sketchybar"} +PLUGIN_DIR="$CONFIG_DIR/plugins" + +# 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 \ + color="$TRANSPARENT" \ + shadow=off \ + corner_radius=0 \ + margin=0 \ + padding_left=5 \ + padding_right=5 \ + 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 \ + padding_left=2 \ + padding_right=2 + +sketchybar --add event aerospace_workspace_change + +sketchybar --add item clock center \ + --set clock \ + update_freq=1 \ + background.color="$CAPSULE" \ + background.drawing=on \ + 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" center \ + --set "space.$sid" \ + icon="$label" \ + label.drawing=off \ + background.color="$PRIMARY" \ + background.drawing=off \ + click_script="aerospace workspace $sid" \ + script="$PLUGIN_DIR/workspace.sh" \ + --subscribe "space.$sid" aerospace_workspace_change +done + +sketchybar --add bracket workspaces '/space\..*/' \ + --set workspaces \ + background.color="$CAPSULE" \ + background.height=27 \ + background.corner_radius=9 \ + background.drawing=on + +sketchybar --add item volume right \ + --set volume \ + background.color="$CAPSULE" \ + background.drawing=on \ + script="$PLUGIN_DIR/volume.sh" \ + --subscribe volume volume_change mouse.scrolled mouse.clicked system_woke + +add_alias() { + source=$1 + name=$2 + position=$3 + click_script=${4-} + + sketchybar --add alias "$source" "$position" || return 0 + sketchybar --rename "$source" "$name" + sketchybar --set "$name" \ + alias.color="$ON_SURFACE" \ + alias.scale=0.82 \ + alias.update_freq=2 \ + click_script="$click_script" +} + +# The query returns only currently available items. Missing apps or permissions +# therefore leave the rest of the bar usable instead of failing startup. +menu_items=$(sketchybar --query default_menu_items 2>/dev/null || :) +printf '%s\n' "$menu_items" \ + | sed -n 's/^[[:space:]]*"\(.*\)"[,[:space:]]*$/\1/p' \ + | ( + index=0 + codex_added=false + while IFS= read -r source; do + index=$((index + 1)) + case "$source" in + CodexBar,*) + test "$codex_added" = false || continue + add_alias "$source" codexbar left 'open -b com.steipete.codexbar' + codex_added=true + ;; + Stats,*Network*) + add_alias "$source" "stats.net.$index" right 'open -a Stats' + ;; + Stats,*CPU*|Stats,*GPU*|Stats,*RAM*|Stats,*Memory*|Stats,*Sensor*|Stats,*Battery*) + add_alias "$source" "stats.sys.$index" right 'open -a Stats' + ;; + Stats,*|Control\ Center,*|Control\ Centre,*|SystemUIServer,*|Window\ Server,*|TextInputMenuAgent,*) + ;; + *) + add_alias "$source" "tray.$index" left '' + ;; + esac + done + ) + +for group in tray stats.net stats.sys; do + sketchybar --add bracket "$group.group" "/$group\\..*/" \ + --set "$group.group" \ + background.color="$CAPSULE" \ + background.height=27 \ + background.corner_radius=9 \ + background.drawing=on +done + +sketchybar --update + +if command -v aerospace >/dev/null 2>&1; then + focused=$(aerospace list-workspaces --focused 2>/dev/null | head -n 1) + test -z "$focused" || sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE="$focused" +fi diff --git a/home/dot_config/sketchybar/plugins/executable_clock.sh b/home/dot_config/sketchybar/plugins/executable_clock.sh new file mode 100755 index 0000000..351e2d7 --- /dev/null +++ b/home/dot_config/sketchybar/plugins/executable_clock.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu + +sketchybar --set "$NAME" label="$(date '+%B %d %H:%M:%S')" diff --git a/home/dot_config/sketchybar/plugins/executable_volume.sh b/home/dot_config/sketchybar/plugins/executable_volume.sh new file mode 100755 index 0000000..bc2dfed --- /dev/null +++ b/home/dot_config/sketchybar/plugins/executable_volume.sh @@ -0,0 +1,46 @@ +#!/bin/sh +set -eu + +get_volume() { + osascript -e 'output volume of (get volume settings)' 2>/dev/null || printf '0\n' +} + +case ${SENDER:-} in + mouse.clicked) + osascript -e 'set volume output muted not (output muted of (get volume settings))' >/dev/null + volume=$(get_volume) + ;; + mouse.scrolled) + volume=$(get_volume) + case $volume in *[!0-9]*|'') volume=0 ;; esac + case ${SCROLL_DELTA:-0} in + -*) volume=$((volume - 3)) ;; + 0|'') ;; + *) volume=$((volume + 3)) ;; + esac + test "$volume" -ge 0 || volume=0 + test "$volume" -le 100 || volume=100 + osascript -e "set volume output volume $volume" >/dev/null + ;; + volume_change) + volume=${INFO:-0} + ;; + *) + volume=$(get_volume) + ;; +esac + +muted=$(osascript -e 'output muted of (get volume settings)' 2>/dev/null || printf 'false\n') +case $volume in *[!0-9]*|'') volume=0 ;; esac + +if test "$muted" = true || test "$volume" -eq 0; then + icon='󰖁' +elif test "$volume" -lt 30; then + icon='󰕿' +elif test "$volume" -lt 60; then + icon='󰖀' +else + icon='󰕾' +fi + +sketchybar --set "$NAME" icon="$icon" label="$volume%" diff --git a/home/dot_config/sketchybar/plugins/executable_workspace.sh b/home/dot_config/sketchybar/plugins/executable_workspace.sh new file mode 100755 index 0000000..bad3338 --- /dev/null +++ b/home/dot_config/sketchybar/plugins/executable_workspace.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -eu + +CONFIG_DIR=${CONFIG_DIR:-"$HOME/.config/sketchybar"} +ON_SURFACE=0xfff2f0f4 +ON_PRIMARY=0xff381e72 +test -r "$CONFIG_DIR/colors.sh" && . "$CONFIG_DIR/colors.sh" + +sid=${NAME#space.} +if test "${FOCUSED_WORKSPACE:-}" = "$sid"; then + sketchybar --set "$NAME" background.drawing=on icon.color="$ON_PRIMARY" +else + sketchybar --set "$NAME" background.drawing=off icon.color="$ON_SURFACE" +fi diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh new file mode 100755 index 0000000..b0db1ef --- /dev/null +++ b/tests/sketchybar.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -eu + +repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +config="$repo/home/dot_config/sketchybar" + +grep -Fq 'tap "FelixKratz/formulae", trusted: { formula: "sketchybar" }' "$repo/home/dot_config/brew/Brewfile" +grep -Fq 'brew "FelixKratz/formulae/sketchybar"' "$repo/home/dot_config/brew/Brewfile" +grep -Fq 'cask "codexbar"' "$repo/home/dot_config/brew/Brewfile" +grep -Fq '.config/sketchybar/**' "$repo/home/.chezmoiignore" +grep -Fq "'exec-and-forget sketchybar'" "$repo/home/dot_config/aerospace/aerospace.toml" +grep -Fq 'FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE' "$repo/home/dot_config/aerospace/aerospace.toml" + +for file in \ + "$config/executable_sketchybarrc" \ + "$config/plugins/executable_clock.sh" \ + "$config/plugins/executable_volume.sh" \ + "$config/plugins/executable_workspace.sh"; do + test -x "$file" + sh -n "$file" +done + +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT HUP INT TERM +mkdir "$tmp/bin" + +cat >"$tmp/bin/sketchybar" <<'EOF' +#!/bin/sh +if test "${1-} ${2-}" = "--query default_menu_items"; then + printf '%s\n' "$MOCK_MENU_ITEMS" +else + printf '%s\n' "$*" >>"$MOCK_SKETCHYBAR_LOG" +fi +EOF + +cat >"$tmp/bin/osascript" <<'EOF' +#!/bin/sh +printf '%s\n' "$*" >>"$MOCK_OSASCRIPT_LOG" +case "$*" in + *'output volume of'*) printf '%s\n' "${MOCK_VOLUME:-50}" ;; + *'output muted of'*) printf '%s\n' "${MOCK_MUTED:-false}" ;; +esac +EOF + +chmod +x "$tmp/bin/sketchybar" "$tmp/bin/osascript" +export PATH="$tmp/bin:$PATH" +export MOCK_SKETCHYBAR_LOG="$tmp/sketchybar.log" +export MOCK_OSASCRIPT_LOG="$tmp/osascript.log" +export MOCK_MENU_ITEMS='[ + "CodexBar,(1)", + "Stats,CPU_Mini(2)", + "Stats,Network_Speed(3)", + "Control Center,WiFi(4)", + "KeePassXC,(5)" +]' + +: >"$MOCK_SKETCHYBAR_LOG" +CONFIG_DIR="$config" sh "$config/executable_sketchybarrc" +grep -Fq -- '--add alias CodexBar,(1) left' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias Stats,CPU_Mini(2) right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias Stats,Network_Speed(3) right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias KeePassXC,(5) left' "$MOCK_SKETCHYBAR_LOG" +if grep -Fq -- '--add alias Control Center,WiFi(4)' "$MOCK_SKETCHYBAR_LOG"; then + echo 'Control Center must not be duplicated in tray aliases' >&2 + exit 1 +fi + +: >"$MOCK_SKETCHYBAR_LOG" +NAME=space.3 FOCUSED_WORKSPACE=3 sh "$config/plugins/executable_workspace.sh" +grep -Fq 'background.drawing=on' "$MOCK_SKETCHYBAR_LOG" +grep -Fq 'icon.color=0xff381e72' "$MOCK_SKETCHYBAR_LOG" + +: >"$MOCK_SKETCHYBAR_LOG" +NAME=space.3 FOCUSED_WORKSPACE=4 sh "$config/plugins/executable_workspace.sh" +grep -Fq 'background.drawing=off' "$MOCK_SKETCHYBAR_LOG" +grep -Fq 'icon.color=0xfff2f0f4' "$MOCK_SKETCHYBAR_LOG" + +: >"$MOCK_OSASCRIPT_LOG" +NAME=volume SENDER=mouse.scrolled SCROLL_DELTA=1 MOCK_VOLUME=98 \ + sh "$config/plugins/executable_volume.sh" +grep -Fq 'set volume output volume 100' "$MOCK_OSASCRIPT_LOG" + +: >"$MOCK_OSASCRIPT_LOG" +NAME=volume SENDER=mouse.scrolled SCROLL_DELTA=-1 MOCK_VOLUME=2 \ + sh "$config/plugins/executable_volume.sh" +grep -Fq 'set volume output volume 0' "$MOCK_OSASCRIPT_LOG" + +: >"$MOCK_OSASCRIPT_LOG" +NAME=volume SENDER=mouse.clicked sh "$config/plugins/executable_volume.sh" +grep -Fq 'set volume output muted not (output muted of (get volume settings))' "$MOCK_OSASCRIPT_LOG" + +echo 'SketchyBar checks passed.' From b41ee6d1fe1cd0759a791c5a8c0b8639c2db8128 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sat, 12 Sep 2026 23:58:29 +0800 Subject: [PATCH 02/30] feat(sketchybar): generate Matugen palette Generate only the five colors consumed by SketchyBar, reload after theme changes, and include the template hash in the existing theme apply trigger.\n\nValidation: rendered the template with Matugen; parsed the rendered macOS TOML; tests/sketchybar.sh; git diff --check. --- .../run_onchange_after_6-apply-theme.sh.tmpl | 1 + home/dot_config/matugen/config.toml.tmpl | 5 +++++ home/dot_config/sketchybar/colors.sh.tera | 6 ++++++ tests/sketchybar.sh | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 home/dot_config/sketchybar/colors.sh.tera diff --git a/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl index 79bc486..d2b0d14 100644 --- a/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl +++ b/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl @@ -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 }} diff --git a/home/dot_config/matugen/config.toml.tmpl b/home/dot_config/matugen/config.toml.tmpl index b7751da..78e8245 100644 --- a/home/dot_config/matugen/config.toml.tmpl +++ b/home/dot_config/matugen/config.toml.tmpl @@ -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" diff --git a/home/dot_config/sketchybar/colors.sh.tera b/home/dot_config/sketchybar/colors.sh.tera new file mode 100644 index 0000000..02f908c --- /dev/null +++ b/home/dot_config/sketchybar/colors.sh.tera @@ -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}} diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index b0db1ef..7403a56 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -10,6 +10,8 @@ grep -Fq 'cask "codexbar"' "$repo/home/dot_config/brew/Brewfile" grep -Fq '.config/sketchybar/**' "$repo/home/.chezmoiignore" grep -Fq "'exec-and-forget sketchybar'" "$repo/home/dot_config/aerospace/aerospace.toml" grep -Fq 'FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE' "$repo/home/dot_config/aerospace/aerospace.toml" +grep -Fq '[templates.sketchybar]' "$repo/home/dot_config/matugen/config.toml.tmpl" +grep -Fq '# sketchybar:' "$repo/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl" for file in \ "$config/executable_sketchybarrc" \ @@ -43,6 +45,22 @@ esac EOF chmod +x "$tmp/bin/sketchybar" "$tmp/bin/osascript" + +if command -v matugen >/dev/null 2>&1; then + cat >"$tmp/matugen.toml" </dev/null + grep -Eq '^CAPSULE=0x4d[0-9a-fA-F]{6}$' "$tmp/colors.sh" + grep -Eq '^ON_SURFACE=0xff[0-9a-fA-F]{6}$' "$tmp/colors.sh" + grep -Eq '^PRIMARY=0xff[0-9a-fA-F]{6}$' "$tmp/colors.sh" + grep -Eq '^ON_PRIMARY=0xff[0-9a-fA-F]{6}$' "$tmp/colors.sh" +fi + export PATH="$tmp/bin:$PATH" export MOCK_SKETCHYBAR_LOG="$tmp/sketchybar.log" export MOCK_OSASCRIPT_LOG="$tmp/osascript.log" From fba00903a3de2006643b517bef4177c0aaecf4dd Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sat, 12 Sep 2026 23:59:51 +0800 Subject: [PATCH 03/30] ci: verify SketchyBar config on macOS Run the repository's focused SketchyBar shell and Matugen checks after the macOS chezmoi apply job.\n\nValidation: Psych.parse_file; tests/sketchybar.sh; full chezmoi dry-run. --- .github/workflows/test-distros.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-distros.yaml b/.github/workflows/test-distros.yaml index 9817872..623c158 100644 --- a/.github/workflows/test-distros.yaml +++ b/.github/workflows/test-distros.yaml @@ -50,6 +50,8 @@ jobs: - name: chezmoi init dotfiles run: | chezmoi init collieiscute --branch "$CHEZMOI_BRANCH" --apply -v + - name: Test SketchyBar configuration + run: "$HOME/.local/share/chezmoi/tests/sketchybar.sh" windows: name: windows:latest From 48b33fdb1432c03a5799a516edf38697dc7c227c Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 00:00:01 +0800 Subject: [PATCH 04/30] docs: document SketchyBar rollout Record first-run permissions and app settings, the native alias fallback boundary, and the 30-project research plan used for the implementation. --- README.md | 18 ++ sketchybar-plan.html | 664 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 682 insertions(+) create mode 100644 sketchybar-plan.html diff --git a/README.md b/README.md index 989f713..32b9ed1 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,21 @@ chezmoi init --apply collieiscute -v - `cmd+option`/Alt key bindings deliberately avoided (macOS 26 Tahoe intercepts them). - Kitty, Alacritty, and Ghostty point at the fixed `noctalia` theme path; Noctalia or Matugen owns the generated colors. +### SketchyBar (macOS) + +- AeroSpace starts SketchyBar, reserves 36 px at the top, and sends workspace changes through the official `aerospace_workspace_change` event. +- The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. +- Left: third-party tray aliases and the first CodexBar status item. Center: `{Month day HH:MM:SS}` plus workspaces `1..9` and `M`. Right: 3%-step volume plus active Stats CPU/GPU/RAM/network/sensor/battery items. +- CodexBar remains the OAuth and quota owner; SketchyBar only mirrors its native status item. No browser cookies, tokens, custom quota server, or `showy-quota` config is stored here. + +After the first `chezmoi apply`: + +1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. In Stats, enable the compact items you actually want mirrored. +2. Allow SketchyBar under **System Settings → Privacy & Security → Screen & System Audio Recording**, then run `sketchybar --reload`. +3. Optionally set **System Settings → Control Center → Automatically hide and show the menu bar → Always**. SIP stays enabled. + +If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar; a CodexBar CLI fallback is intentionally deferred until the native item is proven unavailable on this Mac. + ### Hyprland - Desc-keyed Lua `hl.monitor(...)` overrides plus a fallback that selects the highest resolution and refresh rate available at that resolution. @@ -227,6 +242,9 @@ AeroSpace restores the swapped root layouts and window states where possible; it ### macOS extras - [`aerospace`](https://github.com/nikitabobko/AeroSpace) — tiling WM. +- [`sketchybar`](https://github.com/FelixKratz/SketchyBar) — transparent, event-driven status bar. +- [`CodexBar`](https://github.com/steipete/CodexBar) — Codex quota source and native meter mirrored by SketchyBar. +- [`Stats`](https://github.com/exelban/stats) — native system metrics mirrored by SketchyBar aliases. - [`karabiner-elements`](https://karabiner-elements.pqrs.org) — keyboard remapper. - Desktop wallpaper is deployed by chezmoi to `~/.config/wallpapers`; `run_onchange_after_6-apply-theme.sh.tmpl` applies the initial wallpaper and Matugen palette, and `chezmoi theme [IMAGE]` changes both later. diff --git a/sketchybar-plan.html b/sketchybar-plan.html new file mode 100644 index 0000000..4d9af33 --- /dev/null +++ b/sketchybar-plan.html @@ -0,0 +1,664 @@ + + + + + + + SketchyBar 導入計畫 + + + + + +
+

Collie's Dotfiles · macOS

+

SketchyBar 導入計畫

+

以 SketchyBar 取代原生 menu bar 的主要顯示層,串接 AeroSpace、CodexBar、Stats 與 Matugen;重現 Noctalia 的透明背景、半透明 capsule 與三區配置,而且不停用 SIP。

+
+ 狀態:待實作 + 平台:macOS + 方案:SketchyBar + AeroSpace + CodexBar + 更新:2026-09-12 +
+
+ +
+
+
+

決策

+
+

採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;Codex 用量優先鏡像 CodexBar 原生 status item。SketchyBar 只負責排列、capsule、AeroSpace workspace、時鐘與音量互動。

+

不採用 Simple Bar、SbarLua、大型第三方設定集或 showy-quota。單一 Codex meter 不值得再加入 Bash 4、獨立 renderer、cache daemon 與第二套主題設定。

+
+ +
+
+

範圍

+ 先把真正會用的部分做好 +
+
+
+

要做

+
    +
  • AeroSpace workspace 1–9 與 magic
  • +
  • Workspace 點擊切換與狀態即時更新
  • +
  • CodexBar 原生 Codex meter alias;點擊開啟 CodexBar
  • +
  • 時鐘:月份、日期與秒數
  • +
  • 音量顯示、靜音與滾輪每次 3%
  • +
  • Stats aliases:網路、CPU、GPU、RAM、溫度、電池
  • +
  • 必要的第三方 tray/menu extras aliases
  • +
  • Matugen wallpaper palette 與即時重載
  • +
  • 透明 bar、30% surface-variant capsule、無陰影
  • +
  • 多螢幕、notch 與 AeroSpace 頂部空間
  • +
+
+
+

明確不做

+
    +
  • Media widget
  • +
  • Notification badges
  • +
  • 重製 CodexBar popup 或多 provider quota strips
  • +
  • Session popup
  • +
  • Clipboard history
  • +
  • Wallpaper Engine/W Engine
  • +
  • Noctalia lockscreen、dock、launcher 或 notification daemon
  • +
  • 永久封鎖原生 menu bar 顯示的私有 API/SIP hack
  • +
+
+
+
+ +
+
+

目標配置

+ 約 36px 高,外層完全透明 +
+ +

CodexBar 與 Stats 內容由各自 app 產生;SketchyBar 負責 alias、位置、縮放與群組背景。

+
+ +
+
+

資料流與責任

+
+
┌────────────┐ workspace event ┌─────────────┐
+│ AeroSpace  │ ───────────────▶│             │
+└────────────┘                 │             │
+┌────────────┐ native aliases  │ SketchyBar  │
+│ Stats.app  │ ───────────────▶│             │
+└────────────┘                 │             │
+┌────────────┐ status alias    │             │
+│ CodexBar   │ ───────────────▶│             │
+└────────────┘                 └──────┬──────┘
+                                     ▲
+┌────────────┐ generated colors.sh   │
+│  Matugen   │ ──────────────────────┘
+└────────────┘
+
    +
  • AeroSpace:workspace 真實狀態、切換與變更事件。
  • +
  • Stats:系統監控與感測器;不在 SketchyBar 重寫資料採集。
  • +
  • CodexBar:沿用既有 codex = oauth 設定負責登入、用量取得、meter 與 popup;SketchyBar 不讀取 token,也不啟用 browser-cookie source。
  • +
  • Matugen:只生成色彩變數,不生成整份 SketchyBar 行為設定。
  • +
  • SketchyBar:呈現、排列、capsule、滑鼠互動與 hotload。
  • +
+
+ +
+
+

30 個公開實作調查

+ GitHub sketchybar topic,依星數排序;2026-09-12 快照 +
+

檢查前 30 個 repository 的中繼資料、檔案樹與代表性動態 item 後,23 個以 shell 或 Lua 為主。常見做法是低頻資料用 update_freq、狀態變化用事件訂閱、高頻監控才使用 C/Rust helper;沒有理由為單一 Codex meter 先導入完整 Lua 或 helper 架構。

+
    +
  • 直接 CodexBar → SketchyBar 的案例很少:showy-quota 功能完整,但只有 18★、4 位公開貢獻者,而且為多 provider、多宿主需求加入快取、renderer 與本機 serve 管理。
  • +
  • 精簡案例:fenetikm/dotfiles 每 120 秒執行 CodexBar CLI,以 JSON 檔快取五分鐘,再用 jq 取百分比。
  • +
  • 自行抓 provider 不划算:frankroeder/dotfiles 為 AI usage 維護 Lua popup、Python OAuth clients、快取、節流與測試,規模遠超過目前需求。
  • +
  • 最符合現有架構:SketchyBar 官方 alias 已能鏡像第三方 status item;計畫本來就因 Stats aliases 需要 Screen Recording,因此加入 CodexBar 不增加新的權限類型。
  • +
+ +

CodexBar 顯示方案

+
+ + + + + + + + +
方案新增負擔可靠性決定
CodexBar 原生 item → SketchyBar aliasCodexBar cask;無 parser、daemon 或額外 script沿用 CodexBar 自己的 meter 與資料來源;需原生 item 能被 query採用
自製 CodexBar CLI item一支 shell script、jq、last-known-good cache不依賴原生 item;JSON schema 變更時需維護只有 alias 實機失敗才做
showy-quotaBash 4、安裝流程、renderer、快取與 serve lifecycle多 provider、tmux、Zellij 共用時最完整目前不採用
直接讀 OAuth/私有 endpoint自行處理憑證、refresh、schema、429 與 cache維護面最大排除
+
+ +
+ 查看全部 30 個樣本 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Repository★/fork主要做法對本計畫的啟示
1FelixKratz/SketchyBar12,357/178C 核心;官方 shell 範例item、event、alias 的語法基準
2FelixKratz/dotfiles2,028/137SbarLua + C event providers高頻資料才值得 helper;本案先不需要
3crissNb/Dynamic-Island-Sketchybar528/32Shell + C helper動態島特效不在需求內
4mocki-toki/barik522/36原生 Swift bar屬完整替代品,不是 SketchyBar item 範例
5m4xshen/dotfiles438/12Nix 管理的 shell plugins每個低頻 item 一支小 script
6hbthen3rd/dotfiles-archived256/8Shell polling plugins大量獨立 poller 容易累積維護負擔
7rockyzhang24/dotfiles241/15Shell items + C CPU helper事件與 helper 可混用,不必全盤 Lua 化
8mehd-io/dotfiles186/23Shell + AeroSpace eventsworkspace 應由 AeroSpace 主動 trigger
9r17x/universe135/12Nix 封裝 SbarLua + C helpers可重現但超出 chezmoi 所需複雜度
10marsdevx/macOS-config91/5SbarLua + C event providers接近 FelixKratz 完整架構
11falleco/dotfiles84/7SbarLua + helpers + AeroSpace可借鏡事件接線,不照搬 framework
12TheGoldenPatrik1/sketchybar-config64/3大型 Lua widgets 與 popup功能豐富,但包含多個已排除項目
13slano-ls/SketchyBar53/1單體 shell config小設定可保持直接,不需模組系統
14joncrangle/sketchybar-system-stats51/6Rust event provider多項系統 metrics 才值得集中 provider
15smnatale/dotfiles47/0Shell + AeroSpace;CPU/disk/memory pollers證明基本 bar 不必使用 Lua
16haydenrou/dotfiles44/1分離式 shell scripts簡單、容易替換,但外部資料各自 polling
17Kcraft059/sketchybar-config43/6Shell config + macmon重用現成資料工具比重寫 sensor 好
18CCMurphy-dev/Aegis42/2原生 Swift 全包 bar完整 shell 替代品;不是可摘取的 Codex item
19frankroeder/dotfiles42/8SbarLua + Python AI usage clients自行抓 OAuth 與畫 popup 的成本過高
20FelixKratz/SketchyBarHelper36/3C 通訊 helper只有效能證據出現後才考慮
21Nikolaidp24/SketchyBar34/4Shell config定時 item 可維持原生 shell
22Efterklang/sketchybar34/0SbarLua + system-stats provider完整 widget suite 才能攤平相依成本
23fedeoliv/dotfiles29/0Lua dotfiles;bar 以外部內容管理外部設定降低 repo 可讀性,不採用
24deniserdogan/dotfiles26/1Shell items 與 theme-change eventMatugen 完成後只 trigger reload
25eminarican/dotfiles24/0Shell items/plugins靜態色彩檔與小型 scripts 足夠
26DeevsDeevs/dotfiles20/4chezmoi;只納管依賴與 yabai triggerbar 本體可與 bootstrap 分離
27xrce/dots19/1多套 shell SketchyBar profiles多 profile 會複製大量 item;本案只保留一套
28refcell/dots19/0Shell bar + Dynamic Island 副本一般 bar 與特效層應避免同時維護
29slashspace/dotfiles19/2Shell + AeroSpace + theme renderer最接近本案:小 scripts、事件、集中主題輸出
30jellydn/dotfiles19/3SbarLua + C helpers + AeroSpace成熟但比需求更重
+
+
+

Stars、forks 與排序是調查日快照,會隨 GitHub 狀態變動。另以 CodexBar、showy-quota、fenetikm 與 frankroeder 做焦點抽查,不把低星案例誤當成主要相依。

+
+ +
+
+

預計變更檔案

+ 維持最小檔案集 +
+
+ + + + + + + + + + + + + + + + + + +
檔案動作用途
home/dot_config/brew/Brewfile修改加入 FelixKratz tap、SketchyBar formula 與 CodexBar cask。
home/.chezmoiignore修改.config/sketchybar 限定為 macOS。
home/dot_config/sketchybar/executable_sketchybarrc新增bar 外觀、workspace、aliases、groups 與事件訂閱。
home/dot_config/sketchybar/colors.sh.tera新增Matugen 輸入模板;輸出為未納管的 colors.sh
home/dot_config/sketchybar/plugins/executable_clock.sh新增每秒更新 Noctalia 格式時鐘。
home/dot_config/sketchybar/plugins/executable_volume.sh新增處理音量事件、靜音與 3% 滾輪調整。
home/dot_config/sketchybar/plugins/executable_codexbar.sh條件式新增只有原生 CodexBar alias 在實機不可用時,才以 CLI JSON + last-known-good cache 顯示。
home/dot_config/codexbar/modify_private_config.json沿用維持 Codex 使用 OAuth、Claude 使用 CLI;不把憑證寫入 dotfiles。
home/dot_config/matugen/config.toml.tmpl修改註冊 SketchyBar 色彩輸出及 reload post-hook。
home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl修改把新 Matugen 模板 hash 納入重跑條件。
home/dot_config/aerospace/aerospace.toml修改啟動 SketchyBar、發出 workspace event、預留頂部高度。
README.md修改記錄 macOS bar、Matugen 與一次性 Stats 設定。
+
+
+ +
+
+

實作階段

+ 依風險與相依性排序 +
+ +
+ 階段 1:安裝與最小可見 bar +
+
規模:小相依:無目標:先確認平台可用
+
    +
  1. 將 SketchyBar 與 CodexBar 加入 Brewfile,保留現有 Stats。
  2. +
  3. 將 SketchyBar 目錄加入 macOS-only ignore 規則。
  4. +
  5. 建立最小 sketchybarrc:透明 bar、高度 36、JetBrainsMono Nerd Font、測試 clock item。
  6. +
  7. 透過 AeroSpace after-startup-command 啟動,避免額外維護一份 LaunchAgent。
  8. +
+

驗收

+
    +
  • +
  • +
  • +
+
+
+ +
+ 階段 2:AeroSpace workspace 垂直切片 +
+
規模:中相依:階段 1目標:先完成核心互動
+
    +
  1. 建立 workspace items:1 2 3 4 5 6 7 8 9 magic
  2. +
  3. 加入 aerospace_workspace_change custom event。
  4. +
  5. 讓 AeroSpace 的 exec-on-workspace-change 傳遞 focused workspace。
  6. +
  7. 點擊 workspace 時呼叫 aerospace workspace
  8. +
  9. 設定 outer.top = 36;多螢幕有差異時才改成 per-monitor gap。
  10. +
+

驗收

+
    +
  • +
  • +
  • +
+
Checkpoint A:若 workspace、多螢幕或 top gap 不穩定,先修正;在此之前不接 Matugen 或 Stats。
+
+
+ +
+ 階段 3:Matugen 與 Noctalia 視覺 +
+
規模:中相依:階段 2目標:配色單一來源
+
    +
  1. 新增 Matugen 的 colors.sh.tera,只輸出 SketchyBar 需要的 Material tokens。
  2. +
  3. surface_variant 轉成約 30% alpha 的 ARGB,文字使用 on_surface
  4. +
  5. 讓靜態 sketchybarrc source 生成的 colors.sh;檔案不存在時使用少量安全預設色。
  6. +
  7. Matugen post-hook 呼叫 sketchybar --reload
  8. +
  9. 將模板 hash 加入既有 apply-theme onchange script。
  10. +
+

驗收

+
    +
  • +
  • +
  • +
+
+
+ +
+ 階段 4:CodexBar、Stats aliases 與 tray +
+
規模:中相依:階段 3目標:重用既有監控
+
    +
  1. 啟動 CodexBar,沿用既有 Codex OAuth source、合併多 provider icons,選擇精簡的 meter 或 icon + percent layout。
  2. +
  3. 在 Stats GUI 一次性啟用實際需要的 CPU、GPU、RAM、Network、Sensors 與 Battery menu items。
  4. +
  5. 執行 sketchybar --query default_menu_items,取得 CodexBar、Stats 與 tray 的本機真實 owner/name;不要預先猜測名稱。
  6. +
  7. 將 CodexBar 原生 item 加為 alias;點擊執行 open -b com.steipete.codexbar,不實作 Accessibility click helper。
  8. +
  9. 只加入確定存在的 aliases;網路與系統監控分成兩個 bracket capsule。
  10. +
  11. 授予 SketchyBar Screen Recording 權限;這同時涵蓋 CodexBar、Stats 與 tray aliases。
  12. +
  13. 只有 CodexBar 不出現在 query 結果時,才新增 120 秒 CLI fallback;保留最後一次成功資料,失敗顯示 ,不啟動常駐 server。
  14. +
+

驗收

+
    +
  • +
  • +
  • +
+

CodexBar 與 Stats preferences 先保留為一次性 GUI 設定;只有確認跨機器重建需求後,才考慮納管其偏好檔。

+
+
+ +
+ 階段 5:時鐘、音量與多螢幕收尾 +
+
規模:中相依:階段 4目標:完成日常使用
+
    +
  1. 加入每秒 clock script,格式對齊 %B %d %H:%M:%S
  2. +
  3. volume item 訂閱 volume_changemouse.scrolled
  4. +
  5. 滾輪依方向調整 3%,並夾在 0–100;點擊切換靜音。
  6. +
  7. 驗證內建螢幕、外接螢幕、notch、睡眠喚醒與登入啟動。
  8. +
  9. 隱藏原生 macOS menu bar;只用系統支援的 auto-hide,不加入永久阻擋游標觸發的 hack。
  10. +
+

驗收

+
    +
  • +
  • +
  • +
+
Checkpoint B:完成後執行 chezmoi diffchezmoi apply,再進行一次乾淨登入驗證。
+
+
+ +
+ 階段 6:文件與提交 +
+
規模:小相依:階段 5目標:可重建、可回復
+
    +
  1. README 記錄 SketchyBar 架構、CodexBar/Stats 一次性設定與 Screen Recording 權限。
  2. +
  3. 確認 diff 無 token、個人路徑或生成的 colors.sh
  4. +
  5. 以小型提交拆分:套件、核心 bar、主題、aliases、文件。
  6. +
+

驗收

+
    +
  • +
  • +
  • +
+
+
+
+ +
+
+

風險與處理

+
+
+ + + + + + + + + + + +
風險影響處理方式
Stats alias 名稱依機器或版本不同在實機 query 後才加入;缺少 alias 時略過,不讓 bar 啟動失敗。
CodexBar 原生 status item 在 macOS 26 未出現在 query先確認「Allow in the Menu Bar」;仍不可用才啟用 CLI fallback,不修改 Control Center plist、不用私有 API。參見 CodexBar #1109
第三方 showy-quota 維護規模較小且功能過量不列為必要相依;只有未來要同時供 SketchyBar、tmux、Zellij 顯示多 provider 時重評。
內建與外接螢幕 bar 高度不同先用 36px;只有測試失敗才加入 per-monitor AeroSpace gap。
Matugen reload 時色彩檔短暫不存在靜態 config 提供最小 fallback palette。
原生 menu bar 在游標頂端再次出現接受 macOS auto-hide 行為;不使用私有 API 或 SIP hack。
Stats GUI state 尚未納入 chezmoiREADME 記錄一次性設定;只有實際跨機重建痛點出現時再自動化。
+
+
+ +
+
+

完成定義

+
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
+
+ +
+
+

實作參考與來源

+
+ +
+
+ +
+

實作已在 feature/sketchybar 分支進行;此文件保留調查依據、階段與實機驗收項目。

+
+ + From c9bac3ae9e526ffdebe5bdc8f9e6654ea7bc4843 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 00:01:08 +0800 Subject: [PATCH 05/30] fix(sketchybar): reload after menu apps start Delay the initial reload briefly so CodexBar and Stats can publish their native menu items before alias discovery. Start SketchyBar only when reload finds no running instance.\n\nValidation: tests/sketchybar.sh; AeroSpace TOML parsing; git diff --check. --- home/dot_config/aerospace/aerospace.toml | 3 ++- sketchybar-plan.html | 2 +- tests/sketchybar.sh | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/home/dot_config/aerospace/aerospace.toml b/home/dot_config/aerospace/aerospace.toml index c526beb..2abbc51 100644 --- a/home/dot_config/aerospace/aerospace.toml +++ b/home/dot_config/aerospace/aerospace.toml @@ -20,7 +20,8 @@ start-at-login = true after-startup-command = [ 'exec-and-forget open -gja CodexBar', 'exec-and-forget open -gja Stats', - 'exec-and-forget sketchybar', + # ponytail: one delayed reload covers login-item startup; add an app event helper only if three seconds proves insufficient. + 'exec-and-forget /bin/sh -c "sleep 3; sketchybar --reload || sketchybar"', ] exec-on-workspace-change = ['/bin/bash', '-c', diff --git a/sketchybar-plan.html b/sketchybar-plan.html index 4d9af33..3564160 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -658,7 +658,7 @@

實作參考與來源

-

實作已在 feature/sketchybar 分支進行;此文件保留調查依據、階段與實機驗收項目。

+

實作已納入此 dotfiles repository;本文件保留調查依據、階段與實機驗收項目。

diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index 7403a56..9b9af0a 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -8,7 +8,7 @@ grep -Fq 'tap "FelixKratz/formulae", trusted: { formula: "sketchybar" }' "$repo/ grep -Fq 'brew "FelixKratz/formulae/sketchybar"' "$repo/home/dot_config/brew/Brewfile" grep -Fq 'cask "codexbar"' "$repo/home/dot_config/brew/Brewfile" grep -Fq '.config/sketchybar/**' "$repo/home/.chezmoiignore" -grep -Fq "'exec-and-forget sketchybar'" "$repo/home/dot_config/aerospace/aerospace.toml" +grep -Fq 'sleep 3; sketchybar --reload || sketchybar' "$repo/home/dot_config/aerospace/aerospace.toml" grep -Fq 'FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE' "$repo/home/dot_config/aerospace/aerospace.toml" grep -Fq '[templates.sketchybar]' "$repo/home/dot_config/matugen/config.toml.tmpl" grep -Fq '# sketchybar:' "$repo/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl" From 787e5537bf89012385843a0a59f29d486c147c4c Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 00:10:18 +0800 Subject: [PATCH 06/30] fix(ci): isolate SketchyBar palette test Point workspace checks at the source fixture so an applied Matugen palette in HOME cannot change deterministic fallback-color assertions.\n\nReproduced with a conflicting HOME palette before the fix and passed with the same setup after it. --- tests/sketchybar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index 9b9af0a..026e3c6 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -84,12 +84,12 @@ if grep -Fq -- '--add alias Control Center,WiFi(4)' "$MOCK_SKETCHYBAR_LOG"; then fi : >"$MOCK_SKETCHYBAR_LOG" -NAME=space.3 FOCUSED_WORKSPACE=3 sh "$config/plugins/executable_workspace.sh" +CONFIG_DIR="$config" NAME=space.3 FOCUSED_WORKSPACE=3 sh "$config/plugins/executable_workspace.sh" grep -Fq 'background.drawing=on' "$MOCK_SKETCHYBAR_LOG" grep -Fq 'icon.color=0xff381e72' "$MOCK_SKETCHYBAR_LOG" : >"$MOCK_SKETCHYBAR_LOG" -NAME=space.3 FOCUSED_WORKSPACE=4 sh "$config/plugins/executable_workspace.sh" +CONFIG_DIR="$config" NAME=space.3 FOCUSED_WORKSPACE=4 sh "$config/plugins/executable_workspace.sh" grep -Fq 'background.drawing=off' "$MOCK_SKETCHYBAR_LOG" grep -Fq 'icon.color=0xfff2f0f4' "$MOCK_SKETCHYBAR_LOG" From 13885bb43074ac3f921ece7f490e7b6f6a479df0 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 00:51:00 +0800 Subject: [PATCH 07/30] fix(sketchybar): refine live macOS layout --- README.md | 4 +-- .../sketchybar/executable_sketchybarrc | 23 ++++++++++++--- tests/sketchybar.sh | 29 ++++++++++++------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 32b9ed1..f7fb0d7 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ chezmoi init --apply collieiscute -v After the first `chezmoi apply`: 1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. In Stats, enable the compact items you actually want mirrored. -2. Allow SketchyBar under **System Settings → Privacy & Security → Screen & System Audio Recording**, then run `sketchybar --reload`. -3. Optionally set **System Settings → Control Center → Automatically hide and show the menu bar → Always**. SIP stays enabled. +2. Allow the terminal that launches SketchyBar under **System Settings → Privacy & Security → Screen & System Audio Recording**, then fully restart SketchyBar; `sketchybar --reload` alone does not refresh TCC permissions. +3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This is required to avoid a second system bar; it does not require a reboot, and SIP stays enabled. If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar; a CodexBar CLI fallback is intentionally deferred until the native item is proven unavailable on this Mac. diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 6a5df60..bf52c6d 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -14,6 +14,8 @@ test -r "$CONFIG_DIR/colors.sh" && . "$CONFIG_DIR/colors.sh" sketchybar --bar \ position=top \ height=36 \ + y_offset=0 \ + topmost=off \ color="$TRANSPARENT" \ shadow=off \ corner_radius=0 \ @@ -42,6 +44,8 @@ sketchybar --add event aerospace_workspace_change sketchybar --add item clock center \ --set clock \ update_freq=1 \ + padding_left=5 \ + padding_right=5 \ background.color="$CAPSULE" \ background.drawing=on \ script="$PLUGIN_DIR/clock.sh" @@ -62,6 +66,8 @@ done sketchybar --add bracket workspaces '/space\..*/' \ --set workspaces \ + padding_left=5 \ + padding_right=5 \ background.color="$CAPSULE" \ background.height=27 \ background.corner_radius=9 \ @@ -69,6 +75,8 @@ sketchybar --add bracket workspaces '/space\..*/' \ sketchybar --add item volume right \ --set volume \ + padding_left=5 \ + padding_right=5 \ background.color="$CAPSULE" \ background.drawing=on \ script="$PLUGIN_DIR/volume.sh" \ @@ -100,18 +108,23 @@ printf '%s\n' "$menu_items" \ while IFS= read -r source; do index=$((index + 1)) case "$source" in - CodexBar,*) + CodexBar,*|*,codexbar-*) test "$codex_added" = false || continue add_alias "$source" codexbar left 'open -b com.steipete.codexbar' + sketchybar --set codexbar \ + padding_left=5 \ + padding_right=5 \ + background.color="$CAPSULE" \ + background.drawing=on codex_added=true ;; - Stats,*Network*) + Stats,*Network*|*,Network_*) add_alias "$source" "stats.net.$index" right 'open -a Stats' ;; - Stats,*CPU*|Stats,*GPU*|Stats,*RAM*|Stats,*Memory*|Stats,*Sensor*|Stats,*Battery*) + Stats,*CPU*|Stats,*GPU*|Stats,*RAM*|Stats,*Memory*|Stats,*Sensor*|*,CPU_*|*,GPU_*|*,RAM_*|*,Memory_*|*,Sensor*) add_alias "$source" "stats.sys.$index" right 'open -a Stats' ;; - Stats,*|Control\ Center,*|Control\ Centre,*|SystemUIServer,*|Window\ Server,*|TextInputMenuAgent,*) + Stats,*|Karabiner-Console-User-Server,*|Control\ Center,*|Control\ Centre,*|SystemUIServer,*|Window\ Server,*|TextInputMenuAgent,*|*,Clock*|*,Siri*|*,BentoBox-*|*,WiFi*|*,Battery*|*,AudioVideoModule*|*,Item-*) ;; *) add_alias "$source" "tray.$index" left '' @@ -123,6 +136,8 @@ printf '%s\n' "$menu_items" \ for group in tray stats.net stats.sys; do sketchybar --add bracket "$group.group" "/$group\\..*/" \ --set "$group.group" \ + padding_left=5 \ + padding_right=5 \ background.color="$CAPSULE" \ background.height=27 \ background.corner_radius=9 \ diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index 026e3c6..c5ff06d 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -65,21 +65,28 @@ export PATH="$tmp/bin:$PATH" export MOCK_SKETCHYBAR_LOG="$tmp/sketchybar.log" export MOCK_OSASCRIPT_LOG="$tmp/osascript.log" export MOCK_MENU_ITEMS='[ - "CodexBar,(1)", - "Stats,CPU_Mini(2)", - "Stats,Network_Speed(3)", - "Control Center,WiFi(4)", - "KeePassXC,(5)" + "Karabiner-Console-User-Server,(1)", + "控制中心,WiFi(2)", + "控制中心,Network_speed(3)", + "控制中心,Sensors_mini(4)", + "控制中心,RAM_tachometer(5)", + "控制中心,codexbar-merged(6)", + "控制中心,Item-0(7)", + "KeePassXC,(8)" ]' : >"$MOCK_SKETCHYBAR_LOG" CONFIG_DIR="$config" sh "$config/executable_sketchybarrc" -grep -Fq -- '--add alias CodexBar,(1) left' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias Stats,CPU_Mini(2) right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias Stats,Network_Speed(3) right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias KeePassXC,(5) left' "$MOCK_SKETCHYBAR_LOG" -if grep -Fq -- '--add alias Control Center,WiFi(4)' "$MOCK_SKETCHYBAR_LOG"; then - echo 'Control Center must not be duplicated in tray aliases' >&2 +grep -Fq -- '--add alias 控制中心,codexbar-merged(6) left' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,Sensors_mini(4) right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,RAM_tachometer(5) right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,Network_speed(3) right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias KeePassXC,(8) left' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--set codexbar padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--set workspaces padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--set stats.net.group padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" +if grep -Eq -- '--add alias (控制中心,(WiFi|Item-0)|Karabiner-Console-User-Server)' "$MOCK_SKETCHYBAR_LOG"; then + echo 'System menu items must not be duplicated in tray aliases' >&2 exit 1 fi From 68a123828c9fe7e1e9e797be676f06dd0aaafe05 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 00:51:05 +0800 Subject: [PATCH 08/30] fix(ci): avoid huge Arch external diff --- .github/workflows/test-distros.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-distros.yaml b/.github/workflows/test-distros.yaml index 623c158..81a3840 100644 --- a/.github/workflows/test-distros.yaml +++ b/.github/workflows/test-distros.yaml @@ -24,7 +24,8 @@ jobs: pacman -Syu --noconfirm sudo git chezmoi useradd -m dev echo "dev ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/dev - sudo -H -u dev --preserve-env=CHEZMOI_GITHUB_ACCESS_TOKEN chezmoi init collieiscute --branch "$CHEZMOI_BRANCH" --apply -v + # Verbose mode prints the full OpenWhispr external (80+ MB) and makes this job flaky. + sudo -H -u dev --preserve-env=CHEZMOI_GITHUB_ACCESS_TOKEN chezmoi init collieiscute --branch "$CHEZMOI_BRANCH" --apply ubuntu: name: ubuntu:latest From cc0ad94a6965b96589f4d554cb2b82a5857ae9b5 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 00:52:26 +0800 Subject: [PATCH 09/30] revert: keep verbose Arch CI output --- .github/workflows/test-distros.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-distros.yaml b/.github/workflows/test-distros.yaml index 81a3840..623c158 100644 --- a/.github/workflows/test-distros.yaml +++ b/.github/workflows/test-distros.yaml @@ -24,8 +24,7 @@ jobs: pacman -Syu --noconfirm sudo git chezmoi useradd -m dev echo "dev ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/dev - # Verbose mode prints the full OpenWhispr external (80+ MB) and makes this job flaky. - sudo -H -u dev --preserve-env=CHEZMOI_GITHUB_ACCESS_TOKEN chezmoi init collieiscute --branch "$CHEZMOI_BRANCH" --apply + sudo -H -u dev --preserve-env=CHEZMOI_GITHUB_ACCESS_TOKEN chezmoi init collieiscute --branch "$CHEZMOI_BRANCH" --apply -v ubuntu: name: ubuntu:latest From 80ad0da86a081a526ca4fc3603a8c36b58cc4be0 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 01:11:09 +0800 Subject: [PATCH 10/30] fix(sketchybar): start bar after login when no process exists --- README.md | 3 ++- home/dot_config/aerospace/aerospace.toml | 3 ++- tests/sketchybar.sh | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f7fb0d7..fa2012a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ chezmoi init --apply collieiscute -v ### SketchyBar (macOS) - AeroSpace starts SketchyBar, reserves 36 px at the top, and sends workspace changes through the official `aerospace_workspace_change` event. +- Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. - Left: third-party tray aliases and the first CodexBar status item. Center: `{Month day HH:MM:SS}` plus workspaces `1..9` and `M`. Right: 3%-step volume plus active Stats CPU/GPU/RAM/network/sensor/battery items. - CodexBar remains the OAuth and quota owner; SketchyBar only mirrors its native status item. No browser cookies, tokens, custom quota server, or `showy-quota` config is stored here. @@ -68,7 +69,7 @@ chezmoi init --apply collieiscute -v After the first `chezmoi apply`: 1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. In Stats, enable the compact items you actually want mirrored. -2. Allow the terminal that launches SketchyBar under **System Settings → Privacy & Security → Screen & System Audio Recording**, then fully restart SketchyBar; `sketchybar --reload` alone does not refresh TCC permissions. +2. Allow **AeroSpace** under **System Settings → Privacy & Security → Screen & System Audio Recording** for login startup. If launching SketchyBar manually from a terminal, allow that terminal too; its permission does not cover AeroSpace. After granting permission, quit SketchyBar and restart AeroSpace to launch a fresh bar; `sketchybar --reload` alone does not refresh TCC permissions. 3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This is required to avoid a second system bar; it does not require a reboot, and SIP stays enabled. If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar; a CodexBar CLI fallback is intentionally deferred until the native item is proven unavailable on this Mac. diff --git a/home/dot_config/aerospace/aerospace.toml b/home/dot_config/aerospace/aerospace.toml index 2abbc51..0c861d8 100644 --- a/home/dot_config/aerospace/aerospace.toml +++ b/home/dot_config/aerospace/aerospace.toml @@ -21,7 +21,8 @@ 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. - 'exec-and-forget /bin/sh -c "sleep 3; sketchybar --reload || sketchybar"', + # --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', diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index c5ff06d..1e4e35d 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -8,11 +8,28 @@ grep -Fq 'tap "FelixKratz/formulae", trusted: { formula: "sketchybar" }' "$repo/ grep -Fq 'brew "FelixKratz/formulae/sketchybar"' "$repo/home/dot_config/brew/Brewfile" grep -Fq 'cask "codexbar"' "$repo/home/dot_config/brew/Brewfile" grep -Fq '.config/sketchybar/**' "$repo/home/.chezmoiignore" -grep -Fq 'sleep 3; sketchybar --reload || sketchybar' "$repo/home/dot_config/aerospace/aerospace.toml" grep -Fq 'FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE' "$repo/home/dot_config/aerospace/aerospace.toml" grep -Fq '[templates.sketchybar]' "$repo/home/dot_config/matugen/config.toml.tmpl" grep -Fq '# sketchybar:' "$repo/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl" +# SketchyBar --reload returns success even when no bar is running. +startup=$(sed -n 's/.*exec-and-forget \/bin\/sh -c "\(sleep 3;.*\)".*/\1/p' "$repo/home/dot_config/aerospace/aerospace.toml") +test -n "$startup" +for running in 0 1; do + calls=$( + pgrep() { return "$running"; } + sleep() { :; } + sketchybar() { printf 'sketchybar%s\n' "${1:+ $1}"; } + eval "$startup" + ) + expected=sketchybar + test "$running" -ne 0 || expected='sketchybar --reload' + if test "$calls" != "$expected"; then + printf 'Startup (pgrep exit %s): expected "%s", got "%s"\n' "$running" "$expected" "$calls" >&2 + exit 1 + fi +done + for file in \ "$config/executable_sketchybarrc" \ "$config/plugins/executable_clock.sh" \ From a1c1dd009369ab33e3300c20c70dd738e65524fb Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 01:19:41 +0800 Subject: [PATCH 11/30] fix(sketchybar): keep clock and workspaces clear of the notch --- README.md | 4 ++-- home/dot_config/sketchybar/executable_sketchybarrc | 7 +++++-- tests/sketchybar.sh | 5 +++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fa2012a..42468d4 100644 --- a/README.md +++ b/README.md @@ -63,12 +63,12 @@ chezmoi init --apply collieiscute -v - AeroSpace starts SketchyBar, reserves 36 px at the top, and sends workspace changes through the official `aerospace_workspace_change` event. - Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. -- Left: third-party tray aliases and the first CodexBar status item. Center: `{Month day HH:MM:SS}` plus workspaces `1..9` and `M`. Right: 3%-step volume plus active Stats CPU/GPU/RAM/network/sensor/battery items. +- Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: 3%-step volume plus active Stats CPU/GPU/RAM/network/sensor/battery items. - CodexBar remains the OAuth and quota owner; SketchyBar only mirrors its native status item. No browser cookies, tokens, custom quota server, or `showy-quota` config is stored here. After the first `chezmoi apply`: -1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. In Stats, enable the compact items you actually want mirrored. +1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. Select its icon-and-percentage style and weekly metric for a readable quota meter; the default icon alone can be hard to distinguish. In Stats, enable the compact items you actually want mirrored. 2. Allow **AeroSpace** under **System Settings → Privacy & Security → Screen & System Audio Recording** for login startup. If launching SketchyBar manually from a terminal, allow that terminal too; its permission does not cover AeroSpace. After granting permission, quit SketchyBar and restart AeroSpace to launch a fresh bar; `sketchybar --reload` alone does not refresh TCC permissions. 3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This is required to avoid a second system bar; it does not require a reboot, and SIP stays enabled. diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index bf52c6d..45d6769 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -14,6 +14,7 @@ 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" \ @@ -41,7 +42,8 @@ sketchybar --default \ sketchybar --add event aerospace_workspace_change -sketchybar --add item clock center \ +# 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 \ padding_left=5 \ @@ -53,7 +55,7 @@ sketchybar --add item clock center \ 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" center \ + sketchybar --add item "space.$sid" e \ --set "space.$sid" \ icon="$label" \ label.drawing=off \ @@ -114,6 +116,7 @@ printf '%s\n' "$menu_items" \ sketchybar --set codexbar \ padding_left=5 \ padding_right=5 \ + alias.scale=1 \ background.color="$CAPSULE" \ background.drawing=on codex_added=true diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index 1e4e35d..0546b2b 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -94,6 +94,11 @@ export MOCK_MENU_ITEMS='[ : >"$MOCK_SKETCHYBAR_LOG" CONFIG_DIR="$config" sh "$config/executable_sketchybarrc" +grep -Fq -- 'notch_width=200' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add item clock q --set clock' "$MOCK_SKETCHYBAR_LOG" +for sid in 1 2 3 4 5 6 7 8 9 magic; do + grep -Fq -- "--add item space.$sid e --set space.$sid" "$MOCK_SKETCHYBAR_LOG" +done grep -Fq -- '--add alias 控制中心,codexbar-merged(6) left' "$MOCK_SKETCHYBAR_LOG" grep -Fq -- '--add alias 控制中心,Sensors_mini(4) right' "$MOCK_SKETCHYBAR_LOG" grep -Fq -- '--add alias 控制中心,RAM_tachometer(5) right' "$MOCK_SKETCHYBAR_LOG" From 33370f4bca7ac24b7d90568a37f71a49270647dc Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 01:19:41 +0800 Subject: [PATCH 12/30] fix(arch): install LocalSend dependency missing from AUR metadata --- home/.chezmoidata/packages.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index 1fc348f..d51a083 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -36,6 +36,8 @@ packages: - keepassxc - kitty - lazygit + # ponytail: explicit LocalSend dependency until AUR .SRCINFO matches its PKGBUILD. + - libayatana-indicator - libreoffice-fresh - linux-headers - linux-wallpaperengine-git From 7700422fcf61b85ab19e9b98223177c89e4ca192 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 01:27:13 +0800 Subject: [PATCH 13/30] fix(sketchybar): keep aliases stable and preserve chart colors --- README.md | 1 + .../sketchybar/executable_sketchybarrc | 6 ++++-- tests/sketchybar.sh | 17 ++++++++++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 42468d4..ccac69e 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ chezmoi init --apply collieiscute -v - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. - Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: 3%-step volume plus active Stats CPU/GPU/RAM/network/sensor/battery items. - CodexBar remains the OAuth and quota owner; SketchyBar only mirrors its native status item. No browser cookies, tokens, custom quota server, or `showy-quota` config is stored here. +- Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. After the first `chezmoi apply`: diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 45d6769..de322c1 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -85,15 +85,17 @@ sketchybar --add item volume right \ --subscribe volume volume_change mouse.scrolled mouse.clicked system_woke add_alias() { - source=$1 + # The query suffix is a changing list index, not part of a named window. + # Keep it only for unnamed items (Owner,(n)); named aliases can rebind natively. + source=$(printf '%s\n' "$1" | sed 's/\([^,]\)([0-9][0-9]*)$/\1/') name=$2 position=$3 click_script=${4-} sketchybar --add alias "$source" "$position" || return 0 sketchybar --rename "$source" "$name" + # Preserve raster colors: tinting an opaque chart makes it a solid rectangle. sketchybar --set "$name" \ - alias.color="$ON_SURFACE" \ alias.scale=0.82 \ alias.update_freq=2 \ click_script="$click_script" diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh index 0546b2b..1110cb1 100755 --- a/tests/sketchybar.sh +++ b/tests/sketchybar.sh @@ -89,7 +89,8 @@ export MOCK_MENU_ITEMS='[ "控制中心,RAM_tachometer(5)", "控制中心,codexbar-merged(6)", "控制中心,Item-0(7)", - "KeePassXC,(8)" + "KeePassXC,(8)", + "控制中心,CPU_line_chart(109)" ]' : >"$MOCK_SKETCHYBAR_LOG" @@ -99,10 +100,16 @@ grep -Fq -- '--add item clock q --set clock' "$MOCK_SKETCHYBAR_LOG" for sid in 1 2 3 4 5 6 7 8 9 magic; do grep -Fq -- "--add item space.$sid e --set space.$sid" "$MOCK_SKETCHYBAR_LOG" done -grep -Fq -- '--add alias 控制中心,codexbar-merged(6) left' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,Sensors_mini(4) right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,RAM_tachometer(5) right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,Network_speed(3) right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,codexbar-merged left' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,Sensors_mini right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,RAM_tachometer right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,Network_speed right' "$MOCK_SKETCHYBAR_LOG" +grep -Fq -- '--add alias 控制中心,CPU_line_chart right' "$MOCK_SKETCHYBAR_LOG" +if grep -Fq -- 'alias.color=' "$MOCK_SKETCHYBAR_LOG"; then + echo 'Tinting opaque Stats chart images turns the whole chart into a solid block' >&2 + exit 1 +fi +# Unnamed items still need the index to be addressable. grep -Fq -- '--add alias KeePassXC,(8) left' "$MOCK_SKETCHYBAR_LOG" grep -Fq -- '--set codexbar padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" grep -Fq -- '--set workspaces padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" From 2ccde66a88c5605685653bebdd8650fd21231ab0 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 03:33:02 +0800 Subject: [PATCH 14/30] ci: keep dotfiles checks focused on installation --- .github/workflows/test-distros.yaml | 2 - tests/sketchybar.sh | 146 ---------------------------- 2 files changed, 148 deletions(-) delete mode 100755 tests/sketchybar.sh diff --git a/.github/workflows/test-distros.yaml b/.github/workflows/test-distros.yaml index 623c158..9817872 100644 --- a/.github/workflows/test-distros.yaml +++ b/.github/workflows/test-distros.yaml @@ -50,8 +50,6 @@ jobs: - name: chezmoi init dotfiles run: | chezmoi init collieiscute --branch "$CHEZMOI_BRANCH" --apply -v - - name: Test SketchyBar configuration - run: "$HOME/.local/share/chezmoi/tests/sketchybar.sh" windows: name: windows:latest diff --git a/tests/sketchybar.sh b/tests/sketchybar.sh deleted file mode 100755 index 1110cb1..0000000 --- a/tests/sketchybar.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/sh -set -eu - -repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -config="$repo/home/dot_config/sketchybar" - -grep -Fq 'tap "FelixKratz/formulae", trusted: { formula: "sketchybar" }' "$repo/home/dot_config/brew/Brewfile" -grep -Fq 'brew "FelixKratz/formulae/sketchybar"' "$repo/home/dot_config/brew/Brewfile" -grep -Fq 'cask "codexbar"' "$repo/home/dot_config/brew/Brewfile" -grep -Fq '.config/sketchybar/**' "$repo/home/.chezmoiignore" -grep -Fq 'FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE' "$repo/home/dot_config/aerospace/aerospace.toml" -grep -Fq '[templates.sketchybar]' "$repo/home/dot_config/matugen/config.toml.tmpl" -grep -Fq '# sketchybar:' "$repo/home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl" - -# SketchyBar --reload returns success even when no bar is running. -startup=$(sed -n 's/.*exec-and-forget \/bin\/sh -c "\(sleep 3;.*\)".*/\1/p' "$repo/home/dot_config/aerospace/aerospace.toml") -test -n "$startup" -for running in 0 1; do - calls=$( - pgrep() { return "$running"; } - sleep() { :; } - sketchybar() { printf 'sketchybar%s\n' "${1:+ $1}"; } - eval "$startup" - ) - expected=sketchybar - test "$running" -ne 0 || expected='sketchybar --reload' - if test "$calls" != "$expected"; then - printf 'Startup (pgrep exit %s): expected "%s", got "%s"\n' "$running" "$expected" "$calls" >&2 - exit 1 - fi -done - -for file in \ - "$config/executable_sketchybarrc" \ - "$config/plugins/executable_clock.sh" \ - "$config/plugins/executable_volume.sh" \ - "$config/plugins/executable_workspace.sh"; do - test -x "$file" - sh -n "$file" -done - -tmp=$(mktemp -d) -trap 'rm -rf "$tmp"' EXIT HUP INT TERM -mkdir "$tmp/bin" - -cat >"$tmp/bin/sketchybar" <<'EOF' -#!/bin/sh -if test "${1-} ${2-}" = "--query default_menu_items"; then - printf '%s\n' "$MOCK_MENU_ITEMS" -else - printf '%s\n' "$*" >>"$MOCK_SKETCHYBAR_LOG" -fi -EOF - -cat >"$tmp/bin/osascript" <<'EOF' -#!/bin/sh -printf '%s\n' "$*" >>"$MOCK_OSASCRIPT_LOG" -case "$*" in - *'output volume of'*) printf '%s\n' "${MOCK_VOLUME:-50}" ;; - *'output muted of'*) printf '%s\n' "${MOCK_MUTED:-false}" ;; -esac -EOF - -chmod +x "$tmp/bin/sketchybar" "$tmp/bin/osascript" - -if command -v matugen >/dev/null 2>&1; then - cat >"$tmp/matugen.toml" </dev/null - grep -Eq '^CAPSULE=0x4d[0-9a-fA-F]{6}$' "$tmp/colors.sh" - grep -Eq '^ON_SURFACE=0xff[0-9a-fA-F]{6}$' "$tmp/colors.sh" - grep -Eq '^PRIMARY=0xff[0-9a-fA-F]{6}$' "$tmp/colors.sh" - grep -Eq '^ON_PRIMARY=0xff[0-9a-fA-F]{6}$' "$tmp/colors.sh" -fi - -export PATH="$tmp/bin:$PATH" -export MOCK_SKETCHYBAR_LOG="$tmp/sketchybar.log" -export MOCK_OSASCRIPT_LOG="$tmp/osascript.log" -export MOCK_MENU_ITEMS='[ - "Karabiner-Console-User-Server,(1)", - "控制中心,WiFi(2)", - "控制中心,Network_speed(3)", - "控制中心,Sensors_mini(4)", - "控制中心,RAM_tachometer(5)", - "控制中心,codexbar-merged(6)", - "控制中心,Item-0(7)", - "KeePassXC,(8)", - "控制中心,CPU_line_chart(109)" -]' - -: >"$MOCK_SKETCHYBAR_LOG" -CONFIG_DIR="$config" sh "$config/executable_sketchybarrc" -grep -Fq -- 'notch_width=200' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add item clock q --set clock' "$MOCK_SKETCHYBAR_LOG" -for sid in 1 2 3 4 5 6 7 8 9 magic; do - grep -Fq -- "--add item space.$sid e --set space.$sid" "$MOCK_SKETCHYBAR_LOG" -done -grep -Fq -- '--add alias 控制中心,codexbar-merged left' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,Sensors_mini right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,RAM_tachometer right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,Network_speed right' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--add alias 控制中心,CPU_line_chart right' "$MOCK_SKETCHYBAR_LOG" -if grep -Fq -- 'alias.color=' "$MOCK_SKETCHYBAR_LOG"; then - echo 'Tinting opaque Stats chart images turns the whole chart into a solid block' >&2 - exit 1 -fi -# Unnamed items still need the index to be addressable. -grep -Fq -- '--add alias KeePassXC,(8) left' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--set codexbar padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--set workspaces padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" -grep -Fq -- '--set stats.net.group padding_left=5 padding_right=5' "$MOCK_SKETCHYBAR_LOG" -if grep -Eq -- '--add alias (控制中心,(WiFi|Item-0)|Karabiner-Console-User-Server)' "$MOCK_SKETCHYBAR_LOG"; then - echo 'System menu items must not be duplicated in tray aliases' >&2 - exit 1 -fi - -: >"$MOCK_SKETCHYBAR_LOG" -CONFIG_DIR="$config" NAME=space.3 FOCUSED_WORKSPACE=3 sh "$config/plugins/executable_workspace.sh" -grep -Fq 'background.drawing=on' "$MOCK_SKETCHYBAR_LOG" -grep -Fq 'icon.color=0xff381e72' "$MOCK_SKETCHYBAR_LOG" - -: >"$MOCK_SKETCHYBAR_LOG" -CONFIG_DIR="$config" NAME=space.3 FOCUSED_WORKSPACE=4 sh "$config/plugins/executable_workspace.sh" -grep -Fq 'background.drawing=off' "$MOCK_SKETCHYBAR_LOG" -grep -Fq 'icon.color=0xfff2f0f4' "$MOCK_SKETCHYBAR_LOG" - -: >"$MOCK_OSASCRIPT_LOG" -NAME=volume SENDER=mouse.scrolled SCROLL_DELTA=1 MOCK_VOLUME=98 \ - sh "$config/plugins/executable_volume.sh" -grep -Fq 'set volume output volume 100' "$MOCK_OSASCRIPT_LOG" - -: >"$MOCK_OSASCRIPT_LOG" -NAME=volume SENDER=mouse.scrolled SCROLL_DELTA=-1 MOCK_VOLUME=2 \ - sh "$config/plugins/executable_volume.sh" -grep -Fq 'set volume output volume 0' "$MOCK_OSASCRIPT_LOG" - -: >"$MOCK_OSASCRIPT_LOG" -NAME=volume SENDER=mouse.clicked sh "$config/plugins/executable_volume.sh" -grep -Fq 'set volume output muted not (output muted of (get volume settings))' "$MOCK_OSASCRIPT_LOG" - -echo 'SketchyBar checks passed.' From 9879b5591fc23da1af4e89dd3e2bf884007596d8 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 03:33:02 +0800 Subject: [PATCH 15/30] fix(sketchybar): open native usage and control center menus --- README.md | 8 +++- .../sketchybar/executable_sketchybarrc | 17 +++++++- .../plugins/executable_native-menu.sh | 39 +++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100755 home/dot_config/sketchybar/plugins/executable_native-menu.sh diff --git a/README.md b/README.md index ccac69e..394ad44 100644 --- a/README.md +++ b/README.md @@ -63,15 +63,19 @@ chezmoi init --apply collieiscute -v - AeroSpace starts SketchyBar, reserves 36 px at the top, and sends workspace changes through the official `aerospace_workspace_change` event. - Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. -- Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: 3%-step volume plus active Stats CPU/GPU/RAM/network/sensor/battery items. +- Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: Control Center, 3%-step volume, and active Stats CPU/GPU/RAM/network/sensor/battery items. - CodexBar remains the OAuth and quota owner; SketchyBar only mirrors its native status item. No browser cookies, tokens, custom quota server, or `showy-quota` config is stored here. +- Hover or click the CodexBar meter to open its native usage menu. Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). These are SketchyBar buttons using System Events accessibility actions; native panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. - Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. After the first `chezmoi apply`: 1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. Select its icon-and-percentage style and weekly metric for a readable quota meter; the default icon alone can be hard to distinguish. In Stats, enable the compact items you actually want mirrored. 2. Allow **AeroSpace** under **System Settings → Privacy & Security → Screen & System Audio Recording** for login startup. If launching SketchyBar manually from a terminal, allow that terminal too; its permission does not cover AeroSpace. After granting permission, quit SketchyBar and restart AeroSpace to launch a fresh bar; `sketchybar --reload` alone does not refresh TCC permissions. -3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This is required to avoid a second system bar; it does not require a reboot, and SIP stays enabled. +3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This hides the system bar until the pointer touches the top edge; it does not permanently disable it. No reboot or SIP change is needed. +4. Allow the SketchyBar launcher (AeroSpace, or the terminal for manual launches) to control **System Events** if macOS requests Automation permission. It also needs Accessibility permission for native menu buttons. + +CI validates installation with `chezmoi init --apply -v` across supported operating systems. Bar interactions are checked on the actual Mac, not with mocked feature-test scripts. If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar; a CodexBar CLI fallback is intentionally deferred until the native item is proven unavailable on this Mac. diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index de322c1..47987b6 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -75,6 +75,17 @@ sketchybar --add bracket workspaces '/space\..*/' \ background.corner_radius=9 \ background.drawing=on +sketchybar --add item control_center right \ + --set control_center \ + icon="󰒓" \ + label.drawing=off \ + padding_left=5 \ + padding_right=5 \ + background.color="$CAPSULE" \ + background.drawing=on \ + script="$PLUGIN_DIR/native-menu.sh" \ + --subscribe control_center mouse.clicked + sketchybar --add item volume right \ --set volume \ padding_left=5 \ @@ -114,13 +125,15 @@ printf '%s\n' "$menu_items" \ case "$source" in CodexBar,*|*,codexbar-*) test "$codex_added" = false || continue - add_alias "$source" codexbar left 'open -b com.steipete.codexbar' + add_alias "$source" codexbar left '' sketchybar --set codexbar \ padding_left=5 \ padding_right=5 \ alias.scale=1 \ background.color="$CAPSULE" \ - background.drawing=on + background.drawing=on \ + script="$PLUGIN_DIR/native-menu.sh" \ + --subscribe codexbar mouse.entered mouse.clicked codex_added=true ;; Stats,*Network*|*,Network_*) diff --git a/home/dot_config/sketchybar/plugins/executable_native-menu.sh b/home/dot_config/sketchybar/plugins/executable_native-menu.sh new file mode 100755 index 0000000..22f1af3 --- /dev/null +++ b/home/dot_config/sketchybar/plugins/executable_native-menu.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# --update also runs item scripts; never open menus during startup or reload. +case "${SENDER:-}" in + mouse.entered|mouse.clicked) ;; + *) exit 0 ;; +esac + +case "${NAME:-}" in + codexbar) + # Opening the app does not open its status menu. Avoid toggling it shut + # when a click follows hover. CodexBar 0.60 keeps a zero-size menu when + # closed, so existence alone does not mean it is visible. + osascript <<'APPLESCRIPT' +tell application "System Events" to tell process "CodexBar" + tell menu bar item 1 of menu bar 2 + if not (exists menu 1) or (size of menu 1 is {0, 0}) then click + end tell +end tell +APPLESCRIPT + ;; + control_center) + osascript <<'APPLESCRIPT' +tell application "System Events" to tell process "ControlCenter" + repeat with statusItem in menu bar items of menu bar 1 + set itemID to missing value + try + set itemID to value of attribute "AXIdentifier" of statusItem + end try + if itemID is "com.apple.menuextra.controlcenter" then + click statusItem + return + end if + end repeat + error "Control Center status item was not found." +end tell +APPLESCRIPT + ;; +esac From e2fcf9c51b84a9f6973bcb70d1e6d66389fd330c Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 03:47:41 +0800 Subject: [PATCH 16/30] feat(sketchybar): separate CodexBar hover and click actions --- README.md | 7 +- .../sketchybar/executable_sketchybarrc | 19 +++- .../sketchybar/plugins/executable_codexbar.sh | 73 +++++++++++++ .../plugins/executable_native-menu.sh | 7 +- sketchybar-plan.html | 101 ++++++++++++------ 5 files changed, 166 insertions(+), 41 deletions(-) create mode 100755 home/dot_config/sketchybar/plugins/executable_codexbar.sh diff --git a/README.md b/README.md index 394ad44..016e4f1 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,9 @@ chezmoi init --apply collieiscute -v - Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. - Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: Control Center, 3%-step volume, and active Stats CPU/GPU/RAM/network/sensor/battery items. -- CodexBar remains the OAuth and quota owner; SketchyBar only mirrors its native status item. No browser cookies, tokens, custom quota server, or `showy-quota` config is stored here. -- Hover or click the CodexBar meter to open its native usage menu. Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). These are SketchyBar buttons using System Events accessibility actions; native panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. +- CodexBar remains the OAuth and quota owner. SketchyBar mirrors its native meter and reads its CLI JSON for a small Matugen-colored usage tooltip; no browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. +- Hover shows the cached Codex quota summary; leaving hides it. Left-click opens CodexBar's native detailed menu. Right-click refreshes the summary; background updates run every 60 seconds and on wake. Missing quota windows are omitted, and failures keep the previous rows with a warning. macOS-provided `jq` parses the CLI response and `lockf` prevents overlapping refreshes. +- Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. - Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. After the first `chezmoi apply`: @@ -77,7 +78,7 @@ After the first `chezmoi apply`: CI validates installation with `chezmoi init --apply -v` across supported operating systems. Bar interactions are checked on the actual Mac, not with mocked feature-test scripts. -If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar; a CodexBar CLI fallback is intentionally deferred until the native item is proven unavailable on this Mac. +If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar. The CLI supplies the hover summary only; it does not replace the native bar meter. ### Hyprland diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 47987b6..a3e0a6b 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -132,8 +132,23 @@ printf '%s\n' "$menu_items" \ alias.scale=1 \ background.color="$CAPSULE" \ background.drawing=on \ - script="$PLUGIN_DIR/native-menu.sh" \ - --subscribe codexbar mouse.entered mouse.clicked + popup.align=left \ + popup.height=28 \ + popup.background.color="0xff${CAPSULE#????}" \ + popup.background.corner_radius=10 \ + popup.background.border_width=1 \ + popup.background.border_color="0x33${ON_SURFACE#????}" \ + update_freq=60 \ + script="$PLUGIN_DIR/codexbar.sh" \ + --subscribe codexbar mouse.entered mouse.exited mouse.clicked system_woke + sketchybar --add item codexbar.status popup.codexbar \ + --set codexbar.status \ + width=330 \ + icon.drawing=off \ + label="Codex · 正在讀取…" \ + label.color="$PRIMARY" \ + label.padding_left=12 \ + label.padding_right=12 codex_added=true ;; Stats,*Network*|*,Network_*) diff --git a/home/dot_config/sketchybar/plugins/executable_codexbar.sh b/home/dot_config/sketchybar/plugins/executable_codexbar.sh new file mode 100755 index 0000000..d2df02a --- /dev/null +++ b/home/dot_config/sketchybar/plugins/executable_codexbar.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +case "${SENDER:-}" in + mouse.entered) + sketchybar --set codexbar popup.drawing=on + exit 0 + ;; + mouse.exited) + sketchybar --set codexbar popup.drawing=off + exit 0 + ;; + mouse.clicked) + case "${BUTTON:-left}" in + left) + sketchybar --set codexbar popup.drawing=off + exec "${CONFIG_DIR:-$HOME/.config/sketchybar}/plugins/native-menu.sh" + ;; + right) ;; + *) exit 0 ;; + esac + ;; + forced|routine|system_woke) ;; + *) exit 0 ;; +esac + +# Keep only rendered labels in SketchyBar, not account JSON or credentials. +# macOS lockf prevents overlapping refreshes and releases on process exit. +cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/sketchybar" +mkdir -p "$cache_dir" || exit 1 +exec 9>"$cache_dir/codexbar.lock" +/usr/bin/lockf -s -t 0 9 || exit 0 + +sketchybar --set codexbar.status label="Codex · 更新中…" +if ! data=$(codexbar usage --provider codex --source oauth --format json --json-only --no-credits 2>/dev/null); then + sketchybar --set codexbar.status label="Codex · 更新失敗,資料未更新" + exit 1 +fi + +if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' + (if type == "array" then .[] elif .providers then .providers[] else . end) + | select(.provider == "codex" and .error == null and (.usage | type) == "object") + | .usage as $usage + | (if .stale == true then "注意:來源回報為快取資料" else empty end), + ([{title: "5 小時", window: $usage.primary}, + {title: "每週", window: $usage.secondary}, + {title: "每月", window: $usage.tertiary}, + ($usage.extraRateWindows[]? | {title: .title, window: (.window // .)}), + ($usage.windows[]? | {title: (.title // .name), window: (.window // .)})] + | map(select((.window.usedPercent | type) == "number")) + | if length == 0 then "目前沒有回報額度區間" + else .[] + | ((.title // "額度") | tostring | gsub("[\r\n\t]"; " ")) as $title + | ([100, ([0, (100 - .window.usedPercent)] | max)] | min | round) as $remaining + | "\($title) · 剩餘 \($remaining)%" + end) +'); then + sketchybar --set codexbar.status label="Codex · 回應無法讀取,資料未更新" + exit 1 +fi + +# One IPC batch replaces the tooltip rows without flashing an empty popup. +set -- --remove '/codexbar\.usage\..*/' \ + --set codexbar.status "label=Codex · 讀取 $(date +%H:%M:%S)" +index=0 +while IFS= read -r row; do + index=$((index + 1)) + set -- "$@" --add item "codexbar.usage.$index" popup.codexbar \ + --set "codexbar.usage.$index" width=330 icon.drawing=off \ + "label=$row" label.max_chars=40 label.padding_left=12 label.padding_right=12 +done <SketchyBar 導入計畫

以 SketchyBar 取代原生 menu bar 的主要顯示層,串接 AeroSpace、CodexBar、Stats 與 Matugen;重現 Noctalia 的透明背景、半透明 capsule 與三區配置,而且不停用 SIP。

- 狀態:待實作 + 狀態:本機互動已驗證,完整登入/外接螢幕待驗收 平台:macOS 方案:SketchyBar + AeroSpace + CodexBar - 更新:2026-09-12 + 更新:2026-09-13
@@ -288,8 +288,9 @@

SketchyBar 導入計畫

決策

-

採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;Codex 用量優先鏡像 CodexBar 原生 status item。SketchyBar 只負責排列、capsule、AeroSpace workspace、時鐘與音量互動。

+

採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;bar 上的 Codex 用量鏡像 CodexBar 原生 status item,hover 摘要才讀 CodexBar CLI。SketchyBar 負責排列、Matugen capsule、workspace、時鐘、音量與控制中心入口。

不採用 Simple Bar、SbarLua、大型第三方設定集或 showy-quota。單一 Codex meter 不值得再加入 Bash 4、獨立 renderer、cache daemon 與第二套主題設定。

+

CI 只驗證安裝:已移除新增的 tests/sketchybar.sh 與其 CI 步驟。四平台保留 chezmoi init --apply -v;功能在實際 macOS 上操作驗收。最新安裝結果以 PR #155 checks 為準,不把上一個 commit 的成功當成本次成功。

@@ -303,7 +304,8 @@

要做

  • AeroSpace workspace 1–9 與 magic
  • Workspace 點擊切換與狀態即時更新
  • -
  • CodexBar 原生 Codex meter alias;點擊開啟 CodexBar
  • +
  • CodexBar 原生 meter;hover 小摘要、左鍵原生詳細選單、右鍵刷新摘要
  • +
  • SketchyBar 最右側控制中心按鈕,開啟 macOS 原生 Wi-Fi/專注模式面板
  • 時鐘:月份、日期與秒數
  • 音量顯示、靜音與滾輪每次 3%
  • Stats aliases:網路、CPU、GPU、RAM、溫度、電池
  • @@ -318,7 +320,7 @@

    明確不做

    • Media widget
    • Notification badges
    • -
    • 重製 CodexBar popup 或多 provider quota strips
    • +
    • 重製 CodexBar 完整詳細面板或多 provider quota strips;僅自製 Codex hover 小摘要
    • Session popup
    • Clipboard history
    • Wallpaper Engine/W Engine
    • @@ -334,7 +336,7 @@

      明確不做

      目標配置

      約 36px 高,外層完全透明 -
+ +
+
+

CodexBar 互動分工

+ 2026-09-13 使用者確認 +
+

dotfiles 指定 salemsayed/codexbar-meter:bar。依該 Noctalia 外掛目前程式,hover 是小型用量提示、左鍵切換詳細面板、右鍵刷新;macOS 採用相同分工,但詳細面板沿用 CodexBar 原生介面。

+
+ + + + + + + + + +
操作SketchyBar 行為資料與限制
Hover元件下方顯示 Matugen 配色的小摘要,不打開原生選單只顯示已讀取的 Codex 額度;缺少的區間略過,不補假數值
移開游標收起小摘要摘要不是可操作的大型面板
左鍵收起摘要,開啟 CodexBar 原生詳細選單完整帳號、重置時間、費用與設定仍由 CodexBar 維護;位置與樣式不完全等同 Noctalia
右鍵立即刷新小摘要只重新讀 CodexBar CLI,不改登入來源或開啟其他視窗
背景更新每 60 秒及喚醒時刷新macOS lockf 避免重複請求;失敗保留舊列並標示未更新
+
+

使用 macOS 內建 jq 處理既有 CodexBar CLI JSON;只把摘要文字留在 SketchyBar 記憶體,不保存帳號 JSON、token 或額外 quota server。摘要使用不透明的 Matugen surface-variant 底色,避免與下方視窗文字混在一起。

+

本機檢查:實際 hover、移出、左右鍵及 CLI 暫時不可用/恢復均已驗證。需要手動重跑真實 CLI 更新時,可執行 SENDER=routine NAME=codexbar ~/.config/sketchybar/plugins/codexbar.sh;失敗會回傳非零狀態,不另建功能測試腳本或加入 CI。

@@ -362,7 +387,7 @@

資料流與責任

┌────────────┐ native aliases │ SketchyBar │ │ Stats.app │ ───────────────▶│ │ └────────────┘ │ │ -┌────────────┐ status alias │ │ +┌────────────┐ alias / JSON │ │ │ CodexBar │ ───────────────▶│ │ └────────────┘ └──────┬──────┘ ▲ @@ -372,7 +397,7 @@

資料流與責任

  • AeroSpace:workspace 真實狀態、切換與變更事件。
  • Stats:系統監控與感測器;不在 SketchyBar 重寫資料採集。
  • -
  • CodexBar:沿用既有 codex = oauth 設定負責登入、用量取得、meter 與 popup;SketchyBar 不讀取 token,也不啟用 browser-cookie source。
  • +
  • CodexBar:沿用既有 codex = oauth 設定負責登入、用量取得、meter 與詳細選單;SketchyBar 以 codexbar usage --provider codex --source oauth --format json --json-only --no-credits 讀取摘要,不直接讀 token 或啟用 browser-cookie source。
  • Matugen:只生成色彩變數,不生成整份 SketchyBar 行為設定。
  • SketchyBar:呈現、排列、capsule、滑鼠互動與 hotload。
@@ -388,7 +413,7 @@

30 個公開實作調查

  • 直接 CodexBar → SketchyBar 的案例很少:showy-quota 功能完整,但只有 18★、4 位公開貢獻者,而且為多 provider、多宿主需求加入快取、renderer 與本機 serve 管理。
  • 精簡案例:fenetikm/dotfiles 每 120 秒執行 CodexBar CLI,以 JSON 檔快取五分鐘,再用 jq 取百分比。
  • 自行抓 provider 不划算:frankroeder/dotfiles 為 AI usage 維護 Lua popup、Python OAuth clients、快取、節流與測試,規模遠超過目前需求。
  • -
  • 最符合現有架構:SketchyBar 官方 alias 已能鏡像第三方 status item;計畫本來就因 Stats aliases 需要 Screen Recording,因此加入 CodexBar 不增加新的權限類型。
  • +
  • 最符合現有架構:SketchyBar 官方 alias 鏡像第三方 status item,CodexBar 與 Stats 共用螢幕錄製授權。2026-09-13 補上原生選單操作後,AeroSpace 另需輔助使用及對 System Events 的自動化授權。
  • CodexBar 顯示方案

    @@ -396,8 +421,8 @@

    CodexBar 顯示方案

    - - + + @@ -464,12 +489,14 @@

    預計變更檔案

    - + + +
    方案新增負擔可靠性決定
    CodexBar 原生 item → SketchyBar aliasCodexBar cask;無 parser、daemon 或額外 script沿用 CodexBar 自己的 meter 與資料來源;需原生 item 能被 query採用
    自製 CodexBar CLI item一支 shell script、jq、last-known-good cache不依賴原生 item;JSON schema 變更時需維護只有 alias 實機失敗才做
    CodexBar 原生 item → SketchyBar aliasCodexBar cask、原生選單 click helper沿用 CodexBar 自己的 meter 與資料來源;需原生 item 能被 querybar meter 採用
    CodexBar CLI → hover 摘要一支 shell script、macOS 內建 jq/lockf;舊摘要留在 bar 記憶體JSON schema 變更時需維護摘要 parser,不重寫登入與用量抓取僅 hover 採用,不替代原生 meter
    showy-quotaBash 4、安裝流程、renderer、快取與 serve lifecycle多 provider、tmux、Zellij 共用時最完整目前不採用
    直接讀 OAuth/私有 endpoint自行處理憑證、refresh、schema、429 與 cache維護面最大排除
    home/dot_config/sketchybar/colors.sh.tera新增Matugen 輸入模板;輸出為未納管的 colors.sh
    home/dot_config/sketchybar/plugins/executable_clock.sh新增每秒更新 Noctalia 格式時鐘。
    home/dot_config/sketchybar/plugins/executable_volume.sh新增處理音量事件、靜音與 3% 滾輪調整。
    home/dot_config/sketchybar/plugins/executable_codexbar.sh條件式新增只有原生 CodexBar alias 在實機不可用時,才以 CLI JSON + last-known-good cache 顯示。
    home/dot_config/sketchybar/plugins/executable_codexbar.sh新增Hover/左鍵/右鍵分流、每分鐘 CLI 摘要更新;失敗保留舊摘要。
    home/dot_config/sketchybar/plugins/executable_native-menu.sh新增以 System Events 開啟 CodexBar 詳細選單與原生控制中心。
    home/dot_config/codexbar/modify_private_config.json沿用維持 Codex 使用 OAuth、Claude 使用 CLI;不把憑證寫入 dotfiles。
    home/dot_config/matugen/config.toml.tmpl修改註冊 SketchyBar 色彩輸出及 reload post-hook。
    home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl修改把新 Matugen 模板 hash 納入重跑條件。
    home/dot_config/aerospace/aerospace.toml修改啟動 SketchyBar、發出 workspace event、預留頂部高度。
    README.md修改記錄 macOS bar、Matugen 與一次性 Stats 設定。
    .github/workflows/test-distros.yaml移除功能測試步驟保留四平台安裝驗證及 -v;刪除 tests/sketchybar.sh
    @@ -489,13 +516,13 @@

    實作階段

  • 將 SketchyBar 與 CodexBar 加入 Brewfile,保留現有 Stats。
  • 將 SketchyBar 目錄加入 macOS-only ignore 規則。
  • 建立最小 sketchybarrc:透明 bar、高度 36、JetBrainsMono Nerd Font、測試 clock item。
  • -
  • 透過 AeroSpace after-startup-command 啟動,避免額外維護一份 LaunchAgent。
  • +
  • 透過 AeroSpace after-startup-command 啟動,避免額外維護一份 LaunchAgent。先檢查程序是否存在再決定啟動/reload,不能依賴 --reload 的 exit code 判斷。
  • 驗收

      -
    • -
    • -
    • +
    • +
    • +
    @@ -535,8 +562,8 @@

    驗收

    驗收

    • -
    • -
    • +
    • +
    @@ -549,16 +576,19 @@

    驗收

  • 啟動 CodexBar,沿用既有 Codex OAuth source、合併多 provider icons,選擇精簡的 meter 或 icon + percent layout。
  • 在 Stats GUI 一次性啟用實際需要的 CPU、GPU、RAM、Network、Sensors 與 Battery menu items。
  • 執行 sketchybar --query default_menu_items,取得 CodexBar、Stats 與 tray 的本機真實 owner/name;不要預先猜測名稱。
  • -
  • 將 CodexBar 原生 item 加為 alias;點擊執行 open -b com.steipete.codexbar,不實作 Accessibility click helper。
  • +
  • 將 CodexBar 原生 item 加為 alias;左鍵以 System Events 按原生 status item。單純 open -b 不會打開用量選單;0.60 的選單關閉時仍存在,需檢查零尺寸狀態。
  • 只加入確定存在的 aliases;網路與系統監控分成兩個 bracket capsule。
  • -
  • 授予 SketchyBar Screen Recording 權限;這同時涵蓋 CodexBar、Stats 與 tray aliases。
  • -
  • 只有 CodexBar 不出現在 query 結果時,才新增 120 秒 CLI fallback;保留最後一次成功資料,失敗顯示 ,不啟動常駐 server。
  • +
  • 登入啟動由 AeroSpace 負責,螢幕錄製授權應給 AeroSpace;終端機手動啟動則另需授權該終端機。新授權後完整重啟程序,只有 reload 不夠。
  • +
  • Hover 以 60 秒 CLI 更新的小摘要呈現;失敗保留舊摘要並警告,不替代 alias、不啟動常駐 server。
  • +
  • 具名 alias 移除 query 的變動 (n) 索引,保留原生圖像顏色;實測 Stats 重啟後不需 reload 就能重新綁定。
  • 驗收

    • -
    • -
    • +
    • +
    • +
    • +

    CodexBar 與 Stats preferences 先保留為一次性 GUI 設定;只有確認跨機器重建需求後,才考慮納管其偏好檔。

    @@ -572,14 +602,16 @@

    驗收

  • 加入每秒 clock script,格式對齊 %B %d %H:%M:%S
  • volume item 訂閱 volume_changemouse.scrolled
  • 滾輪依方向調整 3%,並夾在 0–100;點擊切換靜音。
  • +
  • 最右側設定按鈕以固定 com.apple.menuextra.controlcenter 識別碼開啟原生控制中心;Wi-Fi/勿擾模式仍使用系統原生面板。
  • 驗證內建螢幕、外接螢幕、notch、睡眠喚醒與登入啟動。
  • 隱藏原生 macOS menu bar;只用系統支援的 auto-hide,不加入永久阻擋游標觸發的 hack。
  • 驗收

      -
    • +
    • +
    Checkpoint B:完成後執行 chezmoi diffchezmoi apply,再進行一次乾淨登入驗證。
    @@ -593,6 +625,7 @@

    驗收

  • README 記錄 SketchyBar 架構、CodexBar/Stats 一次性設定與 Screen Recording 權限。
  • 確認 diff 無 token、個人路徑或生成的 colors.sh
  • 以小型提交拆分:套件、核心 bar、主題、aliases、文件。
  • +
  • 同步更新本計畫;四平台 CI 只跑安裝流程,不加入套件功能 mock 測試。保留 -v,確認 PR 最新 commit 的 checks 全過。
  • 驗收

      @@ -613,11 +646,12 @@

      風險與處理

      風險影響處理方式 Stats alias 名稱依機器或版本不同中在實機 query 後才加入;缺少 alias 時略過,不讓 bar 啟動失敗。 - CodexBar 原生 status item 在 macOS 26 未出現在 query中先確認「Allow in the Menu Bar」;仍不可用才啟用 CLI fallback,不修改 Control Center plist、不用私有 API。參見 CodexBar #1109。 + CodexBar 原生 status item 在 macOS 26 未出現在 query中先確認「Allow in the Menu Bar」與 AeroSpace 螢幕錄製授權;CLI 目前只提供 hover 摘要,不宣稱可取代缺失的原生 item。參見 CodexBar #1109。 + CodexBar CLI 更新失敗或回報缺少額度中失敗保留舊摘要並警告;成功但沒有某區間時略過該區間,不把未知額度當 0%。 第三方 showy-quota 維護規模較小且功能過量低不列為必要相依;只有未來要同時供 SketchyBar、tmux、Zellij 顯示多 provider 時重評。 內建與外接螢幕 bar 高度不同中先用 36px;只有測試失敗才加入 per-monitor AeroSpace gap。 Matugen reload 時色彩檔短暫不存在低靜態 config 提供最小 fallback palette。 - 原生 menu bar 在游標頂端再次出現低接受 macOS auto-hide 行為;不使用私有 API 或 SIP hack。 + 原生 menu bar 在游標頂端或控制中心開啟時再次出現低「永遠自動隱藏」不是永不顯示;原生面板開啟時可能蓋住 bar,先點外部收起。重新開機不會改變此設計,不使用私有 API、滑鼠攔截或 SIP hack。 Stats GUI state 尚未納入 chezmoi低README 記錄一次性設定;只有實際跨機重建痛點出現時再自動化。 @@ -629,12 +663,12 @@

      風險與處理

      完成定義

        -
      • +
      • -
      • -
      • +
      • +
    @@ -647,6 +681,9 @@

    實作參考與來源

  • SketchyBar components、brackets 與 aliases
  • SketchyBar 查詢原生 menu item 名稱
  • SketchyBar events
  • +
  • SketchyBar 原生 popup
  • +
  • 實際使用的 Noctalia CodexBar Meter:hover/左鍵/右鍵行為來源
  • +
  • Apple:自動隱藏與顯示選單列
  • AeroSpace 官方 SketchyBar 整合
  • CodexBar 官方 repository 與 CLI
  • CodexBar Codex OAuth/CLI 資料來源
  • From cba4ee999cd66ac04184f1ac97ce9897c27c1387 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 04:20:38 +0800 Subject: [PATCH 17/30] fix(arch): drop redundant LocalSend dependency override --- home/.chezmoidata/packages.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index d51a083..1fc348f 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -36,8 +36,6 @@ packages: - keepassxc - kitty - lazygit - # ponytail: explicit LocalSend dependency until AUR .SRCINFO matches its PKGBUILD. - - libayatana-indicator - libreoffice-fresh - linux-headers - linux-wallpaperengine-git From 1538335efe267d5a9adf475c9f8238cf387e1825 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 04:20:38 +0800 Subject: [PATCH 18/30] feat(sketchybar): include Claude usage in shared hover summary --- README.md | 8 +- .../sketchybar/executable_sketchybarrc | 2 +- .../sketchybar/plugins/executable_codexbar.sh | 26 +++-- sketchybar-plan.html | 110 ++++++++++++++++-- 4 files changed, 120 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 016e4f1..fc6c4a8 100644 --- a/README.md +++ b/README.md @@ -64,20 +64,22 @@ chezmoi init --apply collieiscute -v - Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. - Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: Control Center, 3%-step volume, and active Stats CPU/GPU/RAM/network/sensor/battery items. -- CodexBar remains the OAuth and quota owner. SketchyBar mirrors its native meter and reads its CLI JSON for a small Matugen-colored usage tooltip; no browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. -- Hover shows the cached Codex quota summary; leaving hides it. Left-click opens CodexBar's native detailed menu. Right-click refreshes the summary; background updates run every 60 seconds and on wake. Missing quota windows are omitted, and failures keep the previous rows with a warning. macOS-provided `jq` parses the CLI response and `lockf` prevents overlapping refreshes. +- CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. SketchyBar mirrors one native meter and reads `codexbar usage --provider both` for a Matugen-colored summary; the command honors each provider's configured source. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. +- Hover shows separate Codex and Claude sections in the same tooltip; leaving hides it. Left-click opens CodexBar's native detailed menu, where the Codex/Claude tabs switch providers. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete provider response keeps the last complete summary with a warning instead of presenting partial data as fresh. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. - Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. - Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. After the first `chezmoi apply`: -1. Open CodexBar and Stats once. In CodexBar, enable Codex with OAuth and use one merged status item if multiple providers are enabled. Select its icon-and-percentage style and weekly metric for a readable quota meter; the default icon alone can be hard to distinguish. In Stats, enable the compact items you actually want mirrored. +1. Open CodexBar and Stats once. Enable Codex with OAuth and Claude with CLI, then use one merged status item. The managed CodexBar config sets these sources for existing provider entries; if an app upgrade resets them, reapply `~/.config/codexbar/config.json`. Select an icon-and-percentage style and weekly metric for a readable quota meter. In Stats, enable the compact items you actually want mirrored. 2. Allow **AeroSpace** under **System Settings → Privacy & Security → Screen & System Audio Recording** for login startup. If launching SketchyBar manually from a terminal, allow that terminal too; its permission does not cover AeroSpace. After granting permission, quit SketchyBar and restart AeroSpace to launch a fresh bar; `sketchybar --reload` alone does not refresh TCC permissions. 3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This hides the system bar until the pointer touches the top edge; it does not permanently disable it. No reboot or SIP change is needed. 4. Allow the SketchyBar launcher (AeroSpace, or the terminal for manual launches) to control **System Events** if macOS requests Automation permission. It also needs Accessibility permission for native menu buttons. CI validates installation with `chezmoi init --apply -v` across supported operating systems. Bar interactions are checked on the actual Mac, not with mocked feature-test scripts. +App menus (File/Edit/Window/Help) are not yet integrated, and the native menu bar can still overlap this transparent bar when revealed. This remains open work, not a completed replacement. See [the 30-source comparison and pending choices](sketchybar-plan.html#native-menu-research-title); no menu-coordination daemon or additional launcher has been installed. + If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar. The CLI supplies the hover summary only; it does not replace the native bar meter. ### Hyprland diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index a3e0a6b..85a2aae 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -145,7 +145,7 @@ printf '%s\n' "$menu_items" \ --set codexbar.status \ width=330 \ icon.drawing=off \ - label="Codex · 正在讀取…" \ + label="CodexBar · 正在讀取…" \ label.color="$PRIMARY" \ label.padding_left=12 \ label.padding_right=12 diff --git a/home/dot_config/sketchybar/plugins/executable_codexbar.sh b/home/dot_config/sketchybar/plugins/executable_codexbar.sh index d2df02a..12590aa 100755 --- a/home/dot_config/sketchybar/plugins/executable_codexbar.sh +++ b/home/dot_config/sketchybar/plugins/executable_codexbar.sh @@ -30,20 +30,26 @@ mkdir -p "$cache_dir" || exit 1 exec 9>"$cache_dir/codexbar.lock" /usr/bin/lockf -s -t 0 9 || exit 0 -sketchybar --set codexbar.status label="Codex · 更新中…" -if ! data=$(codexbar usage --provider codex --source oauth --format json --json-only --no-credits 2>/dev/null); then - sketchybar --set codexbar.status label="Codex · 更新失敗,資料未更新" +sketchybar --set codexbar.status label="CodexBar · 更新中…" +# Both providers keep their own configured source: Codex OAuth, Claude CLI. +if ! data=$(codexbar usage --provider both --format json --json-only --no-credits 2>/dev/null); then + sketchybar --set codexbar.status label="CodexBar · 更新失敗,資料未更新" exit 1 fi if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' - (if type == "array" then .[] elif .providers then .providers[] else . end) - | select(.provider == "codex" and .error == null and (.usage | type) == "object") + [(if type == "array" then .[] elif .providers then .providers[] else . end) + | select(.provider == "codex" or .provider == "claude")] + | if (map(.provider) | sort) == ["claude", "codex"] + and all(.error == null and (.usage | type) == "object") + then .[] else error("Incomplete provider response") end + | (if .provider == "codex" then "Codex · OAuth" else "Claude · CLI" end) as $provider | .usage as $usage - | (if .stale == true then "注意:來源回報為快取資料" else empty end), - ([{title: "5 小時", window: $usage.primary}, + | $provider, + (if .stale == true then "注意:來源回報為快取資料" else empty end), + ([{title: ({"300": "5 小時", "10080": "每週"}[($usage.primary.windowMinutes | tostring)] // "主要額度"), window: $usage.primary}, {title: "每週", window: $usage.secondary}, - {title: "每月", window: $usage.tertiary}, + {title: "其他額度", window: $usage.tertiary}, ($usage.extraRateWindows[]? | {title: .title, window: (.window // .)}), ($usage.windows[]? | {title: (.title // .name), window: (.window // .)})] | map(select((.window.usedPercent | type) == "number")) @@ -54,13 +60,13 @@ if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' | "\($title) · 剩餘 \($remaining)%" end) '); then - sketchybar --set codexbar.status label="Codex · 回應無法讀取,資料未更新" + sketchybar --set codexbar.status label="CodexBar · 回應不完整,資料未更新" exit 1 fi # One IPC batch replaces the tooltip rows without flashing an empty popup. set -- --remove '/codexbar\.usage\..*/' \ - --set codexbar.status "label=Codex · 讀取 $(date +%H:%M:%S)" + --set codexbar.status "label=CodexBar · 讀取 $(date +%H:%M:%S)" index=0 while IFS= read -r row; do index=$((index + 1)) diff --git a/sketchybar-plan.html b/sketchybar-plan.html index 447faaf..24dcc85 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -276,7 +276,7 @@

    SketchyBar 導入計畫

    以 SketchyBar 取代原生 menu bar 的主要顯示層,串接 AeroSpace、CodexBar、Stats 與 Matugen;重現 Noctalia 的透明背景、半透明 capsule 與三區配置,而且不停用 SIP。

    - 狀態:本機互動已驗證,完整登入/外接螢幕待驗收 + 狀態:Codex/Claude 互動已驗證;App 選單/雙 bar 共存待決策 平台:macOS 方案:SketchyBar + AeroSpace + CodexBar 更新:2026-09-13 @@ -288,9 +288,10 @@

    SketchyBar 導入計畫

    決策

    -

    採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;bar 上的 Codex 用量鏡像 CodexBar 原生 status item,hover 摘要才讀 CodexBar CLI。SketchyBar 負責排列、Matugen capsule、workspace、時鐘、音量與控制中心入口。

    +

    採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;bar 上鏡像 CodexBar 單一原生 status item,hover 摘要才讀 CLI,分段顯示 Codex OAuth 與 Claude CLI 用量。SketchyBar 負責排列、Matugen capsule、workspace、時鐘、音量與控制中心入口。

    不採用 Simple Bar、SbarLua、大型第三方設定集或 showy-quota。單一 Codex meter 不值得再加入 Bash 4、獨立 renderer、cache daemon 與第二套主題設定。

    CI 只驗證安裝:已移除新增的 tests/sketchybar.sh 與其 CI 步驟。四平台保留 chezmoi init --apply -v;功能在實際 macOS 上操作驗收。最新安裝結果以 PR #155 checks 為準,不把上一個 commit 的成功當成本次成功。

    +

    依賴清理:移除本 MR 額外列出的 libayatana-indicator 與註解。master 已加入 paru --mflags "--syncdeps --noconfirm",由安裝流程依 PKGBUILD 解決缺漏;本 MR 不再重複指定傳遞依賴。AUR 的 PKGBUILD 與 .SRCINFO 當次仍不一致,這不是宣稱 AUR 中繼資料已修復。

    @@ -304,7 +305,7 @@

    要做

    • AeroSpace workspace 1–9 與 magic
    • Workspace 點擊切換與狀態即時更新
    • -
    • CodexBar 原生 meter;hover 小摘要、左鍵原生詳細選單、右鍵刷新摘要
    • +
    • CodexBar 原生 meter;hover 同時摘要 Codex/Claude、左鍵原生詳細選單、右鍵刷新摘要
    • SketchyBar 最右側控制中心按鈕,開啟 macOS 原生 Wi-Fi/專注模式面板
    • 時鐘:月份、日期與秒數
    • 音量顯示、靜音與滾輪每次 3%
    • @@ -320,7 +321,7 @@

      明確不做

      • Media widget
      • Notification badges
      • -
      • 重製 CodexBar 完整詳細面板或多 provider quota strips;僅自製 Codex hover 小摘要
      • +
      • 重製 CodexBar 完整詳細面板或多 provider 常駐 strips;僅在同一 hover 摘要區分 Codex/Claude
      • Session popup
      • Clipboard history
      • Wallpaper Engine/W Engine
      • @@ -365,16 +366,16 @@

        CodexBar 互動分工

        - + - + - +
        操作SketchyBar 行為資料與限制
        Hover元件下方顯示 Matugen 配色的小摘要,不打開原生選單只顯示已讀取的 Codex 額度;缺少的區間略過,不補假數值
        Hover元件下方顯示 Matugen 配色的小摘要,不打開原生選單分段顯示 Codex OAuth 與 Claude CLI 額度;缺少的區間略過,不補假數值
        移開游標收起小摘要摘要不是可操作的大型面板
        左鍵收起摘要,開啟 CodexBar 原生詳細選單完整帳號、重置時間、費用與設定仍由 CodexBar 維護;位置與樣式不完全等同 Noctalia
        左鍵收起摘要,開啟 CodexBar 原生詳細選單,可切 Codex/Claude 分頁帳號、重置時間、費用與設定仍由 CodexBar 維護;CLI 來源能提供的細節以套件為準
        右鍵立即刷新小摘要只重新讀 CodexBar CLI,不改登入來源或開啟其他視窗
        背景更新每 60 秒及喚醒時刷新macOS lockf 避免重複請求;失敗保留舊列並標示未更新
        背景更新每 60 秒及喚醒時刷新兩個 providermacOS lockf 避免重複請求;任一來源失敗或回應不完整時保留上次完整摘要並警告

    使用 macOS 內建 jq 處理既有 CodexBar CLI JSON;只把摘要文字留在 SketchyBar 記憶體,不保存帳號 JSON、token 或額外 quota server。摘要使用不透明的 Matugen surface-variant 底色,避免與下方視窗文字混在一起。

    -

    本機檢查:實際 hover、移出、左右鍵及 CLI 暫時不可用/恢復均已驗證。需要手動重跑真實 CLI 更新時,可執行 SENDER=routine NAME=codexbar ~/.config/sketchybar/plugins/codexbar.sh;失敗會回傳非零狀態,不另建功能測試腳本或加入 CI。

    +

    本機檢查:Codex/Claude 的 hover 摘要與原生分頁均已實際開啟並比對;Claude 當次 5 小時剩餘 100%、每週 96%,兩邊一致。移出、左右鍵及 CLI 暫時不可用/恢復也已驗證。需要手動重跑真實 CLI 更新時,可執行 SENDER=routine NAME=codexbar ~/.config/sketchybar/plugins/codexbar.sh;失敗會回傳非零狀態,不另建功能測試腳本或加入 CI。

    @@ -397,7 +398,7 @@

    資料流與責任

    • AeroSpace:workspace 真實狀態、切換與變更事件。
    • Stats:系統監控與感測器;不在 SketchyBar 重寫資料採集。
    • -
    • CodexBar:沿用既有 codex = oauth 設定負責登入、用量取得、meter 與詳細選單;SketchyBar 以 codexbar usage --provider codex --source oauth --format json --json-only --no-credits 讀取摘要,不直接讀 token 或啟用 browser-cookie source。
    • +
    • CodexBar:來源設定為 codex = oauthclaude = cli;以 codexbar usage --provider both --format json --json-only --no-credits 讀取兩者摘要。0.60 的 CLI 會沿用各自 config source,不套同一個全域 source;SketchyBar 不直接讀 token 或啟用 browser-cookie source。來源選擇實作Claude CLI 限制
    • Matugen:只生成色彩變數,不生成整份 SketchyBar 行為設定。
    • SketchyBar:呈現、排列、capsule、滑鼠互動與 hotload。
    @@ -472,6 +473,90 @@

    CodexBar 顯示方案

    Stars、forks 與排序是調查日快照,會隨 GitHub 狀態變動。另以 CodexBar、showy-quota、fenetikm 與 frankroeder 做焦點抽查,不把低星案例誤當成主要相依。

    +
    +
    +

    App 選單與雙 bar 共存:30 份來源比較

    + 2026-09-13;20 份 dotfiles + 10 份工具/官方參考 +
    +

    App 選單可另外提供入口,兩條 bar 也不必重疊;但這是兩個不同問題。目前設定只鏡像 CodexBar/Stats 等 status items,沒有把前景 App 的 File、Edit、Window、Help 搬進 SketchyBar,也沒有實作原生列展開時的互斥顯示。此部分仍待選定方案,不能算成「完整取代 macOS menu bar」。

    +

    本機唯讀查詢已取得 Kitty 的 Apple、kitty、Shell、Edit、Window、Help,證明資料可以經由輔助使用取得。接下來需要決定的是入口、呈現方式與維護者,而不是把選單命令硬編碼。原生 alias 的範圍可見 官方 components 文件

    + +

    樣本顯示什麼

    +

    20 份 dotfiles 中,8 份含 App 選單相關程式,但多份沿用 Felix 的同一套 helper,而且部分切換入口已註解。其餘 12 份的抽查 bar 入口未見完整 App 選單映射;這不表示其作者不用 App 選單,也不能推論他們全部接受重疊。下表是固定 commit 的靜態查核,不是 20 套在本機逐一安裝的相容性認證。

    +

    Felix 的做法是平常顯示 workspace/App 名稱,點 App 時切換成選單標題;點標題再交給 macOS 開 submenu,不是重製所有選單。這能節省常駐寬度,但原生 submenu 的位置、滑鼠追蹤與選單列顯示仍受系統控制。

    +

    其 C helper使用 Accessibility,也引用私有 SkyLight/SLPS 符號;「不用停用 SIP」不等於「全用穩定公開 API」。這份 dotfiles 雖有 2,028★,GitHub contributors 當次只列 1 個公開帳號;不能把核心 SketchyBar 的維護規模算到個人設定上。

    + +

    可選路線

    +
    + + + + + + + + + + +
    路線App 選單是否解決重疊取捨
    原生快捷鍵Control-F2/Fn-Control-F2,接方向鍵與 Return零依賴的備用入口;原生列仍會展開。Apple 文件
    按需 App 選單模式App 按鈕切換選單,或小型 popup不能單獨保證最接近目前單條 bar 的外觀;須處理長選單、動態項目、前景 App 與瀏海,不能盲目複製 helper。切換入口
    Menuwhere/Raycast現成階層 popup/搜尋命令否;只減少使用頂端原生列的需要完整選單交由產品維護,仍需安裝與授權;目前 Brewfile 未納管。MenuwhereRaycast
    Hammerspoon官方 API 取得並執行選單;自行建立 popup否,另需協調顯示工具成熟但不是免設定插件;只為這個功能加入另一套 Lua 常駐環境並不省事。popup API 與限制
    兩條 bar 交替顯示保留完整原生 App 選單目標是同時只顯示一條需可靠偵測選單生命週期;游標高度猜測不涵蓋所有叫出方式。既有工具
    上下分離/SketchyBar 移到底部保留頂端原生選單以幾何位置避免同區重疊依賴最少、保留原生功能,但不再是目前的單條頂端配置。底部實例
    +
    + +

    為什麼不直接改 topmost

    +

    topmost=windowtopmost=on 不同:本機同版本 2.24.0 原始碼分別設定 floating-window 與 status-window 圖層。它們只改誰畫在上面,不會隱藏下層文字,也不會接管 App 選單。透明 bar 即使畫到最上層,仍可能透出原生文字;改不透明背景則會犧牲現有透明視覺。見 版本相符的實作官方設定

    +

    sketchybar-toggle 的 README 把 window 模式描述成位於原生選單列之上,不能照單全收。它的實作每約 16 ms 讀游標位置與按鍵,不查原生選單是否真的開啟;因此鍵盤或目前 Control Center 按鈕叫出面板時,不一定觸發隱藏。顯示控制也會操作整條 bar 並把 y_offset 重設為 0;多螢幕與自訂位移需要另行驗收。

    +

    此工具當次為 21★、2 個公開 contributor 帳號,不足以證明多人持續維護或全面相容。它證明「不重疊的交替策略有人做」,但不是可直接宣稱成熟、完整、零副作用的答案。滑鼠位置輪詢也不同於攔截滑鼠事件;兩者都不應在未選定方案前加入登入流程。

    + +

    對目前設定的建議

    +

    若維持頂端單條、透明、避開瀏海:優先考慮按需 App 選單入口,且把原生列互斥列為獨立驗收。不要把整排 File/Edit/View/Window/Help 永久塞進目前時鐘與 workspace 旁。若最優先是現成產品與低維護,Menuwhere 類階層 popup 比自行重製完整選單更符合需求;若最優先是零新常駐程序與穩定不重疊,則改採上下分離。這些選擇各有取捨,目前不擅自改 bar 位置、安裝工具或加入私有 API。

    +
      +
    • +
    • +
    • +
    + +
    + 30 份來源與逐項判讀 +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    #來源App 選單原生列/位置判讀與限制
    1FelixKratz/dotfiles
    2,028★
    點 App 名稱切換 menus/spaces;C helper 讀取並 AXPress。bar.lua 未設 topmost;沒有完整的原生列互斥協調。可借鏡按需切換;helper 含私有 SkyLight API。
    2rockyzhang24/dotfiles
    242★
    front_app 顯示名稱/圖示,沒有 Edit/Help 映射。topmost=window。高星外觀設定不等於完整 App 選單。
    3mehd-io/dotfiles
    186★
    點 App 項目開 Mission Control。頂端 40 pt;未設互斥切換。Mission Control 不是 App 選單。
    4m4xshen/dotfiles
    438★
    目前入口沒有 App 選單;front_app plugin 檔不代表已使用。頂端、y_offset=16;沒有原生列狀態協調。位移不等於保證不重疊。
    5falleco/dotfiles
    84★
    Felix 型 menus/spaces 切換,front_app 點擊已接線。topmost=window。沿用同一 helper 家族,非獨立成熟產品。
    6marsdevx/macOS-config
    91★
    有 menus 模組,但 front_app 入口整份註解。topmost=window。不能把存在的 menus.lua 算成預設可點。
    7jellydn/dotfiles
    19★
    載入 menus,但 require(front_app) 已註解。topmost=off。已查入口未提供原本的切換按鈕。
    8r17x/universe
    135★
    Felix 型切換;以 sbar_menus 指令列 helper 操作。topmost=window。Nix 封裝不會自動解決原生列重疊。
    9smnatale/dotfiles
    47★
    Apple 圖示開系統設定,未映射 App 選單。頂端 32 pt;沒有互斥切換。系統設定入口不等於 Edit/Help。
    10haydenrou/dotfiles
    44★
    抽查 bar 入口未見 App 選單映射。54 pt、margin=12;沒有互斥切換。偏狀態與工作區顯示。
    11deniserdogan/dotfiles
    26★
    front_app 與控制項分開,未見 App 選單映射。頂端 38 pt、y_offset=5。有控制中心不代表有 App 選單。
    12eminarican/dotfiles
    24★
    抽查 bar 入口未見完整 App 選單。頂端 40 pt、y_offset=10。2023 年設定;不當作現版 macOS 相容保證。
    13frankroeder/dotfiles
    42★
    多套 top/bottom/island 設定;抽查入口未見完整 App 選單。共用設定 topmost=off,另有底部 bar。分區顯示可借鏡,整套設定負擔較大。
    14slano-ls/SketchyBar
    53★
    有開 Raycast 的入口,沒有直接接 Search Menu Items。頂端 38 pt、topmost=on。開啟 launcher 不等於已解決 App 選單。
    15TheGoldenPatrik1/sketchybar-config
    64★
    Felix 型 menus/spaces 切換,front_app 已接線。topmost=window。2025 年設定;原生 submenu 仍由 macOS 開啟。
    16Kcraft059/sketchybar-config
    43★
    Shell 建立 menu items,以 bundled menubar -s 點擊。設定寫 topmost=everything,非官方文件列值。可參考 shell 路線;不照抄二進位與未文件化設定。
    17Efterklang/sketchybar
    34★
    App 點擊開自繪 popup;AppleScript 讀取/點擊選單。另保留 Felix menus 模組;未見原生列互斥。自繪兩層與 30 項上限,深層/特殊名稱仍需維護。
    18Nikolaidp24/SketchyBar
    34★
    點 App 圖示開 Mission Control,非 App 選單。topmost=window,頂端 45 pt。與 mehd-io 類型相同。
    19slashspace/dotfiles
    19★
    沒有在 bar 重製 App 選單。position=bottom、height=36。以位置分離避開頂端原生列,是明確不同路線。
    20fenetikm/dotfiles
    13★
    狀態/工作區/AI 用量入口,抽查未見完整 App 選單。頂端,依螢幕使用不同高度;未見互斥切換。不能由漂亮 bar 推論作者如何使用每個 App 選單。
    21sketchybar-toggle21★;另建常駐程序,以約 60 Hz 游標位置輪詢交替顯示。不讀原生 menu 的真實開關;控制中心/鍵盤叫出選單是覆蓋範圍缺口。
    22SketchyBar 官方 Bar Propertiestopmost、hidden、position、y_offset 的正式設定。圖層與幾何設定,不是 App 選單移植器。
    23SketchyBar 官方 Componentsalias 鏡像 status items;需螢幕錄製權限。不是 File/Edit/Help 的完整選單 API。
    24SketchyBar 官方討論 #281討論阻擋頂端滑鼠事件,以及後來的切換工具。事件攔截會影響螢幕邊緣操作,不當作穩定預設。
    25SketchyBar 官方討論 #229作者列出 topmost 覆蓋或 yabai menubar_opacity。舊討論不是 macOS 26 實測;侵入式方法不納入本案。
    26Apple 鍵盤快捷鍵Control-F2/Fn-Control-F2 聚焦選單列;另有 Help 快捷鍵。零新套件,可保留完整原生選單;不會協調 SketchyBar。
    27Apple Menu Bar 設定自動隱藏後,游標到頂端仍會顯示。Always 指永遠自動隱藏,不是永遠禁止顯示。
    28Raycast NavigationSearch Menu Bar Items 搜尋並執行前景 App 的選單命令。需要輔助使用;不是 bar 上常駐的 File/Edit 橫列。
    29Hammerspoon application/menubar 官方 APIgetMenuItems/selectMenuItem 提供選單資料與操作。16,091★ 的工具,但 popup 仍須自行接線,並非裝好就有。
    30Menuwhere 官方操作文件快捷鍵或修飾鍵+右鍵,在游標旁顯示 App 的階層選單。現成產品、可處理替代選單項;不能順帶阻止原生 bar 顯示。
    +
    +
    +

    GitHub 數據為 2026-09-13 快照。Dotfiles 連結固定在本次檢查的 commit;星數代表 repository,不代表 menu helper 單獨的使用量或維護人數。未設定原生列偏好的 repository,不推測作者的系統偏好。

    +
    +

    預計變更檔案

    @@ -573,7 +658,7 @@

    驗收

    規模:中相依:階段 3目標:重用既有監控
      -
    1. 啟動 CodexBar,沿用既有 Codex OAuth source、合併多 provider icons,選擇精簡的 meter 或 icon + percent layout。
    2. +
    3. 啟動 CodexBar,來源使用 Codex OAuth/Claude CLI;合併 provider icons,hover 在同一摘要區分兩者,左鍵原生面板可切 provider。
    4. 在 Stats GUI 一次性啟用實際需要的 CPU、GPU、RAM、Network、Sensors 與 Battery menu items。
    5. 執行 sketchybar --query default_menu_items,取得 CodexBar、Stats 與 tray 的本機真實 owner/name;不要預先猜測名稱。
    6. 將 CodexBar 原生 item 加為 alias;左鍵以 System Events 按原生 status item。單純 open -b 不會打開用量選單;0.60 的選單關閉時仍存在,需檢查零尺寸狀態。
    7. @@ -589,6 +674,7 @@

      驗收

    8. +
    9. CodexBar 與 Stats preferences 先保留為一次性 GUI 設定;只有確認跨機器重建需求後,才考慮納管其偏好檔。

    @@ -651,7 +737,7 @@

    風險與處理

    第三方 showy-quota 維護規模較小且功能過量低不列為必要相依;只有未來要同時供 SketchyBar、tmux、Zellij 顯示多 provider 時重評。 內建與外接螢幕 bar 高度不同中先用 36px;只有測試失敗才加入 per-monitor AeroSpace gap。 Matugen reload 時色彩檔短暫不存在低靜態 config 提供最小 fallback palette。 - 原生 menu bar 在游標頂端或控制中心開啟時再次出現低「永遠自動隱藏」不是永不顯示;原生面板開啟時可能蓋住 bar,先點外部收起。重新開機不會改變此設計,不使用私有 API、滑鼠攔截或 SIP hack。 + 原生 menu bar 在頂端、鍵盤或控制中心開啟時重疊中尚未解決;不把重疊當成完成狀態。依30 份來源比較選定 App 選單入口與互斥/位置分離策略,再驗收;SIP 保持啟用。 Stats GUI state 尚未納入 chezmoi低README 記錄一次性設定;只有實際跨機重建痛點出現時再自動化。 @@ -668,7 +754,7 @@

    完成定義

  • -
  • +
  • From 7b797921c5ab37ccedd3ece71771abe3d62b1adc Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 11:23:54 +0800 Subject: [PATCH 19/30] feat(sketchybar): show quota reset countdowns on hover --- README.md | 1 + .../sketchybar/plugins/executable_codexbar.sh | 16 ++++++++++++++-- sketchybar-plan.html | 8 +++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fc6c4a8..ba6c266 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ chezmoi init --apply collieiscute -v - Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: Control Center, 3%-step volume, and active Stats CPU/GPU/RAM/network/sensor/battery items. - CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. SketchyBar mirrors one native meter and reads `codexbar usage --provider both` for a Matugen-colored summary; the command honors each provider's configured source. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. - Hover shows separate Codex and Claude sections in the same tooltip; leaving hides it. Left-click opens CodexBar's native detailed menu, where the Codex/Claude tabs switch providers. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete provider response keeps the last complete summary with a warning instead of presenting partial data as fresh. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. +- Each quota window shows its reset countdown on the next line, keeping the tooltip at 330 px wide. Countdown values use CodexBar's UTC `resetsAt`, round up to minutes, and update with the existing 60-second refresh. Missing or invalid reset times are omitted; an elapsed timestamp says it is waiting for the source to update, rather than claiming the quota has reset. - Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. - Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. diff --git a/home/dot_config/sketchybar/plugins/executable_codexbar.sh b/home/dot_config/sketchybar/plugins/executable_codexbar.sh index 12590aa..bd37c46 100755 --- a/home/dot_config/sketchybar/plugins/executable_codexbar.sh +++ b/home/dot_config/sketchybar/plugins/executable_codexbar.sh @@ -38,7 +38,8 @@ if ! data=$(codexbar usage --provider both --format json --json-only --no-credit fi if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' - [(if type == "array" then .[] elif .providers then .providers[] else . end) + now as $now + | [(if type == "array" then .[] elif .providers then .providers[] else . end) | select(.provider == "codex" or .provider == "claude")] | if (map(.provider) | sort) == ["claude", "codex"] and all(.error == null and (.usage | type) == "object") @@ -57,7 +58,18 @@ if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' else .[] | ((.title // "額度") | tostring | gsub("[\r\n\t]"; " ")) as $title | ([100, ([0, (100 - .window.usedPercent)] | max)] | min | round) as $remaining - | "\($title) · 剩餘 \($remaining)%" + | (try (.window.resetsAt | fromdateiso8601) catch null) as $reset + | "\($title) · 剩餘 \($remaining)%", + (if $reset == null then empty + elif $reset <= $now then "↳ 重置時間已到,等待來源更新" + else (($reset - $now) / 60 | ceil) as $minutes + | (if $minutes >= 1440 then + "\(($minutes / 1440) | floor) 天 \((($minutes % 1440) / 60) | floor) 小時 \($minutes % 60) 分" + elif $minutes >= 60 then + "\(($minutes / 60) | floor) 小時 \($minutes % 60) 分" + else "\($minutes) 分" end) as $duration + | "↳ 約 \($duration)後重置" + end) end) '); then sketchybar --set codexbar.status label="CodexBar · 回應不完整,資料未更新" diff --git a/sketchybar-plan.html b/sketchybar-plan.html index 24dcc85..fa4c113 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -366,7 +366,7 @@

    CodexBar 互動分工

    - + @@ -375,6 +375,7 @@

    CodexBar 互動分工

    操作SketchyBar 行為資料與限制
    Hover元件下方顯示 Matugen 配色的小摘要,不打開原生選單分段顯示 Codex OAuth 與 Claude CLI 額度;缺少的區間略過,不補假數值
    Hover元件下方顯示 Matugen 配色的小摘要;各額度下一行顯示重置倒數,不打開原生選單分段顯示 Codex OAuth 與 Claude CLI;缺少的區間或重置時間略過,不補假數值
    移開游標收起小摘要摘要不是可操作的大型面板
    左鍵收起摘要,開啟 CodexBar 原生詳細選單,可切 Codex/Claude 分頁帳號、重置時間、費用與設定仍由 CodexBar 維護;CLI 來源能提供的細節以套件為準
    右鍵立即刷新小摘要只重新讀 CodexBar CLI,不改登入來源或開啟其他視窗

    使用 macOS 內建 jq 處理既有 CodexBar CLI JSON;只把摘要文字留在 SketchyBar 記憶體,不保存帳號 JSON、token 或額外 quota server。摘要使用不透明的 Matugen surface-variant 底色,避免與下方視窗文字混在一起。

    +

    重置倒數使用 CodexBar 的 RateWindow.resetsAtjq 的 UTC 日期函式計算,向上取整到分鐘,隨既有 60 秒排程更新。例如「↳ 約 4 小時 30 分後重置」;維持 330 px 寬度,以獨立下一行避免長名稱擠掉倒數。無法解析的時間不顯示;期限已過則顯示「重置時間已到,等待來源更新」,不自行推定額度已恢復。

    本機檢查:Codex/Claude 的 hover 摘要與原生分頁均已實際開啟並比對;Claude 當次 5 小時剩餘 100%、每週 96%,兩邊一致。移出、左右鍵及 CLI 暫時不可用/恢復也已驗證。需要手動重跑真實 CLI 更新時,可執行 SENDER=routine NAME=codexbar ~/.config/sketchybar/plugins/codexbar.sh;失敗會回傳非零狀態,不另建功能測試腳本或加入 CI。

    @@ -574,7 +575,7 @@

    預計變更檔案

    home/dot_config/sketchybar/colors.sh.tera新增Matugen 輸入模板;輸出為未納管的 colors.shhome/dot_config/sketchybar/plugins/executable_clock.sh新增每秒更新 Noctalia 格式時鐘。 home/dot_config/sketchybar/plugins/executable_volume.sh新增處理音量事件、靜音與 3% 滾輪調整。 - home/dot_config/sketchybar/plugins/executable_codexbar.sh新增Hover/左鍵/右鍵分流、每分鐘 CLI 摘要更新;失敗保留舊摘要。 + home/dot_config/sketchybar/plugins/executable_codexbar.sh新增Hover/左鍵/右鍵分流、每分鐘 CLI 額度與重置倒數更新;失敗保留舊摘要。 home/dot_config/sketchybar/plugins/executable_native-menu.sh新增以 System Events 開啟 CodexBar 詳細選單與原生控制中心。 home/dot_config/codexbar/modify_private_config.json沿用維持 Codex 使用 OAuth、Claude 使用 CLI;不把憑證寫入 dotfiles。 home/dot_config/matugen/config.toml.tmpl修改註冊 SketchyBar 色彩輸出及 reload post-hook。 @@ -664,7 +665,7 @@

    驗收

  • 將 CodexBar 原生 item 加為 alias;左鍵以 System Events 按原生 status item。單純 open -b 不會打開用量選單;0.60 的選單關閉時仍存在,需檢查零尺寸狀態。
  • 只加入確定存在的 aliases;網路與系統監控分成兩個 bracket capsule。
  • 登入啟動由 AeroSpace 負責,螢幕錄製授權應給 AeroSpace;終端機手動啟動則另需授權該終端機。新授權後完整重啟程序,只有 reload 不夠。
  • -
  • Hover 以 60 秒 CLI 更新的小摘要呈現;失敗保留舊摘要並警告,不替代 alias、不啟動常駐 server。
  • +
  • Hover 以 60 秒 CLI 更新額度與重置倒數;失敗保留舊摘要並警告,不替代 alias、不啟動常駐 server。
  • 具名 alias 移除 query 的變動 (n) 索引,保留原生圖像顏色;實測 Stats 重啟後不需 reload 就能重新綁定。
  • 驗收

    @@ -675,6 +676,7 @@

    驗收

  • +
  • CodexBar 與 Stats preferences 先保留為一次性 GUI 設定;只有確認跨機器重建需求後,才考慮納管其偏好檔。

    From 2bdfd3f955009225c17d2f019dd9bba759e8fd9a Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 13:22:29 +0800 Subject: [PATCH 20/30] feat(sketchybar): group hover quotas into bordered cells --- README.md | 2 +- .../sketchybar/plugins/executable_codexbar.sh | 49 ++++++++++++++----- sketchybar-plan.html | 4 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ba6c266..aadd8e2 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ chezmoi init --apply collieiscute -v - Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: Control Center, 3%-step volume, and active Stats CPU/GPU/RAM/network/sensor/battery items. - CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. SketchyBar mirrors one native meter and reads `codexbar usage --provider both` for a Matugen-colored summary; the command honors each provider's configured source. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. - Hover shows separate Codex and Claude sections in the same tooltip; leaving hides it. Left-click opens CodexBar's native detailed menu, where the Codex/Claude tabs switch providers. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete provider response keeps the last complete summary with a warning instead of presenting partial data as fresh. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. -- Each quota window shows its reset countdown on the next line, keeping the tooltip at 330 px wide. Countdown values use CodexBar's UTC `resetsAt`, round up to minutes, and update with the existing 60-second refresh. Missing or invalid reset times are omitted; an elapsed timestamp says it is waiting for the source to update, rather than claiming the quota has reset. +- Each quota window is a bordered, two-line native item: remaining quota above, reset countdown below, with bold Codex/Claude headings. Borders and secondary text follow the Matugen palette; the tooltip keeps its existing width. Countdown values use CodexBar's UTC `resetsAt`, round up to minutes, and update with the existing 60-second refresh. Missing or invalid reset times are omitted and leave a compact single-line cell; an elapsed timestamp says it is waiting for the source to update, rather than claiming the quota has reset. - Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. - Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. diff --git a/home/dot_config/sketchybar/plugins/executable_codexbar.sh b/home/dot_config/sketchybar/plugins/executable_codexbar.sh index bd37c46..5ada3aa 100755 --- a/home/dot_config/sketchybar/plugins/executable_codexbar.sh +++ b/home/dot_config/sketchybar/plugins/executable_codexbar.sh @@ -46,21 +46,21 @@ if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' then .[] else error("Incomplete provider response") end | (if .provider == "codex" then "Codex · OAuth" else "Claude · CLI" end) as $provider | .usage as $usage - | $provider, - (if .stale == true then "注意:來源回報為快取資料" else empty end), + | (["header", $provider], + (if .stale == true then ["note", "注意:來源回報為快取資料"] else empty end), ([{title: ({"300": "5 小時", "10080": "每週"}[($usage.primary.windowMinutes | tostring)] // "主要額度"), window: $usage.primary}, {title: "每週", window: $usage.secondary}, {title: "其他額度", window: $usage.tertiary}, ($usage.extraRateWindows[]? | {title: .title, window: (.window // .)}), ($usage.windows[]? | {title: (.title // .name), window: (.window // .)})] | map(select((.window.usedPercent | type) == "number")) - | if length == 0 then "目前沒有回報額度區間" + | if length == 0 then ["note", "目前沒有回報額度區間"] else .[] | ((.title // "額度") | tostring | gsub("[\r\n\t]"; " ")) as $title | ([100, ([0, (100 - .window.usedPercent)] | max)] | min | round) as $remaining | (try (.window.resetsAt | fromdateiso8601) catch null) as $reset - | "\($title) · 剩餘 \($remaining)%", - (if $reset == null then empty + | ["quota", "\($title) · 剩餘 \($remaining)%", + (if $reset == null then "" elif $reset <= $now then "↳ 重置時間已到,等待來源更新" else (($reset - $now) / 60 | ceil) as $minutes | (if $minutes >= 1440 then @@ -69,22 +69,49 @@ if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' "\(($minutes / 60) | floor) 小時 \($minutes % 60) 分" else "\($minutes) 分" end) as $duration | "↳ 約 \($duration)後重置" - end) - end) + end)] + end)) + | join("\t") '); then sketchybar --set codexbar.status label="CodexBar · 回應不完整,資料未更新" exit 1 fi -# One IPC batch replaces the tooltip rows without flashing an empty popup. +CONFIG_DIR=${CONFIG_DIR:-"$HOME/.config/sketchybar"} +ON_SURFACE=0xfff2f0f4 +PRIMARY=0xffd0bcff +test -r "$CONFIG_DIR/colors.sh" && . "$CONFIG_DIR/colors.sh" + +# One native item holds both lines, so its border encloses the whole quota. +# https://felixkratz.github.io/SketchyBar/config/items#background-properties +# One IPC batch replaces the tooltip without flashing an empty popup. set -- --remove '/codexbar\.usage\..*/' \ --set codexbar.status "label=CodexBar · 讀取 $(date +%H:%M:%S)" index=0 -while IFS= read -r row; do +while IFS="$(printf '\t')" read -r kind title reset; do index=$((index + 1)) set -- "$@" --add item "codexbar.usage.$index" popup.codexbar \ - --set "codexbar.usage.$index" width=330 icon.drawing=off \ - "label=$row" label.max_chars=40 label.padding_left=12 label.padding_right=12 + --set "codexbar.usage.$index" width=314 padding_left=10 padding_right=10 \ + icon.drawing=off "label=$title" label.max_chars=40 \ + label.padding_left=10 label.padding_right=10 + case "$kind" in + header) + set -- "$@" label.color="$PRIMARY" label.font.style=Bold + ;; + quota) + set -- "$@" icon.drawing=on "icon=$title" \ + icon.font="JetBrainsMono Nerd Font:Regular:13.0" \ + icon.width=0 icon.padding_left=10 icon.padding_right=0 icon.max_chars=40 \ + label="$reset" label.font.size=12 label.color="0xcc${ON_SURFACE#????}" \ + background.drawing=on background.color="0x08${ON_SURFACE#????}" \ + background.border_width=1 background.border_color="0x55${ON_SURFACE#????}" \ + background.corner_radius=6 background.height=52 \ + icon.y_offset=11 label.y_offset=-11 + if test -z "$reset"; then + set -- "$@" icon.y_offset=0 label.drawing=off background.height=32 + fi + ;; + esac done <CodexBar 互動分工 - + @@ -376,6 +376,7 @@

    CodexBar 互動分工

    使用 macOS 內建 jq 處理既有 CodexBar CLI JSON;只把摘要文字留在 SketchyBar 記憶體,不保存帳號 JSON、token 或額外 quota server。摘要使用不透明的 Matugen surface-variant 底色,避免與下方視窗文字混在一起。

    重置倒數使用 CodexBar 的 RateWindow.resetsAtjq 的 UTC 日期函式計算,向上取整到分鐘,隨既有 60 秒排程更新。例如「↳ 約 4 小時 30 分後重置」;維持 330 px 寬度,以獨立下一行避免長名稱擠掉倒數。無法解析的時間不顯示;期限已過則顯示「重置時間已到,等待來源更新」,不自行推定額度已恢復。

    +

    每個額度使用一個 SketchyBar 原生 item 的背景與框線,透過 icon/label 的垂直偏移放入兩行文字;不是另外開視窗或畫圖片。方格採 1 pt 框線、6 pt 圓角及 Matugen 衍生色,倒數字級較小;沒有重置時間時收成單行格。外框寬度保持不變。

    本機檢查:Codex/Claude 的 hover 摘要與原生分頁均已實際開啟並比對;Claude 當次 5 小時剩餘 100%、每週 96%,兩邊一致。移出、左右鍵及 CLI 暫時不可用/恢復也已驗證。需要手動重跑真實 CLI 更新時,可執行 SENDER=routine NAME=codexbar ~/.config/sketchybar/plugins/codexbar.sh;失敗會回傳非零狀態,不另建功能測試腳本或加入 CI。

    @@ -677,6 +678,7 @@

    驗收

  • +
  • CodexBar 與 Stats preferences 先保留為一次性 GUI 設定;只有確認跨機器重建需求後,才考慮納管其偏好檔。

    From f75cb59f43bd32154ceabd8b0d38dc262b8778f1 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 18:21:35 +0800 Subject: [PATCH 21/30] feat(sketchybar): alternate visibility with native menu bar --- README.md | 8 +- ...ange_after_3-build-sketchybar-menu.sh.tmpl | 18 +++ .../sketchybar/executable_sketchybarrc | 17 +++ .../sketchybar/menu-bar-events.swift | 79 ++++++++++++ sketchybar-plan.html | 115 ++++++++++++++++-- 5 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl create mode 100644 home/dot_config/sketchybar/menu-bar-events.swift diff --git a/README.md b/README.md index aadd8e2..feb1df3 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,9 @@ chezmoi init --apply collieiscute -v - CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. SketchyBar mirrors one native meter and reads `codexbar usage --provider both` for a Matugen-colored summary; the command honors each provider's configured source. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. - Hover shows separate Codex and Claude sections in the same tooltip; leaving hides it. Left-click opens CodexBar's native detailed menu, where the Codex/Claude tabs switch providers. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete provider response keeps the last complete summary with a warning instead of presenting partial data as fresh. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. - Each quota window is a bordered, two-line native item: remaining quota above, reset countdown below, with bold Codex/Claude headings. Borders and secondary text follow the Matugen palette; the tooltip keeps its existing width. Countdown values use CodexBar's UTC `resetsAt`, round up to minutes, and update with the existing 60-second refresh. Missing or invalid reset times are omitted and leave a compact single-line cell; an elapsed timestamp says it is waiting for the source to update, rather than claiming the quota has reset. -- Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. Control Center can temporarily reveal the system menu bar; click outside to dismiss it before using bar buttons again. +- Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. +- `sb-menu-events` listens for the public Carbon `kEventMenuBarShown` / `kEventMenuBarHidden` broadcasts and alternates the two bars. It also covers the top screen edge and system keyboard shortcuts, without polling the cursor, intercepting input, or changing SIP. If any display still has a native menu bar, SketchyBar stays hidden. Closing a panel with Esc does not always hide the native bar immediately; SketchyBar returns when macOS actually retracts it. +- Chezmoi compiles `menu-bar-events.swift` with the existing Xcode Command Line Tools. SketchyBar starts one locked listener, re-synchronizes on reload, and stops the listener when the main bar exits. A supervising shell restores SketchyBar if the listener crashes; reload to restart coordination. No separate LaunchAgent or third-party package is installed. - Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. After the first `chezmoi apply`: @@ -79,7 +81,9 @@ After the first `chezmoi apply`: CI validates installation with `chezmoi init --apply -v` across supported operating systems. Bar interactions are checked on the actual Mac, not with mocked feature-test scripts. -App menus (File/Edit/Window/Help) are not yet integrated, and the native menu bar can still overlap this transparent bar when revealed. This remains open work, not a completed replacement. See [the 30-source comparison and pending choices](sketchybar-plan.html#native-menu-research-title); no menu-coordination daemon or additional launcher has been installed. +App menu buttons (File/Edit/Window/Help) and direct Stats CPU/RAM popup clicks are not yet integrated. Native menus remain available through the top edge or Control-F2 / Fn-Control-F2, with alternating visibility. See [the plan and acceptance status](sketchybar-plan.html#native-integration-tasks); external-monitor, sleep and login checks remain separate from the installation CI. + +For a live coordination check, open Control Center with Fn-C and run `sketchybar --query bar | jq -e '.hidden == "on"'`. After dismissing it and moving the pointer away so the native bar retracts, the same check with `"off"` must succeed. This checks the running Mac, not a mocked feature test. If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar. The CLI supplies the hover summary only; it does not replace the native bar meter. diff --git a/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl new file mode 100644 index 0000000..e454cde --- /dev/null +++ b/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl @@ -0,0 +1,18 @@ +{{- if eq .chezmoi.os "darwin" -}} +#!/bin/sh +set -eu +# helper: {{ include "dot_config/sketchybar/menu-bar-events.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" + +# 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 }} diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 85a2aae..feb3c9b 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -179,6 +179,23 @@ 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 + if command -v aerospace >/dev/null 2>&1; then focused=$(aerospace list-workspaces --focused 2>/dev/null | head -n 1) test -z "$focused" || sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE="$focused" diff --git a/home/dot_config/sketchybar/menu-bar-events.swift b/home/dot_config/sketchybar/menu-bar-events.swift new file mode 100644 index 0000000..b333ed9 --- /dev/null +++ b/home/dot_config/sketchybar/menu-bar-events.swift @@ -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() diff --git a/sketchybar-plan.html b/sketchybar-plan.html index 5b9e279..f8098c3 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -276,7 +276,7 @@

    SketchyBar 導入計畫

    以 SketchyBar 取代原生 menu bar 的主要顯示層,串接 AeroSpace、CodexBar、Stats 與 Matugen;重現 Noctalia 的透明背景、半透明 capsule 與三區配置,而且不停用 SIP。

    - 狀態:Codex/Claude 互動已驗證;App 選單/雙 bar 共存待決策 + 狀態:交替顯示已接線;Stats CPU/RAM 與 App 按鈕待實作 平台:macOS 方案:SketchyBar + AeroSpace + CodexBar 更新:2026-09-13 @@ -289,6 +289,7 @@

    SketchyBar 導入計畫

    決策

    採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;bar 上鏡像 CodexBar 單一原生 status item,hover 摘要才讀 CLI,分段顯示 Codex OAuth 與 Claude CLI 用量。SketchyBar 負責排列、Matugen capsule、workspace、時鐘、音量與控制中心入口。

    +

    2026-09-13 新增已確認範圍:點 CPU/RAM 開啟各自的 Stats 詳細面板、從 SketchyBar 操作前景 App 選單,且原生選單列與 SketchyBar 不互相覆蓋。交替顯示已透過公開 Carbon 事件接線;CPU/RAM 詳細面板與 App 按鈕仍待實作。控制中心替代品比較見下方調查:保留原生控制中心,不新增第三方套件。交替顯示不等於原生列永遠不出現。

    不採用 Simple Bar、SbarLua、大型第三方設定集或 showy-quota。單一 Codex meter 不值得再加入 Bash 4、獨立 renderer、cache daemon 與第二套主題設定。

    CI 只驗證安裝:已移除新增的 tests/sketchybar.sh 與其 CI 步驟。四平台保留 chezmoi init --apply -v;功能在實際 macOS 上操作驗收。最新安裝結果以 PR #155 checks 為準,不把上一個 commit 的成功當成本次成功。

    依賴清理:移除本 MR 額外列出的 libayatana-indicator 與註解。master 已加入 paru --mflags "--syncdeps --noconfirm",由安裝流程依 PKGBUILD 解決缺漏;本 MR 不再重複指定傳遞依賴。AUR 的 PKGBUILD 與 .SRCINFO 當次仍不一致,這不是宣稱 AUR 中繼資料已修復。

    @@ -310,6 +311,9 @@

    要做

  • 時鐘:月份、日期與秒數
  • 音量顯示、靜音與滾輪每次 3%
  • Stats aliases:網路、CPU、GPU、RAM、溫度、電池
  • +
  • 補完 CPU/RAM 點擊:打開對應 Stats 原生詳細面板,不只是啟動 Stats
  • +
  • 前景 App 按鈕:按需顯示 Apple/kitty/Shell/Edit/Window/Help 等真實選單
  • +
  • 原生選單列與 SketchyBar 互斥顯示,涵蓋滑鼠、鍵盤及各原生面板入口
  • 必要的第三方 tray/menu extras aliases
  • Matugen wallpaper palette 與即時重載
  • 透明 bar、30% surface-variant capsule、無陰影
  • @@ -337,8 +341,9 @@

    明確不做

    目標配置

    約 36px 高,外層完全透明 -

    圖為區域示意。實機使用 SketchyBar 原生 q/e 將時鐘、workspace 分置瀏海兩側,notch_width=200;內建螢幕瀏海實測 x=646–825 pt,時鐘右緣 630、workspace 左緣 837。Alias 保留原生圖像色彩,避免把不透明 Stats 圖表染成黑塊。

    +

    App 選單入口是新增規劃,尚未出現在實機;只保留一個精簡按鈕,長選單按需展開,不把整排選單常駐塞到瀏海旁。新增後須重新量測空間,不能沿用舊截圖宣稱驗收完成。

    @@ -403,6 +409,7 @@

    資料流與責任

  • CodexBar:來源設定為 codex = oauthclaude = cli;以 codexbar usage --provider both --format json --json-only --no-credits 讀取兩者摘要。0.60 的 CLI 會沿用各自 config source,不套同一個全域 source;SketchyBar 不直接讀 token 或啟用 browser-cookie source。來源選擇實作Claude CLI 限制
  • Matugen:只生成色彩變數,不生成整份 SketchyBar 行為設定。
  • SketchyBar:呈現、排列、capsule、滑鼠互動與 hotload。
  • +
  • 原生互動接線:沿用 native-menu.sh 作為面板入口;sb-menu-events 獨立接收 macOS 原生列顯示/隱藏事件,所以鍵盤與頂端滑鼠也能交替。Stats CPU/RAM 與 App 按鈕尚未接完;詳細內容仍交由原生面板維護。
  • @@ -480,8 +487,8 @@

    CodexBar 顯示方案

    App 選單與雙 bar 共存:30 份來源比較

    2026-09-13;20 份 dotfiles + 10 份工具/官方參考 -

    App 選單可另外提供入口,兩條 bar 也不必重疊;但這是兩個不同問題。目前設定只鏡像 CodexBar/Stats 等 status items,沒有把前景 App 的 File、Edit、Window、Help 搬進 SketchyBar,也沒有實作原生列展開時的互斥顯示。此部分仍待選定方案,不能算成「完整取代 macOS menu bar」。

    -

    本機唯讀查詢已取得 Kitty 的 Apple、kitty、Shell、Edit、Window、Help,證明資料可以經由輔助使用取得。接下來需要決定的是入口、呈現方式與維護者,而不是把選單命令硬編碼。原生 alias 的範圍可見 官方 components 文件

    +

    App 選單入口與兩條 bar 不重疊,已由使用者確認都要做。交替顯示現已接線,原生列展開時 SketchyBar 暫時隱藏;不是把原生控制中心變成脫離選單列的獨立視窗。App 按鈕仍未實作,CPU/RAM 點擊仍是 open -a Stats。以下保留方案依據,進度見階段 4A–4D

    +

    本機唯讀查詢已取得 Kitty 的 Apple、kitty、Shell、Edit、Window、Help,證明資料可以經由輔助使用取得。規劃採前景 App 按鈕按需列出真實選單,再開原生子選單;不硬編碼命令,也不將 alias 誤當 App 選單 API。原生 alias 的範圍可見 官方 components 文件

    樣本顯示什麼

    20 份 dotfiles 中,8 份含 App 選單相關程式,但多份沿用 Felix 的同一套 helper,而且部分切換入口已註解。其餘 12 份的抽查 bar 入口未見完整 App 選單映射;這不表示其作者不用 App 選單,也不能推論他們全部接受重疊。下表是固定 commit 的靜態查核,不是 20 套在本機逐一安裝的相容性認證。

    @@ -507,12 +514,13 @@

    為什麼不直接改 topmost

    topmost=windowtopmost=on 不同:本機同版本 2.24.0 原始碼分別設定 floating-window 與 status-window 圖層。它們只改誰畫在上面,不會隱藏下層文字,也不會接管 App 選單。透明 bar 即使畫到最上層,仍可能透出原生文字;改不透明背景則會犧牲現有透明視覺。見 版本相符的實作官方設定

    sketchybar-toggle 的 README 把 window 模式描述成位於原生選單列之上,不能照單全收。它的實作每約 16 ms 讀游標位置與按鍵,不查原生選單是否真的開啟;因此鍵盤或目前 Control Center 按鈕叫出面板時,不一定觸發隱藏。顯示控制也會操作整條 bar 並把 y_offset 重設為 0;多螢幕與自訂位移需要另行驗收。

    此工具當次為 21★、2 個公開 contributor 帳號,不足以證明多人持續維護或全面相容。它證明「不重疊的交替策略有人做」,但不是可直接宣稱成熟、完整、零副作用的答案。滑鼠位置輪詢也不同於攔截滑鼠事件;兩者都不應在未選定方案前加入登入流程。

    +

    永久隱藏的查核補充:yabai 的 menubar_opacity 0.0使原生列不回應滑鼠,但原生全螢幕有恢復顯示的例外;實作直接呼叫私有 SkyLight API,不能一概說這項功能需要停用 SIP。它在本機 SIP 啟用下的相容性尚未驗證,也不會自動移植選單,因此不列入預設方案。zapmenu則限制游標進入頂端 4 px;當次只有 4★、1 個公開貢獻帳號,且仍保留鍵盤叫出原生列的路徑,不是完整互斥方案。

    對目前設定的建議

    -

    若維持頂端單條、透明、避開瀏海:優先考慮按需 App 選單入口,且把原生列互斥列為獨立驗收。不要把整排 File/Edit/View/Window/Help 永久塞進目前時鐘與 workspace 旁。若最優先是現成產品與低維護,Menuwhere 類階層 popup 比自行重製完整選單更符合需求;若最優先是零新常駐程序與穩定不重疊,則改採上下分離。這些選擇各有取捨,目前不擅自改 bar 位置、安裝工具或加入私有 API。

    +

    採用方向:按需 App 選單入口,原生列展開期間與 SketchyBar 交替顯示。實機確認公開 Carbon 的 kEventMenuBarShownkEventMenuBarHidden 會通知背景程序,已用於交替接線;不改 topmost、不封鎖游標。Menuwhere 保留為 App 選單入口的候選,不預先加入依賴。多螢幕/重新登入等尚未實際完成的驗收仍保留未勾選。

      -
    • -
    • +
    • +
    @@ -559,6 +567,33 @@

    對目前設定的建議

    GitHub 數據為 2026-09-13 快照。Dotfiles 連結固定在本次檢查的 commit;星數代表 repository,不代表 menu helper 單獨的使用量或維護人數。未設定原生列偏好的 repository,不推測作者的系統偏好。

    +
    +
    +

    控制中心替代品:8 條路線比較

    + 2026-09-13;官方文件/上游原始碼查核,未安裝候選 +
    +

    結論:尚未找到同時比原生面板好串、功能不缺、又不增加明顯維護負擔的完整替代品。OnlySwitch 最接近現成開源控制面板;BetterTouchTool 的外部呼叫介面較完整,但需要自行組裝面板。現階段建議維持「SketchyBar 按鈕 → 原生控制中心」,把精力放在互斥顯示,而不是再維護一套系統控制介面。

    +
    +
    操作SketchyBar 行為資料與限制
    Hover元件下方顯示 Matugen 配色的小摘要;各額度下一行顯示重置倒數,不打開原生選單分段顯示 Codex OAuth 與 Claude CLI;缺少的區間或重置時間略過,不補假數值
    Hover元件下方顯示 Matugen 配色的小摘要;各額度以框線分格,格內上行是剩餘比例、下行是重置倒數,不打開原生選單以粗體標題分組 Codex OAuth 與 Claude CLI;缺少的區間或重置時間略過,不補假數值
    移開游標收起小摘要摘要不是可操作的大型面板
    左鍵收起摘要,開啟 CodexBar 原生詳細選單,可切 Codex/Claude 分頁帳號、重置時間、費用與設定仍由 CodexBar 維護;CLI 來源能提供的細節以套件為準
    右鍵立即刷新小摘要只重新讀 CodexBar CLI,不改登入來源或開啟其他視窗
    + + + + + + + + + + + +
    候選如何接 SketchyBar完整度/缺口維護與決定
    macOS 控制中心沿用現有 AXIdentifier 點擊入口;系統另有 Fn-C保留 Wi-Fi、Bluetooth、Focus、音量、顯示器、螢幕鏡像等原生操作與隱私狀態;面板位置/外觀由 macOS 決定。兩種入口在本機都會帶出原生列。Apple 隨系統維護、零新增第三方套件。保留;由小型 Carbon listener 協調交替顯示。
    OnlySwitch/Only Controldeep link 可執行開關,另可設定全域快捷鍵開面板;SketchyBar 需送此快捷鍵或做 AX 點擊。未找到文件化的「直接顯示面板」URL;不能把執行開關當成開啟面板。有 Bluetooth、音訊及外觀開關;Wi-Fi Switch 在 Evolution gallery、勿擾在 Shortcuts gallery,不等於完整 Wi-Fi 網路選擇/Focus 面板。沒有查到 Matugen 匯入介面。5,914★、25 個公開貢獻帳號;2.7.4,2026-09-11。主要提交仍集中作者,貢獻者含翻譯。保留候選,不預設安裝
    One Switch原生 status item/快捷操作;本次官方產品頁未找到可直接叫出面板的文件化 CLI/URL。現成開關集合:AirPods、深色模式、防睡眠等;沒有足夠證據視為完整網路/Focus/控制中心替代。商業產品,無可比的核心原始碼 stars/貢獻人數。不為未證實的串接優勢付出新依賴。
    BetterTouchTool Floating Menusbtt://、AppleScript 可觸發已設定的顯示/隱藏面板動作;可指定游標或螢幕位置。可自訂按鈕、slider、子選單;不是裝好就有完整控制中心。資料、動作、Matugen 同步與面板設定仍要自行組裝。商業產品、文件完整,但目前未安裝。只有未來另需大量 BTT 自動化時才值得重評。
    Raycastraycast:// 可叫出已啟用命令;文件列有執行確認提示,不能假設無提示。有音量、Bluetooth 等命令;核心是搜尋/執行操作,不是一個完整控制中心。Wi-Fi/Focus 等需逐項確認命令、extension 或 Shortcuts。核心為商業產品,extension 社群不等於面板核心維護人數。只為控制中心引入 launcher 不划算。
    Supercharge官方建議透過 Shortcuts 執行動作;目前 FAQ 明確表示自訂 URL scheme 尚未支援。音訊裝置切換、靜音、顯示開關等便利功能,不是完整控制中心;官方也不打算做成完整 toggler。獨立開發者商業產品,無可比核心 stars。與本案需求差距較大,不採用。
    SketchyBar 自繪 popup
    Felix Wi-Fideniserdogan 控制中心
    直接使用原生 popup/slider,與現有 Matugen 色彩最容易整合。Felix 的 Wi-Fi 是資訊/複製;denis 的 Network 是開系統設定、CPU/RAM 開 Activity Monitor,另有音量 slider。不是完整 Wi-Fi/Bluetooth 選擇器。前者個人 dotfiles 2,028★、後者 26★;不是獨立控制中心產品。重寫採集還會重複 Stats;不整套搬入。
    Aegis本身就是 menu bar/notch shell,非 SketchyBar 控制中心插件。提供 workspace、系統狀態與 HUD;與已選的 SketchyBar 架構重疊,還包含不需要的 media/notification 功能。42★、1 個公開貢獻帳號;最後 push 2026-08-20。排除整套換 bar 的路線。
    + +

    Stars/公開貢獻帳號來自當日 GitHub API,並不等於持續核心維護人數;商業產品不拿無關的 extensions repository 星數充數。以上是文件與原始碼比較,尚未逐一做本機相容性測試;不承諾所有候選功能只用公開 API 或免額外授權。SIP 必須保持啟用。

    +

    OnlySwitch 是否真的更省事?

    +

    若只需要幾個開關,它有價值;但目前需求是從 SketchyBar 進入完整設定面板。上游面板仍有自己的 status item、快捷鍵與顯示生命週期,不會替我們協調 App 選單、Stats 和原生列。若之後只缺一個快捷動作,先用 Apple 內建 shortcuts run,不為此安裝另一個常駐程式;目前不新增任何快捷動作。

    +

    替換門檻:有可靠的面板呼叫方式、在本機 SIP 啟用時可用、Wi-Fi 網路/Bluetooth 裝置/Focus 模式與期限/音訊輸出/亮度及鏡像等實際需要的操作逐項無缺漏,而且比保留原生面板更省維護。任何缺項要明列原生備援,不能宣稱完全取代。原生詳細面板沿用系統外觀;Matugen 僅保證 SketchyBar 自有按鈕與 popup。

    + +

    預計變更檔案

    @@ -577,7 +612,9 @@

    預計變更檔案

    home/dot_config/sketchybar/plugins/executable_clock.sh新增每秒更新 Noctalia 格式時鐘。 home/dot_config/sketchybar/plugins/executable_volume.sh新增處理音量事件、靜音與 3% 滾輪調整。 home/dot_config/sketchybar/plugins/executable_codexbar.sh新增Hover/左鍵/右鍵分流、每分鐘 CLI 額度與重置倒數更新;失敗保留舊摘要。 - home/dot_config/sketchybar/plugins/executable_native-menu.sh新增以 System Events 開啟 CodexBar 詳細選單與原生控制中心。 + home/dot_config/sketchybar/plugins/executable_native-menu.sh沿用並擴充目前開 CodexBar/控制中心;接下來加入 Stats CPU/RAM、App 選單。交替顯示由共用 listener 處理。 + home/dot_config/sketchybar/menu-bar-events.swift新增公開 Carbon 事件 listener;操作 SketchyBar hidden,重載/喚醒同步,不攔截滑鼠。 + home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl新增僅 macOS 編譯至 ~/.local/bin/sb-menu-events,由現有四平台安裝 CI 覆蓋編譯流程。 home/dot_config/codexbar/modify_private_config.json沿用維持 Codex 使用 OAuth、Claude 使用 CLI;不把憑證寫入 dotfiles。 home/dot_config/matugen/config.toml.tmpl修改註冊 SketchyBar 色彩輸出及 reload post-hook。 home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl修改把新 Matugen 模板 hash 納入重跑條件。 @@ -684,17 +721,66 @@

    驗收

    +
    + 階段 4A–4D:補完原生互動與不重疊(4A 已接線,其餘仍待完成) +
    +
    規模:中;互斥偵測有未知風險相依:現有 aliases 可見目標:能操作且不覆蓋
    +

    4A:公開事件交替顯示(已接線)

    +
      +
    1. menu-bar-events.swift 接收公開 Carbon kEventMenuBarShownkEventMenuBarHidden,分別隱藏/恢復 SketchyBar;Apple SDK 的 CarbonEvents.h 明確記載這兩種事件會通知所有已註冊程序。本機 Fn-C、頂端滑鼠、Control-F2/Esc 已取得成對事件。
    2. +
    3. 初始、reload、喚醒、Space/螢幕變更時,用公開 CGWindowListCopyWindowInfo 同步目前可見的 Window Server Menubar。NSMenu.menuBarVisible() 只描述呼叫的 App,本機背景查詢始終為 false,不能用來判斷前景列。
    4. +
    5. 由 SketchyBar 設定檔啟動一個有 lockf 鎖的 listener,包含父程序並選最早啟動的 SketchyBar PID;不把短命 CLI 當主程序。主 bar 退出時 listener 也退出,外層 shell 在 helper 錯誤/崩潰時恢復 bar;正常 reload 不建立重複 listener。
    6. +
    +

    檔案:menu-bar-events.swiftexecutable_sketchybarrc 與 macOS-only 編譯 hook;原有按鈕腳本不必各加一份判斷。無固定頻率輪詢、無 event tap、無私有 SkyLight、無新增第三方套件或 LaunchAgent。listener 在本機閒置時觀察到 CPU 0.0%、RSS 約 31 MiB;這是一次觀察,不是效能保證。

    +
    Checkpoint C:原生列是否可見才是恢復依據。Esc 有時只關面板、未收起原生列,此時維持 SketchyBar 隱藏是正確行為;游標離開、系統收起後才恢復。任何螢幕仍顯示原生列時,先讓所有 SketchyBar 顯示區一起暫避;多螢幕實際驗收仍待完成。helper 崩潰時優先恢復可用的 bar,需 reload 才重新啟動交替。
    +
      +
    • +
    • +
    • +
    +

    以上是實機操作與畫面/狀態查核,不宣稱 compositor 層級逐幀原子切換。外接螢幕、全螢幕、睡眠喚醒、重新登入與主 bar 重啟仍列後續驗收;既有四平台安裝 CI 不替代這些操作。

    + +

    4B:Stats CPU/RAM 接完整

    +
      +
    1. 取得本機 CPU/RAM 對應 status item 的穩定識別資訊,優先使用上游既有入口;需要 AX 點擊時不依賴易變索引,名稱相同/模組缺少時不得點到其他項目。
    2. +
    3. 取代目前 CPU/RAM 的 open -a Stats:CPU 開 CPU 詳細面板、RAM 開記憶體詳細面板,保留 Stats 原本提供的圖表、詳細數據與設定入口。不新增 CPU/mem polling 或自行重製面板。
    4. +
    5. 整合 4A 的互斥方式,驗證開啟、切換、關閉與 Stats 重啟後重新綁定;不得影響其他 Stats aliases 或 Codex/Claude hover。
    6. +
    +

    相依:4A;檔案:executable_sketchybarrcplugins/executable_native-menu.sh。本階段只補 CPU/RAM 互動,不順帶擴大其他監控模組。

    +
      +
    • +
    • +
    + +

    4C:接入前景 App 選單

    +
      +
    1. 新增單一前景 App 按鈕,隨 App 切換更新;點擊按需顯示真實頂層選單,含 Apple 與 App 自身選單,不硬編碼 Kitty 專用清單。
    2. +
    3. 選擇標題後操作原生子選單,保留深層項目、Help、快捷鍵、停用狀態與 Option 替代項目。不得因入口取得焦點而誤操作另一個 App;長標題/多項目不能靜默截斷。
    4. +
    5. 接上 4A 互斥,保留 Control-F2 作為完整原生備援。若現有公開 AX 路線在特定 App 失效,先列出缺口,再決定是否改用 Menuwhere;不直接複製 Felix 的私有 API helper。
    6. +
    +

    相依:4A,接線與 4B 共用;檔案:executable_sketchybarrcplugins/executable_native-menu.sh。驗收使用 c22;至少涵蓋 Kitty、目前使用的瀏覽器及一個長/深層選單 App,並實際執行安全命令確認不是只有標題能顯示。

    + +

    4D:整體互斥、控制中心與恢復驗收

    +
      +
    1. 依本次比較暫保留原生控制中心,整合既有設定按鈕;實際確認 Wi-Fi 網路清單、Focus 模式/期限、Bluetooth/音訊輸出等入口,不為驗收任意切斷網路或更動專注模式。
    2. +
    3. 逐一操作頂端滑鼠、App 按鈕、CPU/RAM、CodexBar、控制中心及系統快捷鍵;以螢幕錄影/截圖檢查開啟、深層選單、Esc、點外部、命令執行後、切換 App 的所有狀態,不只拍靜態成功畫面。
    4. +
    5. 內建/外接螢幕、瀏海、全螢幕進出、喚醒及重新登入後重做;驗證錯誤或程序退出不會讓 SketchyBar 永久消失,且失敗時仍有原生選單可操作。
    6. +
    +

    相依:4B+4C;驗收使用 c23/c24。登入接線只有在 4A 證實需要時才改 AeroSpace;不得另起重複 bar。功能以實機使用驗證,不新增功能測試腳本。

    +
    +
    +
    階段 5:時鐘、音量與多螢幕收尾
    -
    規模:中相依:階段 4目標:完成日常使用
    +
    規模:中相依:階段 4 與 4A–4D目標:完成日常使用
    1. 加入每秒 clock script,格式對齊 %B %d %H:%M:%S
    2. volume item 訂閱 volume_changemouse.scrolled
    3. 滾輪依方向調整 3%,並夾在 0–100;點擊切換靜音。
    4. 最右側設定按鈕以固定 com.apple.menuextra.controlcenter 識別碼開啟原生控制中心;Wi-Fi/勿擾模式仍使用系統原生面板。
    5. 驗證內建螢幕、外接螢幕、notch、睡眠喚醒與登入啟動。
    6. -
    7. 隱藏原生 macOS menu bar;只用系統支援的 auto-hide,不加入永久阻擋游標觸發的 hack。
    8. +
    9. 保留系統 auto-hide,搭配階段 4A–4D 的互斥顯示;auto-hide 本身不算完成不重疊,不加入永久阻擋游標觸發的 hack。

    驗收

      @@ -714,7 +800,7 @@

      驗收

      1. README 記錄 SketchyBar 架構、CodexBar/Stats 一次性設定與 Screen Recording 權限。
      2. 確認 diff 無 token、個人路徑或生成的 colors.sh
      3. -
      4. 以小型提交拆分:套件、核心 bar、主題、aliases、文件。
      5. +
      6. 延續 PR #155,以小型提交拆分已驗證的邏輯變更;階段是工作順序,不代表每階段自動另開 MR。未完成的實機驗收保持未勾選。
      7. 同步更新本計畫;四平台 CI 只跑安裝流程,不加入套件功能 mock 測試。保留 -v,確認 PR 最新 commit 的 checks 全過。

      驗收

      @@ -741,7 +827,8 @@

      風險與處理

      第三方 showy-quota 維護規模較小且功能過量低不列為必要相依;只有未來要同時供 SketchyBar、tmux、Zellij 顯示多 provider 時重評。 內建與外接螢幕 bar 高度不同中先用 36px;只有測試失敗才加入 per-monitor AeroSpace gap。 Matugen reload 時色彩檔短暫不存在低靜態 config 提供最小 fallback palette。 - 原生 menu bar 在頂端、鍵盤或控制中心開啟時重疊中尚未解決;不把重疊當成完成狀態。依30 份來源比較選定 App 選單入口與互斥/位置分離策略,再驗收;SIP 保持啟用。 + 原生 menu bar 與 SketchyBar 的狀態不同步中4A 已接公開 Carbon 事件與 reload/喚醒同步。CGWindow 的 Menubar 名稱已在 macOS 26.6.2 查核,跨版本仍需實機驗收;helper 崩潰先恢復 bar,reload 重啟交替;SIP 保持啟用。 + 替代控制中心只有快捷開關,缺少完整操作中按替換門檻逐項查核;目前保留原生面板,不以 Wi-Fi 開關冒充網路選擇器,也不宣稱原生面板能套完整 Matugen。 Stats GUI state 尚未納入 chezmoi低README 記錄一次性設定;只有實際跨機重建痛點出現時再自動化。 @@ -754,11 +841,13 @@

      完成定義

    • -
    • +
    • +
    • +
    From 449d7cdfb03708a09a1e9e5b24b9ca95e176dc14 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 18:32:51 +0800 Subject: [PATCH 22/30] fix(brew): trust formula entries without redundant tap installs --- home/dot_config/brew/Brewfile | 7 ++----- sketchybar-plan.html | 1 + 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/home/dot_config/brew/Brewfile b/home/dot_config/brew/Brewfile index 828e1d6..ea647e6 100644 --- a/home/dot_config/brew/Brewfile +++ b/home/dot_config/brew/Brewfile @@ -1,6 +1,3 @@ -tap "FelixKratz/formulae", trusted: { formula: "sketchybar" } -tap "cxwx/cxbrew", trusted: { formula: "matugen" } - brew "bat" brew "bear" brew "btop" @@ -16,7 +13,7 @@ brew "dos2unix" brew "eza" brew "fastfetch" brew "fd" -brew "FelixKratz/formulae/sketchybar" +brew "FelixKratz/formulae/sketchybar", trusted: true brew "fish" brew "gh" brew "git" @@ -24,7 +21,7 @@ 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" diff --git a/sketchybar-plan.html b/sketchybar-plan.html index f8098c3..8884572 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -292,6 +292,7 @@

    決策

    2026-09-13 新增已確認範圍:點 CPU/RAM 開啟各自的 Stats 詳細面板、從 SketchyBar 操作前景 App 選單,且原生選單列與 SketchyBar 不互相覆蓋。交替顯示已透過公開 Carbon 事件接線;CPU/RAM 詳細面板與 App 按鈕仍待實作。控制中心替代品比較見下方調查:保留原生控制中心,不新增第三方套件。交替顯示不等於原生列永遠不出現。

    不採用 Simple Bar、SbarLua、大型第三方設定集或 showy-quota。單一 Codex meter 不值得再加入 Bash 4、獨立 renderer、cache daemon 與第二套主題設定。

    CI 只驗證安裝:已移除新增的 tests/sketchybar.sh 與其 CI 步驟。四平台保留 chezmoi init --apply -v;功能在實際 macOS 上操作驗收。最新安裝結果以 PR #155 checks 為準,不把上一個 commit 的成功當成本次成功。

    +

    本次 macOS 安裝修正:第一輪 CI 卡在明列的 brew tap 驗證未信任的其他 formula,尚未執行 helper 編譯。移除兩個多餘 tap 宣告,改在完整名稱的 SketchyBar/Matugen brew 項目上使用 trusted: true;由 Homebrew 原生安裝路徑取得 tap,只信任這兩個套件,不信任整個 tap,也不關閉信任檢查。

    依賴清理:移除本 MR 額外列出的 libayatana-indicator 與註解。master 已加入 paru --mflags "--syncdeps --noconfirm",由安裝流程依 PKGBUILD 解決缺漏;本 MR 不再重複指定傳遞依賴。AUR 的 PKGBUILD 與 .SRCINFO 當次仍不一致,這不是宣稱 AUR 中繼資料已修復。

    From 20067b35160867355de6b33b70376f06c64aaf3b Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 19:28:24 +0800 Subject: [PATCH 23/30] refactor: simplify SketchyBar plan and group Stats items --- .../sketchybar/executable_sketchybarrc | 24 +- .../plugins/executable_native-menu.sh | 17 - sketchybar-plan.html | 934 ++---------------- 3 files changed, 75 insertions(+), 900 deletions(-) diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index feb3c9b..9fa7834 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -75,17 +75,6 @@ sketchybar --add bracket workspaces '/space\..*/' \ background.corner_radius=9 \ background.drawing=on -sketchybar --add item control_center right \ - --set control_center \ - icon="󰒓" \ - label.drawing=off \ - padding_left=5 \ - padding_right=5 \ - background.color="$CAPSULE" \ - background.drawing=on \ - script="$PLUGIN_DIR/native-menu.sh" \ - --subscribe control_center mouse.clicked - sketchybar --add item volume right \ --set volume \ padding_left=5 \ @@ -107,6 +96,10 @@ add_alias() { sketchybar --rename "$source" "$name" # Preserve raster colors: tinting an opaque chart makes it a solid rectangle. sketchybar --set "$name" \ + icon.drawing=off \ + label.drawing=off \ + padding_left=1 \ + padding_right=1 \ alias.scale=0.82 \ alias.update_freq=2 \ click_script="$click_script" @@ -151,11 +144,8 @@ printf '%s\n' "$menu_items" \ label.padding_right=12 codex_added=true ;; - Stats,*Network*|*,Network_*) - add_alias "$source" "stats.net.$index" right 'open -a Stats' - ;; - Stats,*CPU*|Stats,*GPU*|Stats,*RAM*|Stats,*Memory*|Stats,*Sensor*|*,CPU_*|*,GPU_*|*,RAM_*|*,Memory_*|*,Sensor*) - add_alias "$source" "stats.sys.$index" right 'open -a Stats' + Stats,*Network*|*,Network_*|Stats,*CPU*|Stats,*GPU*|Stats,*RAM*|Stats,*Memory*|Stats,*Sensor*|*,CPU_*|*,GPU_*|*,RAM_*|*,Memory_*|*,Sensor*) + add_alias "$source" "stats.$index" right 'open -a Stats' ;; Stats,*|Karabiner-Console-User-Server,*|Control\ Center,*|Control\ Centre,*|SystemUIServer,*|Window\ Server,*|TextInputMenuAgent,*|*,Clock*|*,Siri*|*,BentoBox-*|*,WiFi*|*,Battery*|*,AudioVideoModule*|*,Item-*) ;; @@ -166,7 +156,7 @@ printf '%s\n' "$menu_items" \ done ) -for group in tray stats.net stats.sys; do +for group in tray stats; do sketchybar --add bracket "$group.group" "/$group\\..*/" \ --set "$group.group" \ padding_left=5 \ diff --git a/home/dot_config/sketchybar/plugins/executable_native-menu.sh b/home/dot_config/sketchybar/plugins/executable_native-menu.sh index 920850b..6210b2a 100755 --- a/home/dot_config/sketchybar/plugins/executable_native-menu.sh +++ b/home/dot_config/sketchybar/plugins/executable_native-menu.sh @@ -16,23 +16,6 @@ tell application "System Events" to tell process "CodexBar" if not (exists menu 1) or (size of menu 1 is {0, 0}) then click end tell end tell -APPLESCRIPT - ;; - control_center) - osascript <<'APPLESCRIPT' -tell application "System Events" to tell process "ControlCenter" - repeat with statusItem in menu bar items of menu bar 1 - set itemID to missing value - try - set itemID to value of attribute "AXIdentifier" of statusItem - end try - if itemID is "com.apple.menuextra.controlcenter" then - click statusItem - return - end if - end repeat - error "Control Center status item was not found." -end tell APPLESCRIPT ;; esac diff --git a/sketchybar-plan.html b/sketchybar-plan.html index 8884572..b109a2b 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -3,879 +3,81 @@ - - SketchyBar 導入計畫 + SketchyBar 計畫 - - +
    -

    Collie's Dotfiles · macOS

    -

    SketchyBar 導入計畫

    -

    以 SketchyBar 取代原生 menu bar 的主要顯示層,串接 AeroSpace、CodexBar、Stats 與 Matugen;重現 Noctalia 的透明背景、半透明 capsule 與三區配置,而且不停用 SIP。

    -
    - 狀態:交替顯示已接線;Stats CPU/RAM 與 App 按鈕待實作 - 平台:macOS - 方案:SketchyBar + AeroSpace + CodexBar - 更新:2026-09-13 -
    + Collie's dotfiles · macOS · 更新 2026-09-13 +

    SketchyBar 計畫

    +

    保留目前好用的原生列/SketchyBar 交替顯示。只補齊日常資訊,不重做 macOS 的選單和控制中心。

    +

    本輪狀態:實作中;下列驗收完成後才標示通過。

    - -
    -
    -
    -

    決策

    -
    -

    採用 SketchyBar,並加入 CodexBar meter。系統監控重用 Stats menu-bar items;bar 上鏡像 CodexBar 單一原生 status item,hover 摘要才讀 CLI,分段顯示 Codex OAuth 與 Claude CLI 用量。SketchyBar 負責排列、Matugen capsule、workspace、時鐘、音量與控制中心入口。

    -

    2026-09-13 新增已確認範圍:點 CPU/RAM 開啟各自的 Stats 詳細面板、從 SketchyBar 操作前景 App 選單,且原生選單列與 SketchyBar 不互相覆蓋。交替顯示已透過公開 Carbon 事件接線;CPU/RAM 詳細面板與 App 按鈕仍待實作。控制中心替代品比較見下方調查:保留原生控制中心,不新增第三方套件。交替顯示不等於原生列永遠不出現。

    -

    不採用 Simple Bar、SbarLua、大型第三方設定集或 showy-quota。單一 Codex meter 不值得再加入 Bash 4、獨立 renderer、cache daemon 與第二套主題設定。

    -

    CI 只驗證安裝:已移除新增的 tests/sketchybar.sh 與其 CI 步驟。四平台保留 chezmoi init --apply -v;功能在實際 macOS 上操作驗收。最新安裝結果以 PR #155 checks 為準,不把上一個 commit 的成功當成本次成功。

    -

    本次 macOS 安裝修正:第一輪 CI 卡在明列的 brew tap 驗證未信任的其他 formula,尚未執行 helper 編譯。移除兩個多餘 tap 宣告,改在完整名稱的 SketchyBar/Matugen brew 項目上使用 trusted: true;由 Homebrew 原生安裝路徑取得 tap,只信任這兩個套件,不信任整個 tap,也不關閉信任檢查。

    -

    依賴清理:移除本 MR 額外列出的 libayatana-indicator 與註解。master 已加入 paru --mflags "--syncdeps --noconfirm",由安裝流程依 PKGBUILD 解決缺漏;本 MR 不再重複指定傳遞依賴。AUR 的 PKGBUILD 與 .SRCINFO 當次仍不一致,這不是宣稱 AUR 中繼資料已修復。

    -
    - -
    -
    -

    範圍

    - 先把真正會用的部分做好 -
    -
    -
    -

    要做

    -
      -
    • AeroSpace workspace 1–9 與 magic
    • -
    • Workspace 點擊切換與狀態即時更新
    • -
    • CodexBar 原生 meter;hover 同時摘要 Codex/Claude、左鍵原生詳細選單、右鍵刷新摘要
    • -
    • SketchyBar 最右側控制中心按鈕,開啟 macOS 原生 Wi-Fi/專注模式面板
    • -
    • 時鐘:月份、日期與秒數
    • -
    • 音量顯示、靜音與滾輪每次 3%
    • -
    • Stats aliases:網路、CPU、GPU、RAM、溫度、電池
    • -
    • 補完 CPU/RAM 點擊:打開對應 Stats 原生詳細面板,不只是啟動 Stats
    • -
    • 前景 App 按鈕:按需顯示 Apple/kitty/Shell/Edit/Window/Help 等真實選單
    • -
    • 原生選單列與 SketchyBar 互斥顯示,涵蓋滑鼠、鍵盤及各原生面板入口
    • -
    • 必要的第三方 tray/menu extras aliases
    • -
    • Matugen wallpaper palette 與即時重載
    • -
    • 透明 bar、30% surface-variant capsule、無陰影
    • -
    • 多螢幕、notch 與 AeroSpace 頂部空間
    • -
    -
    -
    -

    明確不做

    -
      -
    • Media widget
    • -
    • Notification badges
    • -
    • 重製 CodexBar 完整詳細面板或多 provider 常駐 strips;僅在同一 hover 摘要區分 Codex/Claude
    • -
    • Session popup
    • -
    • Clipboard history
    • -
    • Wallpaper Engine/W Engine
    • -
    • Noctalia lockscreen、dock、launcher 或 notification daemon
    • -
    • 永久封鎖原生 menu bar 顯示的私有 API/SIP hack
    • -
    -
    -
    -
    - -
    -
    -

    目標配置

    - 約 36px 高,外層完全透明 -
    - -

    圖為區域示意。實機使用 SketchyBar 原生 q/e 將時鐘、workspace 分置瀏海兩側,notch_width=200;內建螢幕瀏海實測 x=646–825 pt,時鐘右緣 630、workspace 左緣 837。Alias 保留原生圖像色彩,避免把不透明 Stats 圖表染成黑塊。

    -

    App 選單入口是新增規劃,尚未出現在實機;只保留一個精簡按鈕,長選單按需展開,不把整排選單常駐塞到瀏海旁。新增後須重新量測空間,不能沿用舊截圖宣稱驗收完成。

    -
    - -
    -
    -

    CodexBar 互動分工

    - 2026-09-13 使用者確認 -
    -

    dotfiles 指定 salemsayed/codexbar-meter:bar。依該 Noctalia 外掛目前程式,hover 是小型用量提示、左鍵切換詳細面板、右鍵刷新;macOS 採用相同分工,但詳細面板沿用 CodexBar 原生介面。

    -
    - - - - - - - - - -
    操作SketchyBar 行為資料與限制
    Hover元件下方顯示 Matugen 配色的小摘要;各額度以框線分格,格內上行是剩餘比例、下行是重置倒數,不打開原生選單以粗體標題分組 Codex OAuth 與 Claude CLI;缺少的區間或重置時間略過,不補假數值
    移開游標收起小摘要摘要不是可操作的大型面板
    左鍵收起摘要,開啟 CodexBar 原生詳細選單,可切 Codex/Claude 分頁帳號、重置時間、費用與設定仍由 CodexBar 維護;CLI 來源能提供的細節以套件為準
    右鍵立即刷新小摘要只重新讀 CodexBar CLI,不改登入來源或開啟其他視窗
    背景更新每 60 秒及喚醒時刷新兩個 providermacOS lockf 避免重複請求;任一來源失敗或回應不完整時保留上次完整摘要並警告
    -
    -

    使用 macOS 內建 jq 處理既有 CodexBar CLI JSON;只把摘要文字留在 SketchyBar 記憶體,不保存帳號 JSON、token 或額外 quota server。摘要使用不透明的 Matugen surface-variant 底色,避免與下方視窗文字混在一起。

    -

    重置倒數使用 CodexBar 的 RateWindow.resetsAtjq 的 UTC 日期函式計算,向上取整到分鐘,隨既有 60 秒排程更新。例如「↳ 約 4 小時 30 分後重置」;維持 330 px 寬度,以獨立下一行避免長名稱擠掉倒數。無法解析的時間不顯示;期限已過則顯示「重置時間已到,等待來源更新」,不自行推定額度已恢復。

    -

    每個額度使用一個 SketchyBar 原生 item 的背景與框線,透過 icon/label 的垂直偏移放入兩行文字;不是另外開視窗或畫圖片。方格採 1 pt 框線、6 pt 圓角及 Matugen 衍生色,倒數字級較小;沒有重置時間時收成單行格。外框寬度保持不變。

    -

    本機檢查:Codex/Claude 的 hover 摘要與原生分頁均已實際開啟並比對;Claude 當次 5 小時剩餘 100%、每週 96%,兩邊一致。移出、左右鍵及 CLI 暫時不可用/恢復也已驗證。需要手動重跑真實 CLI 更新時,可執行 SENDER=routine NAME=codexbar ~/.config/sketchybar/plugins/codexbar.sh;失敗會回傳非零狀態,不另建功能測試腳本或加入 CI。

    -
    - -
    -
    -

    資料流與責任

    -
    -
    ┌────────────┐ workspace event ┌─────────────┐
    -│ AeroSpace  │ ───────────────▶│             │
    -└────────────┘                 │             │
    -┌────────────┐ native aliases  │ SketchyBar  │
    -│ Stats.app  │ ───────────────▶│             │
    -└────────────┘                 │             │
    -┌────────────┐ alias / JSON    │             │
    -│ CodexBar   │ ───────────────▶│             │
    -└────────────┘                 └──────┬──────┘
    -                                     ▲
    -┌────────────┐ generated colors.sh   │
    -│  Matugen   │ ──────────────────────┘
    -└────────────┘
    -
      -
    • AeroSpace:workspace 真實狀態、切換與變更事件。
    • -
    • Stats:系統監控與感測器;不在 SketchyBar 重寫資料採集。
    • -
    • CodexBar:來源設定為 codex = oauthclaude = cli;以 codexbar usage --provider both --format json --json-only --no-credits 讀取兩者摘要。0.60 的 CLI 會沿用各自 config source,不套同一個全域 source;SketchyBar 不直接讀 token 或啟用 browser-cookie source。來源選擇實作Claude CLI 限制
    • -
    • Matugen:只生成色彩變數,不生成整份 SketchyBar 行為設定。
    • -
    • SketchyBar:呈現、排列、capsule、滑鼠互動與 hotload。
    • -
    • 原生互動接線:沿用 native-menu.sh 作為面板入口;sb-menu-events 獨立接收 macOS 原生列顯示/隱藏事件,所以鍵盤與頂端滑鼠也能交替。Stats CPU/RAM 與 App 按鈕尚未接完;詳細內容仍交由原生面板維護。
    • -
    -
    - -
    -
    -

    30 個公開實作調查

    - GitHub sketchybar topic,依星數排序;2026-09-12 快照 -
    -

    檢查前 30 個 repository 的中繼資料、檔案樹與代表性動態 item 後,23 個以 shell 或 Lua 為主。常見做法是低頻資料用 update_freq、狀態變化用事件訂閱、高頻監控才使用 C/Rust helper;沒有理由為單一 Codex meter 先導入完整 Lua 或 helper 架構。

    -
      -
    • 直接 CodexBar → SketchyBar 的案例很少:showy-quota 功能完整,但只有 18★、4 位公開貢獻者,而且為多 provider、多宿主需求加入快取、renderer 與本機 serve 管理。
    • -
    • 精簡案例:fenetikm/dotfiles 每 120 秒執行 CodexBar CLI,以 JSON 檔快取五分鐘,再用 jq 取百分比。
    • -
    • 自行抓 provider 不划算:frankroeder/dotfiles 為 AI usage 維護 Lua popup、Python OAuth clients、快取、節流與測試,規模遠超過目前需求。
    • -
    • 最符合現有架構:SketchyBar 官方 alias 鏡像第三方 status item,CodexBar 與 Stats 共用螢幕錄製授權。2026-09-13 補上原生選單操作後,AeroSpace 另需輔助使用及對 System Events 的自動化授權。
    • -
    - -

    CodexBar 顯示方案

    -
    - - - - - - - - -
    方案新增負擔可靠性決定
    CodexBar 原生 item → SketchyBar aliasCodexBar cask、原生選單 click helper沿用 CodexBar 自己的 meter 與資料來源;需原生 item 能被 querybar meter 採用
    CodexBar CLI → hover 摘要一支 shell script、macOS 內建 jq/lockf;舊摘要留在 bar 記憶體JSON schema 變更時需維護摘要 parser,不重寫登入與用量抓取僅 hover 採用,不替代原生 meter
    showy-quotaBash 4、安裝流程、renderer、快取與 serve lifecycle多 provider、tmux、Zellij 共用時最完整目前不採用
    直接讀 OAuth/私有 endpoint自行處理憑證、refresh、schema、429 與 cache維護面最大排除
    -
    - -
    - 查看全部 30 個樣本 -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #Repository★/fork主要做法對本計畫的啟示
    1FelixKratz/SketchyBar12,357/178C 核心;官方 shell 範例item、event、alias 的語法基準
    2FelixKratz/dotfiles2,028/137SbarLua + C event providers高頻資料才值得 helper;本案先不需要
    3crissNb/Dynamic-Island-Sketchybar528/32Shell + C helper動態島特效不在需求內
    4mocki-toki/barik522/36原生 Swift bar屬完整替代品,不是 SketchyBar item 範例
    5m4xshen/dotfiles438/12Nix 管理的 shell plugins每個低頻 item 一支小 script
    6hbthen3rd/dotfiles-archived256/8Shell polling plugins大量獨立 poller 容易累積維護負擔
    7rockyzhang24/dotfiles241/15Shell items + C CPU helper事件與 helper 可混用,不必全盤 Lua 化
    8mehd-io/dotfiles186/23Shell + AeroSpace eventsworkspace 應由 AeroSpace 主動 trigger
    9r17x/universe135/12Nix 封裝 SbarLua + C helpers可重現但超出 chezmoi 所需複雜度
    10marsdevx/macOS-config91/5SbarLua + C event providers接近 FelixKratz 完整架構
    11falleco/dotfiles84/7SbarLua + helpers + AeroSpace可借鏡事件接線,不照搬 framework
    12TheGoldenPatrik1/sketchybar-config64/3大型 Lua widgets 與 popup功能豐富,但包含多個已排除項目
    13slano-ls/SketchyBar53/1單體 shell config小設定可保持直接,不需模組系統
    14joncrangle/sketchybar-system-stats51/6Rust event provider多項系統 metrics 才值得集中 provider
    15smnatale/dotfiles47/0Shell + AeroSpace;CPU/disk/memory pollers證明基本 bar 不必使用 Lua
    16haydenrou/dotfiles44/1分離式 shell scripts簡單、容易替換,但外部資料各自 polling
    17Kcraft059/sketchybar-config43/6Shell config + macmon重用現成資料工具比重寫 sensor 好
    18CCMurphy-dev/Aegis42/2原生 Swift 全包 bar完整 shell 替代品;不是可摘取的 Codex item
    19frankroeder/dotfiles42/8SbarLua + Python AI usage clients自行抓 OAuth 與畫 popup 的成本過高
    20FelixKratz/SketchyBarHelper36/3C 通訊 helper只有效能證據出現後才考慮
    21Nikolaidp24/SketchyBar34/4Shell config定時 item 可維持原生 shell
    22Efterklang/sketchybar34/0SbarLua + system-stats provider完整 widget suite 才能攤平相依成本
    23fedeoliv/dotfiles29/0Lua dotfiles;bar 以外部內容管理外部設定降低 repo 可讀性,不採用
    24deniserdogan/dotfiles26/1Shell items 與 theme-change eventMatugen 完成後只 trigger reload
    25eminarican/dotfiles24/0Shell items/plugins靜態色彩檔與小型 scripts 足夠
    26DeevsDeevs/dotfiles20/4chezmoi;只納管依賴與 yabai triggerbar 本體可與 bootstrap 分離
    27xrce/dots19/1多套 shell SketchyBar profiles多 profile 會複製大量 item;本案只保留一套
    28refcell/dots19/0Shell bar + Dynamic Island 副本一般 bar 與特效層應避免同時維護
    29slashspace/dotfiles19/2Shell + AeroSpace + theme renderer最接近本案:小 scripts、事件、集中主題輸出
    30jellydn/dotfiles19/3SbarLua + C helpers + AeroSpace成熟但比需求更重
    -
    -
    -

    Stars、forks 與排序是調查日快照,會隨 GitHub 狀態變動。另以 CodexBar、showy-quota、fenetikm 與 frankroeder 做焦點抽查,不把低星案例誤當成主要相依。

    -
    - -
    -
    -

    App 選單與雙 bar 共存:30 份來源比較

    - 2026-09-13;20 份 dotfiles + 10 份工具/官方參考 -
    -

    App 選單入口與兩條 bar 不重疊,已由使用者確認都要做。交替顯示現已接線,原生列展開時 SketchyBar 暫時隱藏;不是把原生控制中心變成脫離選單列的獨立視窗。App 按鈕仍未實作,CPU/RAM 點擊仍是 open -a Stats。以下保留方案依據,進度見階段 4A–4D

    -

    本機唯讀查詢已取得 Kitty 的 Apple、kitty、Shell、Edit、Window、Help,證明資料可以經由輔助使用取得。規劃採前景 App 按鈕按需列出真實選單,再開原生子選單;不硬編碼命令,也不將 alias 誤當 App 選單 API。原生 alias 的範圍可見 官方 components 文件

    - -

    樣本顯示什麼

    -

    20 份 dotfiles 中,8 份含 App 選單相關程式,但多份沿用 Felix 的同一套 helper,而且部分切換入口已註解。其餘 12 份的抽查 bar 入口未見完整 App 選單映射;這不表示其作者不用 App 選單,也不能推論他們全部接受重疊。下表是固定 commit 的靜態查核,不是 20 套在本機逐一安裝的相容性認證。

    -

    Felix 的做法是平常顯示 workspace/App 名稱,點 App 時切換成選單標題;點標題再交給 macOS 開 submenu,不是重製所有選單。這能節省常駐寬度,但原生 submenu 的位置、滑鼠追蹤與選單列顯示仍受系統控制。

    -

    其 C helper使用 Accessibility,也引用私有 SkyLight/SLPS 符號;「不用停用 SIP」不等於「全用穩定公開 API」。這份 dotfiles 雖有 2,028★,GitHub contributors 當次只列 1 個公開帳號;不能把核心 SketchyBar 的維護規模算到個人設定上。

    - -

    可選路線

    -
    - - - - - - - - - - -
    路線App 選單是否解決重疊取捨
    原生快捷鍵Control-F2/Fn-Control-F2,接方向鍵與 Return零依賴的備用入口;原生列仍會展開。Apple 文件
    按需 App 選單模式App 按鈕切換選單,或小型 popup不能單獨保證最接近目前單條 bar 的外觀;須處理長選單、動態項目、前景 App 與瀏海,不能盲目複製 helper。切換入口
    Menuwhere/Raycast現成階層 popup/搜尋命令否;只減少使用頂端原生列的需要完整選單交由產品維護,仍需安裝與授權;目前 Brewfile 未納管。MenuwhereRaycast
    Hammerspoon官方 API 取得並執行選單;自行建立 popup否,另需協調顯示工具成熟但不是免設定插件;只為這個功能加入另一套 Lua 常駐環境並不省事。popup API 與限制
    兩條 bar 交替顯示保留完整原生 App 選單目標是同時只顯示一條需可靠偵測選單生命週期;游標高度猜測不涵蓋所有叫出方式。既有工具
    上下分離/SketchyBar 移到底部保留頂端原生選單以幾何位置避免同區重疊依賴最少、保留原生功能,但不再是目前的單條頂端配置。底部實例
    -
    - -

    為什麼不直接改 topmost

    -

    topmost=windowtopmost=on 不同:本機同版本 2.24.0 原始碼分別設定 floating-window 與 status-window 圖層。它們只改誰畫在上面,不會隱藏下層文字,也不會接管 App 選單。透明 bar 即使畫到最上層,仍可能透出原生文字;改不透明背景則會犧牲現有透明視覺。見 版本相符的實作官方設定

    -

    sketchybar-toggle 的 README 把 window 模式描述成位於原生選單列之上,不能照單全收。它的實作每約 16 ms 讀游標位置與按鍵,不查原生選單是否真的開啟;因此鍵盤或目前 Control Center 按鈕叫出面板時,不一定觸發隱藏。顯示控制也會操作整條 bar 並把 y_offset 重設為 0;多螢幕與自訂位移需要另行驗收。

    -

    此工具當次為 21★、2 個公開 contributor 帳號,不足以證明多人持續維護或全面相容。它證明「不重疊的交替策略有人做」,但不是可直接宣稱成熟、完整、零副作用的答案。滑鼠位置輪詢也不同於攔截滑鼠事件;兩者都不應在未選定方案前加入登入流程。

    -

    永久隱藏的查核補充:yabai 的 menubar_opacity 0.0使原生列不回應滑鼠,但原生全螢幕有恢復顯示的例外;實作直接呼叫私有 SkyLight API,不能一概說這項功能需要停用 SIP。它在本機 SIP 啟用下的相容性尚未驗證,也不會自動移植選單,因此不列入預設方案。zapmenu則限制游標進入頂端 4 px;當次只有 4★、1 個公開貢獻帳號,且仍保留鍵盤叫出原生列的路徑,不是完整互斥方案。

    - -

    對目前設定的建議

    -

    採用方向:按需 App 選單入口,原生列展開期間與 SketchyBar 交替顯示。實機確認公開 Carbon 的 kEventMenuBarShownkEventMenuBarHidden 會通知背景程序,已用於交替接線;不改 topmost、不封鎖游標。Menuwhere 保留為 App 選單入口的候選,不預先加入依賴。多螢幕/重新登入等尚未實際完成的驗收仍保留未勾選。

    -
      -
    • -
    • -
    • -
    - -
    - 30 份來源與逐項判讀 -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #來源App 選單原生列/位置判讀與限制
    1FelixKratz/dotfiles
    2,028★
    點 App 名稱切換 menus/spaces;C helper 讀取並 AXPress。bar.lua 未設 topmost;沒有完整的原生列互斥協調。可借鏡按需切換;helper 含私有 SkyLight API。
    2rockyzhang24/dotfiles
    242★
    front_app 顯示名稱/圖示,沒有 Edit/Help 映射。topmost=window。高星外觀設定不等於完整 App 選單。
    3mehd-io/dotfiles
    186★
    點 App 項目開 Mission Control。頂端 40 pt;未設互斥切換。Mission Control 不是 App 選單。
    4m4xshen/dotfiles
    438★
    目前入口沒有 App 選單;front_app plugin 檔不代表已使用。頂端、y_offset=16;沒有原生列狀態協調。位移不等於保證不重疊。
    5falleco/dotfiles
    84★
    Felix 型 menus/spaces 切換,front_app 點擊已接線。topmost=window。沿用同一 helper 家族,非獨立成熟產品。
    6marsdevx/macOS-config
    91★
    有 menus 模組,但 front_app 入口整份註解。topmost=window。不能把存在的 menus.lua 算成預設可點。
    7jellydn/dotfiles
    19★
    載入 menus,但 require(front_app) 已註解。topmost=off。已查入口未提供原本的切換按鈕。
    8r17x/universe
    135★
    Felix 型切換;以 sbar_menus 指令列 helper 操作。topmost=window。Nix 封裝不會自動解決原生列重疊。
    9smnatale/dotfiles
    47★
    Apple 圖示開系統設定,未映射 App 選單。頂端 32 pt;沒有互斥切換。系統設定入口不等於 Edit/Help。
    10haydenrou/dotfiles
    44★
    抽查 bar 入口未見 App 選單映射。54 pt、margin=12;沒有互斥切換。偏狀態與工作區顯示。
    11deniserdogan/dotfiles
    26★
    front_app 與控制項分開,未見 App 選單映射。頂端 38 pt、y_offset=5。有控制中心不代表有 App 選單。
    12eminarican/dotfiles
    24★
    抽查 bar 入口未見完整 App 選單。頂端 40 pt、y_offset=10。2023 年設定;不當作現版 macOS 相容保證。
    13frankroeder/dotfiles
    42★
    多套 top/bottom/island 設定;抽查入口未見完整 App 選單。共用設定 topmost=off,另有底部 bar。分區顯示可借鏡,整套設定負擔較大。
    14slano-ls/SketchyBar
    53★
    有開 Raycast 的入口,沒有直接接 Search Menu Items。頂端 38 pt、topmost=on。開啟 launcher 不等於已解決 App 選單。
    15TheGoldenPatrik1/sketchybar-config
    64★
    Felix 型 menus/spaces 切換,front_app 已接線。topmost=window。2025 年設定;原生 submenu 仍由 macOS 開啟。
    16Kcraft059/sketchybar-config
    43★
    Shell 建立 menu items,以 bundled menubar -s 點擊。設定寫 topmost=everything,非官方文件列值。可參考 shell 路線;不照抄二進位與未文件化設定。
    17Efterklang/sketchybar
    34★
    App 點擊開自繪 popup;AppleScript 讀取/點擊選單。另保留 Felix menus 模組;未見原生列互斥。自繪兩層與 30 項上限,深層/特殊名稱仍需維護。
    18Nikolaidp24/SketchyBar
    34★
    點 App 圖示開 Mission Control,非 App 選單。topmost=window,頂端 45 pt。與 mehd-io 類型相同。
    19slashspace/dotfiles
    19★
    沒有在 bar 重製 App 選單。position=bottom、height=36。以位置分離避開頂端原生列,是明確不同路線。
    20fenetikm/dotfiles
    13★
    狀態/工作區/AI 用量入口,抽查未見完整 App 選單。頂端,依螢幕使用不同高度;未見互斥切換。不能由漂亮 bar 推論作者如何使用每個 App 選單。
    21sketchybar-toggle21★;另建常駐程序,以約 60 Hz 游標位置輪詢交替顯示。不讀原生 menu 的真實開關;控制中心/鍵盤叫出選單是覆蓋範圍缺口。
    22SketchyBar 官方 Bar Propertiestopmost、hidden、position、y_offset 的正式設定。圖層與幾何設定,不是 App 選單移植器。
    23SketchyBar 官方 Componentsalias 鏡像 status items;需螢幕錄製權限。不是 File/Edit/Help 的完整選單 API。
    24SketchyBar 官方討論 #281討論阻擋頂端滑鼠事件,以及後來的切換工具。事件攔截會影響螢幕邊緣操作,不當作穩定預設。
    25SketchyBar 官方討論 #229作者列出 topmost 覆蓋或 yabai menubar_opacity。舊討論不是 macOS 26 實測;侵入式方法不納入本案。
    26Apple 鍵盤快捷鍵Control-F2/Fn-Control-F2 聚焦選單列;另有 Help 快捷鍵。零新套件,可保留完整原生選單;不會協調 SketchyBar。
    27Apple Menu Bar 設定自動隱藏後,游標到頂端仍會顯示。Always 指永遠自動隱藏,不是永遠禁止顯示。
    28Raycast NavigationSearch Menu Bar Items 搜尋並執行前景 App 的選單命令。需要輔助使用;不是 bar 上常駐的 File/Edit 橫列。
    29Hammerspoon application/menubar 官方 APIgetMenuItems/selectMenuItem 提供選單資料與操作。16,091★ 的工具,但 popup 仍須自行接線,並非裝好就有。
    30Menuwhere 官方操作文件快捷鍵或修飾鍵+右鍵,在游標旁顯示 App 的階層選單。現成產品、可處理替代選單項;不能順帶阻止原生 bar 顯示。
    -
    -
    -

    GitHub 數據為 2026-09-13 快照。Dotfiles 連結固定在本次檢查的 commit;星數代表 repository,不代表 menu helper 單獨的使用量或維護人數。未設定原生列偏好的 repository,不推測作者的系統偏好。

    -
    - -
    -
    -

    控制中心替代品:8 條路線比較

    - 2026-09-13;官方文件/上游原始碼查核,未安裝候選 -
    -

    結論:尚未找到同時比原生面板好串、功能不缺、又不增加明顯維護負擔的完整替代品。OnlySwitch 最接近現成開源控制面板;BetterTouchTool 的外部呼叫介面較完整,但需要自行組裝面板。現階段建議維持「SketchyBar 按鈕 → 原生控制中心」,把精力放在互斥顯示,而不是再維護一套系統控制介面。

    -
    - - - - - - - - - - - - -
    候選如何接 SketchyBar完整度/缺口維護與決定
    macOS 控制中心沿用現有 AXIdentifier 點擊入口;系統另有 Fn-C保留 Wi-Fi、Bluetooth、Focus、音量、顯示器、螢幕鏡像等原生操作與隱私狀態;面板位置/外觀由 macOS 決定。兩種入口在本機都會帶出原生列。Apple 隨系統維護、零新增第三方套件。保留;由小型 Carbon listener 協調交替顯示。
    OnlySwitch/Only Controldeep link 可執行開關,另可設定全域快捷鍵開面板;SketchyBar 需送此快捷鍵或做 AX 點擊。未找到文件化的「直接顯示面板」URL;不能把執行開關當成開啟面板。有 Bluetooth、音訊及外觀開關;Wi-Fi Switch 在 Evolution gallery、勿擾在 Shortcuts gallery,不等於完整 Wi-Fi 網路選擇/Focus 面板。沒有查到 Matugen 匯入介面。5,914★、25 個公開貢獻帳號;2.7.4,2026-09-11。主要提交仍集中作者,貢獻者含翻譯。保留候選,不預設安裝
    One Switch原生 status item/快捷操作;本次官方產品頁未找到可直接叫出面板的文件化 CLI/URL。現成開關集合:AirPods、深色模式、防睡眠等;沒有足夠證據視為完整網路/Focus/控制中心替代。商業產品,無可比的核心原始碼 stars/貢獻人數。不為未證實的串接優勢付出新依賴。
    BetterTouchTool Floating Menusbtt://、AppleScript 可觸發已設定的顯示/隱藏面板動作;可指定游標或螢幕位置。可自訂按鈕、slider、子選單;不是裝好就有完整控制中心。資料、動作、Matugen 同步與面板設定仍要自行組裝。商業產品、文件完整,但目前未安裝。只有未來另需大量 BTT 自動化時才值得重評。
    Raycastraycast:// 可叫出已啟用命令;文件列有執行確認提示,不能假設無提示。有音量、Bluetooth 等命令;核心是搜尋/執行操作,不是一個完整控制中心。Wi-Fi/Focus 等需逐項確認命令、extension 或 Shortcuts。核心為商業產品,extension 社群不等於面板核心維護人數。只為控制中心引入 launcher 不划算。
    Supercharge官方建議透過 Shortcuts 執行動作;目前 FAQ 明確表示自訂 URL scheme 尚未支援。音訊裝置切換、靜音、顯示開關等便利功能,不是完整控制中心;官方也不打算做成完整 toggler。獨立開發者商業產品,無可比核心 stars。與本案需求差距較大,不採用。
    SketchyBar 自繪 popup
    Felix Wi-Fideniserdogan 控制中心
    直接使用原生 popup/slider,與現有 Matugen 色彩最容易整合。Felix 的 Wi-Fi 是資訊/複製;denis 的 Network 是開系統設定、CPU/RAM 開 Activity Monitor,另有音量 slider。不是完整 Wi-Fi/Bluetooth 選擇器。前者個人 dotfiles 2,028★、後者 26★;不是獨立控制中心產品。重寫採集還會重複 Stats;不整套搬入。
    Aegis本身就是 menu bar/notch shell,非 SketchyBar 控制中心插件。提供 workspace、系統狀態與 HUD;與已選的 SketchyBar 架構重疊,還包含不需要的 media/notification 功能。42★、1 個公開貢獻帳號;最後 push 2026-08-20。排除整套換 bar 的路線。
    -
    -

    Stars/公開貢獻帳號來自當日 GitHub API,並不等於持續核心維護人數;商業產品不拿無關的 extensions repository 星數充數。以上是文件與原始碼比較,尚未逐一做本機相容性測試;不承諾所有候選功能只用公開 API 或免額外授權。SIP 必須保持啟用。

    -

    OnlySwitch 是否真的更省事?

    -

    若只需要幾個開關,它有價值;但目前需求是從 SketchyBar 進入完整設定面板。上游面板仍有自己的 status item、快捷鍵與顯示生命週期,不會替我們協調 App 選單、Stats 和原生列。若之後只缺一個快捷動作,先用 Apple 內建 shortcuts run,不為此安裝另一個常駐程式;目前不新增任何快捷動作。

    -

    替換門檻:有可靠的面板呼叫方式、在本機 SIP 啟用時可用、Wi-Fi 網路/Bluetooth 裝置/Focus 模式與期限/音訊輸出/亮度及鏡像等實際需要的操作逐項無缺漏,而且比保留原生面板更省維護。任何缺項要明列原生備援,不能宣稱完全取代。原生詳細面板沿用系統外觀;Matugen 僅保證 SketchyBar 自有按鈕與 popup。

    -
    - -
    -
    -

    預計變更檔案

    - 維持最小檔案集 -
    -
    - - - - - - - - - - - - - - - - - - - - - - -
    檔案動作用途
    home/dot_config/brew/Brewfile修改加入 FelixKratz tap、SketchyBar formula 與 CodexBar cask。
    home/.chezmoiignore修改.config/sketchybar 限定為 macOS。
    home/dot_config/sketchybar/executable_sketchybarrc新增bar 外觀、workspace、aliases、groups 與事件訂閱。
    home/dot_config/sketchybar/colors.sh.tera新增Matugen 輸入模板;輸出為未納管的 colors.sh
    home/dot_config/sketchybar/plugins/executable_clock.sh新增每秒更新 Noctalia 格式時鐘。
    home/dot_config/sketchybar/plugins/executable_volume.sh新增處理音量事件、靜音與 3% 滾輪調整。
    home/dot_config/sketchybar/plugins/executable_codexbar.sh新增Hover/左鍵/右鍵分流、每分鐘 CLI 額度與重置倒數更新;失敗保留舊摘要。
    home/dot_config/sketchybar/plugins/executable_native-menu.sh沿用並擴充目前開 CodexBar/控制中心;接下來加入 Stats CPU/RAM、App 選單。交替顯示由共用 listener 處理。
    home/dot_config/sketchybar/menu-bar-events.swift新增公開 Carbon 事件 listener;操作 SketchyBar hidden,重載/喚醒同步,不攔截滑鼠。
    home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl新增僅 macOS 編譯至 ~/.local/bin/sb-menu-events,由現有四平台安裝 CI 覆蓋編譯流程。
    home/dot_config/codexbar/modify_private_config.json沿用維持 Codex 使用 OAuth、Claude 使用 CLI;不把憑證寫入 dotfiles。
    home/dot_config/matugen/config.toml.tmpl修改註冊 SketchyBar 色彩輸出及 reload post-hook。
    home/.chezmoiscripts/run_onchange_after_6-apply-theme.sh.tmpl修改把新 Matugen 模板 hash 納入重跑條件。
    home/dot_config/aerospace/aerospace.toml修改啟動 SketchyBar、發出 workspace event、預留頂部高度。
    README.md修改記錄 macOS bar、Matugen 與一次性 Stats 設定。
    .github/workflows/test-distros.yaml移除功能測試步驟保留四平台安裝驗證及 -v;刪除 tests/sketchybar.sh
    -
    -
    - -
    -
    -

    實作階段

    - 依風險與相依性排序 -
    - -
    - 階段 1:安裝與最小可見 bar -
    -
    規模:小相依:無目標:先確認平台可用
    -
      -
    1. 將 SketchyBar 與 CodexBar 加入 Brewfile,保留現有 Stats。
    2. -
    3. 將 SketchyBar 目錄加入 macOS-only ignore 規則。
    4. -
    5. 建立最小 sketchybarrc:透明 bar、高度 36、JetBrainsMono Nerd Font、測試 clock item。
    6. -
    7. 透過 AeroSpace after-startup-command 啟動,避免額外維護一份 LaunchAgent。先檢查程序是否存在再決定啟動/reload,不能依賴 --reload 的 exit code 判斷。
    8. -
    -

    驗收

    -
      -
    • -
    • -
    • -
    -
    -
    - -
    - 階段 2:AeroSpace workspace 垂直切片 -
    -
    規模:中相依:階段 1目標:先完成核心互動
    -
      -
    1. 建立 workspace items:1 2 3 4 5 6 7 8 9 magic
    2. -
    3. 加入 aerospace_workspace_change custom event。
    4. -
    5. 讓 AeroSpace 的 exec-on-workspace-change 傳遞 focused workspace。
    6. -
    7. 點擊 workspace 時呼叫 aerospace workspace
    8. -
    9. 設定 outer.top = 36;多螢幕有差異時才改成 per-monitor gap。
    10. -
    -

    驗收

    -
      -
    • -
    • -
    • -
    -
    Checkpoint A:若 workspace、多螢幕或 top gap 不穩定,先修正;在此之前不接 Matugen 或 Stats。
    -
    -
    - -
    - 階段 3:Matugen 與 Noctalia 視覺 -
    -
    規模:中相依:階段 2目標:配色單一來源
    -
      -
    1. 新增 Matugen 的 colors.sh.tera,只輸出 SketchyBar 需要的 Material tokens。
    2. -
    3. surface_variant 轉成約 30% alpha 的 ARGB,文字使用 on_surface
    4. -
    5. 讓靜態 sketchybarrc source 生成的 colors.sh;檔案不存在時使用少量安全預設色。
    6. -
    7. Matugen post-hook 呼叫 sketchybar --reload
    8. -
    9. 將模板 hash 加入既有 apply-theme onchange script。
    10. -
    -

    驗收

    -
      -
    • -
    • -
    • -
    -
    -
    - -
    - 階段 4:CodexBar、Stats aliases 與 tray -
    -
    規模:中相依:階段 3目標:重用既有監控
    -
      -
    1. 啟動 CodexBar,來源使用 Codex OAuth/Claude CLI;合併 provider icons,hover 在同一摘要區分兩者,左鍵原生面板可切 provider。
    2. -
    3. 在 Stats GUI 一次性啟用實際需要的 CPU、GPU、RAM、Network、Sensors 與 Battery menu items。
    4. -
    5. 執行 sketchybar --query default_menu_items,取得 CodexBar、Stats 與 tray 的本機真實 owner/name;不要預先猜測名稱。
    6. -
    7. 將 CodexBar 原生 item 加為 alias;左鍵以 System Events 按原生 status item。單純 open -b 不會打開用量選單;0.60 的選單關閉時仍存在,需檢查零尺寸狀態。
    8. -
    9. 只加入確定存在的 aliases;網路與系統監控分成兩個 bracket capsule。
    10. -
    11. 登入啟動由 AeroSpace 負責,螢幕錄製授權應給 AeroSpace;終端機手動啟動則另需授權該終端機。新授權後完整重啟程序,只有 reload 不夠。
    12. -
    13. Hover 以 60 秒 CLI 更新額度與重置倒數;失敗保留舊摘要並警告,不替代 alias、不啟動常駐 server。
    14. -
    15. 具名 alias 移除 query 的變動 (n) 索引,保留原生圖像顏色;實測 Stats 重啟後不需 reload 就能重新綁定。
    16. -
    -

    驗收

    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -

    CodexBar 與 Stats preferences 先保留為一次性 GUI 設定;只有確認跨機器重建需求後,才考慮納管其偏好檔。

    -
    -
    - -
    - 階段 4A–4D:補完原生互動與不重疊(4A 已接線,其餘仍待完成) -
    -
    規模:中;互斥偵測有未知風險相依:現有 aliases 可見目標:能操作且不覆蓋
    -

    4A:公開事件交替顯示(已接線)

    -
      -
    1. menu-bar-events.swift 接收公開 Carbon kEventMenuBarShownkEventMenuBarHidden,分別隱藏/恢復 SketchyBar;Apple SDK 的 CarbonEvents.h 明確記載這兩種事件會通知所有已註冊程序。本機 Fn-C、頂端滑鼠、Control-F2/Esc 已取得成對事件。
    2. -
    3. 初始、reload、喚醒、Space/螢幕變更時,用公開 CGWindowListCopyWindowInfo 同步目前可見的 Window Server Menubar。NSMenu.menuBarVisible() 只描述呼叫的 App,本機背景查詢始終為 false,不能用來判斷前景列。
    4. -
    5. 由 SketchyBar 設定檔啟動一個有 lockf 鎖的 listener,包含父程序並選最早啟動的 SketchyBar PID;不把短命 CLI 當主程序。主 bar 退出時 listener 也退出,外層 shell 在 helper 錯誤/崩潰時恢復 bar;正常 reload 不建立重複 listener。
    6. -
    -

    檔案:menu-bar-events.swiftexecutable_sketchybarrc 與 macOS-only 編譯 hook;原有按鈕腳本不必各加一份判斷。無固定頻率輪詢、無 event tap、無私有 SkyLight、無新增第三方套件或 LaunchAgent。listener 在本機閒置時觀察到 CPU 0.0%、RSS 約 31 MiB;這是一次觀察,不是效能保證。

    -
    Checkpoint C:原生列是否可見才是恢復依據。Esc 有時只關面板、未收起原生列,此時維持 SketchyBar 隱藏是正確行為;游標離開、系統收起後才恢復。任何螢幕仍顯示原生列時,先讓所有 SketchyBar 顯示區一起暫避;多螢幕實際驗收仍待完成。helper 崩潰時優先恢復可用的 bar,需 reload 才重新啟動交替。
    -
      -
    • -
    • -
    • -
    -

    以上是實機操作與畫面/狀態查核,不宣稱 compositor 層級逐幀原子切換。外接螢幕、全螢幕、睡眠喚醒、重新登入與主 bar 重啟仍列後續驗收;既有四平台安裝 CI 不替代這些操作。

    - -

    4B:Stats CPU/RAM 接完整

    -
      -
    1. 取得本機 CPU/RAM 對應 status item 的穩定識別資訊,優先使用上游既有入口;需要 AX 點擊時不依賴易變索引,名稱相同/模組缺少時不得點到其他項目。
    2. -
    3. 取代目前 CPU/RAM 的 open -a Stats:CPU 開 CPU 詳細面板、RAM 開記憶體詳細面板,保留 Stats 原本提供的圖表、詳細數據與設定入口。不新增 CPU/mem polling 或自行重製面板。
    4. -
    5. 整合 4A 的互斥方式,驗證開啟、切換、關閉與 Stats 重啟後重新綁定;不得影響其他 Stats aliases 或 Codex/Claude hover。
    6. -
    -

    相依:4A;檔案:executable_sketchybarrcplugins/executable_native-menu.sh。本階段只補 CPU/RAM 互動,不順帶擴大其他監控模組。

    -
      -
    • -
    • -
    - -

    4C:接入前景 App 選單

    -
      -
    1. 新增單一前景 App 按鈕,隨 App 切換更新;點擊按需顯示真實頂層選單,含 Apple 與 App 自身選單,不硬編碼 Kitty 專用清單。
    2. -
    3. 選擇標題後操作原生子選單,保留深層項目、Help、快捷鍵、停用狀態與 Option 替代項目。不得因入口取得焦點而誤操作另一個 App;長標題/多項目不能靜默截斷。
    4. -
    5. 接上 4A 互斥,保留 Control-F2 作為完整原生備援。若現有公開 AX 路線在特定 App 失效,先列出缺口,再決定是否改用 Menuwhere;不直接複製 Felix 的私有 API helper。
    6. -
    -

    相依:4A,接線與 4B 共用;檔案:executable_sketchybarrcplugins/executable_native-menu.sh。驗收使用 c22;至少涵蓋 Kitty、目前使用的瀏覽器及一個長/深層選單 App,並實際執行安全命令確認不是只有標題能顯示。

    - -

    4D:整體互斥、控制中心與恢復驗收

    -
      -
    1. 依本次比較暫保留原生控制中心,整合既有設定按鈕;實際確認 Wi-Fi 網路清單、Focus 模式/期限、Bluetooth/音訊輸出等入口,不為驗收任意切斷網路或更動專注模式。
    2. -
    3. 逐一操作頂端滑鼠、App 按鈕、CPU/RAM、CodexBar、控制中心及系統快捷鍵;以螢幕錄影/截圖檢查開啟、深層選單、Esc、點外部、命令執行後、切換 App 的所有狀態,不只拍靜態成功畫面。
    4. -
    5. 內建/外接螢幕、瀏海、全螢幕進出、喚醒及重新登入後重做;驗證錯誤或程序退出不會讓 SketchyBar 永久消失,且失敗時仍有原生選單可操作。
    6. -
    -

    相依:4B+4C;驗收使用 c23/c24。登入接線只有在 4A 證實需要時才改 AeroSpace;不得另起重複 bar。功能以實機使用驗證,不新增功能測試腳本。

    -
    -
    - -
    - 階段 5:時鐘、音量與多螢幕收尾 -
    -
    規模:中相依:階段 4 與 4A–4D目標:完成日常使用
    -
      -
    1. 加入每秒 clock script,格式對齊 %B %d %H:%M:%S
    2. -
    3. volume item 訂閱 volume_changemouse.scrolled
    4. -
    5. 滾輪依方向調整 3%,並夾在 0–100;點擊切換靜音。
    6. -
    7. 最右側設定按鈕以固定 com.apple.menuextra.controlcenter 識別碼開啟原生控制中心;Wi-Fi/勿擾模式仍使用系統原生面板。
    8. -
    9. 驗證內建螢幕、外接螢幕、notch、睡眠喚醒與登入啟動。
    10. -
    11. 保留系統 auto-hide,搭配階段 4A–4D 的互斥顯示;auto-hide 本身不算完成不重疊,不加入永久阻擋游標觸發的 hack。
    12. -
    -

    驗收

    -
      -
    • -
    • -
    • -
    • -
    -
    Checkpoint B:完成後執行 chezmoi diffchezmoi apply,再進行一次乾淨登入驗證。
    -
    -
    - -
    - 階段 6:文件與提交 -
    -
    規模:小相依:階段 5目標:可重建、可回復
    -
      -
    1. README 記錄 SketchyBar 架構、CodexBar/Stats 一次性設定與 Screen Recording 權限。
    2. -
    3. 確認 diff 無 token、個人路徑或生成的 colors.sh
    4. -
    5. 延續 PR #155,以小型提交拆分已驗證的邏輯變更;階段是工作順序,不代表每階段自動另開 MR。未完成的實機驗收保持未勾選。
    6. -
    7. 同步更新本計畫;四平台 CI 只跑安裝流程,不加入套件功能 mock 測試。保留 -v,確認 PR 最新 commit 的 checks 全過。
    8. -
    -

    驗收

    -
      -
    • -
    • -
    • -
    -
    -
    -
    - -
    -
    -

    風險與處理

    -
    -
    - - - - - - - - - - - - - -
    風險影響處理方式
    Stats alias 名稱依機器或版本不同在實機 query 後才加入;缺少 alias 時略過,不讓 bar 啟動失敗。
    CodexBar 原生 status item 在 macOS 26 未出現在 query先確認「Allow in the Menu Bar」與 AeroSpace 螢幕錄製授權;CLI 目前只提供 hover 摘要,不宣稱可取代缺失的原生 item。參見 CodexBar #1109
    CodexBar CLI 更新失敗或回報缺少額度失敗保留舊摘要並警告;成功但沒有某區間時略過該區間,不把未知額度當 0%。
    第三方 showy-quota 維護規模較小且功能過量不列為必要相依;只有未來要同時供 SketchyBar、tmux、Zellij 顯示多 provider 時重評。
    內建與外接螢幕 bar 高度不同先用 36px;只有測試失敗才加入 per-monitor AeroSpace gap。
    Matugen reload 時色彩檔短暫不存在靜態 config 提供最小 fallback palette。
    原生 menu bar 與 SketchyBar 的狀態不同步4A 已接公開 Carbon 事件與 reload/喚醒同步。CGWindow 的 Menubar 名稱已在 macOS 26.6.2 查核,跨版本仍需實機驗收;helper 崩潰先恢復 bar,reload 重啟交替;SIP 保持啟用。
    替代控制中心只有快捷開關,缺少完整操作替換門檻逐項查核;目前保留原生面板,不以 Wi-Fi 開關冒充網路選擇器,也不宣稱原生面板能套完整 Matugen。
    Stats GUI state 尚未納入 chezmoiREADME 記錄一次性設定;只有實際跨機重建痛點出現時再自動化。
    -
    -
    - -
    -
    -

    完成定義

    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    - -
    -
    -

    實作參考與來源

    -
    - -
    -
    - -
    -

    實作已納入此 dotfiles repository;本文件保留調查依據、階段與實機驗收項目。

    -
    +
    +

    目標配置

    +
    +
    左側App tray GPT 圖示+用量 · Claude 圖示+用量
    +
    瀏海兩側月份/日期/時鐘 1 2 3 4 5 6 7 8 9 M
    +
    右側Stats:CPU · RAM · 感測器 · 網路 音量 目前輸入法
    +
    +

    區域示意,不是完成截圖。Stats 只顯示實際啟用的模組,全部共用一個緊密背景框;GPT/Claude 同樣共用一組。透明列、高 36 pt、Matugen 半透明 capsule 與瀏海保留區不變。

    +
    +
    +

    日常操作

    +
    + + + + + + + + + + +
    項目行為
    App tray顯示實際執行中的常駐 App 圖示,點擊開原生 status 選單;不是 File/Edit/Help 的移植。
    GPT/Claude同一組同時顯示兩個原生圖示與用量;hover 共用分格摘要與重置倒數;左鍵開原生詳細選單,右鍵刷新兩者。
    Stats重用原生資料與圖示,縮減空白、合成一組;點擊對應模組開原生詳細面板。
    輸入法顯示目前輸入法的原生圖示,切換語言/中英文模式後同步。
    App 選單游標到頂端,或 FnControlF2,使用完整原生選單。
    控制中心FnC。SketchyBar 不再放控制中心按鈕。
    交替顯示原生列出現時 SketchyBar 隱藏;原生列真正收起後恢復。沿用公開 Carbon 事件,不更改 SIP。
    +
    +
    +

    本輪任務與驗收

    +
      +
    1. 精簡:移除控制中心按鈕及腳本分支,刪除舊方案比較與取消的待辦;保留目前交替功能。
    2. +
    3. Tray/輸入法:修正 macOS 把圖示統一命名為 Item-0 所造成的遺漏;實測 App 圖示、點擊及輸入法切換。
    4. +
    5. Stats:共用一個背景框並縮減 padding;實際點擊 CPU/RAM 驗證對應面板。
    6. +
    7. 雙 AI 圖示:使用 CodexBar 既有原生 meter,不新增圖片 renderer 或用量服務;實測兩端 hover/click 與倒數。
    8. +
    9. 交付:套用本機、擷取畫面;推送現有 MR #155,等待最新 commit 的四平台安裝 CI 全部通過。
    10. +
    +

    不新增功能測試腳本。CI 維持 chezmoi init --apply -v 的安裝驗證;介面在實機操作,不以 CI 綠燈代替功能驗收。

    +

    外接螢幕、睡眠及重新登入屬實際使用驗收;未在本輪操作的情境不標記通過。

    +
    +
    +

    維護邊界

    +

    App 擁有資料與詳細面板;SketchyBar 只負責排列、摘要及入口;Matugen 只更新配色。保留登入與授權設定,不存取或納管 token、cookie、帳號用量 JSON。

    +

    安裝與權限步驟見 README;最新結果見 MR checks

    +

    依據:SketchyBar bracket/aliasCodexBar 原生圖示設定

    +
    +
    From 1b4cff0e91013a9c30b336abeaa87ecbac271c95 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 19:41:00 +0800 Subject: [PATCH 24/30] feat: complete compact SketchyBar widgets and dynamic workspaces --- README.md | 22 ++-- ...un_onchange_after_2-codexbar-icons.sh.tmpl | 7 ++ ...ange_after_3-build-sketchybar-menu.sh.tmpl | 4 + .../sketchybar/executable_sketchybarrc | 104 ++++++++++-------- .../plugins/executable_native-menu.sh | 22 ++-- .../plugins/executable_workspace.sh | 31 +++++- home/dot_config/sketchybar/status-items.swift | 90 +++++++++++++++ sketchybar-plan.html | 23 ++-- 8 files changed, 221 insertions(+), 82 deletions(-) create mode 100644 home/.chezmoiscripts/run_onchange_after_2-codexbar-icons.sh.tmpl create mode 100644 home/dot_config/sketchybar/status-items.swift diff --git a/README.md b/README.md index feb1df3..2c0da83 100644 --- a/README.md +++ b/README.md @@ -63,29 +63,31 @@ chezmoi init --apply collieiscute -v - AeroSpace starts SketchyBar, reserves 36 px at the top, and sends workspace changes through the official `aerospace_workspace_change` event. - Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. -- Left: third-party tray aliases and the first CodexBar status item. The clock sits left of the notch (`q`), with workspaces `1..9` and `M` on its right (`e`); `notch_width=200` reserves the center. Right: Control Center, 3%-step volume, and active Stats CPU/GPU/RAM/network/sensor/battery items. -- CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. SketchyBar mirrors one native meter and reads `codexbar usage --provider both` for a Matugen-colored summary; the command honors each provider's configured source. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. -- Hover shows separate Codex and Claude sections in the same tooltip; leaving hides it. Left-click opens CodexBar's native detailed menu, where the Codex/Claude tabs switch providers. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete provider response keeps the last complete summary with a warning instead of presenting partial data as fresh. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. +- Left: native GPT and Claude icons/meters in one capsule, with one shared hover summary. No App tray. The clock sits left of the notch (`q`), with occupied workspaces on its right (`e`); `notch_width=200` reserves the center. Right: one compact Stats group, 3%-step volume, and the native input-source icon. +- Workspaces `1..9` and `magic` (`M`) are visible only when occupied or currently focused. Keyboard shortcuts still reach hidden, empty workspaces. One script batches visibility and highlighting, responds to workspace/window events, and refreshes every two seconds to cover background window moves that emit no native event. +- CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. Two native status items are grouped visually by SketchyBar; `codexbar usage --provider both` supplies the Matugen-colored summary. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. +- Hover over either icon shows both providers and their reset countdowns; leaving hides it. Left-click opens that provider's native detailed menu. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete response keeps the last complete summary with a warning. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. - Each quota window is a bordered, two-line native item: remaining quota above, reset countdown below, with bold Codex/Claude headings. Borders and secondary text follow the Matugen palette; the tooltip keeps its existing width. Countdown values use CodexBar's UTC `resetsAt`, round up to minutes, and update with the existing 60-second refresh. Missing or invalid reset times are omitted and leave a compact single-line cell; an elapsed timestamp says it is waiting for the source to update, rather than claiming the quota has reset. -- Click the rightmost settings icon to open macOS Control Center (Wi-Fi, Bluetooth, Focus, etc.). Native menu buttons use System Events accessibility actions; panels keep their original screen anchors. +- Stats hover shows its native localized tooltip (module/widget name, not a live metric summary); click opens the corresponding Stats panel. The input-source alias mirrors native language/mode changes and opens the native input menu on click. Panels keep their original screen anchors. +- Use Fn-C for Control Center and the top edge or Fn-Control-F2 for the full App menu. Neither has a duplicate SketchyBar button. - `sb-menu-events` listens for the public Carbon `kEventMenuBarShown` / `kEventMenuBarHidden` broadcasts and alternates the two bars. It also covers the top screen edge and system keyboard shortcuts, without polling the cursor, intercepting input, or changing SIP. If any display still has a native menu bar, SketchyBar stays hidden. Closing a panel with Esc does not always hide the native bar immediately; SketchyBar returns when macOS actually retracts it. -- Chezmoi compiles `menu-bar-events.swift` with the existing Xcode Command Line Tools. SketchyBar starts one locked listener, re-synchronizes on reload, and stops the listener when the main bar exits. A supervising shell restores SketchyBar if the listener crashes; reload to restart coordination. No separate LaunchAgent or third-party package is installed. -- Named aliases bind without the query's changing `(n)` suffix, so app restarts can reattach them. Alias images keep their native colors; tinting opaque Stats charts would turn the whole image into a solid block. Matugen owns the surrounding capsules and regular bar text. +- Chezmoi compiles `sb-menu-events` and `sb-status-items` with the existing Xcode Command Line Tools. The former is one locked listener tied to SketchyBar's lifetime; its supervisor restores the bar after a crash. The latter runs only for setup/clicks, matching native status-window geometry to public Accessibility elements from Stats, CodexBar and TextInputMenuAgent. This identifies macOS 26's otherwise indistinguishable `ControlCenter,Item-0` windows without adding a tray or a new dependency. +- Named aliases omit the query's changing `(n)` suffix. The input-source `Item-0` retains its resolved index: reload after its native status item is recreated or the menu-bar arrangement changes. Alias images keep native colors; Matugen owns the surrounding capsules and regular text. After the first `chezmoi apply`: -1. Open CodexBar and Stats once. Enable Codex with OAuth and Claude with CLI, then use one merged status item. The managed CodexBar config sets these sources for existing provider entries; if an app upgrade resets them, reapply `~/.config/codexbar/config.json`. Select an icon-and-percentage style and weekly metric for a readable quota meter. In Stats, enable the compact items you actually want mirrored. +1. Open CodexBar and Stats once. Enable Codex with OAuth and Claude with CLI. Chezmoi sets CodexBar's native `mergeIcons=false`; relaunch CodexBar if it was already open when this setting was first applied. Select an icon-and-percentage style; SketchyBar groups the two native meters. The managed JSON preserves other settings while selecting these sources for existing provider entries. In Stats, enable the compact items you want mirrored. 2. Allow **AeroSpace** under **System Settings → Privacy & Security → Screen & System Audio Recording** for login startup. If launching SketchyBar manually from a terminal, allow that terminal too; its permission does not cover AeroSpace. After granting permission, quit SketchyBar and restart AeroSpace to launch a fresh bar; `sketchybar --reload` alone does not refresh TCC permissions. 3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This hides the system bar until the pointer touches the top edge; it does not permanently disable it. No reboot or SIP change is needed. -4. Allow the SketchyBar launcher (AeroSpace, or the terminal for manual launches) to control **System Events** if macOS requests Automation permission. It also needs Accessibility permission for native menu buttons. +4. Allow the SketchyBar launcher (AeroSpace, or the terminal for manual launches) under **Accessibility** for native status-item discovery and clicks. The new helper uses Accessibility directly, not System Events automation. CI validates installation with `chezmoi init --apply -v` across supported operating systems. Bar interactions are checked on the actual Mac, not with mocked feature-test scripts. -App menu buttons (File/Edit/Window/Help) and direct Stats CPU/RAM popup clicks are not yet integrated. Native menus remain available through the top edge or Control-F2 / Fn-Control-F2, with alternating visibility. See [the plan and acceptance status](sketchybar-plan.html#native-integration-tasks); external-monitor, sleep and login checks remain separate from the installation CI. +See [the concise plan and acceptance status](sketchybar-plan.html#tasks-title). External-monitor, sleep and login checks remain separate from installation CI. For a live coordination check, open Control Center with Fn-C and run `sketchybar --query bar | jq -e '.hidden == "on"'`. After dismissing it and moving the pointer away so the native bar retracts, the same check with `"off"` must succeed. This checks the running Mac, not a mocked feature test. -If an alias is missing, compare `sketchybar --query default_menu_items` with the app's visible menu-bar item and reload. Missing aliases are skipped without breaking the bar. The CLI supplies the hover summary only; it does not replace the native bar meter. +If an alias is missing, compare `sb-status-items list` and `sketchybar --query default_menu_items` with the native item, then reload. Missing/ambiguous native items are skipped or return an error rather than clicking another app. ### Hyprland diff --git a/home/.chezmoiscripts/run_onchange_after_2-codexbar-icons.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_2-codexbar-icons.sh.tmpl new file mode 100644 index 0000000..42ce586 --- /dev/null +++ b/home/.chezmoiscripts/run_onchange_after_2-codexbar-icons.sh.tmpl @@ -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 }} diff --git a/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl index e454cde..34065a2 100644 --- a/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl +++ b/home/.chezmoiscripts/run_onchange_after_3-build-sketchybar-menu.sh.tmpl @@ -2,12 +2,16 @@ #!/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 || : diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 9fa7834..02e5494 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -62,10 +62,14 @@ for sid in 1 2 3 4 5 6 7 8 9 magic; do background.color="$PRIMARY" \ background.drawing=off \ click_script="aerospace workspace $sid" \ - script="$PLUGIN_DIR/workspace.sh" \ - --subscribe "space.$sid" aerospace_workspace_change + 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\..*/' \ --set workspaces \ padding_left=5 \ @@ -85,9 +89,11 @@ sketchybar --add item volume right \ --subscribe volume volume_change mouse.scrolled mouse.clicked system_woke add_alias() { - # The query suffix is a changing list index, not part of a named window. - # Keep it only for unnamed items (Owner,(n)); named aliases can rebind natively. - source=$(printf '%s\n' "$1" | sed 's/\([^,]\)([0-9][0-9]*)$/\1/') + # 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 name=$2 position=$3 click_script=${4-} @@ -105,58 +111,66 @@ add_alias() { click_script="$click_script" } -# The query returns only currently available items. Missing apps or permissions -# therefore leave the rest of the bar usable instead of failing startup. -menu_items=$(sketchybar --query default_menu_items 2>/dev/null || :) +# 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.height=28 \ + popup.background.color="0xff${CAPSULE#????}" \ + popup.background.corner_radius=10 \ + popup.background.border_width=1 \ + popup.background.border_color="0x33${ON_SURFACE#????}" \ + 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" \ - | sed -n 's/^[[:space:]]*"\(.*\)"[,[:space:]]*$/\1/p' \ + | /usr/bin/jq -r '.[] | [.bundle, .source, .window, .help] | @tsv' \ | ( - index=0 - codex_added=false - while IFS= read -r source; do - index=$((index + 1)) - case "$source" in - CodexBar,*|*,codexbar-*) - test "$codex_added" = false || continue - add_alias "$source" codexbar left '' - sketchybar --set codexbar \ - padding_left=5 \ - padding_right=5 \ + 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" \ alias.scale=1 \ - background.color="$CAPSULE" \ - background.drawing=on \ - popup.align=left \ - popup.height=28 \ + script="$PLUGIN_DIR/codexbar.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.height=28 \ popup.background.color="0xff${CAPSULE#????}" \ - popup.background.corner_radius=10 \ + popup.background.corner_radius=6 \ popup.background.border_width=1 \ popup.background.border_color="0x33${ON_SURFACE#????}" \ - update_freq=60 \ - script="$PLUGIN_DIR/codexbar.sh" \ - --subscribe codexbar mouse.entered mouse.exited mouse.clicked 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 - codex_added=true - ;; - Stats,*Network*|*,Network_*|Stats,*CPU*|Stats,*GPU*|Stats,*RAM*|Stats,*Memory*|Stats,*Sensor*|*,CPU_*|*,GPU_*|*,RAM_*|*,Memory_*|*,Sensor*) - add_alias "$source" "stats.$index" right 'open -a Stats' - ;; - Stats,*|Karabiner-Console-User-Server,*|Control\ Center,*|Control\ Centre,*|SystemUIServer,*|Window\ Server,*|TextInputMenuAgent,*|*,Clock*|*,Siri*|*,BentoBox-*|*,WiFi*|*,Battery*|*,AudioVideoModule*|*,Item-*) + 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 ;; - *) - add_alias "$source" "tray.$index" left '' + com.apple.TextInputMenuAgent) + add_alias "$source" input_source right "$click" + sketchybar --set input_source background.color="$CAPSULE" background.drawing=on \ + padding_left=5 padding_right=5 alias.scale=1 \ + --move input_source before volume ;; esac done ) -for group in tray stats; do +for group in meter stats; do sketchybar --add bracket "$group.group" "/$group\\..*/" \ --set "$group.group" \ padding_left=5 \ diff --git a/home/dot_config/sketchybar/plugins/executable_native-menu.sh b/home/dot_config/sketchybar/plugins/executable_native-menu.sh index 6210b2a..7db5b79 100755 --- a/home/dot_config/sketchybar/plugins/executable_native-menu.sh +++ b/home/dot_config/sketchybar/plugins/executable_native-menu.sh @@ -1,5 +1,15 @@ #!/bin/sh +case "${NAME:-}" in + stats.*) + case "${SENDER:-}" in + mouse.entered) sketchybar --set "$NAME" popup.drawing=on ;; + mouse.exited|mouse.clicked) sketchybar --set "$NAME" popup.drawing=off ;; + esac + exit 0 + ;; +esac + # --update also runs item scripts; never open menus during startup or reload. case "${SENDER:-}" in mouse.clicked) ;; @@ -7,15 +17,7 @@ case "${SENDER:-}" in esac case "${NAME:-}" in - codexbar) - # Opening the app does not open its status menu. CodexBar 0.60 keeps a - # zero-size menu when closed, so existence alone is not visibility. - osascript <<'APPLESCRIPT' -tell application "System Events" to tell process "CodexBar" - tell menu bar item 1 of menu bar 2 - if not (exists menu 1) or (size of menu 1 is {0, 0}) then click - end tell -end tell -APPLESCRIPT + meter.codex|meter.claude) + exec "$HOME/.local/bin/sb-status-items" click com.steipete.codexbar "codexbar-${NAME#meter.}" ;; esac diff --git a/home/dot_config/sketchybar/plugins/executable_workspace.sh b/home/dot_config/sketchybar/plugins/executable_workspace.sh index bad3338..43ac3f1 100755 --- a/home/dot_config/sketchybar/plugins/executable_workspace.sh +++ b/home/dot_config/sketchybar/plugins/executable_workspace.sh @@ -6,9 +6,28 @@ ON_SURFACE=0xfff2f0f4 ON_PRIMARY=0xff381e72 test -r "$CONFIG_DIR/colors.sh" && . "$CONFIG_DIR/colors.sh" -sid=${NAME#space.} -if test "${FOCUSED_WORKSPACE:-}" = "$sid"; then - sketchybar --set "$NAME" background.drawing=on icon.color="$ON_PRIMARY" -else - sketchybar --set "$NAME" background.drawing=off icon.color="$ON_SURFACE" -fi +# One batch updates all slots. Events handle focus/create/close; the 2 s refresh +# also covers moving a background window without a native window event. +# https://nikitabobko.github.io/AeroSpace/commands#list-workspaces +mkdir -p "$HOME/.cache/sketchybar" +exec 9>"$HOME/.cache/sketchybar/workspaces.lock" +lockf -s -t 0 9 || exit 0 +focused=$(aerospace list-workspaces --focused) || exit 1 +occupied=$(aerospace list-workspaces --monitor all --empty no) || exit 1 + +set -- +for sid in 1 2 3 4 5 6 7 8 9 magic; do + visible=off + highlight=off + color=$ON_SURFACE + if test "$sid" = "$focused"; then + visible=on + highlight=on + color=$ON_PRIMARY + elif printf '%s\n' "$occupied" | grep -Fxq "$sid"; then + visible=on + fi + set -- "$@" --set "space.$sid" "drawing=$visible" \ + "background.drawing=$highlight" "icon.color=$color" +done +sketchybar "$@" diff --git a/home/dot_config/sketchybar/status-items.swift b/home/dot_config/sketchybar/status-items.swift new file mode 100644 index 0000000..d342829 --- /dev/null +++ b/home/dot_config/sketchybar/status-items.swift @@ -0,0 +1,90 @@ +import AppKit +import ApplicationServices + +// macOS 26 hosts many status windows in ControlCenter as identical "Item-0". +// Match their geometry to each app's public AXExtrasMenuBar, not the host name. +// https://developer.apple.com/documentation/applicationservices/axuielement_h +func attribute(_ element: AXUIElement, _ key: String) -> CFTypeRef? { + var value: CFTypeRef? + return AXUIElementCopyAttributeValue(element, key as CFString, &value) == .success ? value : nil +} + +struct StatusItem { + let bundle: String + let source: String + let window: String + let help: String + let element: AXUIElement +} + +func statusItems(bundle: String? = nil) -> [StatusItem] { + let windows = (CGWindowListCopyWindowInfo(.optionAll, 0) as? [[String: Any]] ?? []) + .filter { + $0[kCGWindowLayer as String] as? Int == Int(CGWindowLevelForKey(.statusWindow)) + && $0[kCGWindowOwnerName as String] as? String != "Window Server" + && $0[kCGWindowName as String] != nil + } + .sorted { bounds($0).minX > bounds($1).minX } + var result: [StatusItem] = [] + for app in NSWorkspace.shared.runningApplications { + guard let id = app.bundleIdentifier, app.processIdentifier != getpid(), + bundle == nil || bundle == id, + ["com.apple.TextInputMenuAgent", "eu.exelban.Stats", "com.steipete.codexbar"].contains(id) else { continue } + let root = AXUIElementCreateApplication(app.processIdentifier) + AXUIElementSetMessagingTimeout(root, 0.2) + guard let bar = attribute(root, kAXExtrasMenuBarAttribute) else { continue } + let elements = attribute(bar as! AXUIElement, kAXChildrenAttribute) as? [AXUIElement] ?? [] + for element in elements { + AXUIElementSetMessagingTimeout(element, 0.2) + guard let position = attribute(element, kAXPositionAttribute), + let size = attribute(element, kAXSizeAttribute) else { continue } + var origin = CGPoint.zero + var dimensions = CGSize.zero + guard AXValueGetValue(position as! AXValue, .cgPoint, &origin), + AXValueGetValue(size as! AXValue, .cgSize, &dimensions), + dimensions.width > 0, dimensions.height > 0 else { continue } + let center = CGPoint(x: origin.x + dimensions.width / 2, y: origin.y + dimensions.height / 2) + // Zero-size AX placeholders and non-status windows never match. + let matches = windows.enumerated().filter { bounds($0.element).contains(center) } + guard matches.count == 1, let match = matches.first, + let owner = match.element[kCGWindowOwnerName as String] as? String, + let name = match.element[kCGWindowName as String] as? String else { continue } + result.append(StatusItem(bundle: id, source: "\(owner),\(name)(\(match.offset + 1))", + window: name, help: attribute(element, kAXHelpAttribute) as? String ?? "", + element: element)) + } + } + return result +} + +func bounds(_ window: [String: Any]) -> CGRect { + guard let value = window[kCGWindowBounds as String] as? NSDictionary else { return .null } + return CGRect(dictionaryRepresentation: value) ?? .null +} + +let arguments = CommandLine.arguments +guard AXIsProcessTrusted() else { + fputs("sb-status-items: the launcher needs Accessibility permission.\n", stderr) + exit(1) +} +if arguments.count == 2 && arguments[1] == "list" { + let rows = statusItems().map { ["bundle": $0.bundle, "source": $0.source, "window": $0.window, "help": $0.help] } + do { + let data = try JSONSerialization.data(withJSONObject: rows, options: [.sortedKeys]) + print(String(decoding: data, as: UTF8.self)) + } catch { fputs("sb-status-items: \(error)\n", stderr); exit(1) } +} else if arguments.count == 4 && arguments[1] == "click" { + let matches = statusItems(bundle: arguments[2]).filter { $0.window == arguments[3] } + guard matches.count == 1, let item = matches.first else { + fputs("sb-status-items: missing or ambiguous status item; reload SketchyBar.\n", stderr) + exit(1) + } + let status = AXUIElementPerformAction(item.element, kAXPressAction as CFString) + guard status == .success else { + fputs("sb-status-items: native click failed (\(status.rawValue)).\n", stderr) + exit(1) + } +} else { + fputs("usage: sb-status-items list | click BUNDLE_ID WINDOW_NAME\n", stderr) + exit(64) +} diff --git a/sketchybar-plan.html b/sketchybar-plan.html index b109a2b..1240eaa 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -34,13 +34,13 @@ Collie's dotfiles · macOS · 更新 2026-09-13

    SketchyBar 計畫

    保留目前好用的原生列/SketchyBar 交替顯示。只補齊日常資訊,不重做 macOS 的選單和控制中心。

    -

    本輪狀態:實作中;下列驗收完成後才標示通過。

    +

    本輪狀態:已套用本機並完成下列功能驗收;安裝 CI 以 MR 最新 commit 的 checks 為準。

    目標配置

    -
    左側App tray GPT 圖示+用量 · Claude 圖示+用量
    -
    瀏海兩側月份/日期/時鐘 1 2 3 4 5 6 7 8 9 M
    +
    左側GPT 圖示+用量 · Claude 圖示+用量
    +
    瀏海兩側月份/日期/時鐘 有視窗的 workspace +目前所在 workspace
    右側Stats:CPU · RAM · 感測器 · 網路 音量 目前輸入法

    區域示意,不是完成截圖。Stats 只顯示實際啟用的模組,全部共用一個緊密背景框;GPT/Claude 同樣共用一組。透明列、高 36 pt、Matugen 半透明 capsule 與瀏海保留區不變。

    @@ -50,9 +50,9 @@

    日常操作

    - + - + @@ -63,11 +63,12 @@

    日常操作

    本輪任務與驗收

      -
    1. 精簡:移除控制中心按鈕及腳本分支,刪除舊方案比較與取消的待辦;保留目前交替功能。
    2. -
    3. Tray/輸入法:修正 macOS 把圖示統一命名為 Item-0 所造成的遺漏;實測 App 圖示、點擊及輸入法切換。
    4. -
    5. Stats:共用一個背景框並縮減 padding;實際點擊 CPU/RAM 驗證對應面板。
    6. -
    7. 雙 AI 圖示:使用 CodexBar 既有原生 meter,不新增圖片 renderer 或用量服務;實測兩端 hover/click 與倒數。
    8. -
    9. 交付:套用本機、擷取畫面;推送現有 MR #155,等待最新 commit 的四平台安裝 CI 全部通過。
    10. +
    11. 已驗收・精簡:控制中心按鈕與 App tray 均已移除;原生列從頂端出現/收起時,SketchyBar 交替隱藏/恢復。
    12. +
    13. 已驗收・輸入法:ABC 與唯音繁體之間切換,原生圖示同步變化,測試後已恢復原來來源。
    14. +
    15. 已驗收・Stats:共用一個緊密背景框;CPU/RAM/感測器/網路各有原生 hover 名稱提示,CPU/RAM 實際點擊開正確詳細面板。
    16. +
    17. 已驗收・雙 AI 圖示:兩個原生 meter 同組;兩端 hover 都能看 Codex/Claude 分格摘要及重置倒數,左鍵開各自的原生詳細選單。
    18. +
    19. 已驗收・動態 Workspace:專用測試視窗移至 9/magic 時顯示,移走/關閉後隱藏;目前空工作區仍顯示並高亮。事件更新加上每 2 秒校正,涵蓋背景視窗搬移。
    20. +
    21. 安裝 CI:沿用 MR #155;以最新 commit 的 macOS/Arch/Ubuntu/Windows 四平台 checks 為交付依據。

    不新增功能測試腳本。CI 維持 chezmoi init --apply -v 的安裝驗證;介面在實機操作,不以 CI 綠燈代替功能驗收。

    外接螢幕、睡眠及重新登入屬實際使用驗收;未在本輪操作的情境不標記通過。

    @@ -76,7 +77,7 @@

    本輪任務與驗收

    維護邊界

    App 擁有資料與詳細面板;SketchyBar 只負責排列、摘要及入口;Matugen 只更新配色。保留登入與授權設定,不存取或納管 token、cookie、帳號用量 JSON。

    安裝與權限步驟見 README;最新結果見 MR checks

    -

    依據:SketchyBar bracket/aliasCodexBar 原生圖示設定

    +

    依據:SketchyBar bracket/aliasCodexBar 原生圖示Stats 原生 tooltipAeroSpace 工作區查詢

    From 93d5cce5623510451fbc7bbfb28a082bb79afcc9 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Sun, 13 Sep 2026 19:48:27 +0800 Subject: [PATCH 25/30] fix: equalize capsule spacing and compact AI meters --- README.md | 1 + .../sketchybar/executable_sketchybarrc | 23 +++++++++---------- sketchybar-plan.html | 2 ++ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2c0da83..9879e75 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ chezmoi init --apply collieiscute -v - Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. - The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. - Left: native GPT and Claude icons/meters in one capsule, with one shared hover summary. No App tray. The clock sits left of the notch (`q`), with occupied workspaces on its right (`e`); `notch_width=200` reserves the center. Right: one compact Stats group, 3%-step volume, and the native input-source icon. +- Adjacent capsule backgrounds in the same region have an 8 pt gap, whether grouped or standalone; outer screen insets are also 8 pt. Normal capsules own the gap on their left because brackets absorb their members' padding. Notch clearance and flexible space between anchored regions remain separate. AI aliases use 85% native scale with no added internal padding, keeping both percentages in a smaller capsule. - Workspaces `1..9` and `magic` (`M`) are visible only when occupied or currently focused. Keyboard shortcuts still reach hidden, empty workspaces. One script batches visibility and highlighting, responds to workspace/window events, and refreshes every two seconds to cover background window moves that emit no native event. - CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. Two native status items are grouped visually by SketchyBar; `codexbar usage --provider both` supplies the Matugen-colored summary. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. - Hover over either icon shows both providers and their reset countdowns; leaving hides it. Left-click opens that provider's native detailed menu. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete response keeps the last complete summary with a warning. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 02e5494..25197e0 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -2,6 +2,9 @@ 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 @@ -21,8 +24,8 @@ sketchybar --bar \ shadow=off \ corner_radius=0 \ margin=0 \ - padding_left=5 \ - padding_right=5 \ + padding_left="$CAPSULE_GAP" \ + padding_right="$CAPSULE_GAP" \ display=all sketchybar --default \ @@ -46,8 +49,8 @@ sketchybar --add event aerospace_workspace_change sketchybar --add item clock q \ --set clock \ update_freq=1 \ - padding_left=5 \ - padding_right=5 \ + padding_left="$CAPSULE_GAP" \ + padding_right=0 \ background.color="$CAPSULE" \ background.drawing=on \ script="$PLUGIN_DIR/clock.sh" @@ -72,8 +75,6 @@ sketchybar --add item workspace.observer e \ sketchybar --add bracket workspaces '/space\..*/' \ --set workspaces \ - padding_left=5 \ - padding_right=5 \ background.color="$CAPSULE" \ background.height=27 \ background.corner_radius=9 \ @@ -81,8 +82,8 @@ sketchybar --add bracket workspaces '/space\..*/' \ sketchybar --add item volume right \ --set volume \ - padding_left=5 \ - padding_right=5 \ + padding_left="$CAPSULE_GAP" \ + padding_right=0 \ background.color="$CAPSULE" \ background.drawing=on \ script="$PLUGIN_DIR/volume.sh" \ @@ -142,7 +143,7 @@ printf '%s\n' "$menu_items" \ name="meter.${window#codexbar-}" add_alias "$source" "$name" left '' sketchybar --set "$name" \ - alias.scale=1 \ + alias.scale=0.85 padding_left=0 padding_right=0 \ script="$PLUGIN_DIR/codexbar.sh" \ --subscribe "$name" mouse.entered mouse.exited mouse.clicked ;; @@ -163,7 +164,7 @@ printf '%s\n' "$menu_items" \ com.apple.TextInputMenuAgent) add_alias "$source" input_source right "$click" sketchybar --set input_source background.color="$CAPSULE" background.drawing=on \ - padding_left=5 padding_right=5 alias.scale=1 \ + padding_left="$CAPSULE_GAP" padding_right=0 alias.scale=1 \ --move input_source before volume ;; esac @@ -173,8 +174,6 @@ printf '%s\n' "$menu_items" \ for group in meter stats; do sketchybar --add bracket "$group.group" "/$group\\..*/" \ --set "$group.group" \ - padding_left=5 \ - padding_right=5 \ background.color="$CAPSULE" \ background.height=27 \ background.corner_radius=9 \ diff --git a/sketchybar-plan.html b/sketchybar-plan.html index 1240eaa..4bc2d3d 100644 --- a/sketchybar-plan.html +++ b/sketchybar-plan.html @@ -44,6 +44,7 @@

    目標配置

    右側Stats:CPU · RAM · 感測器 · 網路 音量 目前輸入法

    區域示意,不是完成截圖。Stats 只顯示實際啟用的模組,全部共用一個緊密背景框;GPT/Claude 同樣共用一組。透明列、高 36 pt、Matugen 半透明 capsule 與瀏海保留區不變。

    +

    間距規格:同一排列區的相鄰 capsule 外框固定相隔 8 pt,group 與單一 capsule 採同一規則;螢幕左右留白也是 8 pt。瀏海保留區與左右/時鐘/workspace 之間的彈性空白不算 capsule 間距。GPT/Claude 原生 meter 縮至 85%,移除組內額外 padding,保留兩個百分比與 hover/click。

    日常操作

    @@ -68,6 +69,7 @@

    本輪任務與驗收

  • 已驗收・Stats:共用一個緊密背景框;CPU/RAM/感測器/網路各有原生 hover 名稱提示,CPU/RAM 實際點擊開正確詳細面板。
  • 已驗收・雙 AI 圖示:兩個原生 meter 同組;兩端 hover 都能看 Codex/Claude 分格摘要及重置倒數,左鍵開各自的原生詳細選單。
  • 已驗收・動態 Workspace:專用測試視窗移至 9/magic 時顯示,移走/關閉後隱藏;目前空工作區仍顯示並高亮。事件更新加上每 2 秒校正,涵蓋背景視窗搬移。
  • +
  • 已驗收・Capsule 間距:實測 Stats → 音量、音量 → 輸入法均為 8 pt,左右外緣也是 8 pt。AI 群組在相同百分比下由 152 縮至 125 pt(約 18%),兩端 hover/click 已重新實測;群組寬度仍隨原生百分比文字變化。
  • 安裝 CI:沿用 MR #155;以最新 commit 的 macOS/Arch/Ubuntu/Windows 四平台 checks 為交付依據。
  • 不新增功能測試腳本。CI 維持 chezmoi init --apply -v 的安裝驗證;介面在實機操作,不以 CI 綠燈代替功能驗收。

    From f3a781267b9f63fedd4ccc2633e2df2ff8a30652 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Mon, 14 Sep 2026 01:14:58 +0800 Subject: [PATCH 26/30] feat: format SketchyBar clock with uppercase Chinese months --- home/dot_config/sketchybar/plugins/executable_clock.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/home/dot_config/sketchybar/plugins/executable_clock.sh b/home/dot_config/sketchybar/plugins/executable_clock.sh index 351e2d7..81c2275 100755 --- a/home/dot_config/sketchybar/plugins/executable_clock.sh +++ b/home/dot_config/sketchybar/plugins/executable_clock.sh @@ -1,4 +1,8 @@ #!/bin/sh set -eu -sketchybar --set "$NAME" label="$(date '+%B %d %H:%M:%S')" +label=$(date '+%m %d %H:%M:%S' | awk '{ + split("壹 貳 參 肆 伍 陸 柒 捌 玖 拾 拾壹 拾貳", months) + printf "%s月 %d %s", months[$1 + 0], $2, $3 +}') +sketchybar --set "$NAME" label="$label" From 82c2cfc8c8cbd3eb1ccc22fbe174803eb3c4e9fa Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Mon, 14 Sep 2026 01:14:58 +0800 Subject: [PATCH 27/30] docs: keep README focused on setup and usage --- README.md | 275 ++++++++++++++---------------------------------------- 1 file changed, 68 insertions(+), 207 deletions(-) diff --git a/README.md b/README.md index 9879e75..0268711 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,101 @@ # Collie's Dotfiles -[![CI](https://github.com/CollieIsCute/dotfiles/actions/workflows/test-distros.yaml/badge.svg?branch=main)](https://github.com/CollieIsCute/dotfiles/actions/workflows/test-distros.yaml) -![macOS](https://img.shields.io/badge/macOS-Homebrew-000?logo=apple&logoColor=white) -![Arch](https://img.shields.io/badge/Arch-pacman%20%2B%20paru-1793D1?logo=archlinux&logoColor=white) -![Ubuntu](https://img.shields.io/badge/Ubuntu-apt-E95420?logo=ubuntu&logoColor=white) -![Debian](https://img.shields.io/badge/Debian-apt-A81D33?logo=debian&logoColor=white) -![Mint](https://img.shields.io/badge/Linux%20Mint-apt-87CF3E?logo=linuxmint&logoColor=white) -![Windows](https://img.shields.io/badge/Windows-Scoop-0078D4?logo=windows&logoColor=white) - -Personal dotfiles managed with [chezmoi](https://chezmoi.io). One repo, several machines. - ## Install +On macOS, Arch Linux, Ubuntu / Debian / Linux Mint, or Windows: + ```bash chezmoi init --apply collieiscute -v ``` -## Supported platforms +Apply local configuration changes: -| OS | Package manager | Status | -|---|---|---| -| macOS | Homebrew | daily-driven | -| Arch | pacman + paru | daily-driven | -| Ubuntu / Debian / Linux Mint | apt | CI-tested only | -| Windows | Scoop | CI-tested only | +```bash +chezmoi apply -v +``` -## Custom touches worth knowing +## Theme -### Theme +Change the wallpaper and colors on Linux / macOS, or generate colors on Windows: -- Linux uses Noctalia's wallpaper-derived **light** palette; macOS and Windows use Matugen with the same per-app theme paths. -- Generated app themes stay outside chezmoi. [`.chezmoiexternal.toml`](home/.chezmoiexternal.toml) only pins the Matugen template inputs. -- Fish inherits the terminal ANSI palette; cursor themes stay independent. -- Font: **JetBrainsMono Nerd Font** across every terminal / bar / lock screen. +```bash +chezmoi theme IMAGE +``` -### chezmoi quirks I keep tripping over (that this repo handles) +## SketchyBar (macOS) -- `run_onchange_*` scripts only re-run when their **rendered** content changes. Manifest files (`fish_plugins`, `Brewfile`) that aren't templated into the script bodies don't trigger reruns. Both are pinned via embedded sha256 hash comments — see `run_onchange_after_1-setup-fish-and-its-plugins.sh.tmpl` and `install-packages_darwin.tmpl`. -- All apt-based distros share `.packages.ubuntu.apt` and the lazygit-from-GitHub fallback (lazygit isn't in Ubuntu apt). -- Fonts use the Nerd Font patched family (`JetBrainsMono Nerd Font`), not the un-patched JetBrains Mono — drop that distinction and bar icons disappear. +After the first `chezmoi apply`: -### Dropbox +1. Open CodexBar and Stats. In CodexBar, enable Codex with OAuth and Claude with CLI, and select an icon-and-percentage style. Relaunch CodexBar if it was already open during apply. In Stats, enable the items you want displayed. +2. Under **System Settings → Privacy & Security**, allow AeroSpace in **Screen & System Audio Recording** and **Accessibility**. If launching SketchyBar from a terminal, allow that terminal too. +3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. +4. After granting permissions, quit SketchyBar and restart AeroSpace. Reloading SketchyBar alone does not apply new permissions. -- Hyprland starts `dropbox-cli` headlessly when the command is installed (currently via AUR on Arch); other Linux hosts skip it. On a new machine, run `env -u DISPLAY -u WAYLAND_DISPLAY dropbox-cli start` in a terminal and open the printed URL. -- Keep `dropbox.service` and `dropbox@USER.service` disabled; Hyprland is the only startup path. +Daily use: -### Tmux +- **Workspaces:** click a number to switch. Only occupied workspaces and the current workspace are shown; keyboard shortcuts still reach hidden workspaces. +- **GPT / Claude:** hover for both providers' usage and reset countdowns; left-click for that provider's details; right-click to refresh. +- **Stats:** hover for the module name; click for its detailed panel. +- **Input source:** click to open the input-method menu. +- **Volume:** click to toggle mute; scroll to adjust by 3%. +- **Control Center:** press `Fn+C`. +- **App menus:** move the pointer to the top edge or press `Fn+Control+F2`. Move the pointer away after dismissing the menu to restore SketchyBar. -- Prefix is `C-z` (so `C-b` stays free for vim). -- Splits: `prefix |` (horizontal), `prefix -` (vertical), inheriting the current pane's path. -- `set-clipboard on` + `allow-passthrough` → OSC 52 yank works over SSH without X11 forwarding. -- Sessions auto-restore via tmux-resurrect + tmux-continuum on tmux start. +If icons disappear after restarting an app or rearranging native menu-bar items: -### Kitty +```bash +sketchybar --reload +``` -- Used on macOS specifically because [AeroSpace](https://github.com/nikitabobko/AeroSpace) tiles each Ghostty native tab as a separate window — Kitty's custom tab bar appears as a single AXWindow. -- `cmd+option`/Alt key bindings deliberately avoided (macOS 26 Tahoe intercepts them). -- Kitty, Alacritty, and Ghostty point at the fixed `noctalia` theme path; Noctalia or Matugen owns the generated colors. +If they are still missing, check the permissions above and inspect the available items: -### SketchyBar (macOS) +```bash +sb-status-items list +sketchybar --query default_menu_items +``` -- AeroSpace starts SketchyBar, reserves 36 px at the top, and sends workspace changes through the official `aerospace_workspace_change` event. -- Startup checks for the SketchyBar process before starting or reloading it: `sketchybar --reload` can report success even when no bar is running. -- The transparent bar mirrors Noctalia's 30% `surface_variant` capsules. `chezmoi theme IMAGE` regenerates only `~/.config/sketchybar/colors.sh` and reloads the bar. -- Left: native GPT and Claude icons/meters in one capsule, with one shared hover summary. No App tray. The clock sits left of the notch (`q`), with occupied workspaces on its right (`e`); `notch_width=200` reserves the center. Right: one compact Stats group, 3%-step volume, and the native input-source icon. -- Adjacent capsule backgrounds in the same region have an 8 pt gap, whether grouped or standalone; outer screen insets are also 8 pt. Normal capsules own the gap on their left because brackets absorb their members' padding. Notch clearance and flexible space between anchored regions remain separate. AI aliases use 85% native scale with no added internal padding, keeping both percentages in a smaller capsule. -- Workspaces `1..9` and `magic` (`M`) are visible only when occupied or currently focused. Keyboard shortcuts still reach hidden, empty workspaces. One script batches visibility and highlighting, responds to workspace/window events, and refreshes every two seconds to cover background window moves that emit no native event. -- CodexBar owns quota fetching: Codex uses OAuth, and Claude uses the existing Claude CLI login. Two native status items are grouped visually by SketchyBar; `codexbar usage --provider both` supplies the Matugen-colored summary. No browser cookies, tokens, account JSON, custom quota server, or `showy-quota` config is stored here. -- Hover over either icon shows both providers and their reset countdowns; leaving hides it. Left-click opens that provider's native detailed menu. Right-click refreshes both summaries; background updates run every 60 seconds and on wake. Missing quota windows are omitted. A failed or incomplete response keeps the last complete summary with a warning. macOS-provided `jq` parses the response and `lockf` prevents overlapping refreshes. -- Each quota window is a bordered, two-line native item: remaining quota above, reset countdown below, with bold Codex/Claude headings. Borders and secondary text follow the Matugen palette; the tooltip keeps its existing width. Countdown values use CodexBar's UTC `resetsAt`, round up to minutes, and update with the existing 60-second refresh. Missing or invalid reset times are omitted and leave a compact single-line cell; an elapsed timestamp says it is waiting for the source to update, rather than claiming the quota has reset. -- Stats hover shows its native localized tooltip (module/widget name, not a live metric summary); click opens the corresponding Stats panel. The input-source alias mirrors native language/mode changes and opens the native input menu on click. Panels keep their original screen anchors. -- Use Fn-C for Control Center and the top edge or Fn-Control-F2 for the full App menu. Neither has a duplicate SketchyBar button. -- `sb-menu-events` listens for the public Carbon `kEventMenuBarShown` / `kEventMenuBarHidden` broadcasts and alternates the two bars. It also covers the top screen edge and system keyboard shortcuts, without polling the cursor, intercepting input, or changing SIP. If any display still has a native menu bar, SketchyBar stays hidden. Closing a panel with Esc does not always hide the native bar immediately; SketchyBar returns when macOS actually retracts it. -- Chezmoi compiles `sb-menu-events` and `sb-status-items` with the existing Xcode Command Line Tools. The former is one locked listener tied to SketchyBar's lifetime; its supervisor restores the bar after a crash. The latter runs only for setup/clicks, matching native status-window geometry to public Accessibility elements from Stats, CodexBar and TextInputMenuAgent. This identifies macOS 26's otherwise indistinguishable `ControlCenter,Item-0` windows without adding a tray or a new dependency. -- Named aliases omit the query's changing `(n)` suffix. The input-source `Item-0` retains its resolved index: reload after its native status item is recreated or the menu-bar arrangement changes. Alias images keep native colors; Matugen owns the surrounding capsules and regular text. +## Dropbox (Linux) -After the first `chezmoi apply`: +- On a new machine with `dropbox-cli` installed, run `env -u DISPLAY -u WAYLAND_DISPLAY dropbox-cli start` and open the printed URL. +- Keep `dropbox.service` and `dropbox@USER.service` disabled when using Hyprland's Dropbox startup. -1. Open CodexBar and Stats once. Enable Codex with OAuth and Claude with CLI. Chezmoi sets CodexBar's native `mergeIcons=false`; relaunch CodexBar if it was already open when this setting was first applied. Select an icon-and-percentage style; SketchyBar groups the two native meters. The managed JSON preserves other settings while selecting these sources for existing provider entries. In Stats, enable the compact items you want mirrored. -2. Allow **AeroSpace** under **System Settings → Privacy & Security → Screen & System Audio Recording** for login startup. If launching SketchyBar manually from a terminal, allow that terminal too; its permission does not cover AeroSpace. After granting permission, quit SketchyBar and restart AeroSpace to launch a fresh bar; `sketchybar --reload` alone does not refresh TCC permissions. -3. Set **System Settings → Menu Bar → Automatically hide and show the menu bar → Always**. This hides the system bar until the pointer touches the top edge; it does not permanently disable it. No reboot or SIP change is needed. -4. Allow the SketchyBar launcher (AeroSpace, or the terminal for manual launches) under **Accessibility** for native status-item discovery and clicks. The new helper uses Accessibility directly, not System Events automation. +## Noctalia (Linux) -CI validates installation with `chezmoi init --apply -v` across supported operating systems. Bar interactions are checked on the actual Mac, not with mocked feature-test scripts. +- Run `chezmoi apply` after changing the monitor layout. +- If widget edits in Noctalia's GUI override your dotfiles, remove `[desktop_widgets]` and `[lockscreen_widgets]` from `~/.local/state/noctalia/settings.toml`, or copy those choices into your template before applying. +- Enable live wallpapers through Noctalia's **W Engine** bar widget. -See [the concise plan and acceptance status](sketchybar-plan.html#tasks-title). External-monitor, sleep and login checks remain separate from installation CI. +### Display manager recovery (Arch) -For a live coordination check, open Control Center with Fn-C and run `sketchybar --query bar | jq -e '.hidden == "on"'`. After dismissing it and moving the pointer away so the native bar retracts, the same check with `"off"` must succeed. This checks the running Mac, not a mocked feature test. +- Select **SDDM** during `archinstall`; apply the dotfiles to set up Noctalia Greeter. +- If Noctalia Greeter shows a black screen, switch to a TTY with `Ctrl+Alt+F2` and run: -If an alias is missing, compare `sb-status-items list` and `sketchybar --query default_menu_items` with the native item, then reload. Missing/ambiguous native items are skipped or return an error rather than clicking another app. + ```bash + sudo systemctl disable --now greetd.service && sudo systemctl enable --now sddm.service + ``` -### Hyprland +- If TTY switching does not work, boot with `systemd.unit=multi-user.target` from GRUB, then run the same command. -- Desc-keyed Lua `hl.monitor(...)` overrides plus a fallback that selects the highest resolution and refresh rate available at that resolution. -- Cursor: Catppuccin Mocha Teal (Hyprcursor) with Catppuccin Mocha Green as XCursor fallback. -- Electron / fcitx5 / Qt integration env vars set centrally. Noctalia applies GTK light mode and generated GTK/Qt colors through its built-in templates. -- Noctalia v5 owns the desktop shell layer (bar, launcher, notifications, wallpaper, lock screen, idle, screenshots, clipboard). -- Noctalia Shell is installed on Arch and Ubuntu. Noctalia Greeter stays Arch-only; Ubuntu keeps SDDM. -- Wallpapers are deployed by chezmoi to `~/.config/wallpapers`; Noctalia reads that path directly. -- Noctalia is the Linux wallpaper/theme owner for apps with built-in adapters. App integrations write generated theme files and reload apps; chezmoi keeps their main configs pre-aligned so post-hooks do not cause drift. -- Noctalia desktop/lockscreen widget placement is generated from monitor roles and ratios in `20-widgets.generated.toml.tmpl`; run `chezmoi apply` after changing the monitor layout. -- If widgets are edited in Noctalia's GUI, remove `[desktop_widgets]` and `[lockscreen_widgets]` from `~/.local/state/noctalia/settings.toml` or fold the new ratios back into the template; state overrides win over declarative config. -- Wallpaper Engine is opt-in through Noctalia's W Engine bar widget; palettes sync through Noctalia and Steam Workshop selections stay machine-local. +## Voice dictation + +On Arch Linux x86_64, macOS, or Windows x64 with AVX2: + +1. Launch OpenWhispr (`openwhispr` on Linux / Windows). +2. Run `asr-mode sensevoice` for CPU-only recognition or `asr-mode qwen-1.7b` for GPU-backed recognition. Missing models download when selected. +3. In OpenWhispr, set Self-Hosted to `http://127.0.0.1:8080/v1` and paste the printed `OpenWhispr Model ID`. +4. Restart OpenWhispr after `chezmoi apply` to load its shortcuts. + +- **macOS:** grant Microphone and Accessibility permissions. +- **Windows:** reopen the terminal after applying; install the [Visual C++ x64 runtime](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist), allow microphone access, and install a Vulkan GPU driver for Qwen. +- **Linux:** if OpenWhispr 1.9.2 shows a ydotool setup warning, do not add the `input` group or daemon just to dismiss it; `wtype` is already selected. +- Run `asr-mode off` before gaming to stop recognition and release GPU memory. Quit OpenWhispr separately if you also want to disable its UI and shortcut. + +## AI extensions + +- Add plugins and marketplaces in [`home/.chezmoidata/ai.yaml`](home/.chezmoidata/ai.yaml), then run `chezmoi apply` and restart the affected app. +- Place shared user skills in `~/.agents/skills`. +- Review and trust new Codex hooks manually with `/hooks`. +- For OpenCode's zh-TW linting, install [zhtw-mcp](https://github.com/sysprog21/zhtw-mcp) from source with `make install`; its binary must be at `~/.local/bin/zhtw-mcp`. ## Keymappings @@ -138,37 +132,6 @@ If an alias is missing, compare `sb-status-items list` and `sketchybar --query d | `XF86Audio*` | volume / mute / mic mute | | `XF86MonBrightness*` | screen brightness | -Voice dictation uses OpenWhispr on Arch Linux x86_64, macOS and Windows x86_64. Select -`asr-mode sensevoice` for the CPU-only SenseVoice model or `asr-mode qwen-1.7b` -for the GPU-backed Qwen3-ASR model; missing models download only when selected. -Models use `${XDG_CACHE_HOME:-$HOME/.cache}/crispasr` on Linux and -`${XDG_CACHE_HOME:-$HOME/Library/Caches}/crispasr` on macOS, and -`%LOCALAPPDATA%\crispasr` on Windows. -Run `asr-mode off` before gaming to stop CrispASR, close port 8080, and release -its GPU memory. OpenWhispr remains in the tray; quit it separately when its UI -and shortcut are not needed. - -On Linux, launch `openwhispr` from your terminal or the application menu. -OpenWhispr follows the latest official release; do not pin its version. -Linux patches the known Hold startup bug only when the source matches; -changed upstream code is left untouched and does not block updates. - -For first-time OpenWhispr setup, run `asr-mode qwen-1.7b`, then set Self-Hosted -to `http://127.0.0.1:8080/v1` and paste the printed `OpenWhispr Model ID`. Each -successful model switch prints its current absolute path for later connection -tests. Chezmoi keeps OpenWhispr in push mode with `SHIFT+F13` for dictation and -`Meta+F13` for the local voice agent. Linux uses its native Hyprland binding -and `wtype`; macOS requires Microphone and Accessibility access. OpenWhispr -loads these shortcuts at startup, so restart it after `chezmoi apply`. Version -1.9.2 may still show a ydotool setup warning on Linux; `wtype` is already -preferred, so do not add the `input` group or daemon just to dismiss it. - -On Windows x64 with AVX2, reopen your terminal after applying, then run `openwhispr` and -`asr-mode qwen-1.7b` (GPU) or `asr-mode sensevoice` (CPU). -Install the [Visual C++ x64 runtime](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist), -allow microphone access, configure Self-Hosted as above, and install a Vulkan -GPU driver for Qwen. - ### AeroSpace (macOS) | Bind | Action | @@ -179,8 +142,6 @@ GPU driver for Qwen. | `Cmd+Option+S` | toggle the dedicated `magic` workspace | | `Cmd+Option+Shift+S` | move window to the `magic` workspace | -AeroSpace restores the swapped root layouts and window states where possible; its CLI cannot reconstruct nested container geometry. - ### Tmux (prefix = `C-z`) | Bind | Action | @@ -219,103 +180,3 @@ AeroSpace restores the swapped root layouts and window states where possible; it |---|---| | `vi` | `nvim` (when nvim is installed) | | `buu` | `brew update && brew upgrade -y && fisher update` (macOS only) | - -## Tools - -### Shell / multiplexer - -- [`fish`](https://fishshell.com) — primary shell. -- [`tmux`](https://github.com/tmux/tmux) — primary multiplexer. -- [`zellij`](https://zellij.dev) — secondary multiplexer using the generated Noctalia-compatible theme. - -### Editor - -- [`neovim`](https://github.com/neovim/neovim) — LazyVim distro on top. - -### Terminals - -- [`kitty`](https://sw.kovidgoyal.net/kitty/) — macOS daily driver (AeroSpace-friendly tabs). -- [`wezterm`](https://wezterm.org) — cross-platform fallback. -- [`ghostty`](https://ghostty.org) — newer GPU terminal using the generated Noctalia-compatible theme. -- [`alacritty`](https://github.com/alacritty/alacritty) — minimal GPU terminal. - -### Wayland stack (Hyprland) - -- [`hyprland`](https://hyprland.org) — Wayland compositor. -- [`noctalia`](https://github.com/noctalia-dev/noctalia) — desktop shell: bar, launcher, notifications, wallpaper, lock screen, idle behavior, screenshots, clipboard, and control center. -- [`linux-wallpaperengine`](https://github.com/Almamu/linux-wallpaperengine) — opt-in live wallpapers from Steam Wallpaper Engine assets on Hyprland. -- [`sddm`](https://github.com/sddm/sddm) — bootstrap and fallback display manager for fresh Arch installs. -- [`greetd`](https://git.sr.ht/~kennylevinsen/greetd) + [`Noctalia Greeter`](https://github.com/noctalia-dev/noctalia-greeter) — final Wayland login greeter after AUR packages are installed. -- [`fcitx5`](https://github.com/fcitx/fcitx5) + [McBopomofo](https://github.com/openvanilla/fcitx5-mcbopomofo) + chewing — Chinese input with Hsu keyboard support. - -### Display manager bootstrap and recovery - -- Fresh Arch installs should use `SDDM` from `archinstall` first; chezmoi switches to `greetd` only after `noctalia-greeter-session` is installed and setup succeeds. -- `SDDM` stays installed as the fallback display manager. -- The switch script only enables/disables services; it does not stop/start the current graphical session. -- If Noctalia Greeter shows a black screen, switch to a TTY with `Ctrl+Alt+F2` and run `sudo systemctl disable --now greetd.service && sudo systemctl enable --now sddm.service`. -- If TTY switching does not work, boot with `systemd.unit=multi-user.target` from GRUB, then run the same service switch. - -### macOS extras - -- [`aerospace`](https://github.com/nikitabobko/AeroSpace) — tiling WM. -- [`sketchybar`](https://github.com/FelixKratz/SketchyBar) — transparent, event-driven status bar. -- [`CodexBar`](https://github.com/steipete/CodexBar) — Codex quota source and native meter mirrored by SketchyBar. -- [`Stats`](https://github.com/exelban/stats) — native system metrics mirrored by SketchyBar aliases. -- [`karabiner-elements`](https://karabiner-elements.pqrs.org) — keyboard remapper. -- Desktop wallpaper is deployed by chezmoi to `~/.config/wallpapers`; `run_onchange_after_6-apply-theme.sh.tmpl` applies the initial wallpaper and Matugen palette, and `chezmoi theme [IMAGE]` changes both later. - -### CLI tooling - -- File / dir: [`eza`](https://github.com/eza-community/eza), [`fd`](https://github.com/sharkdp/fd), [`ripgrep`](https://github.com/BurntSushi/ripgrep), [`bat`](https://github.com/sharkdp/bat), [`zoxide`](https://github.com/ajeetdsouza/zoxide), [`fzf`](https://github.com/junegunn/fzf). -- System info: [`btop`](https://github.com/aristocratos/btop), [`fastfetch`](https://github.com/fastfetch-cli/fastfetch). -- Git: [`lazygit`](https://github.com/jesseduffield/lazygit), [`tig`](https://github.com/jonas/tig), [`gh`](https://cli.github.com), [`glab`](https://gitlab.com/gitlab-org/cli), [`onefetch`](https://github.com/o2sh/onefetch). -- Build / dev: [`gnu-tar`](https://www.gnu.org/software/tar/), [`bear`](https://github.com/rizsotto/Bear), [`cmake`](https://cmake.org), [`mold`](https://github.com/rui314/mold), [`ninja`](https://ninja-build.org), [`llvm`](https://llvm.org), [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html), [`cppcheck`](https://cppcheck.sourceforge.io), [`uv`](https://github.com/astral-sh/uv). -- Containers: macOS Apple silicon [`container`](https://github.com/apple/container) + third-party [`container-compose`](https://github.com/Mcrich23/Container-Compose); Linux [`podman`](https://podman.io). -- Docs: [`typst`](https://typst.app), [`tldr`](https://tldr.sh). -- OpenCode zh-TW linting: [`zhtw-mcp`](https://github.com/sysprog21/zhtw-mcp) is configured as a local MCP server at `~/.local/bin/zhtw-mcp`. Until upstream publishes releases, install it from source with `make install` so OpenCode can use the fixed binary path. -- OpenCode Claude Code plugin: [`@khalilgharbaoui/opencode-claude-code-plugin`](https://github.com/khalilgharbaoui/opencode-claude-code-plugin) is loaded through OpenCode's native npm plugin support; after changing the plugin list, run `chezmoi apply /home/collie/.config/opencode/opencode.json` and restart OpenCode. - -### AI extensions - -- [`home/.chezmoidata/ai.yaml`](home/.chezmoidata/ai.yaml) is the single inventory for plugins and marketplaces; add entries there without editing templates. -- Claude Code and Codex update configured marketplace plugins with their native startup updaters. -- OpenCode installs configured npm plugins when its generated cache is missing; remove that cache before startup to fetch newer versions. -- Shared user skills live in `~/.agents/skills`, which Codex and OpenCode discover natively. -- `run_after_5-sync-ai-extensions.sh.tmpl` bootstraps missing Codex plugins. -- Review and trust new Codex hooks manually with `/hooks`. - -### Fish plugins (managed by [`fisher`](https://github.com/jorgebucaran/fisher)) - -- `edc/bass` — run bash scripts in fish. -- `jorgebucaran/nvm.fish` — Node version manager. -- `patrickf1/fzf.fish` — fzf integrations. -- `pure-fish/pure` — minimal prompt. - -### Tmux plugins (managed by [`TPM`](https://github.com/tmux-plugins/tpm)) - -- `mrjones2014/smart-splits.nvim` — `Ctrl+Arrow` resize, plays nice with neovim. -- `tmux-plugins/tmux-sensible` — sensible defaults. -- `tmux-plugins/tmux-continuum` — auto-save/restore on start. -- `tmux-plugins/tmux-resurrect` — manual save/restore + nvim session capture. - -### Neovim — LazyVim core extras - -- `zbirenbaum/copilot.lua` — Copilot. -- `giuxtaposition/blink-cmp-copilot` — Copilot source for blink.cmp. -- `neovim/nvim-lspconfig` + `p00f/clangd_extensions.nvim` — LSP, with extra clangd polish. -- `nvim-treesitter/nvim-treesitter` — syntax. -- `ibhagwan/fzf-lua` — fuzzy finder. -- `sphamba/smear-cursor.nvim` — animated cursor. -- `folke/snacks.nvim` — utility collection. - -## Layout - -``` -home/ # chezmoi source root (.chezmoiroot=home) -├── .chezmoidata/packages.yaml # canonical package list (paru + apt) -├── .chezmoiexternal.toml # pinned external template inputs -├── .chezmoiscripts/ # run_once / run_onchange bootstrap -├── .chezmoitemplates/ # macOS install template (Brewfile pass-thru) -└── dot_config/ # → ~/.config/... -``` From c14a25864a40afeaac6a9a616bbe545aa934e4b1 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Mon, 14 Sep 2026 01:14:58 +0800 Subject: [PATCH 28/30] chore: keep SketchyBar plan local only --- .gitignore | 1 + sketchybar-plan.html | 86 -------------------------------------------- 2 files changed, 1 insertion(+), 86 deletions(-) delete mode 100644 sketchybar-plan.html diff --git a/.gitignore b/.gitignore index 1832962..e8fa921 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ tmux-client-*.log .opencode/package-lock.json .opencode/bun.lock .opencode/opencode-loop/ +/sketchybar-plan.html diff --git a/sketchybar-plan.html b/sketchybar-plan.html deleted file mode 100644 index 4bc2d3d..0000000 --- a/sketchybar-plan.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - SketchyBar 計畫 - - - -
    -
    - Collie's dotfiles · macOS · 更新 2026-09-13 -

    SketchyBar 計畫

    -

    保留目前好用的原生列/SketchyBar 交替顯示。只補齊日常資訊,不重做 macOS 的選單和控制中心。

    -

    本輪狀態:已套用本機並完成下列功能驗收;安裝 CI 以 MR 最新 commit 的 checks 為準。

    -
    -
    -

    目標配置

    -
    -
    左側GPT 圖示+用量 · Claude 圖示+用量
    -
    瀏海兩側月份/日期/時鐘 有視窗的 workspace +目前所在 workspace
    -
    右側Stats:CPU · RAM · 感測器 · 網路 音量 目前輸入法
    -
    -

    區域示意,不是完成截圖。Stats 只顯示實際啟用的模組,全部共用一個緊密背景框;GPT/Claude 同樣共用一組。透明列、高 36 pt、Matugen 半透明 capsule 與瀏海保留區不變。

    -

    間距規格:同一排列區的相鄰 capsule 外框固定相隔 8 pt,group 與單一 capsule 採同一規則;螢幕左右留白也是 8 pt。瀏海保留區與左右/時鐘/workspace 之間的彈性空白不算 capsule 間距。GPT/Claude 原生 meter 縮至 85%,移除組內額外 padding,保留兩個百分比與 hover/click。

    -
    -
    -

    日常操作

    -
    項目行為
    App tray顯示實際執行中的常駐 App 圖示,點擊開原生 status 選單;不是 File/Edit/Help 的移植。
    Workspace動態顯示有視窗的工作區;目前所在工作區即使空白也保留。其他空工作區隱藏,數字順序與 magic → M 不變;鍵盤仍能切到未顯示的工作區。
    GPT/Claude同一組同時顯示兩個原生圖示與用量;hover 共用分格摘要與重置倒數;左鍵開原生詳細選單,右鍵刷新兩者。
    Stats重用原生資料與圖示,縮減空白、合成一組;點擊對應模組開原生詳細面板。
    Stats重用原生資料與圖示,縮減空白、合成一組。Hover 顯示 Stats 原生名稱提示(例如「CPU:線狀圖」),移開收起;點擊開對應詳細面板。
    輸入法顯示目前輸入法的原生圖示,切換語言/中英文模式後同步。
    App 選單游標到頂端,或 FnControlF2,使用完整原生選單。
    控制中心FnC。SketchyBar 不再放控制中心按鈕。
    - - - - - - - - - - -
    項目行為
    Workspace動態顯示有視窗的工作區;目前所在工作區即使空白也保留。其他空工作區隱藏,數字順序與 magic → M 不變;鍵盤仍能切到未顯示的工作區。
    GPT/Claude同一組同時顯示兩個原生圖示與用量;hover 共用分格摘要與重置倒數;左鍵開原生詳細選單,右鍵刷新兩者。
    Stats重用原生資料與圖示,縮減空白、合成一組。Hover 顯示 Stats 原生名稱提示(例如「CPU:線狀圖」),移開收起;點擊開對應詳細面板。
    輸入法顯示目前輸入法的原生圖示,切換語言/中英文模式後同步。
    App 選單游標到頂端,或 FnControlF2,使用完整原生選單。
    控制中心FnC。SketchyBar 不再放控制中心按鈕。
    交替顯示原生列出現時 SketchyBar 隱藏;原生列真正收起後恢復。沿用公開 Carbon 事件,不更改 SIP。
    -
    -
    -

    本輪任務與驗收

    -
      -
    1. 已驗收・精簡:控制中心按鈕與 App tray 均已移除;原生列從頂端出現/收起時,SketchyBar 交替隱藏/恢復。
    2. -
    3. 已驗收・輸入法:ABC 與唯音繁體之間切換,原生圖示同步變化,測試後已恢復原來來源。
    4. -
    5. 已驗收・Stats:共用一個緊密背景框;CPU/RAM/感測器/網路各有原生 hover 名稱提示,CPU/RAM 實際點擊開正確詳細面板。
    6. -
    7. 已驗收・雙 AI 圖示:兩個原生 meter 同組;兩端 hover 都能看 Codex/Claude 分格摘要及重置倒數,左鍵開各自的原生詳細選單。
    8. -
    9. 已驗收・動態 Workspace:專用測試視窗移至 9/magic 時顯示,移走/關閉後隱藏;目前空工作區仍顯示並高亮。事件更新加上每 2 秒校正,涵蓋背景視窗搬移。
    10. -
    11. 已驗收・Capsule 間距:實測 Stats → 音量、音量 → 輸入法均為 8 pt,左右外緣也是 8 pt。AI 群組在相同百分比下由 152 縮至 125 pt(約 18%),兩端 hover/click 已重新實測;群組寬度仍隨原生百分比文字變化。
    12. -
    13. 安裝 CI:沿用 MR #155;以最新 commit 的 macOS/Arch/Ubuntu/Windows 四平台 checks 為交付依據。
    14. -
    -

    不新增功能測試腳本。CI 維持 chezmoi init --apply -v 的安裝驗證;介面在實機操作,不以 CI 綠燈代替功能驗收。

    -

    外接螢幕、睡眠及重新登入屬實際使用驗收;未在本輪操作的情境不標記通過。

    -
    -
    -

    維護邊界

    -

    App 擁有資料與詳細面板;SketchyBar 只負責排列、摘要及入口;Matugen 只更新配色。保留登入與授權設定,不存取或納管 token、cookie、帳號用量 JSON。

    -

    安裝與權限步驟見 README;最新結果見 MR checks

    -

    依據:SketchyBar bracket/aliasCodexBar 原生圖示Stats 原生 tooltipAeroSpace 工作區查詢

    -
    - - - From 2b769ece983b0980be8558416237c80f0d9a62d4 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Mon, 14 Sep 2026 01:32:48 +0800 Subject: [PATCH 29/30] refactor: share SketchyBar styling and mouse event handling --- .../sketchybar/executable_sketchybarrc | 86 ++++++------------- .../sketchybar/plugins/executable_codexbar.sh | 30 +------ .../plugins/executable_native-menu.sh | 25 +++--- 3 files changed, 41 insertions(+), 100 deletions(-) diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 25197e0..33b97f5 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -40,6 +40,10 @@ sketchybar --default \ 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 @@ -47,13 +51,7 @@ 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 \ - padding_left="$CAPSULE_GAP" \ - padding_right=0 \ - background.color="$CAPSULE" \ - background.drawing=on \ - script="$PLUGIN_DIR/clock.sh" + --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 @@ -73,20 +71,10 @@ sketchybar --add item workspace.observer e \ script="$PLUGIN_DIR/workspace.sh" \ --subscribe workspace.observer aerospace_workspace_change space_windows_change front_app_switched system_woke -sketchybar --add bracket workspaces '/space\..*/' \ - --set workspaces \ - background.color="$CAPSULE" \ - background.height=27 \ - background.corner_radius=9 \ - background.drawing=on +sketchybar --add bracket workspaces '/space\..*/' sketchybar --add item volume right \ - --set volume \ - padding_left="$CAPSULE_GAP" \ - padding_right=0 \ - background.color="$CAPSULE" \ - background.drawing=on \ - script="$PLUGIN_DIR/volume.sh" \ + --set volume script="$PLUGIN_DIR/volume.sh" \ --subscribe volume volume_change mouse.scrolled mouse.clicked system_woke add_alias() { @@ -95,32 +83,16 @@ add_alias() { *,Item-0\(*|*,\(*) source=$1 ;; *) source=$(printf '%s\n' "$1" | sed 's/([0-9][0-9]*)$//') ;; esac - name=$2 - position=$3 - click_script=${4-} - - sketchybar --add alias "$source" "$position" || return 0 - sketchybar --rename "$source" "$name" - # Preserve raster colors: tinting an opaque chart makes it a solid rectangle. - sketchybar --set "$name" \ - icon.drawing=off \ - label.drawing=off \ - padding_left=1 \ - padding_right=1 \ - alias.scale=0.82 \ - alias.update_freq=2 \ - click_script="$click_script" + 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.height=28 \ - popup.background.color="0xff${CAPSULE#????}" \ + popup.align=left \ popup.background.corner_radius=10 \ - popup.background.border_width=1 \ - popup.background.border_color="0x33${ON_SURFACE#????}" \ update_freq=60 script="$PLUGIN_DIR/codexbar.sh" \ --subscribe codexbar system_woke sketchybar --add item codexbar.status popup.codexbar \ @@ -143,18 +115,14 @@ printf '%s\n' "$menu_items" \ name="meter.${window#codexbar-}" add_alias "$source" "$name" left '' sketchybar --set "$name" \ - alias.scale=0.85 padding_left=0 padding_right=0 \ - script="$PLUGIN_DIR/codexbar.sh" \ + 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.height=28 \ - popup.background.color="0xff${CAPSULE#????}" \ + popup.align=right \ popup.background.corner_radius=6 \ - popup.background.border_width=1 \ - popup.background.border_color="0x33${ON_SURFACE#????}" \ script="$PLUGIN_DIR/native-menu.sh" \ --subscribe "stats.$window" mouse.entered mouse.exited mouse.clicked \ --add item "hint.$window" "popup.stats.$window" \ @@ -163,21 +131,28 @@ printf '%s\n' "$menu_items" \ ;; com.apple.TextInputMenuAgent) add_alias "$source" input_source right "$click" - sketchybar --set input_source background.color="$CAPSULE" background.drawing=on \ - padding_left="$CAPSULE_GAP" padding_right=0 alias.scale=1 \ - --move input_source before volume + 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\\..*/" \ - --set "$group.group" \ - background.color="$CAPSULE" \ - background.height=27 \ - background.corner_radius=9 \ - background.drawing=on + 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 @@ -198,8 +173,3 @@ else sketchybar --bar hidden=off printf '%s\n' 'SketchyBar menu coordination is unavailable; run chezmoi apply.' >&2 fi - -if command -v aerospace >/dev/null 2>&1; then - focused=$(aerospace list-workspaces --focused 2>/dev/null | head -n 1) - test -z "$focused" || sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE="$focused" -fi diff --git a/home/dot_config/sketchybar/plugins/executable_codexbar.sh b/home/dot_config/sketchybar/plugins/executable_codexbar.sh index 5ada3aa..b8b3d4a 100755 --- a/home/dot_config/sketchybar/plugins/executable_codexbar.sh +++ b/home/dot_config/sketchybar/plugins/executable_codexbar.sh @@ -1,28 +1,5 @@ #!/bin/sh -case "${SENDER:-}" in - mouse.entered) - sketchybar --set codexbar popup.drawing=on - exit 0 - ;; - mouse.exited) - sketchybar --set codexbar popup.drawing=off - exit 0 - ;; - mouse.clicked) - case "${BUTTON:-left}" in - left) - sketchybar --set codexbar popup.drawing=off - exec "${CONFIG_DIR:-$HOME/.config/sketchybar}/plugins/native-menu.sh" - ;; - right) ;; - *) exit 0 ;; - esac - ;; - forced|routine|system_woke) ;; - *) exit 0 ;; -esac - # Keep only rendered labels in SketchyBar, not account JSON or credentials. # macOS lockf prevents overlapping refreshes and releases on process exit. cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/sketchybar" @@ -39,9 +16,7 @@ fi if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' now as $now - | [(if type == "array" then .[] elif .providers then .providers[] else . end) - | select(.provider == "codex" or .provider == "claude")] - | if (map(.provider) | sort) == ["claude", "codex"] + | if type == "array" and (map(.provider) | sort) == ["claude", "codex"] and all(.error == null and (.usage | type) == "object") then .[] else error("Incomplete provider response") end | (if .provider == "codex" then "Codex · OAuth" else "Claude · CLI" end) as $provider @@ -51,8 +26,7 @@ if ! rows=$(printf '%s\n' "$data" | /usr/bin/jq -er ' ([{title: ({"300": "5 小時", "10080": "每週"}[($usage.primary.windowMinutes | tostring)] // "主要額度"), window: $usage.primary}, {title: "每週", window: $usage.secondary}, {title: "其他額度", window: $usage.tertiary}, - ($usage.extraRateWindows[]? | {title: .title, window: (.window // .)}), - ($usage.windows[]? | {title: (.title // .name), window: (.window // .)})] + $usage.extraRateWindows[]?] | map(select((.window.usedPercent | type) == "number")) | if length == 0 then ["note", "目前沒有回報額度區間"] else .[] diff --git a/home/dot_config/sketchybar/plugins/executable_native-menu.sh b/home/dot_config/sketchybar/plugins/executable_native-menu.sh index 7db5b79..d656767 100755 --- a/home/dot_config/sketchybar/plugins/executable_native-menu.sh +++ b/home/dot_config/sketchybar/plugins/executable_native-menu.sh @@ -1,23 +1,20 @@ #!/bin/sh case "${NAME:-}" in - stats.*) - case "${SENDER:-}" in - mouse.entered) sketchybar --set "$NAME" popup.drawing=on ;; - mouse.exited|mouse.clicked) sketchybar --set "$NAME" popup.drawing=off ;; - esac - exit 0 - ;; -esac - -# --update also runs item scripts; never open menus during startup or reload. -case "${SENDER:-}" in - mouse.clicked) ;; + stats.*) popup=$NAME ;; + meter.codex|meter.claude) popup=codexbar ;; *) exit 0 ;; esac -case "${NAME:-}" in - meter.codex|meter.claude) +# Forced/routine updates do nothing; menus only open on an actual click. +case "${SENDER:-}:$popup:${BUTTON:-left}" in + mouse.entered:*) sketchybar --set "$popup" popup.drawing=on ;; + mouse.exited:*|mouse.clicked:stats.*:*) sketchybar --set "$popup" popup.drawing=off ;; + mouse.clicked:codexbar:left) + sketchybar --set codexbar popup.drawing=off exec "$HOME/.local/bin/sb-status-items" click com.steipete.codexbar "codexbar-${NAME#meter.}" ;; + mouse.clicked:codexbar:right) + exec "${CONFIG_DIR:-$HOME/.config/sketchybar}/plugins/codexbar.sh" + ;; esac From 4098364d96a71c035b8e287b031edd4a2b997f88 Mon Sep 17 00:00:00 2001 From: Collie Tsai Date: Mon, 14 Sep 2026 01:35:46 +0800 Subject: [PATCH 30/30] refactor: simplify workspace visibility and volume updates --- .../sketchybar/executable_sketchybarrc | 1 + .../sketchybar/plugins/executable_volume.sh | 22 ++++++---------- .../plugins/executable_workspace.sh | 26 ++++++------------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/home/dot_config/sketchybar/executable_sketchybarrc b/home/dot_config/sketchybar/executable_sketchybarrc index 33b97f5..c12179c 100755 --- a/home/dot_config/sketchybar/executable_sketchybarrc +++ b/home/dot_config/sketchybar/executable_sketchybarrc @@ -59,6 +59,7 @@ for sid in 1 2 3 4 5 6 7 8 9 magic; do 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 \ diff --git a/home/dot_config/sketchybar/plugins/executable_volume.sh b/home/dot_config/sketchybar/plugins/executable_volume.sh index bc2dfed..8a99380 100755 --- a/home/dot_config/sketchybar/plugins/executable_volume.sh +++ b/home/dot_config/sketchybar/plugins/executable_volume.sh @@ -1,18 +1,19 @@ #!/bin/sh set -eu -get_volume() { - osascript -e 'output volume of (get volume settings)' 2>/dev/null || printf '0\n' -} - case ${SENDER:-} in mouse.clicked) osascript -e 'set volume output muted not (output muted of (get volume settings))' >/dev/null - volume=$(get_volume) ;; +esac +case ${SENDER:-} in + volume_change) volume=${INFO:-0} ;; + *) volume=$(osascript -e 'output volume of (get volume settings)' 2>/dev/null || printf '0\n') ;; +esac +case $volume in *[!0-9]*|'') volume=0 ;; esac + +case ${SENDER:-} in mouse.scrolled) - volume=$(get_volume) - case $volume in *[!0-9]*|'') volume=0 ;; esac case ${SCROLL_DELTA:-0} in -*) volume=$((volume - 3)) ;; 0|'') ;; @@ -22,16 +23,9 @@ case ${SENDER:-} in test "$volume" -le 100 || volume=100 osascript -e "set volume output volume $volume" >/dev/null ;; - volume_change) - volume=${INFO:-0} - ;; - *) - volume=$(get_volume) - ;; esac muted=$(osascript -e 'output muted of (get volume settings)' 2>/dev/null || printf 'false\n') -case $volume in *[!0-9]*|'') volume=0 ;; esac if test "$muted" = true || test "$volume" -eq 0; then icon='󰖁' diff --git a/home/dot_config/sketchybar/plugins/executable_workspace.sh b/home/dot_config/sketchybar/plugins/executable_workspace.sh index 43ac3f1..7e4c35d 100755 --- a/home/dot_config/sketchybar/plugins/executable_workspace.sh +++ b/home/dot_config/sketchybar/plugins/executable_workspace.sh @@ -1,10 +1,5 @@ #!/bin/sh -set -eu - -CONFIG_DIR=${CONFIG_DIR:-"$HOME/.config/sketchybar"} -ON_SURFACE=0xfff2f0f4 -ON_PRIMARY=0xff381e72 -test -r "$CONFIG_DIR/colors.sh" && . "$CONFIG_DIR/colors.sh" +set -efu # One batch updates all slots. Events handle focus/create/close; the 2 s refresh # also covers moving a background window without a native window event. @@ -15,19 +10,14 @@ lockf -s -t 0 9 || exit 0 focused=$(aerospace list-workspaces --focused) || exit 1 occupied=$(aerospace list-workspaces --monitor all --empty no) || exit 1 -set -- -for sid in 1 2 3 4 5 6 7 8 9 magic; do - visible=off - highlight=off - color=$ON_SURFACE +set -- --set '/^space\./' drawing=off background.drawing=off icon.highlight=off +IFS=' +' +for sid in $occupied "$focused"; do + case "$sid" in [1-9]|magic) ;; *) continue ;; esac + set -- "$@" --set "space.$sid" drawing=on if test "$sid" = "$focused"; then - visible=on - highlight=on - color=$ON_PRIMARY - elif printf '%s\n' "$occupied" | grep -Fxq "$sid"; then - visible=on + set -- "$@" background.drawing=on icon.highlight=on fi - set -- "$@" --set "space.$sid" "drawing=$visible" \ - "background.drawing=$highlight" "icon.color=$color" done sketchybar "$@"