Compare

8c57a511a35702ddf354f3cb52e04228a36bee82main · 12 commits

merge base 8c57a511a357…

Changed files

FileChanges
README.md +7 −0 raw
flake.lock +115 −25 raw
flake.nix +20 −2 raw
hosts/RyanMac/configuration.nix +38 −17 raw
hosts/RyanMac/taps.nix +0 −16 raw
modules/darwin/random-wallpaper/{module.nix => default.nix} +0 −0 raw
users/ryan/hammerspoon/init.lua +35 −0 raw
users/ryan/home.nix +13 −3 raw
users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/battery.jsx +1 −1 raw
users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/memory.jsx +1 −1 raw
users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/sound.jsx +1 −1 raw
users/ryan/modules/simple-bar/simplebarrc +1 −1 raw
users/ryan/modules/sketchybar/bar-config/README.md +63 −0 raw
users/ryan/modules/sketchybar/bar-config/colors.sh +11 −0 raw
users/ryan/modules/sketchybar/bar-config/icons.sh +35 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/battery.sh +42 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh +37 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh +29 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh +21 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/clock.sh +3 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh +10 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/mail.sh +12 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/network.sh +45 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/space.sh +8 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh +20 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/storage.sh +27 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/volume.sh +36 −0 raw
users/ryan/modules/sketchybar/bar-config/plugins/weather.sh +56 −0 raw
users/ryan/modules/sketchybar/bar-config/sketchybarrc +170 −0 raw
users/ryan/modules/sketchybar/default.nix +19 −0 raw
users/ryan/zen/zenPolicies.nix +1 −0 raw

Commits

HashSubjectAuthorDate
2070b29a Don't follow system settings for reduced motion Ryan Schanzenbacher
6ad98695 add minceraft Ryan Schanzenbacher
b43dfa30 system update Ryan Schanzenbacher
8d7aa6e0 Change to fully be flakes (for homebrew) and install netbird-tui Ryan Schanzenbacher
9cb45d05 keyrepeat parity with linux Ryan Schanzenbacher
2bead7ef add virby and adjust yabai Ryan Schanzenbacher
a6ca159f Add jujutsu and other changes Ryan Schanzenbacher
592f73c2 Window title needs to be on left Ryan Schanzenbacher
6c2bdcff added blueutil to sketchybar env Ryan Schanzenbacher
d2b62c9d Sketchybar now, got tired of the battery issue Ryan Schanzenbacher
27117202 Improve CPU usage for simple-bar and disable linux-builder due to issue for no… Ryan Schanzenbacher
36e50258 hammerspoon listener pt 1 Ryan Schanzenbacher
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..25cd24e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# Bootstrap
+
+```
+sudo nix run --extra-experimental-features "nix-command flakes" github:nix-darwin/nix-darwin -- switch --flake .#<hostname> \
+ --option "extra-substituters" "https://virby-nix-darwin.cachix.org" \
+ --option "extra-trusted-public-keys" "virby-nix-darwin.cachix.org-1:z9GiEZeBU5bEeoDQjyfHPMGPBaIQJOOvYOOjGMKIlLo="
+```
diff --git a/flake.lock b/flake.lock
index 09985fe..c225dc2 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,16 +3,16 @@
"brew-src": {
"flake": false,
"locked": {
- "lastModified": 1781226006,
- "narHash": "sha256-w4ZTuOnhYiDxjaynrMTASzp802QblBWmo3wpB8wVN4Y=",
+ "lastModified": 1785146564,
+ "narHash": "sha256-Sa7/HrfB04H32OJ7/ofxXjiZEbkWtCNOriONYYTL1OA=",
"owner": "Homebrew",
"repo": "brew",
- "rev": "109191be4988470b51a60a5ef1998520aa24c01b",
+ "rev": "b2cfc03346d482f79886de108fee5dc49a6efc10",
"type": "github"
},
"original": {
"owner": "Homebrew",
- "ref": "6.0.1",
+ "ref": "6.0.13",
"repo": "brew",
"type": "github"
}
@@ -24,11 +24,11 @@
]
},
"locked": {
- "lastModified": 1781242433,
- "narHash": "sha256-bchLZZ3sRn740zyvD2icZSnNoTaanN0nw7l6fjVXO+E=",
+ "lastModified": 1785389976,
+ "narHash": "sha256-0tLW8Ff5yt8AH97jw4ZpFJ0OCJ122zIlgWGDmOfU/VU=",
"owner": "nix-darwin",
"repo": "nix-darwin",
- "rev": "aabb2037edfc0f210723b72cd5f528aab5dd3f0b",
+ "rev": "15abb8c98f336cd8bd840d71059adebabe60bf04",
"type": "github"
},
"original": {
@@ -45,11 +45,11 @@
]
},
"locked": {
- "lastModified": 1781189114,
- "narHash": "sha256-5inaamLgUMWy+MOBE9ChF9QAF1o/74LFuHkI0W/9rqc=",
+ "lastModified": 1785531816,
+ "narHash": "sha256-vkMnV0JIyw+g/NmcfoajlGaAO+9a0ezia+FZohQJrik=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "486595d2cf49cfcd649b58a284fa11ac0e34da22",
+ "rev": "bf9ce9fec78f95f374e8dd3b503863a3ec128ebe",
"type": "github"
},
"original": {
@@ -61,11 +61,11 @@
"homebrew-cask": {
"flake": false,
"locked": {
- "lastModified": 1782355557,
- "narHash": "sha256-LjoFTRTn/SxBKSPLX5c1FPJrh0fcR6CPRa2OoRDx6dA=",
+ "lastModified": 1785635183,
+ "narHash": "sha256-m5oRXbzWWRmdr1hwfzRcK6vvvT5qiaDG3M4blTWdCRQ=",
"owner": "homebrew",
"repo": "homebrew-cask",
- "rev": "dabdc97cde3fc9b28db4864dc94697670aab42c3",
+ "rev": "dae004070bf51388a7f9a5160ff5e7da78feeae5",
"type": "github"
},
"original": {
@@ -77,11 +77,11 @@
"homebrew-core": {
"flake": false,
"locked": {
- "lastModified": 1782356982,
- "narHash": "sha256-yhSKSuq4qlcrgxxtRqvdNYJaS5PiLS11Sj+rYrcz134=",
+ "lastModified": 1785632252,
+ "narHash": "sha256-cPRVTvfzohGsJqrytd6QSDn10O00q1NxMajK7pkqPbI=",
"owner": "homebrew",
"repo": "homebrew-core",
- "rev": "3c02d2ed3e52772e24ec557142d3a575e574ab5b",
+ "rev": "bb71705fd502d9d1b6b20be0febe5d4984077336",
"type": "github"
},
"original": {
@@ -90,16 +90,68 @@
"type": "github"
}
},
+ "homebrew-felixkratz": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1780547371,
+ "narHash": "sha256-QGCVV5N1QC56GQK3ck2YJ6J6/S8SQ6rTElz69y58078=",
+ "owner": "FelixKratz",
+ "repo": "homebrew-formulae",
+ "rev": "70b2071488b61c14f773ce9e1bf6626ee851981a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "FelixKratz",
+ "repo": "homebrew-formulae",
+ "type": "github"
+ }
+ },
+ "homebrew-netbird": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1785527306,
+ "narHash": "sha256-EODXrWyUFqBmWJn0tFJ9AmRmqYPfA8eveuBuLW+/6Ds=",
+ "owner": "netbirdio",
+ "repo": "homebrew-tap",
+ "rev": "9ad629107a808a37e5770854ab96574d1e96a8f0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "netbirdio",
+ "repo": "homebrew-tap",
+ "type": "github"
+ }
+ },
+ "netbird-tui": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1784513029,
+ "narHash": "sha256-m9sIguYjsuHz3IWwmNOlvuVTT0Cspe61Y9UyjM3pRVk=",
+ "ref": "refs/heads/main",
+ "rev": "efa74d02ad42ee7aeaf5c97240929063118c6252",
+ "revCount": 1,
+ "type": "git",
+ "url": "https://git.rschanz.org/netbird-tui-flake"
+ },
+ "original": {
+ "type": "git",
+ "url": "https://git.rschanz.org/netbird-tui-flake"
+ }
+ },
"nix-homebrew": {
"inputs": {
"brew-src": "brew-src"
},
"locked": {
- "lastModified": 1781389246,
- "narHash": "sha256-ORqLAo/hoJdsZC7UPAuEHev6S0+XIqKEC7vjo5prz1k=",
+ "lastModified": 1785544760,
+ "narHash": "sha256-qV6OoNuly4ntqpCg7esIeJjUboxSnQNlLPxz+y5h9/o=",
"owner": "zhaofengli",
"repo": "nix-homebrew",
- "rev": "de7953a08ed4bb9245be043e468561c17b89130d",
+ "rev": "937ce52c7d046310571f3a070713804ead496843",
"type": "github"
},
"original": {
@@ -110,11 +162,27 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1781229721,
- "narHash": "sha256-ORvqDbb/LYxiJljGIejapjkc/kJbVote2N1WSb9W45I=",
+ "lastModified": 1785542685,
+ "narHash": "sha256-sjfvXMbG5pCFgpvw2OZAFcZiTvrppCWvnB6cuGrSY08=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "173d0ad7a974f8543a9ab01d2271b2e290341b33",
+ "rev": "f8e81fc7eb063db454f563cdd596fb96a5ad1497",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1782821651,
+ "narHash": "sha256-D5jO0ME1lA9bDHnd5kVawBwItG/N4oZr3FlXmMzLec8=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "e52c192be9d7b2c4bd4aed326c8731b35f8bb75c",
"type": "github"
},
"original": {
@@ -130,11 +198,33 @@
"home-manager": "home-manager",
"homebrew-cask": "homebrew-cask",
"homebrew-core": "homebrew-core",
+ "homebrew-felixkratz": "homebrew-felixkratz",
+ "homebrew-netbird": "homebrew-netbird",
+ "netbird-tui": "netbird-tui",
"nix-homebrew": "nix-homebrew",
"nixpkgs": "nixpkgs",
+ "virby": "virby",
"zen-browser": "zen-browser"
}
},
+ "virby": {
+ "inputs": {
+ "nixpkgs": "nixpkgs_2"
+ },
+ "locked": {
+ "lastModified": 1782970342,
+ "narHash": "sha256-5oVThXJjEFPiCTm6jzAmSCVRuDRi4AG3HgLLjpgLYXA=",
+ "owner": "quinneden",
+ "repo": "virby-nix-darwin",
+ "rev": "a52216470a97ef5970939acf697a00ec61beb0c6",
+ "type": "github"
+ },
+ "original": {
+ "owner": "quinneden",
+ "repo": "virby-nix-darwin",
+ "type": "github"
+ }
+ },
"zen-browser": {
"inputs": {
"home-manager": [
@@ -145,11 +235,11 @@
]
},
"locked": {
- "lastModified": 1781426426,
- "narHash": "sha256-yzxJMNgv/sLishhCT9G2lm7W9CjHSlXWkfbWC7vfjqc=",
+ "lastModified": 1785569296,
+ "narHash": "sha256-ux2+60xcjl4LloKPevqrkP+uY4DNZXhzVd+TYrfE2+w=",
"owner": "0xc000022070",
"repo": "zen-browser-flake",
- "rev": "df336067c1a8af3bfce3f0b88b66dc1c57411b4e",
+ "rev": "945e834a12a97a5e3b08869960e35c7ccfde6f69",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index b48ab94..023a368 100644
--- a/flake.nix
+++ b/flake.nix
@@ -11,7 +11,13 @@
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
- zen-browser = { # Used exclusively for HM module, Zen is installed via cask
+ # My packages
+ netbird-tui = {
+ url = "git+https://git.rschanz.org/netbird-tui-flake";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ # End my packages
+ zen-browser = { # Used exclusively for HM module, Zen is installed via cask (on macos)
url = "github:0xc000022070/zen-browser-flake";
inputs = {
nixpkgs.follows = "nixpkgs";
@@ -27,6 +33,16 @@
url = "github:homebrew/homebrew-cask";
flake = false;
};
+ # Linux builder on macos
+ virby.url = "github:quinneden/virby-nix-darwin";
+ homebrew-netbird = {
+ url = "github:netbirdio/homebrew-tap";
+ flake = false;
+ };
+ homebrew-felixkratz = {
+ url = "github:FelixKratz/homebrew-formulae";
+ flake = false;
+ };
};
outputs = { nixpkgs, home-manager, ... }@inputs: {
@@ -34,7 +50,9 @@
system = "aarch64-darwin";
specialArgs = { inherit inputs; };
modules = [
- { nixpkgs.config.allowUnfree = true; }
+ { nixpkgs.config.allowUnfree = true;
+ nixpkgs.overlays = [ inputs.netbird-tui.overlays.default ]; }
+ inputs.virby.darwinModules.default
./hosts/RyanMac/configuration.nix
home-manager.darwinModules.home-manager
{
diff --git a/hosts/RyanMac/configuration.nix b/hosts/RyanMac/configuration.nix
index 088b774..e8339e6 100644
--- a/hosts/RyanMac/configuration.nix
+++ b/hosts/RyanMac/configuration.nix
@@ -4,7 +4,10 @@ let
defaultBrowser = "zen";
- thirdPartyTaps = import ./taps.nix;
+ thirdPartyTaps = {
+ "netbirdio/homebrew-tap" = inputs.homebrew-netbird;
+ "FelixKratz/homebrew-formulae" = inputs.homebrew-felixkratz;
+ };
pinnedNixpkgs = pkgs.writeText "flake-registry.json" (builtins.toJSON {
version = 2;
@@ -45,7 +48,7 @@ in {
# Modules
imports = [
inputs.nix-homebrew.darwinModules.nix-homebrew
- ../../modules/darwin/random-wallpaper/module.nix
+ ../../modules/darwin/random-wallpaper
];
# Define the system's user and home dir location
@@ -77,7 +80,7 @@ in {
bottom_padding = "10";
left_padding = "10";
right_padding = "10";
- window_gap = "5";
+ window_gap = "10";
mouse_modifier = "alt";
mouse_drop_action = "swap";
mouse_action1 = "move";
@@ -124,15 +127,28 @@ in {
../../files/CACerts/RyanCA.crt
];
- nix = {
+ # Virby linux builder
+ services.virby = {
enable = true;
- linux-builder = {
- enable = true;
- ephemeral = true;
+ cores = 4;
+ diskSize = "50GiB";
+ onDemand = {
+ enable = true;
+ ttl = 30;
};
+ rosetta = true;
+ };
+
+ nix = {
+ enable = true;
settings = {
flake-registry = "/etc/nix/flake-registry.json";
- trusted-users = [ "@admin" ];
+ extra-substituters = [
+ "https://virby-nix-darwin.cachix.org"
+ ];
+ extra-trusted-public-keys = [
+ "virby-nix-darwin.cachix.org-1:z9GiEZeBU5bEeoDQjyfHPMGPBaIQJOOvYOOjGMKIlLo="
+ ];
"extra-experimental-features" = [ "nix-command" "flakes" ];
};
};
@@ -151,7 +167,10 @@ in {
enableRosetta = true;
user = "${username}";
mutableTaps = false;
- taps = thirdPartyTaps;
+ taps = {
+ "homebrew/homebrew-core" = inputs.homebrew-core;
+ "homebrew/homebrew-cask" = inputs.homebrew-cask;
+ } // thirdPartyTaps;
trust.taps = builtins.attrNames thirdPartyTaps;
};
@@ -160,16 +179,14 @@ in {
enable = true;
onActivation = {
- cleanup = "zap";
+ #extraFlags = [ "--force-cleanup" ];
};
global.brewfile = true;
- taps = builtins.attrNames thirdPartyTaps;
+ taps = builtins.attrNames config.nix-homebrew.taps;
- brews = [
- "mqttx-cli"
- ];
+ brews = [ ];
casks = [
"utm"
@@ -179,6 +196,10 @@ in {
"tailscale-app"
"netbird-ui"
"ubersicht"
+ "hammerspoon"
+ "gimp"
+ "kdenlive"
+ "rustdesk"
];
};
@@ -240,8 +261,8 @@ in {
AppleInterfaceStyle = "Dark";
# Key repeat rate
- KeyRepeat = 4;
- InitialKeyRepeat = 30;
+ KeyRepeat = 2;
+ InitialKeyRepeat = 35;
# Swap F1-12 to be default
"com.apple.keyboard.fnState" = true;
@@ -401,7 +422,7 @@ in {
# this is fragile so it goes at the bottom
echo "Reloading skhd..."
- sudo -iu ${username} ${pkgs.skhd}/bin/skhd -r
+ sudo -iu ${username} ${pkgs.skhd}/bin/skhd -r || echo "Failed to reload skhd; continuing anyway..."
# Notes if we are running for the first time and writes a message for actions that may need to be done on macOS or linux
MARK="/opt/.nix-complete"
diff --git a/hosts/RyanMac/taps.nix b/hosts/RyanMac/taps.nix
deleted file mode 100644
index 12235d8..0000000
--- a/hosts/RyanMac/taps.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "netbirdio/homebrew-tap" = builtins.fetchGit {
- url = "https://github.com/netbirdio/homebrew-tap";
- rev = "98a5d193c8d2c03b639aad03159004c64496c3e7";
- };
-
- "FelixKratz/homebrew-formulae" = builtins.fetchGit {
- url = "https://github.com/FelixKratz/homebrew-formulae";
- rev = "70b2071488b61c14f773ce9e1bf6626ee851981a";
- };
-
- "emqx/homebrew-mqttx" = builtins.fetchGit {
- url = "https://github.com/emqx/homebrew-mqttx";
- rev = "9889d741ba44940340d12dcad31fe867f62e7ea8";
- };
-}
diff --git a/modules/darwin/random-wallpaper/module.nix b/modules/darwin/random-wallpaper/default.nix
similarity index 100%
rename from modules/darwin/random-wallpaper/module.nix
rename to modules/darwin/random-wallpaper/default.nix
diff --git a/users/ryan/hammerspoon/init.lua b/users/ryan/hammerspoon/init.lua
new file mode 100644
index 0000000..1245a09
--- /dev/null
+++ b/users/ryan/hammerspoon/init.lua
@@ -0,0 +1,35 @@
+-- ~/.hammerspoon/init.lua
+
+local function refreshWidget()
+ hs.http.asyncGet("http://127.0.0.1:7776/widget/sound/refresh", nil, function(status, body, headers)
+ -- optional: log failures
+ if status ~= 200 then
+ print("Widget refresh failed with status: " .. tostring(status))
+ end
+ end)
+end
+
+local function audioWatcherCallback(uid, eventName, scope, element)
+ -- "vmvc" = volume changed, "mute" = mute toggled
+ if eventName == "vmvc" or eventName == "mute" then
+ refreshWidget()
+ end
+end
+
+local function setupAudioWatcher()
+ local device = hs.audiodevice.defaultOutputDevice()
+ if device then
+ device:watcherCallback(audioWatcherCallback)
+ device:watcherStart()
+ end
+end
+
+setupAudioWatcher()
+
+-- Re-attach the watcher if the default output device changes (e.g. plugging in headphones)
+hs.audiodevice.watcher.setCallback(function(event)
+ if event == "dOut " then -- note: the trailing space is intentional, it's part of the event name
+ setupAudioWatcher()
+ end
+end)
+hs.audiodevice.watcher.start()
diff --git a/users/ryan/home.nix b/users/ryan/home.nix
index f1c1b32..ec3fd1e 100644
--- a/users/ryan/home.nix
+++ b/users/ryan/home.nix
@@ -15,9 +15,7 @@
# My own modules
ryan.neovim.enable = true;
- ryan.simple-bar.enable = pkgs.stdenv.isDarwin;
- ryan.taskwarrior-widget.enable = pkgs.stdenv.isDarwin;
- ryan.ubersicht.enable = pkgs.stdenv.isDarwin;
+ ryan.sketchybar.enable = true;
programs.starship = {
enable = true;
@@ -106,6 +104,9 @@
'';
};
+ # Add hammerspoon if on mac
+ home.file.".hammerspoon".source = lib.mkIf pkgs.stdenv.isDarwin ./hammerspoon;
+
programs.zsh = {
enable = true;
initContent = let
@@ -122,6 +123,7 @@
shellAliases = {
cat = "bat --paging=never";
diff = "delta";
+ brew = "env -u XDG_CONFIG_HOME brew";
rebuild = "sudo darwin-rebuild switch --flake ~/.config/nix/.";
};
};
@@ -145,6 +147,10 @@
audacity
ffmpeg
blender
+ nerd-fonts.anonymice
+ jujutsu
+ netbird-tui
+ prismlauncher
];
# Home Manager is pretty good at managing dotfiles. The primary way to manage
@@ -160,6 +166,10 @@
# org.gradle.console=verbose
# org.gradle.daemon.idletimeout=3600000
# '';
+ ".config/homebrew" = {
+ enable = pkgs.stdenv.isDarwin;
+ source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/.homebrew";
+ };
};
# Install User apps to subdir for dock stuff
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/battery.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/battery.jsx
index bfef8f0..a2abffd 100755
--- a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/battery.jsx
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/battery.jsx
@@ -144,7 +144,7 @@ export const Widget = React.memo(() => {
<Icons.Coffee className="battery__caffeinate-icon" />
</SuspenseIcon>
)}
- {percentage}%
+ {`${percentage}%`}
</DataWidget.Widget>
);
});
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/memory.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/memory.jsx
index e4361db..f5dce96 100755
--- a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/memory.jsx
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/memory.jsx
@@ -113,7 +113,7 @@ export const Widget = () => {
Icon={showIcon ? Pie : null}
onClick={onClick}
>
- <div className="memory__content">{used}%</div>
+ <div className="memory__content">{`${used}%`}</div>
</DataWidget.Widget>
);
};
diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/sound.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/sound.jsx
index a3c5cab..3b39594 100755
--- a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/sound.jsx
+++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/sound.jsx
@@ -56,7 +56,7 @@ export const Widget = React.memo(() => {
if (!visible) return;
const output = await Utils.cachedRun(
`osascript -e 'set v to get volume settings' -e 'output volume of v & output muted of v'`,
- refresh,
+ 500,
);
const parts = Utils.cleanupOutput(output).split(", ");
setState({
diff --git a/users/ryan/modules/simple-bar/simplebarrc b/users/ryan/modules/simple-bar/simplebarrc
index 1494208..7e77252 100644
--- a/users/ryan/modules/simple-bar/simplebarrc
+++ b/users/ryan/modules/simple-bar/simplebarrc
@@ -9,7 +9,7 @@
"bottomBar": false,
"sideDecoration": false,
"inlineSpacesOptions": false,
- "disableAnimations": false,
+ "disableAnimations": true,
"spacesBackgroundColorAsForeground": false,
"widgetsBackgroundColorAsForeground": false,
"widgetMaxWidth": "160px",
diff --git a/users/ryan/modules/sketchybar/bar-config/README.md b/users/ryan/modules/sketchybar/bar-config/README.md
new file mode 100644
index 0000000..7b1bf97
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/README.md
@@ -0,0 +1,63 @@
+# SketchyBar config (translated from Waybar)
+
+This is a translation of a Hyprland/Waybar setup (`config` + `style.css`) into an
+equivalent [SketchyBar](https://github.com/FelixKratz/SketchyBar) config for macOS.
+Layout, fonts, and colours mirror the originals as closely as macOS allows.
+
+## Install
+
+```sh
+# 1. SketchyBar + the font your CSS references
+brew tap FelixKratz/formulae
+brew install sketchybar
+brew install --cask font-anonymice-nerd-font # "AnonymicePro Nerd Font"
+
+# 2. Drop these files in place
+cp -r .config/sketchybar ~/.config/ # (or move this folder there)
+chmod +x ~/.config/sketchybar/sketchybarrc ~/.config/sketchybar/plugins/*.sh
+
+# 3. Start it
+brew services start sketchybar
+```
+
+## Optional dependencies (items self-hide / degrade gracefully without them)
+
+| Item | Needs | Install |
+|-------------|--------------------------------|---------------------------------|
+| workspaces | yabai (tiling WM) for `space` | `brew install koekeishiya/formulae/yabai` |
+| bluetooth | `blueutil` | `brew install blueutil` |
+| brightness | `brightness` CLI | `brew install brightness` |
+| weather | `curl` (built in) | — |
+
+> **Spaces / yabai:** the `space` component needs yabai, which needs SIP partially
+> disabled. If you'd rather not touch SIP, [AeroSpace](https://github.com/nikitabobko/AeroSpace)
+> is a SIP-free tiling WM — swap the `space` loop for its `aerospace_workspace_change`
+> event and point `click_script` at `aerospace workspace $sid`.
+
+## Module mapping
+
+| Waybar | SketchyBar | Notes |
+|-----------------------|-----------------------------------|-------|
+| hyprland/workspaces | `space` items (yabai) | number icon, dim→white for active (Waybar's 3px top-border → `icon.highlight`) |
+| hyprland/window | `front_app` | shows app name; window *title* needs a yabai query (noted in plugin) |
+| clock | `clock` | same `%a %d %b %H:%M` format |
+| battery (BAT0) | `battery` | `pmset`; same icons, warning@25 / critical@10, charging bolt |
+| bluetooth | `bluetooth` | `blueutil`; on/off/connected glyphs, click toggles power |
+| network | `network` | ip + wifi/ethernet/off glyph (cidr & RSSI dropped — not exposed on macOS) |
+| pulseaudio | `volume` | native `volume_change`; volume% + icon, mute glyph |
+| backlight | `brightness` | `brightness` CLI; scroll to adjust (flaky on Apple Silicon) |
+| custom/storage | `storage` | `df /`; warning@80 / critical@90 |
+| custom/weather | `weather` | `wttr.in` in °F, click opens wttr.in |
+| custom/mail | `mail` | Mail.app unread count (swap for your provider) |
+| custom/spotify | `spotify` | native `media_change`; title–artist, click play/pause |
+| idle_inhibitor | `caffeinate` | click toggles `caffeinate`; lock/unlock glyphs |
+
+## Omitted (no macOS/SketchyBar equivalent)
+
+- **tray** — the macOS menu bar already renders status/menu-extra icons.
+- **privacy** (screenshare/mic) — macOS shows camera/mic indicators natively.
+- **hyprland/submap** — yabai has no submap concept.
+- **battery#num2** (BAT1) — Macs have a single battery.
+
+Colours in `colors.sh` and glyphs in `icons.sh` are taken verbatim from your
+`style.css` and `config` (glyphs are byte-for-byte identical).
diff --git a/users/ryan/modules/sketchybar/bar-config/colors.sh b/users/ryan/modules/sketchybar/bar-config/colors.sh
new file mode 100644
index 0000000..1ef1264
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/colors.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# Colors translated from style.css (0xAARRGGBB)
+
+export WHITE=0xffffffff
+export TEXT=0xffd9d8d8 # rgba(217,216,216,1) default text / visible+active workspace
+export TEXT_DIM=0x66d9d8d8 # rgba(217,216,216,0.4) inactive workspace
+export RED=0xffee2e24 # rgba(238,46,36,1) urgent / battery+storage critical
+export YELLOW=0xffffd204 # rgba(255,210,4,1) battery+storage warning
+export BAR_COLOR=0x80000000 # rgba(0,0,0,0.25) #waybar background
+export POPUP_BG=0xd9231f20 # rgba(35,31,32,0.85) window#waybar.solo background
+export TRANSPARENT=0x00000000
diff --git a/users/ryan/modules/sketchybar/bar-config/icons.sh b/users/ryan/modules/sketchybar/bar-config/icons.sh
new file mode 100644
index 0000000..39de9cd
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/icons.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# Nerd Font glyphs, byte-for-byte identical to the original Waybar config.
+# Font: AnonymicePro Nerd Font
+
+# battery format-icons (low -> high) + charging
+export BAT_0="󰁺"; export BAT_1="󰁻"; export BAT_2="󰁾"
+export BAT_3="󰂀"; export BAT_4="󰁹"; export BAT_CHARGING="󰂄"
+
+# bluetooth
+export BT_ON="󰂯"; export BT_OFF="󰂲"; export BT_CONNECTED="󰂱"
+
+# network (wifi weak -> strong, ethernet, disconnected)
+export WIFI_1="󰤟"; export WIFI_2="󰤢"; export WIFI_3="󰤥"
+export WIFI_4="󰤨"; export ETHERNET=""; export NET_OFF="󰤭"
+
+# pulseaudio / volume (default set) + muted
+export VOL_0=""; export VOL_1=""; export VOL_2=""
+export VOL_3=""; export VOL_MUTE=""
+
+# backlight / brightness (dim, bright)
+export BL_LOW=""; export BL_HIGH=""
+
+# idle_inhibitor (activated=lock, deactivated=unlock)
+export IDLE_ON=""; export IDLE_OFF=""
+
+# misc
+export DISK=""; export MAIL=""; export SPOTIFY=""
+
+# weather (nf-weather-*, wttr.in weatherCode -> glyph)
+export WEA_SUNNY=""; export WEA_NIGHT=""
+export WEA_DAY_CLOUDY=""; export WEA_NIGHT_CLOUDY=""
+export WEA_CLOUDY=""; export WEA_FOG=""
+export WEA_SHOWERS=""; export WEA_RAIN=""
+export WEA_SNOW=""; export WEA_SLEET=""
+export WEA_THUNDER=""
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/battery.sh b/users/ryan/modules/sketchybar/bar-config/plugins/battery.sh
new file mode 100755
index 0000000..3d01d63
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/battery.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+CACHE="/tmp/sketchybar_${NAME}.txt"
+case "$SENDER" in
+ mouse.entered)
+ [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")"
+ sketchybar --set "$NAME" popup.drawing=on; exit 0 ;;
+ mouse.exited)
+ sketchybar --set "$NAME" popup.drawing=off; exit 0 ;;
+esac
+
+RAW="$(pmset -g batt)"
+PCT="$(printf '%s' "$RAW" | grep -Eo '[0-9]+%' | head -1 | tr -d '%')"
+CHARGING="$(printf '%s' "$RAW" | grep -c 'AC Power')"
+[ -z "$PCT" ] && exit 0
+
+if [ "$PCT" -ge 80 ]; then ICON=$BAT_4
+elif [ "$PCT" -ge 60 ]; then ICON=$BAT_3
+elif [ "$PCT" -ge 40 ]; then ICON=$BAT_2
+elif [ "$PCT" -ge 20 ]; then ICON=$BAT_1
+else ICON=$BAT_0
+fi
+
+COLOR=$TEXT
+if [ "$CHARGING" -ge 1 ]; then ICON=$BAT_CHARGING; COLOR=$TEXT
+elif [ "$PCT" -le 10 ]; then COLOR=$RED
+elif [ "$PCT" -le 25 ]; then COLOR=$YELLOW
+fi
+
+sketchybar --set "$NAME" label="${PCT}% ${ICON}" label.color=$COLOR
+
+# popup: state + time remaining (Waybar battery format-alt "{time}")
+STATE="$(printf '%s' "$RAW" | grep -Eo 'discharging|charging|charged|AC attached|finishing charge' | head -1)"
+TIME="$(printf '%s' "$RAW" | grep -Eo '[0-9]+:[0-9]+' | head -1)"
+LINE="${PCT}%"
+[ -n "$STATE" ] && LINE="$LINE · $STATE"
+if [ -n "$TIME" ] && [ "$TIME" != "0:00" ]; then
+ if [ "$CHARGING" -ge 1 ]; then LINE="$LINE · $TIME to full"; else LINE="$LINE · $TIME remaining"; fi
+fi
+printf '%s' "$LINE" > "$CACHE"
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh b/users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh
new file mode 100755
index 0000000..80185c0
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/bluetooth.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+CACHE="/tmp/sketchybar_${NAME}.txt"
+case "$SENDER" in
+ mouse.entered)
+ [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")"
+ sketchybar --set "$NAME" popup.drawing=on; exit 0 ;;
+ mouse.exited)
+ sketchybar --set "$NAME" popup.drawing=off; exit 0 ;;
+esac
+
+if ! command -v blueutil >/dev/null 2>&1; then
+ sketchybar --set "$NAME" label="$BT_OFF" label.color=$TEXT_DIM
+ printf '%s' "blueutil not installed" > "$CACHE"
+ exit 0
+fi
+
+if [ "$1" = "toggle" ]; then
+ if [ "$(blueutil -p)" = "1" ]; then blueutil -p 0; else blueutil -p 1; fi
+fi
+
+if [ "$(blueutil -p)" = "1" ]; then
+ NAMES="$(blueutil --connected 2>/dev/null | grep -o 'name: "[^"]*"' | sed 's/name: "//; s/"$//' | paste -sd , - | sed 's/,/, /g')"
+ COUNT="$(blueutil --connected 2>/dev/null | grep -c 'name:')"
+ if [ "$COUNT" -gt 0 ]; then
+ sketchybar --set "$NAME" label="$BT_CONNECTED" label.color=$TEXT
+ printf '%s' "Bluetooth on · $COUNT connected: $NAMES" > "$CACHE"
+ else
+ sketchybar --set "$NAME" label="$BT_ON" label.color=$TEXT
+ printf '%s' "Bluetooth on · no devices connected" > "$CACHE"
+ fi
+else
+ sketchybar --set "$NAME" label="$BT_OFF" label.color=$TEXT_DIM
+ printf '%s' "Bluetooth off" > "$CACHE"
+fi
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh b/users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh
new file mode 100755
index 0000000..87a245f
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/brightness.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# backlight -> brightness. Requires the `brightness` CLI (brew install brightness).
+# NOTE: reading/writing internal-display brightness is unreliable on Apple Silicon;
+# the item hides itself if the tool or a readable value is unavailable.
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+command -v brightness >/dev/null 2>&1 || { sketchybar --set "$NAME" drawing=off; exit 0; }
+
+read_val() { brightness -l 2>/dev/null | awk '/display 0: brightness/{print $NF}'; }
+
+if [ "$SENDER" = "mouse.scrolled" ] && command -v bc >/dev/null 2>&1; then
+ CUR="$(read_val)"
+ if [ -n "$CUR" ]; then
+ case "$INFO" in
+ -*) brightness "$(echo "$CUR - 0.05" | bc)" 2>/dev/null ;; # scroll down
+ *) brightness "$(echo "$CUR + 0.05" | bc)" 2>/dev/null ;; # scroll up
+ esac
+ fi
+fi
+
+CUR="$(read_val)"
+[ -z "$CUR" ] && { sketchybar --set "$NAME" drawing=off; exit 0; }
+PCT="$(printf '%.0f' "$(echo "$CUR * 100" | bc -l 2>/dev/null)" 2>/dev/null)"
+[ -z "$PCT" ] && PCT=0
+
+if [ "$PCT" -ge 50 ]; then ICON=$BL_HIGH; else ICON=$BL_LOW; fi
+# Waybar: {percent}% {icon}
+sketchybar --set "$NAME" drawing=on label="${PCT}% ${ICON}"
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh b/users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh
new file mode 100755
index 0000000..ce4c4d2
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/caffeinate.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+# idle_inhibitor -> caffeinate (keeps display + system awake while "activated")
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+PIDFILE="/tmp/sketchybar_caffeinate.pid"
+is_on() { [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; }
+
+if [ "$1" = "toggle" ]; then
+ if is_on; then
+ kill "$(cat "$PIDFILE")" 2>/dev/null; rm -f "$PIDFILE"
+ else
+ caffeinate -d -i -s & echo $! > "$PIDFILE"
+ fi
+fi
+
+if is_on; then
+ sketchybar --set "$NAME" label="$IDLE_ON" label.color=$TEXT # activated
+else
+ sketchybar --set "$NAME" label="$IDLE_OFF" label.color=$TEXT_DIM # deactivated
+fi
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/clock.sh b/users/ryan/modules/sketchybar/bar-config/plugins/clock.sh
new file mode 100755
index 0000000..e78ee89
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/clock.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Waybar: {:%a %d %b %H:%M:%S}
+sketchybar --set "$NAME" label="$(date '+%a %d %b %H:%M:%S')"
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh b/users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh
new file mode 100755
index 0000000..c5d8ed0
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/front_app.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# hyprland/window -> focused application name.
+# (For the literal window *title* instead of the app name, query yabai:
+# yabai -m query --windows --window | jq -r '.title')
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+if [ "$SENDER" = "front_app_switched" ]; then
+ sketchybar --set "$NAME" label="$INFO"
+fi
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/mail.sh b/users/ryan/modules/sketchybar/bar-config/plugins/mail.sh
new file mode 100755
index 0000000..7ce2fe6
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/mail.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+# Waybar used a custom mail.py; here we read Mail.app's unread inbox count.
+# (First run may prompt for Automation permission. Swap this for your provider.)
+source "$HOME/.config/sketchybar/icons.sh"
+
+UNREAD="$(osascript -e 'tell application "Mail" to get unread count of inbox' 2>/dev/null)"
+
+if [ -n "$UNREAD" ] && [ "$UNREAD" -gt 0 ] 2>/dev/null; then
+ sketchybar --set "$NAME" label="$MAIL $UNREAD"
+else
+ sketchybar --set "$NAME" label="$MAIL"
+fi
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/network.sh b/users/ryan/modules/sketchybar/bar-config/plugins/network.sh
new file mode 100755
index 0000000..b6b003a
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/network.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+CACHE="/tmp/sketchybar_${NAME}.txt"
+case "$SENDER" in
+ mouse.entered)
+ [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")"
+ sketchybar --set "$NAME" popup.drawing=on; exit 0 ;;
+ mouse.exited)
+ sketchybar --set "$NAME" popup.drawing=off; exit 0 ;;
+esac
+
+SERVICE="$(route -n get default 2>/dev/null | awk '/interface:/{print $2}')"
+IP="$(ipconfig getifaddr "$SERVICE" 2>/dev/null)"
+
+if [ -z "$IP" ]; then
+ sketchybar --set "$NAME" label="$NET_OFF"
+ printf '%s' "Disconnected" > "$CACHE"
+ exit 0
+fi
+
+WIFI_DEV="$(networksetup -listallhardwareports 2>/dev/null | awk '/Wi-Fi/{getline; print $2}')"
+if [ "$SERVICE" = "$WIFI_DEV" ]; then ICON=$WIFI_4; TYPE="Wi-Fi"; else ICON=$ETHERNET; TYPE="Ethernet"; fi
+
+sketchybar --set "$NAME" label="$IP $ICON"
+
+# ---- popup details: interface, ip/cidr, gateway, SSID ----
+MASK="$(ifconfig "$SERVICE" 2>/dev/null | awk '/inet /{print $4; exit}')"
+CIDR=""
+if [ -n "$MASK" ]; then
+ n=$(( MASK )); c=0
+ while [ "$n" -gt 0 ]; do c=$(( c + (n & 1) )); n=$(( n >> 1 )); done
+ CIDR="$c"
+fi
+GW="$(route -n get default 2>/dev/null | awk '/gateway:/{print $2}')"
+SSID=""
+if [ "$TYPE" = "Wi-Fi" ]; then
+ SSID="$(networksetup -getairportnetwork "$SERVICE" 2>/dev/null | sed -n 's/^Current Wi-Fi Network: //p')"
+fi
+
+LINE="$SERVICE · $TYPE · ${IP}${CIDR:+/$CIDR}"
+[ -n "$GW" ] && LINE="$LINE · gw $GW"
+[ -n "$SSID" ] && LINE="$LINE · $SSID"
+printf '%s' "$LINE" > "$CACHE"
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/space.sh b/users/ryan/modules/sketchybar/bar-config/plugins/space.sh
new file mode 100755
index 0000000..f15ecca
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/space.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+# hyprland/workspaces active/visible -> highlighted (white) icon.
+# Waybar's 3px white top-border on the active button maps to icon.highlight here.
+if [ "$SELECTED" = "true" ]; then
+ sketchybar --set "$NAME" icon.highlight=on
+else
+ sketchybar --set "$NAME" icon.highlight=off
+fi
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh b/users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh
new file mode 100755
index 0000000..8d91297
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/spotify.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+# custom/spotify -> now playing, driven by the native media_change event.
+source "$HOME/.config/sketchybar/icons.sh"
+
+# Waybar on-click: playerctl play-pause
+if [ "$1" = "toggle" ]; then
+ osascript -e 'tell application "Spotify" to playpause' 2>/dev/null
+ exit 0
+fi
+
+STATE="$(printf '%s' "$INFO" | sed -nE 's/.*"state":"([^"]*)".*/\1/p')"
+TITLE="$(printf '%s' "$INFO" | sed -nE 's/.*"title":"([^"]*)".*/\1/p')"
+ARTIST="$(printf '%s' "$INFO" | sed -nE 's/.*"artist":"([^"]*)".*/\1/p')"
+
+# exec-if check-for-music -> only draw while something is playing
+if [ "$STATE" = "playing" ] && [ -n "$TITLE" ]; then
+ sketchybar --set "$NAME" drawing=on label="$TITLE - $ARTIST"
+else
+ sketchybar --set "$NAME" drawing=off
+fi
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/storage.sh b/users/ryan/modules/sketchybar/bar-config/plugins/storage.sh
new file mode 100755
index 0000000..b844bb5
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/storage.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+CACHE="/tmp/sketchybar_${NAME}.txt"
+case "$SENDER" in
+ mouse.entered)
+ [ -f "$CACHE" ] && sketchybar --set "$NAME.details" label="$(cat "$CACHE")"
+ sketchybar --set "$NAME" popup.drawing=on; exit 0 ;;
+ mouse.exited)
+ sketchybar --set "$NAME" popup.drawing=off; exit 0 ;;
+esac
+
+# On APFS the real user usage lives on the Data volume; fall back to /
+VOL="/System/Volumes/Data"; [ -d "$VOL" ] || VOL="/"
+
+DF="$(df -h "$VOL" | awk 'NR==2{gsub("%","",$5); print $2, $3, $4, $5}')"
+set -- $DF; SIZE="$1"; USED="$2"; AVAIL="$3"; PCT="$4"
+[ -z "$PCT" ] && exit 0
+
+COLOR=$TEXT
+if [ "$PCT" -ge 90 ]; then COLOR=$RED # custom-storage.critical
+elif [ "$PCT" -ge 80 ]; then COLOR=$YELLOW # custom-storage.warning
+fi
+
+sketchybar --set "$NAME" label="${PCT}% ${DISK}" label.color=$COLOR
+printf '%s' "Disk · ${USED} used of ${SIZE} (${PCT}%) · ${AVAIL} free" > "$CACHE"
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/volume.sh b/users/ryan/modules/sketchybar/bar-config/plugins/volume.sh
new file mode 100755
index 0000000..667d0e8
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/volume.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+STEP=5 # % per scroll tick (Waybar used scroll-step: 1 — bump this down to taste)
+
+if [ "$SENDER" = "mouse.scrolled" ]; then
+ CUR="$(osascript -e 'output volume of (get volume settings)')"
+ case "$SCROLL_DELTA" in
+ -*) VOL=$(( CUR - STEP )) ;; # scroll down
+ *) VOL=$(( CUR + STEP )) ;; # scroll up
+ esac
+ [ "$VOL" -lt 0 ] && VOL=0
+ [ "$VOL" -gt 100 ] && VOL=100
+ osascript -e "set volume output volume $VOL"
+elif [ "$SENDER" = "volume_change" ]; then
+ VOL="$INFO"
+else
+ VOL="$(osascript -e 'output volume of (get volume settings)')"
+fi
+
+MUTED="$(osascript -e 'output muted of (get volume settings)')"
+
+if [ "$MUTED" = "true" ] || { [ "$VOL" -eq 0 ] 2>/dev/null; }; then
+ sketchybar --set "$NAME" label="$VOL_MUTE" # format-muted
+ exit 0
+fi
+
+if [ "$VOL" -ge 66 ]; then ICON=$VOL_3
+elif [ "$VOL" -ge 33 ]; then ICON=$VOL_2
+elif [ "$VOL" -ge 1 ]; then ICON=$VOL_1
+else ICON=$VOL_0
+fi
+
+# Waybar: {volume} {icon}
+sketchybar --set "$NAME" label="$VOL $ICON"
diff --git a/users/ryan/modules/sketchybar/bar-config/plugins/weather.sh b/users/ryan/modules/sketchybar/bar-config/plugins/weather.sh
new file mode 100755
index 0000000..abf8103
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/plugins/weather.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# custom/weather -> condition icon + temp, with a hover popup ("tooltip").
+# Uses wttr.in JSON (format=j2, the smaller no-hourly variant). Needs jq + curl.
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+CACHE="/tmp/sketchybar_weather.txt"
+
+# --- hover: show/hide the popup, populated from the cached line ---
+case "$SENDER" in
+ mouse.entered)
+ [ -f "$CACHE" ] && sketchybar --set weather.details label="$(cat "$CACHE")"
+ sketchybar --set "$NAME" popup.drawing=on
+ exit 0 ;;
+ mouse.exited)
+ sketchybar --set "$NAME" popup.drawing=off
+ exit 0 ;;
+esac
+
+command -v jq >/dev/null 2>&1 || { sketchybar --set "$NAME" label="wttr?"; exit 0; }
+
+JSON="$(curl -sf --max-time 6 'wttr.in/?format=j2')" \
+ || JSON="$(curl -sf --max-time 6 'wttr.in/?format=j1')" \
+ || { sketchybar --set "$NAME" label="--"; exit 0; }
+
+cc() { printf '%s' "$JSON" | jq -r ".current_condition[0].$1 // empty"; }
+CODE="$(cc weatherCode)"; TEMP="$(cc temp_F)"; FEELS="$(cc FeelsLikeF)"
+HUM="$(cc humidity)"; WSPD="$(cc windspeedMiles)"; WDIR="$(cc winddir16Point)"
+DESC="$(printf '%s' "$JSON" | jq -r '.current_condition[0].weatherDesc[0].value // empty')"
+AREA="$(printf '%s' "$JSON" | jq -r '.nearest_area[0].areaName[0].value // empty')"
+
+# daytime? simple local-hour heuristic (06:00–18:59) for clear/partly-cloudy variants
+HOUR=$(date +%H); HOUR=${HOUR#0}; : "${HOUR:=0}"
+if [ "$HOUR" -ge 6 ] && [ "$HOUR" -lt 19 ]; then DAY=1; else DAY=0; fi
+
+# WWO weatherCode -> nf-weather glyph
+case "$CODE" in
+ 113) [ "$DAY" = 1 ] && ICON="$WEA_SUNNY" || ICON="$WEA_NIGHT" ;;
+ 116) [ "$DAY" = 1 ] && ICON="$WEA_DAY_CLOUDY" || ICON="$WEA_NIGHT_CLOUDY" ;;
+ 119|122) ICON="$WEA_CLOUDY" ;;
+ 143|248|260) ICON="$WEA_FOG" ;;
+ 200|386|389|392|395) ICON="$WEA_THUNDER" ;;
+ 176|263|266|293|296|353) ICON="$WEA_SHOWERS" ;;
+ 299|302|305|308|356|359) ICON="$WEA_RAIN" ;;
+ 179|227|230|323|326|329|332|335|338|368|371) ICON="$WEA_SNOW" ;;
+ 182|185|281|284|311|314|317|320|350|362|365|374|377) ICON="$WEA_SLEET" ;;
+ *) ICON="$WEA_CLOUDY" ;;
+esac
+
+[ -z "$TEMP" ] && exit 0
+
+# bar: <icon> <temp>°F (drop " ${TEMP}°F" if you want the icon only)
+sketchybar --set "$NAME" label="$ICON ${TEMP}°F"
+
+# cache the single-line hover tooltip
+printf '%s' "${AREA:+$AREA · }${DESC} · feels ${FEELS}°F · ${HUM}% humidity · ${WDIR} ${WSPD}mph" > "$CACHE"
diff --git a/users/ryan/modules/sketchybar/bar-config/sketchybarrc b/users/ryan/modules/sketchybar/bar-config/sketchybarrc
new file mode 100755
index 0000000..34238b2
--- /dev/null
+++ b/users/ryan/modules/sketchybar/bar-config/sketchybarrc
@@ -0,0 +1,170 @@
+#!/usr/bin/env bash
+# ============================================================================
+# sketchybarrc — translated from a Hyprland/Waybar config
+# Layout, fonts and colours mirror the original Waybar config + style.css.
+# ============================================================================
+CONFIG_DIR="$HOME/.config/sketchybar"
+PLUGIN_DIR="$CONFIG_DIR/plugins"
+source "$CONFIG_DIR/colors.sh"
+source "$CONFIG_DIR/icons.sh"
+
+FONT="AnonymicePro Nerd Font"
+
+# ---- bar --------------------------------------------------------------------
+# From #waybar { background: rgba(0,0,0,0.25) } and Waybar height:30, position:top
+sketchybar --bar \
+ height=32 \
+ position=top \
+ color=$BAR_COLOR \
+ padding_left=8 \
+ padding_right=8 \
+ corner_radius=0 \
+ border_width=0 \
+ shadow=off
+
+# ---- defaults ---------------------------------------------------------------
+# From * { color:white; font: AnonymicePro Nerd Font 15px; no radius/shadow }
+sketchybar --default \
+ updates=when_shown \
+ icon.font="$FONT:Regular:15.0" \
+ icon.color=$TEXT \
+ icon.padding_left=4 \
+ icon.padding_right=4 \
+ label.font="$FONT:Regular:15.0" \
+ label.color=$TEXT \
+ label.padding_left=4 \
+ label.padding_right=4 \
+ background.corner_radius=0 \
+ background.height=32
+
+# ---- popup helper -----------------------------------------------------------
+# add_popup <item>: attaches a hover "tooltip" popup + a <item>.details child
+# (styled with POPUP_BG from window#waybar.solo) and subscribes to hover events.
+add_popup() {
+ sketchybar --set "$1" \
+ popup.background.color=$POPUP_BG \
+ popup.background.corner_radius=0 \
+ popup.background.border_width=0 \
+ popup.horizontal=off \
+ popup.y_offset=-1 \
+ --subscribe "$1" mouse.entered mouse.exited
+ sketchybar --add item "$1.details" "popup.$1" \
+ --set "$1.details" icon.drawing=off \
+ label.font="$FONT:Regular:13.0" \
+ label.padding_left=12 label.padding_right=12 \
+ background.height=24
+}
+
+# ============================================================================
+# LEFT — hyprland/workspaces -> yabai spaces
+# (hyprland/submap has no yabai/macOS analog -> omitted)
+# ============================================================================
+for sid in 1 2 3 4 5 6 7 8 9; do
+ sketchybar --add space space.$sid left \
+ --set space.$sid \
+ space=$sid \
+ icon=$sid \
+ icon.color=$TEXT_DIM \
+ icon.highlight_color=$TEXT \
+ label.drawing=off \
+ background.drawing=off \
+ padding_left=5 padding_right=5 \
+ script="$PLUGIN_DIR/space.sh" \
+ click_script="osascript -e 'tell application \"System Events\" to key code ${sid} using option down'"
+done
+
+# ============================================================================
+# CENTER — hyprland/window -> front_app
+# ============================================================================
+sketchybar --add item front_app left \
+ --set front_app \
+ icon.drawing=off \
+ label.max_chars=80 \
+ label.padding_left=10 label.padding_right=10 \
+ script="$PLUGIN_DIR/front_app.sh" \
+ --subscribe front_app front_app_switched
+
+# ============================================================================
+# RIGHT — added right-to-left so the visual order matches Waybar's list:
+# spotify weather mail storage brightness bluetooth volume network
+# caffeinate battery clock
+# ============================================================================
+
+# clock (rightmost) — Waybar {:%a %d %b %H:%M}
+sketchybar --add item clock right \
+ --set clock update_freq=1 icon.drawing=off \
+ label.padding_left=10 label.padding_right=16 \
+ script="$PLUGIN_DIR/clock.sh"
+
+# battery (BAT0). Waybar battery#num2 (BAT1) omitted — Macs have one battery.
+sketchybar --add item battery right \
+ --set battery update_freq=60 icon.drawing=off \
+ script="$PLUGIN_DIR/battery.sh" \
+ --subscribe battery power_source_change system_woke
+add_popup battery
+
+# idle_inhibitor -> caffeinate
+sketchybar --add item caffeinate right \
+ --set caffeinate update_freq=15 \
+ script="$PLUGIN_DIR/caffeinate.sh" \
+ click_script="$PLUGIN_DIR/caffeinate.sh toggle"
+
+# network
+sketchybar --add item network right \
+ --set network update_freq=5 icon.drawing=off \
+ click_script="open -a 'System Settings' 2>/dev/null" \
+ script="$PLUGIN_DIR/network.sh"
+add_popup network
+
+# pulseaudio -> volume (native volume_change event)
+sketchybar --add item volume right \
+ --set volume icon.drawing=off script="$PLUGIN_DIR/volume.sh" \
+ --subscribe volume volume_change mouse.scrolled
+
+# bluetooth (needs blueutil)
+sketchybar --add item bluetooth right \
+ --set bluetooth update_freq=30 icon.drawing=off \
+ click_script="$PLUGIN_DIR/bluetooth.sh toggle" \
+ script="$PLUGIN_DIR/bluetooth.sh"
+add_popup bluetooth
+
+# backlight -> brightness (needs the `brightness` CLI; hides itself if absent)
+sketchybar --add item brightness right \
+ --set brightness update_freq=10 icon.drawing=off \
+ script="$PLUGIN_DIR/brightness.sh" \
+ --subscribe brightness mouse.scrolled
+
+# custom/storage
+sketchybar --add item storage right \
+ --set storage update_freq=60 icon.drawing=off \
+ script="$PLUGIN_DIR/storage.sh"
+add_popup storage
+
+# custom/mail (Mail.app unread)
+# sketchybar --add item mail right \
+# --set mail update_freq=60 icon.drawing=off \
+# click_script="open -a Mail" \
+# script="$PLUGIN_DIR/mail.sh"
+
+# custom/weather (wttr.in j2 -> condition icon + temp, hover popup as a "tooltip")
+sketchybar --add item weather right \
+ --set weather update_freq=1800 icon.drawing=off \
+ click_script="open https://wttr.in" \
+ script="$PLUGIN_DIR/weather.sh"
+add_popup weather
+
+# custom/spotify (now playing via native media_change)
+# sketchybar --add item spotify right \
+# --set spotify icon=$SPOTIFY icon.color=$TEXT drawing=off \
+# label.max_chars=30 scroll_texts=on \
+# click_script="$PLUGIN_DIR/spotify.sh toggle" \
+# script="$PLUGIN_DIR/spotify.sh" \
+# --subscribe spotify media_change
+
+# ----------------------------------------------------------------------------
+# Omitted (no SketchyBar / macOS analog):
+# tray -> the macOS menu bar already renders status/menu-extra items
+# privacy -> macOS shows camera/mic indicators natively in the menu bar
+# ----------------------------------------------------------------------------
+
+sketchybar --update
diff --git a/users/ryan/modules/sketchybar/default.nix b/users/ryan/modules/sketchybar/default.nix
new file mode 100644
index 0000000..6d7c264
--- /dev/null
+++ b/users/ryan/modules/sketchybar/default.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.ryan.sketchybar;
+in
+{
+ options.ryan.sketchybar.enable = mkEnableOption "sketchybar";
+
+ config = mkIf cfg.enable {
+ # Enable the service
+ programs.sketchybar = {
+ enable = true;
+ extraPackages = with pkgs; [ jq blueutil ];
+ config = { source = ./bar-config; recursive = true; };
+ };
+ };
+}
diff --git a/users/ryan/zen/zenPolicies.nix b/users/ryan/zen/zenPolicies.nix
index 1fbb5e1..8d39689 100644
--- a/users/ryan/zen/zenPolicies.nix
+++ b/users/ryan/zen/zenPolicies.nix
@@ -117,6 +117,7 @@ in
"xpinstall.whitelist.required" = lock-true;
"dom.webgpu.enabled" = lock-true;
"media.eme.enabled" = lock-true;
+ "ui.preferReducedMotion" = lock-false;
"general.autoScroll" = lock-true;
"general.smoothScroll" = lock-true;
"browser.crashReports.unsubmittedCheck.autoSubmit2" = lock-false;