dot

vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks

4abaf9f

NixOS flake for the desktop, ported from the CachyOS dotfiles

By Gustaf Granström

Hyprland (Lua config) under uwsm with the greetd/gtkgreet login screen, the
patched Waybar and the Eww control centre, as on CachyOS. The programs
hyprland.lua used to start are systemd user services now, and the login apps
each run as their own unit.

The CachyOS kernel (linux-cachyos-latest-lto-zen4) and cachyos-settings are
ported file by file, with ananicy-cpp, bpftune, proton-cachyos and the gaming
packages. Packages that nixpkgs lacks or that were patched locally (waybar,
xembedsniproxy, themed Signal, Claude Desktop, OpenDeck, BreezeX, proton-cachyos)
are in pkgs/.

Tested in a QEMU VM (./vm.sh): boots the CachyOS kernel to the desktop with no
failed units.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

100 files changed +8702 −0

@@ -1,0 +1,6 @@
1+result
2+result-*
3+*.qcow2
4+vm-monitor.sock
5+*.ppm
6+vm-serial.log
@@ -1,0 +1,127 @@
1+# Moving from CachyOS to NixOS
2+
3+The plan keeps your disk as it is. NixOS goes into new btrfs subvolumes next
4+to CachyOS's, and `/home` stays the same `@home` subvolume. Your files, game
5+libraries, app data, `~/.ssh`, Firefox profile and Steam settings never move,
6+and CachyOS stays bootable until you remove it.
7+
8+## 1. Before you start
9+
10+- [ ] Push this repo (`git push`) and the latest `Eiziv/philly`. The install
11+ fetches philly from GitHub.
12+- [ ] Back up what you can't get back to another disk or machine, in case
13+ something goes wrong with the partition: `~/.ssh`, `~/.config/keys`
14+ (the imsh upload key), `~/.config/Signal` (including `config.json`, which
15+ holds the key to Signal's database), `~/.config/mozilla`, game saves that
16+ aren't in a cloud.
17+- [ ] Optional: a read-only snapshot of /home to roll back to:
18+ `sudo btrfs subvolume snapshot -r /home /home/.snapshot-pre-nixos`
19+- [ ] Get the NixOS installer (26.05, graphical or minimal) on a USB stick.
20+
21+## 2. Install
22+
23+Boot the installer. Wired network works out of the box.
24+
25+```bash
26+sudo -i
27+
28+# The btrfs partition's top level, to make NixOS's subvolumes.
29+mkdir -p /mnt/top
30+mount -o subvolid=5 /dev/nvme0n1p2 /mnt/top
31+btrfs subvolume create /mnt/top/@nixos
32+btrfs subvolume create /mnt/top/@nix
33+btrfs subvolume create /mnt/top/@nixos-log
34+umount /mnt/top
35+
36+# Mount the new system the way hosts/desktop/hardware-configuration.nix does.
37+o=noatime,compress=zstd
38+mount -o subvol=@nixos,$o /dev/nvme0n1p2 /mnt
39+mkdir -p /mnt/{nix,var/log,home,boot}
40+mount -o subvol=@nix,$o /dev/nvme0n1p2 /mnt/nix
41+mount -o subvol=@nixos-log,$o /dev/nvme0n1p2 /mnt/var/log
42+mount -o subvol=@home,$o /dev/nvme0n1p2 /mnt/home
43+mount /dev/nvme0n1p1 /mnt/boot
44+
45+# Compare the generated hardware config with the one in the repo; if the
46+# kernel modules or anything else differ, keep the generated lines.
47+nixos-generate-config --root /mnt --show-hardware-config > /tmp/hw.nix
48+diff /tmp/hw.nix /mnt/home/vizie/git/dot/hosts/desktop/hardware-configuration.nix
49+
50+# philly is a private repo: sign in to GitHub for this root shell.
51+nix --extra-experimental-features 'nix-command flakes' shell nixpkgs#gh -c gh auth login
52+git config --global credential.helper '!gh auth git-credential'
53+
54+# Install. The extra cache is where the CachyOS kernel comes from.
55+nixos-install --flake /mnt/home/vizie/git/dot#desktop \
56+ --option extra-substituters https://attic.xuyh0120.win/lantian \
57+ --option extra-trusted-public-keys lantian:EeAUQ+W+6r7EtwnmYjeVwx5kOGEBpjlBfPlzGlTNvHc=
58+
59+# It asks for a root password at the end. Then set yours:
60+nixos-enter --root /mnt -c 'passwd vizie'
61+reboot
62+```
63+
64+The NixOS boot menu (systemd-boot) becomes the default. CachyOS's Limine stays
65+in the firmware boot menu (F8 at the ASUS logo), and also on the ESP, next to it.
66+
67+## 3. First boot
68+
69+- The login screen is the same gtkgreet over the blurred wallpaper.
70+- Home Manager replaces the old symlinks in `~/.config` (pointing into
71+ `~/git/dotfiles`) with its own. The old ones are kept beside them with a
72+ `.pre-nixos` suffix, so nothing is overwritten.
73+- Bolt installs from Flathub in the background on the first boot with network
74+ (`systemctl status flatpak-bolt`).
75+- "Command not found" suggestions need nix-index's database once:
76+ `nix-index` (takes a few minutes).
77+- Check the background programs: `systemctl --user list-units --type=service`.
78+
79+## 4. What changed
80+
81+What is different from CachyOS, all on purpose:
82+
83+- **Packages** are generic x86-64 builds, apart from the kernel (Zen 4) and
84+ proton-cachyos (x86-64-v3). CachyOS rebuilds everything for v3/v4.
85+- **Snapper** root snapshots are replaced by NixOS generations: each rebuild is
86+ a boot menu entry. `/home` was never snapshotted on CachyOS either.
87+- **GTK theme**: set in `modules/home/theming.nix`. nwg-look can't save over
88+ those files any more.
89+- **Signal theme** is built into the package: edit, `rebuild`, `config/signal/restart`.
90+- **Bitwarden** autostart: the same XDG autostart entry, with NixOS's program name.
91+- **fish**: only CachyOS's default fish config was there, and your shell is zsh,
92+ so fish isn't set up.
93+- **vkBasalt's ReShade shaders** in `~/.config/vkBasalt/reshade-shaders` stay in
94+ your home folder; they aren't in this repo (vkBasalt.conf doesn't use them).
95+- **vkSumi**: its config is kept, but the layer itself wasn't installed on
96+ CachyOS (there is no nixpkgs package for it either).
97+
98+Unchanged on purpose, but worth knowing:
99+
100+- **kitty's font**: kitty.conf asks for JetBrains Mono, which was never
101+ installed, so kitty has been showing Noto Sans Mono. That's kept. Add
102+ `nerd-fonts.jetbrains-mono` to `fonts.packages` in `modules/nixos/desktop.nix`
103+ to get the font the config names.
104+- **The login screen opens a kitty terminal** behind gtkgreet
105+ (`hl.exec_cmd("kitty")` in `config/greetd/hyprland.lua`), running as the
106+ greeter user. If gtkgreet ever crashed, that shell would be on screen before
107+ anyone logs in. If it's a leftover from debugging, delete that line.
108+- **`~/.ssh/authorized_keys`** has a key labelled `chris@echoz.io`, which lets
109+ that key log in as you over SSH (port 22 is open, as before). It stays in your
110+ home folder.
111+- **mDNS** (port 5353) stays closed in the firewall, as ufw had it.
112+- **sched-ext** schedulers stay off, as before. See the comment in
113+ `modules/nixos/cachyos.nix` to try one.
114+
115+## 5. Going back to CachyOS
116+
117+It still boots (firmware boot menu). Its `~/.config` links now point into
118+NixOS's store, which CachyOS doesn't have, so rerun the old repo's script there:
119+`~/git/dotfiles/symlink.sh`.
120+
121+## 6. Once you're happy
122+
123+Remove CachyOS: boot NixOS, mount the top level, delete its subvolumes
124+(`@`, `@root`, `@srv`, `@cache`, `@tmp`, `@log`), then its kernels and Limine
125+files from the ESP (look first: `sudo ls -R /boot`; NixOS's own files are
126+`/boot/EFI/nixos`, `/boot/EFI/systemd`, `/boot/EFI/BOOT` and `/boot/loader`),
127+and its firmware boot entry (`efibootmgr`).
@@ -1,0 +1,122 @@
1+# dot
2+
3+NixOS configuration for `desktop`: the CachyOS setup from
4+[Eiziv/dotfiles]https://github.com/Eiziv/dotfiles, rewritten as a flake with
5+Home Manager. Hyprland (Lua config) under uwsm, the greetd/gtkgreet login screen,
6+the patched Waybar and the Eww control centre, the CachyOS kernel and the CachyOS
7+system tweaks.
8+
9+Moving from CachyOS: see [MIGRATION.md]MIGRATION.md.
10+
11+## Layout
12+
13+```
14+flake.nix inputs, the desktop system, the packages below
15+hosts/desktop/ this machine: hostname, disks, Home Manager
16+modules/nixos/
17+ base.nix Nix, boot, locale, network, firewall, user
18+ cachyos.nix CachyOS kernel + cachyos-settings, file by file
19+ nvidia.nix RTX 3080 Ti, open kernel module
20+ desktop.nix Hyprland, uwsm, greetd, audio, Bluetooth, fonts
21+ gaming.nix Steam, proton-cachyos, gamemode, gamescope, ...
22+ apps.nix desktop apps, Claude Cowork, OpenDeck udev, Bolt
23+ vm.nix the test VM
24+modules/home/
25+ files.nix links config/ into ~/.config (was symlink.sh)
26+ services.nix desktop background programs as systemd user services
27+ shell.nix zsh (as cachyos-zsh-config), git, gh, ssh
28+ theming.nix GTK, cursor
29+ packages.nix tools the scripts call; imsh-clients, philly
30+config/ the dotfiles themselves
31+pkgs/ packages not in nixpkgs, or patched
32+```
33+
34+## Everyday use
35+
36+```bash
37+sudo nixos-rebuild switch --flake ~/git/dot # apply changes (alias: rebuild)
38+nix flake update --flake ~/git/dot # newer packages; then rebuild (alias: update)
39+```
40+
41+Every rebuild is a boot menu entry, so a bad one is undone by picking the one
42+before it.
43+
44+**Editing configs.** `~/.config/hypr`, `waybar`, `kitty`, `dunst`, `fuzzel`, `nvim`,
45+`qt6ct`, Kvantum, `kdeglobals`, the Philly theme, Firefox's `user.js` and
46+`userChrome.css` and `~/.p10k.zsh` link straight to `config/` in this checkout,
47+like the old `symlink.sh` did, so edits apply without a rebuild. (Set
48+`dot.liveConfig = false` to have them copied into the Nix store instead.) If the
49+checkout is not at `~/git/dot`, set `dot.repoPath`.
50+
51+Configs that programs write themselves (btop, micro, MangoHud/vkBasalt/vkSumi
52+from GOverlay, `mimeapps.list`) are copied in only when missing and then left to
53+the program.
54+
55+**Signal theme.** `config/signal/theme.css` is built into the Signal package
56+(`pkgs/signal-desktop-themed`), so after editing it run `rebuild` and then
57+`config/signal/restart`.
58+
59+## Background programs
60+
61+Everything Hyprland used to start from `hl.on("hyprland.start")` is a systemd
62+user service now, started with `graphical-session.target` by uwsm and restarted
63+if it crashes:
64+
65+| Service | |
66+|---|---|
67+| `waybar`, `hypridle`, `hyprpaper`, `dunst`, `hyprpolkitagent` | the shell |
68+| `eww` | control centre and notification flyouts |
69+| `blueman-applet`, `nm-applet`, `xembedsniproxy` | tray (after Waybar) |
70+| `xwl-clipbridge` | X11 → Wayland clipboard for Wine/Proton games |
71+| `autostart-apps` | opens Signal, Discord, Faugus, OpenDeck and WowUp, each as its own unit |
72+
73+```bash
74+systemctl --user status waybar
75+journalctl --user -u waybar -f
76+systemctl --user restart waybar
77+```
78+
79+`hyprland.lua` keeps only what has to live in the compositor: the startup window
80+rules that place those apps and hide WowUp's window.
81+
82+## Gaming
83+
84+- Kernel: `linux-cachyos-latest-lto-zen4` from
85+ [xddxdd/nix-cachyos-kernel]https://github.com/xddxdd/nix-cachyos-kernel, the
86+ same build as CachyOS's znver4 `linux-cachyos` (EEVDF, 1000 Hz, ThinLTO, Zen 4).
87+- cachyos-settings: sysctl, I/O schedulers, THP, ntsync, limits, oomd, PCI latency,
88+ ananicy-cpp with CachyOS's rules, bpftune; see `modules/nixos/cachyos.nix`.
89+- `proton-cachyos` is installed for Steam under CachyOS's own internal name
90+ (`proton-cachyos-slr`), so Steam's existing choice of it still applies.
91+ ProtonPlus-installed tools in `~/.steam` carry over as they are.
92+- Launch options: `game-performance %command%`, `gamemoderun %command%`,
93+ `dlss-swapper %command%`, `mangohud %command%`.
94+
95+Not ported: CachyOS builds *all* its packages for x86-64-v3/v4; NixOS uses
96+generic x86-64 builds outside the kernel and Proton. The difference in games is
97+small.
98+
99+## Testing in a VM
100+
101+```bash
102+./vm.sh
103+```
104+
105+Boots this exact system in QEMU, without NVIDIA and with the configs copied in.
106+You land on the desktop; log out (Super+Delete) to see the login screen.
107+
108+## Custom packages
109+
110+| | |
111+|---|---|
112+| `waybar` | nixpkgs' Waybar with the three patches from the old PKGBUILD |
113+| `xembedsniproxy` | standalone build from plasma-workspace 6.7.5, three patches |
114+| `signal-desktop-themed` | Signal with `config/signal/theme.css` and a transparent window |
115+| `claude-desktop` | Anthropic's official Linux .deb, repackaged |
116+| `opendeck` | upstream release .deb, repackaged |
117+| `breezex-cursor` | BreezeX-Black cursor theme |
118+| `proton-cachyos` | CachyOS's Proton release, for Steam |
119+| `python3-desktop` | Python with PyGObject and python-xlib for the scripts |
120+
121+Each builds on its own, e.g. `nix build .#waybar`. The `.deb`-based ones and
122+proton-cachyos say at the top of their file how to update them.
@@ -1,0 +1,37 @@
1+################### File Generated by Goverlay ###################
2+
3+legacy_layout=0
4+background_alpha=0.6
5+round_corners=0
6+background_color=000000
7+font_size=24
8+text_color=FFFFFF
9+position=top-left
10+table_columns=3
11+gpu_text=GPU
12+gpu_stats
13+gpu_core_clock
14+gpu_mem_clock
15+gpu_temp
16+gpu_power
17+gpu_color=2E9762
18+cpu_text=CPU
19+cpu_stats
20+cpu_mhz
21+cpu_temp
22+cpu_power
23+cpu_color=2E97CB
24+vram
25+vram_color=AD64C1
26+ram
27+ram_color=C26693
28+battery
29+battery_color=00FF00
30+fps
31+frame_timing
32+frametime_color=00FF00
33+fps_limit_method=late
34+fps_limit=0
35+log_duration=30
36+autostart_log=0
37+log_interval=100
@@ -1,0 +1,286 @@
1+#? Config file for btop v.1.4.7
2+
3+#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
4+#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
5+color_theme = "Default"
6+
7+#* If the theme set background should be shown, set to False if you want terminal background transparency.
8+theme_background = true
9+
10+#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false.
11+truecolor = true
12+
13+#* Set to true to force tty mode regardless if a real tty has been detected or not.
14+#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols.
15+force_tty = false
16+
17+#* Option to disable presets. Either the default preset, custom presets, or all presets.
18+#* "Off" All presets are enabled.
19+#* "Default" preset is disabled.#* "Custom" presets are disabled.#* "All" presets are disabled.
20+disable_presets = "Off"
21+
22+#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
23+#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
24+#* Use whitespace " " as separator between different presets.
25+#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
26+presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
27+
28+#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists.
29+#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift.
30+vim_keys = false
31+
32+#* Disable all mouse events.
33+disable_mouse = false
34+
35+#* Rounded corners on boxes, is ignored if TTY mode is ON.
36+rounded_corners = true
37+
38+#* Use terminal synchronized output sequences to reduce flickering on supported terminals.
39+terminal_sync = true
40+
41+#* Default symbols to use for graph creation, "braille", "block" or "tty".
42+#* "braille" offers the highest resolution but might not be included in all fonts.
43+#* "block" has half the resolution of braille but uses more common characters.
44+#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY.
45+#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view.
46+graph_symbol = "braille"
47+
48+# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
49+graph_symbol_cpu = "default"
50+
51+# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
52+graph_symbol_gpu = "default"
53+
54+# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
55+graph_symbol_mem = "default"
56+
57+# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
58+graph_symbol_net = "default"
59+
60+# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
61+graph_symbol_proc = "default"
62+
63+#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
64+shown_boxes = "proc net mem cpu gpu0"
65+
66+#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
67+update_ms = 100
68+
69+#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct",
70+#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly.
71+proc_sorting = "cpu lazy"
72+
73+#* Reverse sorting order, True or False.
74+proc_reversed = false
75+
76+#* Show processes as a tree.
77+proc_tree = false
78+
79+#* Use the cpu graph colors in the process list.
80+proc_colors = true
81+
82+#* Use a darkening gradient in the process list.
83+proc_gradient = true
84+
85+#* If process cpu usage should be of the core it's running on or usage of the total available cpu power.
86+proc_per_core = false
87+
88+#* Show process memory as bytes instead of percent.
89+proc_mem_bytes = true
90+
91+#* Show cpu graph for each process.
92+proc_cpu_graphs = true
93+
94+#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)
95+proc_info_smaps = false
96+
97+#* Show proc box on left side of screen instead of right.
98+proc_left = false
99+
100+#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop).
101+proc_filter_kernel = false
102+
103+#* Should the process list follow the selected process when detailed view is open.
104+proc_follow_detailed = true
105+
106+#* In tree-view, always accumulate child process resources in the parent process.
107+proc_aggregate = false
108+
109+#* Should cpu and memory usage display be preserved for dead processes when paused.
110+keep_dead_proc_usage = false
111+
112+#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available.
113+#* Select from a list of detected attributes from the options menu.
114+cpu_graph_upper = "Auto"
115+
116+#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available.
117+#* Select from a list of detected attributes from the options menu.
118+cpu_graph_lower = "Auto"
119+
120+#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
121+show_gpu_info = "Auto"
122+
123+#* Toggles if the lower CPU graph should be inverted.
124+cpu_invert_lower = true
125+
126+#* Set to True to completely disable the lower CPU graph.
127+cpu_single_graph = false
128+
129+#* Show cpu box at bottom of screen instead of top.
130+cpu_bottom = false
131+
132+#* Shows the system uptime in the CPU box.
133+show_uptime = true
134+
135+#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo.
136+show_cpu_watts = true
137+
138+#* Show cpu temperature.
139+check_temp = true
140+
141+#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors.
142+cpu_sensor = "Auto"
143+
144+#* Show temperatures for cpu cores also if check_temp is True and sensors has been found.
145+show_coretemp = true
146+
147+#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core.
148+#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine.
149+#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries.
150+#* Example: "4:0 5:1 6:3"
151+cpu_core_map = ""
152+
153+#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine".
154+temp_scale = "celsius"
155+
156+#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024.
157+base_10_sizes = false
158+
159+#* Show CPU frequency.
160+show_cpu_freq = true
161+
162+#* How to calculate CPU frequency, available values: "first", "range", "lowest", "highest" and "average".
163+freq_mode = "first"
164+
165+#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
166+#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
167+clock_format = "%X"
168+
169+#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort.
170+background_update = true
171+
172+#* Custom cpu model name, empty string to disable.
173+custom_cpu_name = ""
174+
175+#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ".
176+#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter="/boot /home/user", disks_filter="exclude=/boot /home/user"
177+disks_filter = ""
178+
179+#* Show graphs instead of meters for memory values.
180+mem_graphs = true
181+
182+#* Show mem box below net box instead of above.
183+mem_below_net = false
184+
185+#* Count ZFS ARC in cached and available memory.
186+zfs_arc_cached = true
187+
188+#* If swap memory should be shown in memory box.
189+show_swap = true
190+
191+#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk.
192+swap_disk = true
193+
194+#* If mem box should be split to also show disks info.
195+show_disks = true
196+
197+#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar.
198+only_physical = true
199+
200+#* Read disks list from /etc/fstab. This also disables only_physical.
201+use_fstab = true
202+
203+#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)
204+zfs_hide_datasets = false
205+
206+#* Set to true to show available disk space for privileged users.
207+disk_free_priv = false
208+
209+#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view.
210+show_io_stat = true
211+
212+#* Toggles io mode for disks, showing big graphs for disk read/write speeds.
213+io_mode = false
214+
215+#* Set to True to show combined read/write io graphs in io mode.
216+io_graph_combined = false
217+
218+#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ".
219+#* Example: "/mnt/media:100 /:20 /boot:1".
220+io_graph_speeds = ""
221+
222+#* Swap the positions of the upload and download speed graphs. When true, upload will be on top.
223+swap_upload_download = false
224+
225+#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False.
226+net_download = 100
227+
228+net_upload = 100
229+
230+#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest.
231+net_auto = true
232+
233+#* Sync the auto scaling for download and upload to whichever currently has the highest scale.
234+net_sync = true
235+
236+#* Starts with the Network Interface specified here.
237+net_iface = ""
238+
239+#* "True" shows bitrates in base 10 (Kbps, Mbps). "False" shows bitrates in binary sizes (Kibps, Mibps, etc.). "Auto" uses base_10_sizes.
240+base_10_bitrate = "Auto"
241+
242+#* Show battery stats in top right if battery is present.
243+show_battery = true
244+
245+#* Which battery to use if multiple are present. "Auto" for auto detection.
246+selected_battery = "Auto"
247+
248+#* Show power stats of battery next to charge indicator.
249+show_battery_watts = true
250+
251+#* Set loglevel for "~/.local/state/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
252+#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
253+log_level = "WARNING"
254+
255+#* Automatically save current settings to config file on exit.
256+save_config_on_exit = true
257+
258+#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
259+nvml_measure_pcie_speeds = true
260+
261+#* Measure PCIe throughput on AMD cards, may impact performance on certain cards.
262+rsmi_measure_pcie_speeds = true
263+
264+#* Horizontally mirror the GPU graph.
265+gpu_mirror_graph = true
266+
267+#* Set which GPU vendors to show. Available values are "nvidia amd intel apple"
268+shown_gpus = "nvidia amd intel apple"
269+
270+#* Custom gpu0 model name, empty string to disable.
271+custom_gpu_name0 = ""
272+
273+#* Custom gpu1 model name, empty string to disable.
274+custom_gpu_name1 = ""
275+
276+#* Custom gpu2 model name, empty string to disable.
277+custom_gpu_name2 = ""
278+
279+#* Custom gpu3 model name, empty string to disable.
280+custom_gpu_name3 = ""
281+
282+#* Custom gpu4 model name, empty string to disable.
283+custom_gpu_name4 = ""
284+
285+#* Custom gpu5 model name, empty string to disable.
286+custom_gpu_name5 = ""
@@ -1,0 +1,49 @@
1+[global]
2+monitor = 0
3+follow = mouse
4+alignment = center
5+width = 360
6+offset = 15
7+origin = top-right
8+padding = 12
9+horizontal_padding = 12
10+frame_width = 0
11+markup = full
12+icon_position = off
13+font = "DejaVu Sans Mono 10"
14+stack_duplicates = true
15+hide_duplicate_count = true
16+separator_height = 0
17+fullscreen = pushback
18+corner_radius = 15
19+# Left click does what the notification is for (e.g. opens the chat), then closes
20+# it. Middle click only dismisses it, right click dismisses all.
21+mouse_left_click = do_action, close_current
22+mouse_middle_click = close_current
23+mouse_right_click = close_all
24+progress_bar = true
25+
26+
27+[urgency_low]
28+timeout = 4
29+background = "#25252501"
30+foreground = "#ffffff"
31+frame_color = "#25252501"
32+
33+[urgency_normal]
34+timeout = 8
35+background = "#25252501"
36+foreground = "#ffffff"
37+frame_color = "#25252501"
38+
39+[urgency_critical]
40+timeout = 16
41+background = "#25252501"
42+foreground = "#ffffff"
43+frame_color = "#25252501"
44+
45+[status]
46+category = status
47+set_stack_tag = status
48+history_ignore = yes
49+timeout = 2
@@ -1,0 +1,28 @@
1+// Restore previous session
2+user_pref("browser.startup.page", 3);
3+
4+// Middle mouse scroll
5+user_pref("general.autoScroll", true);
6+
7+// Enable browser toolbox
8+user_pref("devtools.chrome.enabled", true);
9+user_pref("devtools.debugger.remote-enabled", true);
10+
11+user_pref("browser.sessionstore.resume_from_crash", true);
12+user_pref("general.aboutConfig.enable", true);
13+user_pref("middlemouse.paste", false);
14+user_pref("sidebar.animation.enabled", false);
15+// user_pref("image.jxl.enabled", true);
16+user_pref("devtools.cache.disabled", true);
17+user_pref("devtools.netmonitor.persistlog", true);
18+user_pref("devtools.webconsole.persistlog", true);
19+user_pref("devtools.webconsole.timestampMessages", true);
20+
21+user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
22+
23+// Hardware video decoding on NVIDIA via VA-API (libva-nvidia-driver).
24+// Firefox blocks NVIDIA for this by default; needs NVD_BACKEND and
25+// MOZ_DISABLE_RDD_SANDBOX from hyprland.lua as well.
26+user_pref("media.hardware-video-decoding.force-enabled", true);
27+user_pref("media.ffmpeg.vaapi.enabled", true);
28+user_pref("widget.dmabuf.force-enabled", true);
@@ -1,0 +1,68 @@
1+@-moz-document url(chrome://browser/content/browser.xul), url(chrome://browser/content/browser.xhtml) {
2+ body {
3+ background-color: transparent !important;
4+ }
5+
6+ #browser {
7+ background-color: rgba(0,0,0,0.200000) !important;
8+ }
9+
10+ #navigator-toolbox {
11+ background-color: rgba(0,0,0,0.200000) !important;
12+ border-bottom: none !important;
13+ }
14+
15+ #firefox-view-button,
16+ toolbarbutton.titlebar-close,
17+ toolbarspring,
18+ toolbarbutton#alltabs-button,
19+ #urlbar-searchmode-switcher,
20+ /* #unified-extensions-button, */
21+ image.tab-close-button.close-icon,
22+ /* toolbarbutton#tabs-newtab-button, */
23+ splitter#sidebar-tools-and-extensions-splitter,
24+ .titlebar-spacer {
25+ display: none !important;
26+ }
27+
28+ #urlbar hbox.urlbar-background,
29+ toolbar#nav-bar {
30+ background: transparent !important;
31+ }
32+
33+ #urlbar[breakout-extend] hbox.urlbar-background {
34+ background: #2E3440 !important;
35+ }
36+
37+ tabs#tabbrowser-tabs {
38+ border-inline-start: none !important;
39+ }
40+
41+
42+ .tabbrowser-tab:hover > .tab-stack > &:not([selected], [multiselected]) {
43+ background-color: rgba(0,0,0,0.200000) !important;
44+ }
45+
46+ .tab-background {
47+ &:is([selected], [multiselected]) {
48+ background-color: rgba(255,255,255,0.1) !important;
49+ }
50+ }
51+
52+ toolbaritem#ublock0_raymondhill_net-browser-action image.toolbarbutton-icon {
53+ filter: grayscale(100%) invert(100%);
54+ }
55+
56+ toolbaritem#_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action image.toolbarbutton-icon,
57+ button[title="Bitwarden"] img {
58+ filter: grayscale(100%) invert(100%) contrast(200%);
59+ }
60+
61+ toolbaritem#addon_darkreader_org-browser-action image.toolbarbutton-icon {
62+ filter: grayscale(100%) invert(100%) contrast(250%);
63+ }
64+
65+ stack.tab-icon-stack {
66+ display: none;
67+ }
68+}
@@ -1,0 +1,7 @@
1+# Apps shown at the top of fuzzel's list, in this order. One desktop file name
2+# per line, as in /usr/share/applications or ~/.local/share/applications.
3+firefox.desktop
4+kitty.desktop
5+signal.desktop
6+discord.desktop
7+steam.desktop
@@ -1,0 +1,37 @@
1+[main]
2+font=NotoSansM Nerd Font Propo:size=12
3+# Size fonts like GTK apps do (96 DPI) rather than by the monitors' real DPI,
4+# which made everything bigger.
5+dpi-aware=no
6+terminal=kitty
7+# Same icon theme as Qt/KDE (qt6ct, kdeglobals). Without it fuzzel mixes each
8+# app's own icon, in different styles and sizes.
9+icon-theme=Papirus-Dark
10+list-executables-in-path=yes
11+placeholder=Search...
12+line-height=36
13+lines=5
14+width=45
15+horizontal-pad=25
16+vertical-pad=25
17+inner-pad=10
18+
19+# RRGGBBAA. Same near-transparent black as Waybar and the control centre, over
20+# Hyprland's blur (the "launcher" layer rule).
21+[colors]
22+background=0000001a
23+text=f2f2f2cc
24+prompt=f2f2f2cc
25+input=f2f2f2ff
26+placeholder=f2f2f2cc
27+match=f4d69fff
28+selection=0000001a
29+selection-text=f2f2f2ff
30+selection-match=f4d69fff
31+counter=f2f2f2cc
32+
33+# Panel 20, rows inside it 10, like the control centre.
34+[border]
35+width=0
36+radius=20
37+selection-radius=10
@@ -1,0 +1,24 @@
1+#!/usr/bin/env bash
2+# Opens fuzzel with the apps in ./favorites at the top of its list. Hyprland's
3+# mod + Return runs this (hyprland.lua).
4+#
5+# Fuzzel can't pin apps; it sorts by how often each one was launched, counted in
6+# its cache ($XDG_CACHE_HOME/fuzzel, lines of "name.desktop|count"). So before
7+# every start this sets the favourites' counts far above anything normal use
8+# reaches, highest first, and leaves every other app's count as it is.
9+
10+dir="$(dirname "$(readlink -f "$0")")"
11+cache="${XDG_CACHE_HOME:-$HOME/.cache}/fuzzel"
12+touch "$cache"
13+
14+awk -F'|' -v OFS='|' '
15+ FNR == NR {
16+ sub(/#.*/, ""); gsub(/[ \t]/, "")
17+ if ($0 != "" && !($0 in fav)) { fav[$0] = 1000000 - n++; order[n] = $0 }
18+ next
19+ }
20+ !($1 in fav) && $0 != "" { print }
21+ END { for (i = 1; i <= n; i++) print order[i], fav[order[i]] }
22+' "$dir/favorites" "$cache" > "$cache.tmp" && mv "$cache.tmp" "$cache"
23+
24+exec fuzzel "$@"
@@ -1,0 +1,91 @@
1+-- gtkgreet runs as a full-screen layer (-l); blur the wallpaper behind it so the
2+-- login screen looks like hyprlock (blur 5/2 + dim from style.css).
3+hl.layer_rule({
4+ match = {
5+ namespace = "gtk-layer-shell",
6+ },
7+ blur = true,
8+ ignore_alpha = 0,
9+})
10+
11+hl.monitor({
12+ output = "HDMI-A-2",
13+ mode = "2560x1440@280",
14+ position = "0x0",
15+ scale = 1,
16+ vrr = 0,
17+})
18+
19+hl.monitor({
20+ output = "DP-1",
21+ disabled = true,
22+})
23+
24+-- Same cursor as the desktop session. The greeter user has no settings of its
25+-- own, so without this Hyprland falls back to the default (Adwaita) cursor.
26+-- gtkgreet's own window reads GNOME's cursor setting instead; that default comes
27+-- from greetd/99_greeter-cursor.gschema.override in /usr/share/glib-2.0/schemas.
28+hl.env("XCURSOR_THEME", "BreezeX-Black")
29+hl.env("XCURSOR_SIZE", "28")
30+
31+hl.config({
32+ misc = {
33+ disable_hyprland_logo = true,
34+ disable_splash_rendering = true,
35+ -- Wallpaper's average colour (dimmed like style.css) instead of black,
36+ -- shown for the moment before hyprpaper has drawn the wallpaper.
37+ background_color = "rgb(0d1513)",
38+ },
39+ general = {
40+ border_size = 2,
41+ gaps_in = 5,
42+ gaps_out = 17,
43+ layout = "dwindle",
44+ col = {
45+ active_border = "rgb(aaffff)",
46+ inactive_border = "rgba(00000000)",
47+ },
48+ },
49+ decoration = {
50+ rounding = 10,
51+ blur = {
52+ enabled = true,
53+ size = 5,
54+ passes = 2,
55+ new_optimizations = true,
56+ },
57+ shadow = {
58+ enabled = false,
59+ render_power = 3,
60+ range = 4,
61+ color = "rgba(1a1a1aee)",
62+ },
63+ },
64+ input = {
65+ kb_layout = "no",
66+ follow_mouse = 1,
67+ sensitivity = 0,
68+ accel_profile = "flat",
69+ },
70+ debug = {
71+ disable_logs = false,
72+ },
73+})
74+
75+-- Login screen fades in over the wallpaper and out again when you log in.
76+hl.curve("easeOutQuint", { type = "bezier", points = { {0.23, 1}, {0.32, 1} } })
77+hl.curve("almostLinear", { type = "bezier", points = { {0.5, 0.5}, {0.75, 1} } })
78+hl.animation({ leaf = "layersIn", enabled = true, speed = 5, bezier = "easeOutQuint", style = "fade" })
79+hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 5, bezier = "almostLinear" })
80+hl.animation({ leaf = "layersOut", enabled = true, speed = 3, bezier = "almostLinear", style = "fade" })
81+hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 3, bezier = "almostLinear" })
82+
83+hl.on("hyprland.start", function()
84+ hl.exec_cmd("hyprpaper -c /etc/greetd/hyprpaper.conf")
85+ -- gtkgreet exits once the login succeeds: let its fade-out finish, stop
86+ -- hyprpaper (it segfaults if the compositor disappears under it), then quit
87+ -- this Hyprland cleanly so greetd starts the session right away. Without this
88+ -- the greeter got killed and Hyprland/hyprpaper/portals crashed on the way out.
89+ hl.exec_cmd("gtkgreet -l -c /etc/greetd/session.sh -s /etc/greetd/style.css; sleep 0.35; pkill -xf 'hyprpaper -c /etc/greetd/hyprpaper.conf'; sleep 0.1; hyprctl dispatch 'hl.dsp.exit()'")
90+ hl.exec_cmd("kitty")
91+end)
@@ -1,0 +1,7 @@
1+wallpaper {
2+ monitor =
3+ path = /etc/greetd/wallpapers
4+ timeout = 30
5+}
6+
7+splash = false
@@ -1,0 +1,11 @@
1+#!/bin/sh
2+# Session started by the login screen (gtkgreet -c /etc/greetd/session.sh).
3+# The VT is briefly visible between the greeter quitting and Hyprland taking
4+# over the screen, so clear it, hide the text cursor, and keep uwsm's startup
5+# report off it. Hyprland itself runs as a systemd unit and logs to the journal.
6+#
7+# greetd starts this with a bare environment, so it goes through a login shell
8+# first: that loads NixOS's /etc/profile and Home Manager's session variables,
9+# which uwsm then hands to the systemd user session (and so to every service).
10+printf '\033[2J\033[3J\033[H\033[?25l'
11+exec /run/current-system/sw/bin/bash -lc 'exec uwsm start hyprland-uwsm.desktop >/dev/null 2>&1'
@@ -1,0 +1,64 @@
1+/* greetd login screen (gtkgreet -l), styled like hypr/hyprlock.conf in the dotfiles:
2+ full-screen layer, Hyprland blurs the wallpaper behind it (layer rule in
3+ hyprland.lua), translucent black boxes, #eee text, rounding 10. */
4+* {
5+ font-family: "NotoSans Nerd Font", "Noto Sans", sans-serif;
6+ color: #eee;
7+ text-shadow: none;
8+ box-shadow: none;
9+}
10+
11+window {
12+ background-color: rgba(0, 0, 0, 0.25);
13+}
14+
15+#clock {
16+ font-weight: 300;
17+ margin-bottom: 40px;
18+}
19+
20+/* The "Username:" / "Password:" prompt sits left of the box and can't be
21+ moved above it, so hide it; the red "Login failed" label is not a first
22+ child and stays visible. */
23+#body box > label:first-child {
24+ color: transparent;
25+ font-size: 1px;
26+ min-width: 0;
27+ margin: 0;
28+ padding: 0;
29+}
30+
31+/* Content fades in just after Hyprland's layer fade (see hyprland.lua). */
32+@keyframes greet-in {
33+ from { opacity: 0; }
34+ to { opacity: 1; }
35+}
36+
37+#body {
38+ animation: greet-in 500ms ease-out 150ms backwards;
39+}
40+
41+/* "Login failed" */
42+#body box > label:last-child:not(#clock) {
43+ animation: greet-in 250ms ease-out;
44+}
45+
46+#input-field {
47+ transition: background-color 200ms ease-out;
48+ background: rgba(0, 0, 0, 0.2);
49+ border: none;
50+ border-radius: 10px;
51+ min-height: 50px;
52+ padding: 0 16px;
53+ font-size: 15px;
54+ caret-color: #eee;
55+}
56+
57+#input-field:focus {
58+ background: rgba(0, 0, 0, 0.28);
59+}
60+
61+/* Session command and buttons: Enter logs in, like the lock screen. */
62+#command-selector, button {
63+ opacity: 0;
64+}
@@ -1,0 +1,24 @@
1+general {
2+ lock_cmd=pidof hyprlock || hyprlock
3+ before_sleep_cmd=loginctl lock-session
4+ after_sleep_cmd=hyprctl dispatch 'hl.dsp.dpms({ action = "on" })'
5+}
6+
7+# Lock after 10 minutes idle (respects inhibitors, e.g. fullscreen video).
8+listener {
9+ timeout=600
10+ on-timeout=loginctl lock-session
11+}
12+
13+listener {
14+ timeout=1
15+ on-resume=hyprctl dispatch 'hl.dsp.dpms({ action = "on" })'
16+ on-timeout=true
17+ ignore_inhibit=true
18+}
19+
20+listener {
21+ timeout=300
22+ on-timeout=hyprctl dispatch 'hl.dsp.dpms({ action = "off" })'
23+ ignore_inhibit=true
24+}
@@ -1,0 +1,343 @@
1+-- Generated by hyprlang2lua. Review TODOs before reloading Hyprland.
2+
3+local mod = "SUPER"
4+
5+--GENERAL#
6+
7+--Monitor settings
8+hl.monitor({
9+ output = "HDMI-A-2",
10+ mode = "2560x1440@280",
11+ position = "0x0",
12+ scale = 1,
13+ vrr = 0,
14+})
15+
16+hl.monitor({
17+ output = "DP-1",
18+ mode = "2560x1440@144",
19+ position = "2560x-900",
20+ scale = 1,
21+ transform = 1,
22+ vrr = 0,
23+})
24+
25+--monitor=DP-1,disable
26+
27+-- WS on HDMI-A-2
28+hl.workspace_rule({
29+ workspace = "1",
30+ monitor = "HDMI-A-2",
31+ persistent = true,
32+})
33+
34+hl.workspace_rule({
35+ workspace = "2",
36+ monitor = "HDMI-A-2",
37+ persistent = true,
38+})
39+
40+-- WS on DP-1
41+hl.workspace_rule({
42+ workspace = "3",
43+ monitor = "DP-1",
44+ persistent = true,
45+})
46+
47+hl.workspace_rule({
48+ workspace = "4",
49+ monitor = "DP-1",
50+ persistent = true,
51+})
52+
53+hl.workspace_rule({
54+ workspace = "5",
55+ monitor = "DP-1",
56+ persistent = true,
57+})
58+
59+hl.window_rule({
60+ name = "windowrule-1",
61+ match = {
62+ title = "^RuneLite.*",
63+ },
64+ float = true,
65+})
66+
67+hl.window_rule({
68+ name = "windowrule-2",
69+ match = {
70+ class = "net-runelite-client-RuneLite",
71+ title = "win.*",
72+ },
73+ no_focus = true,
74+})
75+
76+hl.window_rule({
77+ name = "windowrule-3",
78+ match = {
79+ class = ".*",
80+ },
81+ immediate = true,
82+})
83+
84+-- Battle.net (Proton via Faugus). The class is shared by every umu game without
85+-- its own ID, so match on the title too.
86+hl.window_rule({
87+ name = "battlenet-float",
88+ match = {
89+ class = "^steam_app_default$",
90+ title = "^Battle\\.net.*",
91+ },
92+ float = true,
93+})
94+
95+-- xembedsniproxy (pkgs/xembedsniproxy) puts Wine/Proton tray icons such as
96+-- Battle.net into Waybar's tray. It keeps a small helper window mapped for the
97+-- icons it hosts, which Hyprland would otherwise show as a floating window.
98+hl.window_rule({
99+ name = "xembedsniproxy",
100+ match = {
101+ class = "^xembedsniproxy$",
102+ },
103+ opacity = "0.0 override 0.0 override",
104+ border_size = 0,
105+ no_blur = true,
106+ no_shadow = true,
107+ no_anim = true,
108+ no_focus = true,
109+ no_initial_focus = true,
110+})
111+
112+-- Dolphin transparency now comes from Kvantum's own translucent_windows
113+-- (see kvantum/RiceDark.kvconfig) rather than a forced window opacity here:
114+-- Kvantum punches real per-pixel alpha into the background only, so text and
115+-- icons stay fully opaque and legible, same mechanism as kitty (which also
116+-- has no windowrule here) -- decoration.blur is already global+enabled above
117+-- with ignore_opacity defaulting true, so it blurs through automatically.
118+-- If Dolphin still looks flat/opaque after applying the Kvantum theme, that
119+-- rule can come back as a fallback:
120+-- hl.window_rule({
121+-- name = "windowrule-4",
122+-- match = { class = "^(org.kde.dolphin)$" },
123+-- opacity = "0.90 0.80",
124+-- })
125+
126+-- Claude's desktop app creates its window opaque (transparent: false in the
127+-- Electron bundle) and has no user stylesheet, so unlike kitty and Dolphin it
128+-- can't put alpha in the background only. A forced window opacity is the
129+-- closest it gets: decoration.blur frosts what shows through, at the cost of
130+-- the text being translucent too.
131+hl.window_rule({
132+ name = "claude-frosted",
133+ match = { class = "^com\\.anthropic\\.Claude$" },
134+ opacity = "0.80 0.80",
135+})
136+
137+--windowrulev2 = maxsize 1803 1153,title:^(RuneLite)$
138+--windowrulev2 = maxsize 1803 1153,title:^(RuneLite)$
139+
140+hl.layer_rule({
141+ name = "layerrule-1",
142+ match = {
143+ namespace = "waybar",
144+ },
145+ blur = true,
146+ ignore_alpha = 0,
147+ blur_popups = true,
148+})
149+
150+hl.layer_rule({
151+ name = "layerrule-2",
152+ match = {
153+ namespace = "launcher", -- fuzzel
154+ },
155+ ignore_alpha = 0,
156+ blur = true,
157+})
158+
159+hl.layer_rule({
160+ name = "layerrule-3",
161+ match = {
162+ namespace = "notifications",
163+ },
164+ ignore_alpha = 0,
165+ blur = true,
166+})
167+
168+hl.layer_rule({
169+ name = "layerrule-4",
170+ match = {
171+ namespace = "eww-control-center",
172+ },
173+ ignore_alpha = 0,
174+ blur = true,
175+})
176+
177+hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("imsh-shot area -Cufa@~/.config/keys/scrnis -s1"))
178+hl.bind(mod .. " + SHIFT + F", hl.dsp.exec_cmd("imsh-shot screen -Cua@~/.config/keys/scrnis"))
179+hl.bind(mod .. " + SHIFT + R", hl.dsp.exec_cmd("imsh-cast -Cua@~/.config/keys/scrnis"))
180+hl.bind(mod .. " + Return", hl.dsp.exec_cmd("~/.config/fuzzel/launch"))
181+hl.bind(mod .. " + T", hl.dsp.exec_cmd("kitty"))
182+hl.bind(mod .. " + W", hl.dsp.exec_cmd("firefox"))
183+hl.bind(mod .. " + Q", hl.dsp.window.close())
184+hl.bind(mod .. " + F", hl.dsp.window.float({ action = "toggle" }))
185+hl.bind(mod .. " + G", hl.dsp.window.fullscreen({ mode = "fullscreen", action = "toggle" }))
186+hl.bind(mod .. " + mouse_down", hl.dsp.focus({ workspace = "m+1" }))
187+hl.bind(mod .. " + mouse_up", hl.dsp.focus({ workspace = "m-1" }))
188+hl.bind(mod .. " + mouse:272", hl.dsp.window.drag())
189+hl.bind(mod .. " + mouse:273", hl.dsp.window.resize())
190+hl.bind(mod .. " + Delete", hl.dsp.exit())
191+
192+hl.env("LIBVA_DRIVER_NAME", "nvidia")
193+hl.env("NVD_BACKEND", "direct") -- libva-nvidia-driver backend (Firefox hardware video decoding)
194+hl.env("MOZ_DISABLE_RDD_SANDBOX", "1") -- let Firefox's video decoder process reach the NVIDIA driver
195+hl.env("XDG_SESSION_TYPE", "wayland")
196+hl.env("GBM_BACKEND", "nvidia-drm")
197+hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
198+hl.env("XCURSOR_THEME", "BreezeX-Black")
199+hl.env("XCURSOR_SIZE", "28")
200+hl.env("QT_QPA_PLATFORMTHEME", "qt6ct")
201+
202+hl.config({
203+ general = {
204+ border_size = 1,
205+ gaps_in = 5,
206+ gaps_out = 17,
207+ layout = "dwindle",
208+ col = {
209+ active_border = "rgba(00000000)",
210+ inactive_border = "rgba(00000000)",
211+ },
212+ allow_tearing = true,
213+ },
214+ decoration = {
215+ rounding = 10,
216+ -- No visible borders; the focused window is the one that isn't dimmed.
217+ dim_inactive = true,
218+ dim_strength = 0.15,
219+ blur = {
220+ enabled = true,
221+ size = 9,
222+ passes = 3,
223+ new_optimizations = true,
224+ popups = true, -- blur tooltips/context menus (Kvantum's popup_blurring is on for this)
225+ },
226+ shadow = {
227+ enabled = false,
228+ render_power = 3,
229+ range = 4,
230+ color = "rgba(1a1a1aee)",
231+ },
232+ },
233+ input = {
234+ kb_layout = "no",
235+ follow_mouse = 1,
236+ sensitivity = 0,
237+ accel_profile = "flat",
238+ },
239+ --MISC#
240+ misc = {
241+ disable_hyprland_logo = true,
242+ disable_splash_rendering = true,
243+ -- Wallpaper's average colour instead of black, so the moment between the
244+ -- login screen and hyprpaper drawing the wallpaper doesn't flash black.
245+ background_color = "rgb(121d1a)",
246+ },
247+ ecosystem = {
248+ no_donation_nag = true,
249+ },
250+ --BINDS#
251+ binds = {
252+ scroll_event_delay = 10,
253+ },
254+ --EXEC#
255+ --ENVIROMENT#
256+ --CURSOR#
257+ cursor = {
258+ -- Software cursor, for tearing. Hyprland (0.56) refuses to tear while a
259+ -- hardware cursor is visible, because the kernel can't update the cursor
260+ -- plane during a tearing flip. So with a hardware cursor a game only tears
261+ -- while it hides the pointer (e.g. mouselook) and is vsynced otherwise, and
262+ -- switching back and forth left WoW's cursor shape stale. With a software
263+ -- cursor the game tears all the time; the price is that frames are
264+ -- composited (one extra copy) while the pointer is visible.
265+ no_hardware_cursors = true,
266+ },
267+ render = {
268+ -- Fullscreen games (e.g. WoW) scan out straight to the monitor instead of
269+ -- being copied by the compositor. With the software cursor above this only
270+ -- applies while the game hides the pointer.
271+ -- 1 = always; XWayland games don't set a content type, so 2 (auto) won't trigger.
272+ direct_scanout = 1,
273+ },
274+})
275+
276+hl.on("hyprland.start", function()
277+ -- Wallpaper, bar, notifications, the control centre, tray helpers, the
278+ -- clipboard bridge and the polkit agent are systemd user services now
279+ -- (modules/home/services.nix in the dot repo): uwsm starts them with
280+ -- graphical-session.target, restarts them if they crash, and
281+ -- `systemctl --user status <name>` / `journalctl --user -u <name>` show them.
282+
283+ -- Apps opened at login, in their usual places (scripts/autostart-apps starts
284+ -- them, from autostart-apps.service). These placement rules are switched off
285+ -- again after two minutes, so opening the same apps by hand later behaves like
286+ -- any other window.
287+ local startup_rules = {
288+ hl.window_rule({
289+ name = "startup-signal",
290+ match = { class = "^signal$" },
291+ workspace = "3 silent",
292+ }),
293+ hl.window_rule({
294+ name = "startup-discord",
295+ match = { class = "^discord$" },
296+ workspace = "3 silent",
297+ }),
298+ hl.window_rule({
299+ name = "startup-faugus",
300+ match = { class = "^io\\.github\\.Faugus\\.faugus-launcher$" },
301+ workspace = "1 silent",
302+ float = true,
303+ size = { 430, 610 },
304+ move = { 1901, 333 },
305+ }),
306+ }
307+ hl.timer(function()
308+ for _, rule in ipairs(startup_rules) do
309+ rule:set_enabled(false)
310+ end
311+ end, { timeout = 120000, type = "oneshot" })
312+
313+ -- WowUp can't start hidden, so the window it opens at login is sent to a hidden
314+ -- workspace and closed again, which leaves it in the tray. That rule must not
315+ -- outlive the one window: it is dropped as soon as the window has opened (or
316+ -- after a minute if none came), so opening WowUp yourself right after login
317+ -- lands on your workspace like any other window.
318+ local wowup_rule = hl.window_rule({
319+ name = "startup-wowup",
320+ match = { class = "^WowUpCf$" },
321+ workspace = "special:autostart silent",
322+ })
323+ local wowup_pending = true
324+ local function wowup_done()
325+ wowup_pending = false
326+ wowup_rule:set_enabled(false)
327+ end
328+ hl.on("window.open", function(window)
329+ if wowup_pending and window.class == "WowUpCf" then
330+ wowup_done()
331+ -- closing it from inside the open event is too early and gets ignored
332+ local address = window.address
333+ hl.timer(function()
334+ hl.dispatch(hl.dsp.window.close({ window = "address:" .. address }))
335+ end, { timeout = 300, type = "oneshot" })
336+ end
337+ end)
338+ hl.timer(wowup_done, { timeout = 60000, type = "oneshot" })
339+
340+ -- hl.exec_cmd("gsettings set org.gnome.desktop.interface color-scheme prefer-dark")
341+ -- hl.exec_cmd("gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark")
342+end)
343+
@@ -1,0 +1,89 @@
1+# Lock screen, styled to match the greetd login screen (/etc/greetd/style.css):
2+# same bamboo wallpaper, translucent black boxes, #eee text, rounding 10.
3+
4+$font = NotoSans Nerd Font
5+$text = rgb(238, 238, 238)
6+$box = rgba(0, 0, 0, 0.2)
7+
8+general {
9+ hide_cursor = false
10+ ignore_empty_input = true
11+}
12+
13+animations {
14+ enabled = true
15+ bezier = ease, 0.25, 0.1, 0.25, 1
16+ animation = fadeIn, 1, 4, ease
17+ animation = fadeOut, 1, 4, ease
18+ animation = inputFieldDots, 1, 2, ease
19+}
20+
21+background {
22+ monitor =
23+ path = ~/.config/hypr/wallpaper.jpg
24+ # Light blur + dim so the text stays readable over the leaves,
25+ # like the blurred panel behind the login box.
26+ blur_passes = 2
27+ blur_size = 5
28+ brightness = 0.75
29+}
30+
31+# Clock
32+label {
33+ monitor = HDMI-A-2
34+ text = <span font_weight="light">$TIME</span>
35+ color = $text
36+ font_family = $font
37+ font_size = 110
38+ position = 0, 170
39+ halign = center
40+ valign = center
41+}
42+
43+# Date
44+label {
45+ monitor = HDMI-A-2
46+ text = cmd[update:60000] date +"%A %-d %B"
47+ color = rgba(238, 238, 238, 0.75)
48+ font_family = $font
49+ font_size = 18
50+ position = 0, 70
51+ halign = center
52+ valign = center
53+}
54+
55+# Password box
56+input-field {
57+ monitor = HDMI-A-2
58+ size = 320, 50
59+ rounding = 10
60+ outline_thickness = 0
61+ inner_color = $box
62+ outer_color = rgba(0, 0, 0, 0)
63+ check_color = rgba(170, 255, 255, 0.5)
64+ fail_color = rgba(255, 107, 107, 0.6)
65+ capslock_color = rgba(244, 214, 159, 0.5)
66+ font_color = $text
67+ font_family = $font
68+ fade_on_empty = false
69+ placeholder_text = <span foreground="##eeeeeeaa">Password</span>
70+ fail_text = <span foreground="##eeeeee">$FAIL ($ATTEMPTS)</span>
71+ dots_size = 0.22
72+ dots_spacing = 0.4
73+ dots_center = true
74+ position = 0, -60
75+ halign = center
76+ valign = center
77+}
78+
79+# User
80+label {
81+ monitor = HDMI-A-2
82+ text = $USER
83+ color = $text
84+ font_family = $font
85+ font_size = 15
86+ position = 0, 0
87+ halign = center
88+ valign = center
89+}
@@ -1,0 +1,7 @@
1+wallpaper {
2+ monitor =
3+ path = /home/vizie/Pictures/Wallpapers
4+ timeout = 30
5+}
6+
7+splash = false
@@ -1,0 +1,68 @@
1+#!/usr/bin/env bash
2+# Apps opened at login. Run once per login by autostart-apps.service (see
3+# modules/home/services.nix), after Waybar, so the tray exists.
4+#
5+# OpenDeck, WowUp to the tray
6+# Faugus its window on workspace 1
7+# Signal, Discord workspace 3, Signal on top and Discord below it
8+#
9+# Which workspace a window lands on (and Faugus' position) is decided by the
10+# temporary "startup-*" window rules in hyprland.lua, which also closes the window
11+# WowUp opens. This script starts the apps and then stacks Signal
12+# above Discord. Apps that are already running are left alone, so it is safe to run
13+# again by hand.
14+
15+SIGNAL_HEIGHT=802 # px; Discord gets the rest of the (portrait) monitor
16+
17+running() { pgrep -f "$1" >/dev/null 2>&1; }
18+# Each app is started as its own systemd service (app-*.service), with systemd as
19+# its parent rather than this script: Discord and WowUp run in a bubblewrap
20+# sandbox that exits with its parent process, so they would close as soon as
21+# this script finished. `systemctl --user list-units 'app-*'` shows them.
22+launch() { uwsm app -t service -- "$@" >/dev/null 2>&1; }
23+dispatch() { hyprctl dispatch "$1" >/dev/null 2>&1; }
24+
25+# Tray apps only show an icon if the tray already exists when they start.
26+for _ in $(seq 40); do
27+ busctl --user status org.kde.StatusNotifierWatcher >/dev/null 2>&1 && break
28+ sleep 0.5
29+done
30+
31+# OpenDeck starts hidden with its flag. WowUp has one too but shows its window
32+# anyway; hyprland.lua closes that window (see "startup-wowup" there).
33+running '(^|/)\.?opendeck(-wrapped)? ' || launch opendeck --hide
34+running 'wowup-cf' || launch wowup-cf --hidden
35+running 'faugus\.(launcher|tray_only)' || launch faugus-launcher
36+running 'signal-desktop.*/app\.asar' || launch signal-desktop # themed, see pkgs/signal-desktop-themed
37+running '/Discord( |$)' || launch discord
38+
39+# --- Stack Signal above Discord ------------------------------------------------
40+
41+# Prints "address y width height" of the first mapped, tiled window whose class is $1,
42+# ignoring small ones (Discord's updater splash).
43+window() {
44+ hyprctl clients -j | python3 -c '
45+import json, sys
46+for c in json.load(sys.stdin):
47+ if c["class"] == sys.argv[1] and c.get("mapped", True) and not c["floating"] and c["size"][0] > 500:
48+ print(c["address"], c["at"][1], c["size"][0], c["size"][1])
49+ break
50+' "$1"
51+}
52+
53+for _ in $(seq 180); do
54+ read -r signal_addr signal_y signal_w signal_h <<<"$(window signal)"
55+ read -r discord_addr discord_y _ _ <<<"$(window discord)"
56+ [ -n "$signal_addr" ] && [ -n "$discord_addr" ] && break
57+ sleep 0.5
58+done
59+[ -n "$signal_addr" ] && [ -n "$discord_addr" ] || exit 0
60+
61+if [ "$signal_y" -gt "$discord_y" ]; then
62+ dispatch "hl.dsp.window.move({ direction = \"u\", window = \"address:$signal_addr\" })"
63+ sleep 0.3
64+ read -r signal_addr signal_y signal_w signal_h <<<"$(window signal)"
65+fi
66+if [ "$signal_h" != "$SIGNAL_HEIGHT" ]; then
67+ dispatch "hl.dsp.window.resize({ x = $signal_w, y = $SIGNAL_HEIGHT, window = \"address:$signal_addr\" })"
68+fi
@@ -1,0 +1,111 @@
1+#!/usr/bin/env python3
2+"""Bridge the X11 CLIPBOARD to the Wayland clipboard.
3+
4+Hyprland only forwards XWayland clipboard changes while an X11 window has
5+keyboard focus. Wine/Proton sets the clipboard asynchronously from a hidden
6+window, so copies from games often never reach Wayland. This watches CLIPBOARD
7+ownership via XFixes and pushes any new text to wl-copy.
8+"""
9+import os
10+import subprocess
11+import sys
12+import time
13+
14+from Xlib import X, Xatom, display
15+from Xlib.ext import xfixes
16+
17+TIMEOUT = 2.0
18+
19+
20+def read_selection(d, win, sel, target, prop):
21+ win.convert_selection(sel, target, prop, X.CurrentTime)
22+ d.flush()
23+ deadline = time.monotonic() + TIMEOUT
24+ while time.monotonic() < deadline:
25+ while d.pending_events():
26+ e = d.next_event()
27+ if e.type != X.SelectionNotify:
28+ continue
29+ if e.property == X.NONE:
30+ return None
31+ p = win.get_full_property(prop, X.AnyPropertyType, sizehint=1 << 20)
32+ win.delete_property(prop)
33+ if p is None:
34+ return None
35+ if p.property_type == d.intern_atom("INCR"):
36+ return read_incr(d, win, prop)
37+ return bytes(p.value)
38+ time.sleep(0.01)
39+ return None
40+
41+
42+def read_incr(d, win, prop):
43+ chunks = []
44+ d.flush()
45+ deadline = time.monotonic() + TIMEOUT * 5
46+ while time.monotonic() < deadline:
47+ while d.pending_events():
48+ e = d.next_event()
49+ if e.type == X.PropertyNotify and e.atom == prop and e.state == X.PropertyNewValue:
50+ p = win.get_full_property(prop, X.AnyPropertyType, sizehint=1 << 20)
51+ win.delete_property(prop)
52+ d.flush()
53+ if p is None or not p.value:
54+ return b"".join(chunks)
55+ chunks.append(bytes(p.value))
56+ time.sleep(0.005)
57+ return None
58+
59+
60+def wayland_text():
61+ try:
62+ r = subprocess.run(["wl-paste", "-n", "-t", "text"], capture_output=True, timeout=TIMEOUT)
63+ return r.stdout if r.returncode == 0 else None
64+ except subprocess.TimeoutExpired:
65+ return None
66+
67+
68+def main():
69+ # Xwayland may not be up yet when launched from the compositor's autostart.
70+ for _ in range(60):
71+ try:
72+ d = display.Display(os.environ.get("DISPLAY", ":0"))
73+ break
74+ except Exception:
75+ time.sleep(1)
76+ else:
77+ sys.exit("xwl-clipbridge: could not connect to X display")
78+ root = d.screen().root
79+ win = root.create_window(0, 0, 1, 1, 0, d.screen().root_depth,
80+ event_mask=X.PropertyChangeMask)
81+ clip = d.intern_atom("CLIPBOARD")
82+ utf8 = d.intern_atom("UTF8_STRING")
83+ prop = d.intern_atom("XWL_CLIPBRIDGE")
84+ d.xfixes_query_version()
85+ d.xfixes_select_selection_input(root, clip, xfixes.XFixesSetSelectionOwnerNotifyMask)
86+ d.flush()
87+ owner_notify = d.extension_event.SetSelectionOwnerNotify # (type, subcode)
88+ print("xwl-clipbridge: watching X11 CLIPBOARD", file=sys.stderr, flush=True)
89+
90+ while True:
91+ e = d.next_event()
92+ if (e.type, getattr(e, "sub_code", None)) != owner_notify:
93+ continue
94+ if e.owner in (X.NONE, win.id):
95+ continue
96+ # Let the owner settle and give Hyprland's own bridge a chance first.
97+ time.sleep(0.15)
98+ text = read_selection(d, win, clip, utf8, prop)
99+ if text is None:
100+ text = read_selection(d, win, clip, Xatom.STRING, prop)
101+ if not text:
102+ continue
103+ # Skip if Wayland already has it (Hyprland bridged it, or it came from Wayland).
104+ if wayland_text() == text:
105+ continue
106+ subprocess.run(["wl-copy", "--type", "text/plain;charset=utf-8"], input=text)
107+ print(f"xwl-clipbridge: forwarded {len(text)} bytes", file=sys.stderr, flush=True)
108+
109+
110+if __name__ == "__main__":
111+ main()
@@ -1,0 +1,8 @@
1+[General]
2+widgetStyle=kvantum
3+
4+[Icons]
5+Theme=Papirus-Dark
6+
7+[KDE]
8+widgetStyle=kvantum
@@ -1,0 +1,29 @@
1+background_opacity 0
2+font_size 12
3+font_family JetBrainsMono Nerd Font
4+bold_font JetBrains Mono Bold
5+italic_font JetBrains Mono Italic
6+bold_italic_font JetBrains Mono Bold Italic
7+disable_ligatures never
8+color0 #292d3e
9+color1 #ff8288
10+color2 #b4e88d
11+color3 #f4d69f
12+color4 #82aaff
13+color5 #e9c1ff
14+color6 #89ebff
15+color7 #d0d0d0
16+color8 #434758
17+color9 #ff8b92
18+color10 #ddffa7
19+color11 #ffe585
20+color12 #9cc4ff
21+color13 #ddb0f6
22+color14 #a3f7ff
23+color15 #ffffff
24+window_padding_width 10
25+term xterm-256color
26+# Do not restore the last window size or maximized state on startup; the tiling
27+# window manager decides the size. Otherwise one kitty closed while maximized makes
28+# every new one open maximized.
29+remember_window_size no
@@ -1,0 +1,485 @@
1+[%General]
2+author=Tsu Jan
3+comment=A minimalistic dark theme inspired by the Arc GTK theme
4+x11drag=menubar_and_primary_toolbar
5+alt_mnemonic=true
6+left_tabs=true
7+attach_active_tab=true
8+mirror_doc_tabs=true
9+group_toolbar_buttons=false
10+toolbar_item_spacing=1
11+toolbar_interior_spacing=3
12+spread_progressbar=true
13+composite=true
14+menu_shadow_depth=5
15+menu_separator_height=6
16+tooltip_shadow_depth=6
17+splitter_width=4
18+scroll_width=9
19+scroll_arrows=false
20+scroll_min_extent=60
21+slider_width=6
22+slider_handle_width=18
23+slider_handle_length=18
24+center_toolbar_handle=true
25+check_size=14
26+textless_progressbar=false
27+progressbar_thickness=3font
28+menubar_mouse_tracking=true
29+toolbutton_style=1
30+click_behavior=1
31+translucent_windows=true
32+blurring=false
33+popup_blurring=true
34+vertical_spin_indicators=false
35+spin_button_width=32
36+fill_rubberband=false
37+merge_menubar_with_toolbar=true
38+small_icon_size=16
39+large_icon_size=32
40+button_icon_size=16
41+toolbar_icon_size=22
42+combo_as_lineedit=true
43+animate_states=false
44+combo_menu=true
45+hide_combo_checkboxes=true
46+combo_focus_rect=true
47+groupbox_top_label=true
48+inline_spin_indicators=false
49+joined_inactive_tabs=false
50+layout_spacing=6
51+layout_margin=9
52+scrollbar_in_view=true
53+transient_scrollbar=true
54+transient_groove=true
55+submenu_overlap=3
56+tooltip_delay=-1
57+tree_branch_line=true
58+contrast=1.00
59+dialog_button_layout=0
60+drag_from_buttons=false
61+intensity=1.00
62+menu_blur_radius=6
63+no_inactiveness=false
64+no_window_pattern=false
65+opaque=kaffeine,kmplayer,subtitlecomposer,kdenlive,vlc,smplayer,smplayer2,avidemux,avidemux2_qt4,avidemux3_qt4,avidemux3_qt5,kamoso,QtCreator,VirtualBox,VirtualBoxVM,trojita,dragon,digikam,lyx
66+reduce_menu_opacity=90
67+reduce_window_opacity=90
68+respect_DE=true
69+saturation=1.00
70+scrollable_menu=true
71+shadowless_popup=true
72+submenu_delay=250
73+tooltip_blur_radius=6
74+
75+[GeneralColors]
76+window.color=#383c4a
77+base.color=#404552
78+alt.base.color=#3c434f
79+button.color=#414654
80+light.color=#5f677f
81+mid.light.color=#313131
82+dark.color=black
83+mid.color=#191919
84+highlight.color=#5294e2
85+inactive.highlight.color=#5294e2
86+text.color=#ffffffc8
87+window.text.color=#ffffffc8
88+button.text.color=#ffffffc8
89+disabled.text.color=#ffffff73
90+tooltip.text.color=#eefcff
91+highlight.text.color=white
92+link.color=#009DFF
93+link.visited.color=#9E4FFF
94+progress.indicator.text.color=white
95+
96+[Hacks]
97+transparent_ktitle_label=true
98+transparent_dolphin_view=true
99+transparent_pcmanfm_sidepane=true
100+blur_translucent=false
101+transparent_menutitle=true
102+respect_darkness=true
103+force_size_grip=true
104+iconless_pushbutton=true
105+iconless_menu=true
106+disabled_icon_opacity=100
107+lxqtmainmenu_iconsize=22
108+normal_default_pushbutton=true
109+single_top_toolbar=true
110+tint_on_mouseover=0
111+transparent_pcmanfm_view=true
112+blur_only_active_window=false
113+centered_forms=false
114+kinetic_scrolling=false
115+middle_click_scroll=false
116+no_selection_tint=false
117+noninteger_translucency=false
118+style_vertical_toolbars=false
119+
120+[PanelButtonCommand]
121+frame=true
122+frame.element=button
123+frame.top=3
124+frame.bottom=3
125+frame.left=3
126+frame.right=3
127+interior=true
128+interior.element=button
129+indicator.size=9
130+text.normal.color=#ffffffc8
131+text.focus.color=white
132+text.press.color=white
133+text.toggle.color=white
134+text.shadow=0
135+text.margin=1
136+text.iconspacing=4
137+indicator.element=arrow
138+text.margin.top=2
139+text.margin.bottom=2
140+text.margin.left=2
141+text.margin.right=2
142+min_width=+0.3font
143+min_height=+0.3font
144+frame.expansion=6
145+
146+[PanelButtonTool]
147+inherits=PanelButtonCommand
148+
149+[Dock]
150+inherits=PanelButtonCommand
151+interior.element=dock
152+frame.element=dock
153+frame.top=1
154+frame.bottom=1
155+frame.left=1
156+frame.right=1
157+text.normal.color=#ffffffc8
158+
159+[DockTitle]
160+inherits=PanelButtonCommand
161+frame=false
162+interior=false
163+text.normal.color=#ffffff96
164+text.focus.color=white
165+text.bold=true
166+
167+[IndicatorSpinBox]
168+inherits=PanelButtonCommand
169+frame=true
170+interior=true
171+frame.left=1
172+indicator.element=spin
173+indicator.size=10
174+text.normal.color=#ffffffc8
175+
176+[RadioButton]
177+inherits=PanelButtonCommand
178+frame=false
179+interior.element=radio
180+text.normal.color=#ffffffc8
181+text.focus.color=white
182+
183+[CheckBox]
184+inherits=PanelButtonCommand
185+frame=false
186+interior.element=checkbox
187+text.normal.color=#ffffffc8
188+text.focus.color=white
189+
190+[Focus]
191+inherits=PanelButtonCommand
192+frame=false
193+frame.element=focus
194+frame.top=1
195+frame.bottom=1
196+frame.left=1
197+frame.right=1
198+frame.patternsize=20
199+
200+[GenericFrame]
201+inherits=PanelButtonCommand
202+frame=true
203+interior=false
204+frame.element=common
205+interior.element=common
206+frame.top=3
207+frame.bottom=3
208+frame.left=3
209+frame.right=3
210+
211+[LineEdit]
212+inherits=PanelButtonCommand
213+frame.element=lineedit
214+interior.element=lineedit
215+text.margin.left=1
216+text.margin.right=1
217+
218+[DropDownButton]
219+inherits=PanelButtonCommand
220+indicator.element=arrow-down
221+
222+[IndicatorArrow]
223+indicator.element=arrow
224+indicator.size=9
225+
226+[ToolboxTab]
227+inherits=PanelButtonCommand
228+text.normal.color=#ffffffc8
229+text.press.color=#ffffff96
230+text.focus.color=white
231+
232+[Tab]
233+inherits=PanelButtonCommand
234+interior.element=tab
235+text.margin.left=8
236+text.margin.right=8
237+text.margin.top=2
238+text.margin.bottom=2
239+frame.element=tab
240+indicator.element=tab
241+frame.top=4
242+frame.bottom=4
243+frame.left=4
244+frame.right=4
245+text.normal.color=#ffffff78
246+text.focus.color=#ffffffb4
247+text.toggle.color=#ffffffd2
248+frame.expansion=0
249+
250+[TabFrame]
251+inherits=PanelButtonCommand
252+frame.element=tabframe
253+interior.element=tabframe
254+frame.top=4
255+frame.bottom=4
256+frame.left=4
257+frame.right=4
258+
259+[TreeExpander]
260+inherits=PanelButtonCommand
261+indicator.size=12
262+indicator.element=tree
263+
264+[HeaderSection]
265+inherits=PanelButtonCommand
266+interior.element=header
267+frame.element=header
268+frame.top=3
269+frame.bottom=3
270+frame.left=1
271+frame.right=1
272+text.bold=true
273+text.normal.color=#ffffff96
274+text.focus.color=#5796e8
275+text.toggle.color=white
276+frame.expansion=0
277+
278+[SizeGrip]
279+indicator.element=resize-grip
280+
281+[Toolbar]
282+inherits=PanelButtonCommand
283+indicator.element=toolbar
284+indicator.size=5
285+text.margin=0
286+frame=false
287+interior=false
288+interior.element=menubar
289+frame.element=menubar
290+text.normal.color=#ffffffc8
291+text.focus.color=white
292+frame.bottom=0
293+frame.expansion=0
294+
295+[Slider]
296+inherits=PanelButtonCommand
297+frame.element=slider
298+interior.element=slider
299+frame.top=3
300+frame.bottom=3
301+frame.left=3
302+frame.right=3
303+
304+[SliderCursor]
305+inherits=PanelButtonCommand
306+frame=false
307+interior.element=slidercursor
308+
309+[Progressbar]
310+inherits=PanelButtonCommand
311+frame.element=progress
312+interior.element=progress
313+text.margin=0
314+text.normal.color=#ffffffc8
315+text.focus.color=white
316+text.press.color=white
317+text.toggle.color=white
318+text.bold=false
319+frame.expansion=8
320+
321+[ProgressbarContents]
322+inherits=PanelButtonCommand
323+frame=true
324+frame.element=progress-pattern
325+interior.element=progress-pattern
326+
327+[ItemView]
328+inherits=PanelButtonCommand
329+text.margin=0
330+frame.element=itemview
331+interior.element=itemview
332+frame.top=2
333+frame.bottom=2
334+frame.left=2
335+frame.right=2
336+text.margin.top=2
337+text.margin.bottom=2
338+text.margin.left=4
339+text.margin.right=4
340+text.normal.color=#ffffffc8
341+text.focus.color=white
342+text.press.color=white
343+text.toggle.color=white
344+frame.expansion=0
345+
346+[Splitter]
347+indicator.size=48
348+
349+[Scrollbar]
350+inherits=PanelButtonCommand
351+indicator.element=arrow
352+indicator.size=10
353+
354+[ScrollbarSlider]
355+inherits=PanelButtonCommand
356+frame.element=scrollbarslider
357+interior=false
358+frame.left=6
359+frame.right=6
360+frame.top=6
361+frame.bottom=6
362+indicator.element=grip
363+indicator.size=13
364+frame.expansion=48
365+
366+[ScrollbarGroove]
367+inherits=PanelButtonCommand
368+interior=false
369+frame=false
370+
371+[MenuItem]
372+inherits=PanelButtonCommand
373+frame=true
374+frame.element=menuitem
375+interior.element=menuitem
376+indicator.element=menuitem
377+text.normal.color=#ffffffc8
378+text.focus.color=white
379+text.margin.top=1
380+text.margin.bottom=1
381+text.margin.left=15
382+text.margin.right=5
383+frame.top=3
384+frame.bottom=3
385+frame.left=3
386+frame.right=3
387+frame.expansion=0
388+
389+[MenuBar]
390+inherits=PanelButtonCommand
391+frame.element=menubar
392+interior.element=menubar
393+frame.bottom=0
394+frame.expansion=0
395+
396+[MenuBarItem]
397+inherits=PanelButtonCommand
398+interior=true
399+interior.element=menubaritem
400+frame.element=menubaritem
401+frame.top=2
402+frame.bottom=2
403+frame.left=2
404+frame.right=2
405+text.margin.left=4
406+text.margin.right=4
407+text.margin.top=0
408+text.margin.bottom=0
409+text.normal.color=#000000b4
410+text.focus.color=white
411+frame.expansion=0
412+
413+[TitleBar]
414+inherits=PanelButtonCommand
415+frame=false
416+interior.element=titlebar
417+indicator.size=12
418+indicator.element=mdi
419+text.normal.color=#787878
420+text.focus.color=white
421+text.bold=true
422+text.italic=true
423+frame.expansion=0
424+
425+[ComboBox]
426+inherits=PanelButtonCommand
427+interior.element=combo
428+frame.element=combo
429+text.press.color=#ffffffc8
430+indicator.element=carrow
431+
432+[Menu]
433+inherits=PanelButtonCommand
434+frame.top=1
435+frame.bottom=1
436+frame.left=1
437+frame.right=1
438+interior=false
439+frame.element=menu
440+interior.element=menu
441+text.normal.color=#ffffffc8
442+text.shadow=false
443+frame.expansion=0
444+
445+[GroupBox]
446+inherits=GenericFrame
447+frame=false
448+text.shadow=0
449+text.margin=0
450+text.normal.color=#ffffff96
451+text.focus.color=white
452+text.bold=true
453+frame.expansion=0
454+
455+[TabBarFrame]
456+inherits=GenericFrame
457+frame=true
458+frame.element=tabBarFrame
459+interior=false
460+frame.top=4
461+frame.bottom=4
462+frame.left=4
463+frame.right=4
464+
465+[ToolTip]
466+inherits=GenericFrame
467+frame.top=3
468+frame.bottom=3
469+frame.left=3
470+frame.right=3
471+interior=false
472+text.shadow=0
473+text.margin=0
474+interior.element=tooltip
475+frame.element=tooltip
476+frame.expansion=0
477+
478+[StatusBar]
479+inherits=GenericFrame
480+frame=false
481+interior=false
482+
483+[Window]
484+interior=true
485+interior.element=window
@@ -1,0 +1,87 @@
1+# RiceDark — Kvantum's bundled KvArcDark theme, unmodified except for the
2+# accent color, changed to match this rig's cyan (see hypr/hyprland.lua's
3+# active_border rgb(aaffff)). Everything else is copied verbatim from
4+# KvArcDark since that's the theme actually confirmed to render correctly
5+# in Dolphin -- minimal diff from a known-good file, on purpose.
6+#
7+# Shapes/frames come from KvArcDark.svg (copied onto this machine by
8+# kvantum/install.sh, not tracked in git -- it's the upstream package asset).
9+
10+[%General]
11+author=Tsu Jan (accent color adapted)
12+comment=A minimalistic dark theme inspired by the Arc GTK theme, cyan accent
13+x11drag=menubar_and_primary_toolbar
14+alt_mnemonic=true
15+left_tabs=true
16+attach_active_tab=true
17+mirror_doc_tabs=true
18+group_toolbar_buttons=false
19+toolbar_item_spacing=1
20+toolbar_interior_spacing=3
21+spread_progressbar=true
22+composite=true
23+menu_shadow_depth=5
24+menu_separator_height=6
25+tooltip_shadow_depth=6
26+splitter_width=4
27+scroll_width=9
28+scroll_arrows=false
29+scroll_min_extent=60
30+slider_width=6
31+slider_handle_width=18
32+slider_handle_length=18
33+center_toolbar_handle=true
34+check_size=14
35+textless_progressbar=false
36+progressbar_thickness=3
37+menubar_mouse_tracking=true
38+toolbutton_style=1
39+click_behavior=0
40+translucent_windows=false
41+blurring=false
42+popup_blurring=false
43+vertical_spin_indicators=false
44+spin_button_width=32
45+fill_rubberband=false
46+merge_menubar_with_toolbar=true
47+small_icon_size=16
48+large_icon_size=32
49+button_icon_size=16
50+toolbar_icon_size=22
51+combo_as_lineedit=true
52+animate_states=false
53+combo_menu=true
54+hide_combo_checkboxes=true
55+combo_focus_rect=true
56+groupbox_top_label=true
57+inline_spin_indicators=false
58+joined_inactive_tabs=false
59+layout_spacing=6
60+layout_margin=9
61+scrollbar_in_view=true
62+transient_scrollbar=true
63+transient_groove=true
64+submenu_overlap=3
65+tooltip_delay=-1
66+tree_branch_line=true
67+
68+[GeneralColors]
69+window.color=#383c4a
70+base.color=#404552
71+alt.base.color=#3c434f
72+button.color=#414654
73+light.color=#5f677f
74+mid.light.color=#313131
75+dark.color=black
76+mid.color=#191919
77+highlight.color=#7fe0e0
78+inactive.highlight.color=#5fb0b0
79+text.color=#ffffffc8
80+window.text.color=#ffffffc8
81+button.text.color=#ffffffc8
82+disabled.text.color=#ffffff73
83+tooltip.text.color=#eefcff
84+highlight.text.color=white
85+link.color=#aaffff
86+link.visited.color=#9E4FFF
87+progress.indicator.text.color=black
@@ -1,0 +1,2 @@
1+[General]
2+theme=KvArcDark#
@@ -1,0 +1,1 @@
1+{}
@@ -1,0 +1,42 @@
1+color-link default "#C6D0F5,#303446"
2+color-link comment "#626880"
3+
4+color-link identifier "#8CAAEE"
5+color-link identifier.class "#8CAAEE"
6+color-link identifier.var "#8CAAEE"
7+
8+color-link constant "#EF9F76"
9+color-link constant.number "#EF9F76"
10+color-link constant.string "#A6D189"
11+
12+color-link symbol "#F4B8E4"
13+color-link symbol.brackets "#EEBEBE"
14+color-link symbol.tag "#8CAAEE"
15+
16+color-link type "#8CAAEE"
17+color-link type.keyword "#E5C890"
18+
19+color-link special "#F4B8E4"
20+color-link statement "#CA9EE6"
21+color-link preproc "#F4B8E4"
22+
23+color-link underlined "#99D1DB"
24+color-link error "bold #E78284"
25+color-link todo "bold #E5C890"
26+
27+color-link diff-added "#A6D189"
28+color-link diff-modified "#E5C890"
29+color-link diff-deleted "#E78284"
30+
31+color-link gutter-error "#E78284"
32+color-link gutter-warning "#E5C890"
33+
34+color-link statusline "#F2D5CF,#292C3C"
35+color-link tabbar "#F2D5CF,#292C3C"
36+color-link indent-char "#51576D"
37+color-link line-number "#51576D"
38+color-link current-line-number "#BABBF1"
39+
40+color-link cursor-line "#414559,#C6D0F5"
41+color-link color-column "#414559"
42+color-link type.extended "default"
@@ -1,0 +1,42 @@
1+color-link default "#4C4F69,#EFF1F5"
2+color-link comment "#ACB0BE"
3+
4+color-link identifier "#1E66F5"
5+color-link identifier.class "#1E66F5"
6+color-link identifier.var "#1E66F5"
7+
8+color-link constant "#FE640B"
9+color-link constant.number "#FE640B"
10+color-link constant.string "#40A02B"
11+
12+color-link symbol "#EA76CB"
13+color-link symbol.brackets "#DD7878"
14+color-link symbol.tag "#1E66F5"
15+
16+color-link type "#1E66F5"
17+color-link type.keyword "#DF8E1D"
18+
19+color-link special "#EA76CB"
20+color-link statement "#8839EF"
21+color-link preproc "#EA76CB"
22+
23+color-link underlined "#04A5E5"
24+color-link error "bold #D20F39"
25+color-link todo "bold #DF8E1D"
26+
27+color-link diff-added "#40A02B"
28+color-link diff-modified "#DF8E1D"
29+color-link diff-deleted "#D20F39"
30+
31+color-link gutter-error "#D20F39"
32+color-link gutter-warning "#DF8E1D"
33+
34+color-link statusline "#4C4F69,#DCE0E8"
35+color-link tabbar "#4C4F69,#DCE0E8"
36+color-link indent-char "#BCC0CC"
37+color-link line-number "#BCC0CC"
38+color-link current-line-number "#7287FD"
39+
40+color-link cursor-line "#CCD0DA,#4C4F69"
41+color-link color-column "#CCD0DA"
42+color-link type.extended "default"
@@ -1,0 +1,42 @@
1+color-link default "#CAD3F5,#24273A"
2+color-link comment "#5B6078"
3+
4+color-link identifier "#8AADF4"
5+color-link identifier.class "#8AADF4"
6+color-link identifier.var "#8AADF4"
7+
8+color-link constant "#F5A97F"
9+color-link constant.number "#F5A97F"
10+color-link constant.string "#A6DA95"
11+
12+color-link symbol "#F5BDE6"
13+color-link symbol.brackets "#F0C6C6"
14+color-link symbol.tag "#8AADF4"
15+
16+color-link type "#8AADF4"
17+color-link type.keyword "#EED49F"
18+
19+color-link special "#F5BDE6"
20+color-link statement "#C6A0F6"
21+color-link preproc "#F5BDE6"
22+
23+color-link underlined "#8AADF4"
24+color-link error "bold #ED8796"
25+color-link todo "bold #EED49F"
26+
27+color-link diff-added "#A6DA95"
28+color-link diff-modified "#EED49F"
29+color-link diff-deleted "#ED8796"
30+
31+color-link gutter-error "#ED8796"
32+color-link gutter-warning "#EED49F"
33+
34+color-link statusline "#F4DBD6,#1E2030"
35+color-link tabbar "#F4DBD6,#1E2030"
36+color-link indent-char "#494D64"
37+color-link line-number "#494D64"
38+color-link current-line-number "#B7BDF8"
39+
40+color-link cursor-line "#363A4F,#CAD3F5"
41+color-link color-column "#363A4F"
42+color-link type.extended "default"
@@ -1,0 +1,42 @@
1+color-link default "#C6D0F5,#1E1E2E"
2+color-link comment "#585B70"
3+
4+color-link identifier "#89B4FA"
5+color-link identifier.class "#89B4FA"
6+color-link identifier.var "#89B4FA"
7+
8+color-link constant "#FAB387"
9+color-link constant.number "#FAB387"
10+color-link constant.string "#A6E3A1"
11+
12+color-link symbol "#F5C2E7"
13+color-link symbol.brackets "#F2CDCD"
14+color-link symbol.tag "#89B4FA"
15+
16+color-link type "#89B4FA"
17+color-link type.keyword "#F9E2AF"
18+
19+color-link special "#F5C2E7"
20+color-link statement "#CBA6F7"
21+color-link preproc "#F5C2E7"
22+
23+color-link underlined "#89DCEB"
24+color-link error "bold #F38BA8"
25+color-link todo "bold #F9E2AF"
26+
27+color-link diff-added "#A6E3A1"
28+color-link diff-modified "#F9E2AF"
29+color-link diff-deleted "#F38BA8"
30+
31+color-link gutter-error "#F38BA8"
32+color-link gutter-warning "#F9E2AF"
33+
34+color-link statusline "#F5E0DC,#181825"
35+color-link tabbar "#F5E0DC,#181825"
36+color-link indent-char "#45475A"
37+color-link line-number "#45475A"
38+color-link current-line-number "#B4BEFE"
39+
40+color-link cursor-line "#313244,#C6D0F5"
41+color-link color-column "#313244"
42+color-link type.extended "default"
@@ -1,0 +1,3 @@
1+{
2+ "colorscheme": "catppuccin-macchiato"
3+}
@@ -1,0 +1,14 @@
1+[Default Applications]
2+x-scheme-handler/discord-409416265891971072=discord-409416265891971072.desktop
3+x-scheme-handler/wowup=WowUpCf.desktop
4+x-scheme-handler/claude=com.anthropic.Claude.desktop
5+x-scheme-handler/opendeck=opendeck-handler.desktop
6+x-scheme-handler/openaction=opendeck-handler.desktop
7+x-scheme-handler/streamdeck=opendeck-handler.desktop
8+inode/directory=philly.desktop
9+x-scheme-handler/sgnl=signal.desktop
10+x-scheme-handler/signalcaptcha=signal.desktop
11+
12+[Added Associations]
13+x-scheme-handler/sgnl=signal.desktop;
14+x-scheme-handler/signalcaptcha=signal.desktop;
@@ -1,0 +1,123 @@
1+vim.opt.tabstop = 2
2+vim.opt.shiftwidth = 2
3+vim.opt.expandtab = true
4+
5+vim.opt.number = true
6+vim.opt.relativenumber = true
7+
8+vim.opt.scrolloff = 5
9+
10+vim.opt.cursorline = true
11+vim.opt.textwidth = 100
12+vim.opt.colorcolumn = '+1'
13+vim.opt.formatoptions = 'cqj'
14+
15+vim.opt.undofile = true
16+vim.opt.undodir = vim.fn.stdpath('data') .. '/undodir'
17+
18+vim.opt.path = vim.o.path .. '**'
19+vim.opt.wildmenu = true
20+vim.opt.wildignore = {
21+ "**/.direnv/**",
22+ "**/node_modules/**",
23+ "**/vendor/**",
24+};
25+
26+vim.api.nvim_set_hl(0, 'Normal', { bg='none' })
27+vim.api.nvim_set_hl(0, 'StatusLine', { bg='none' })
28+vim.api.nvim_set_hl(0, 'StatusLineNC', { bg='none', fg='gray' })
29+vim.api.nvim_set_hl(0, 'WinBar', { bg='none' })
30+vim.api.nvim_set_hl(0, 'WinBarNC', { bg='none', fg='gray' })
31+vim.api.nvim_set_hl(0, 'NormalFloat', { bg='none' })
32+vim.api.nvim_set_hl(0, 'FloatBorder', { bg='none' })
33+vim.api.nvim_set_hl(0, 'Pmenu', { bg='none' })
34+vim.api.nvim_set_hl(0, 'PmenuBorder', { bg='none' })
35+
36+vim.opt.winborder = "rounded";
37+vim.opt.pumborder = "rounded";
38+
39+vim.opt.cmdheight = 1
40+vim.opt.showcmd = true
41+
42+vim.api.nvim_create_autocmd('BufReadPost', {
43+ group = vim.api.nvim_create_augroup('ReturnToLastLine', { clear = true }),
44+ callback = function()
45+ local last = vim.fn.line([['"]])
46+ if last > 1 and last < vim.fn.line("$") then
47+ vim.cmd([[normal! g'"]])
48+ end
49+ end
50+})
51+
52+vim.opt.list = true
53+vim.opt.listchars = {
54+ trail = '~',
55+ tab = '| ',
56+ leadmultispace = ':' .. string.rep(' ', vim.opt.shiftwidth:get() - 1),
57+}
58+
59+local function update()
60+ local listchars = vim.opt_local.listchars:get()
61+ listchars.leadmultispace = ':' .. string.rep(' ', vim.opt_local.shiftwidth:get() - 1)
62+ vim.opt_local.listchars = listchars
63+end
64+
65+local group = vim.api.nvim_create_augroup('ListcharsLeadmultispaceWidth', { clear = true }),
66+
67+vim.api.nvim_create_autocmd('OptionSet', {
68+ group = group,
69+ pattern = 'shiftwidth',
70+ callback = update,
71+})
72+
73+vim.api.nvim_create_autocmd({ 'FileType', 'BufWinEnter' }, {
74+ group = group,
75+ callback = update,
76+})
77+
78+vim.api.nvim_create_autocmd(
79+ {'BufEnter', 'BufAdd', 'BufDelete', 'BufLeave', 'BufModifiedSet', 'BufWrite'},
80+ {
81+ group = vim.api.nvim_create_augroup("WinBarBuffers", { clear = true }),
82+ pattern = "*",
83+ callback = function()
84+ local buffers = {}
85+ local current = vim.api.nvim_win_get_buf(0)
86+
87+ for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
88+ if vim.bo[bufnr].buflisted then
89+ local name = vim.fn.fnamemodify(vim.fn.bufname(bufnr), ":t")
90+ if name == "" then name = "[No name]" end
91+
92+ local highlight = bufnr == current and "WinBar" or "WinBarNC"
93+ local modified = vim.bo[bufnr].modified and "*" or ""
94+
95+ table.insert(buffers, string.format(
96+ "%%#%s# %d:%s%s %%*",
97+ highlight, bufnr, name, modified
98+ ))
99+ end
100+ end
101+
102+ if #buffers > 1 and vim.bo[current].buflisted then
103+ vim.opt_local.winbar = table.concat(buffers)
104+ vim.keymap.set({'n','v'}, '<C-n>', ':bn<cr>', { buffer = current })
105+ vim.keymap.set({'n','v'}, '<C-p>', ':bp<cr>', { buffer = current })
106+
107+ else
108+ vim.opt_local.winbar = ""
109+ pcall(function()
110+ vim.keymap.del({'n','v'}, '<C-n>', { buffer = current })
111+ vim.keymap.del({'n','v'}, '<C-p>', { buffer = current })
112+ end)
113+ end
114+ end
115+ }
116+)
117+
118+vim.keymap.set({'n','v'}, '<C-c>', '"+y')
119+vim.keymap.set({'n','v'}, '<C-v>', '"+p')
120+vim.keymap.set({'n','v'}, '<C-x>', '"+d')
121+
122+-- Use C-q for block visual mode
123+vim.keymap.set({'n','v'}, '<C-q>', '<C-v>', { noremap = true})
@@ -1,0 +1,58 @@
1+/* Philly (~/git/philly): how the desktop client looks.
2+
3+ Same recipe as Waybar, fuzzel and the control centre: near-transparent black
4+ over the compositor's blur, white text, no borders. The client's window is
5+ transparent wherever this theme is, so decoration.blur in hypr/hyprland.lua
6+ does the frosting, exactly as it does for kitty.
7+
8+ Everything is under [data-desktop], so the same daemon opened in a web
9+ browser (which has nothing to blur) keeps its opaque theme. */
10+
11+:root[data-desktop] {
12+ color-scheme: dark;
13+ font-family: "NotoSans Nerd Font", "Noto Sans", sans-serif;
14+ --mono: "JetBrainsMono Nerd Font", "JetBrains Mono", monospace;
15+
16+ --text: #ffffff;
17+ --muted: rgba(255, 255, 255, 0.55);
18+
19+ /* Layers of the same black: the window, then one more layer everywhere on
20+ top of it. Bars, sidebar and file list get the same one, so the window
21+ reads as a single sheet of glass. */
22+ --window: rgba(0, 0, 0, 0.1);
23+ --surface: rgba(0, 0, 0, 0.1);
24+ --content: rgba(0, 0, 0, 0.1);
25+ --field: rgba(0, 0, 0, 0.16);
26+ --border: transparent;
27+
28+ /* Menus float over the file list, which Hyprland can't blur for them, so
29+ they are nearly solid (the control centre's ink colour) to stay legible. */
30+ --popup: rgba(24, 24, 28, 0.92);
31+
32+ /* Monochrome highlights, like the workspace buttons' hover. */
33+ --hover: rgba(255, 255, 255, 0.08);
34+ --selected: rgba(255, 255, 255, 0.12);
35+ --accent: rgba(255, 255, 255, 0.2);
36+ --on-accent: #ffffff;
37+ --outline: rgba(255, 255, 255, 0.6);
38+ /* How full a disk is, in the sidebar: white, on the faint track behind it. */
39+ --meter: rgba(255, 255, 255, 0.75);
40+ --danger: #ff6b6b;
41+
42+ /* Hyprland rounds windows by 10px; controls and rows match it. */
43+ --radius: 10px;
44+ --row-radius: 10px;
45+ --row-inset: 6px;
46+
47+ /* File type icons, in kitty's palette. */
48+ --type-folder: #82aaff;
49+ --type-image: #e9c1ff;
50+ --type-video: #ff8288;
51+ --type-audio: #89ebff;
52+ --type-archive: #f4d69f;
53+ --type-code: #b4e88d;
54+ --type-document: #ff8b92;
55+ --type-sheet: #ddffa7;
56+ --type-font: rgba(255, 255, 255, 0.55);
57+ --type-exec: #9cc4ff;
58+}
@@ -1,0 +1,27 @@
1+[Appearance]
2+custom_palette=false
3+icon_theme=Papirus-Dark
4+standard_dialogs=default
5+style=kvantum
6+
7+[Fonts]
8+fixed="JetBrainsMono Nerd Font,10,-1,5,50,0,0,0,0,0"
9+general="Noto Sans,10,-1,5,50,0,0,0,0,0"
10+
11+[Interface]
12+activate_item_on_single_click=1
13+buttonbox_layout=0
14+cursor_flash_time=1000
15+dialog_buttons_have_icons=1
16+double_click_interval=400
17+gui_effects=@Invalid()
18+keyboard_scheme=2
19+menus_have_icons=true
20+show_shortcuts_in_context_menus=true
21+stylesheets=@Invalid()
22+toolbutton_style=4
23+underline_shortcut=1
24+wheel_scroll_lines=3
25+
26+[SettingsWindow]
27+geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x4_\0\0\x5\x46\0\0\n\0\xff\xff\xfc|\0\0\vU\xff\xff\xfe\t\0\0\0\x1\x2\0\0\0\n\0\0\0\0\0\0\0\0\0\0\0\x4_\0\0\x5\x46)
@@ -1,0 +1,23 @@
1+#!/usr/bin/env bash
2+# Quits Signal and starts it again. Signal reads the theme only at start-up, so run
3+# this after editing theme.css and rebuilding (the themed Signal is built by
4+# pkgs/signal-desktop-themed). The window is put back on the workspace it was on.
5+
6+workspace=$(hyprctl clients -j | python3 -c '
7+import json, sys
8+print(next((c["workspace"]["name"] for c in json.load(sys.stdin) if c["class"] == "signal"), ""))')
9+
10+pid=$(pgrep -f 'signal-desktop.*/app\.asar' | head -1)
11+if [ -n "$pid" ]; then
12+ kill -TERM "$pid"
13+ for _ in $(seq 60); do
14+ kill -0 "$pid" 2>/dev/null || break
15+ sleep 0.25
16+ done
17+fi
18+
19+if [ -n "$workspace" ]; then
20+ hyprctl dispatch "hl.dsp.exec_cmd(\"uwsm app -- signal-desktop\", { workspace = \"$workspace silent\" })" >/dev/null
21+else
22+ uwsm app -- signal-desktop >/dev/null 2>&1
23+fi
@@ -1,0 +1,76 @@
1+/* Signal Desktop: how the main window looks.
2+
3+ Same recipe as Philly, Waybar, fuzzel and the control centre: near-transparent
4+ black over the compositor's blur, white text, no borders, monochrome highlights.
5+ Signal has no user stylesheet, so signal/launch builds a copy of the app with this
6+ file added as stylesheets/custom.css and the main window made transparent;
7+ decoration.blur in hypr/hyprland.lua then does the frosting, as it does for kitty.
8+
9+ Signal draws nearly everything from its --axo-color-* variables, so most of the
10+ theme is just new values for those. It assumes Signal's dark theme (Settings >
11+ Appearance, or "System" with a dark desktop). */
12+
13+:root {
14+ /* The window, and the panes that sit straight on it: glass. */
15+ --axo-color-surface-primary: transparent;
16+ --axo-color-surface-secondary: transparent;
17+ --axo-color-legacy-conversation-header-bg: transparent;
18+
19+ /* One more layer of the same black for things drawn on the glass. */
20+ --axo-color-surface-tertiary: rgba(0, 0, 0, 0.16);
21+ --axo-color-surface-quaternary: rgba(0, 0, 0, 0.16);
22+ --axo-color-surface-card: rgba(0, 0, 0, 0.16);
23+
24+ /* Message bubbles and the text field, monochrome: other people's messages a
25+ faint white, your own a stronger one (Philly's hover and accent). */
26+ --axo-color-surface-message-incoming: rgba(255, 255, 255, 0.08);
27+ --axo-color-surface-message-outgoing: rgba(255, 255, 255, 0.2);
28+
29+ /* Menus and dialogs float over content Hyprland can't blur for them, so they
30+ are nearly solid (the control centre's ink colour) to stay legible. */
31+ --axo-color-material-primary: rgba(24, 24, 28, 0.92);
32+ --axo-color-material-secondary: rgba(24, 24, 28, 0.92);
33+ --axo-color-material-tertiary: rgba(24, 24, 28, 0.92);
34+ --axo-color-material-tertiary-pressed: rgba(44, 44, 50, 0.92);
35+ --axo-color-material-quaternary: rgba(24, 24, 28, 0.92);
36+ --axo-color-material-quaternary-pressed: rgba(44, 44, 50, 0.92);
37+ --axo-color-material-dialog: rgba(24, 24, 28, 0.92);
38+
39+ /* White text; secondary text at the control centre's dim. */
40+ --axo-color-label-primary: #ffffff;
41+ --axo-color-label-secondary: rgba(255, 255, 255, 0.55);
42+ --axo-color-label-placeholder: rgba(255, 255, 255, 0.4);
43+
44+ /* No borders; a white ring for keyboard focus and selection. */
45+ --axo-color-border-primary: transparent;
46+ --axo-color-border-secondary: transparent;
47+ --axo-color-border-focused-outer: rgba(255, 255, 255, 0.6);
48+ --axo-color-border-selected: rgba(255, 255, 255, 0.6);
49+
50+ /* Signal's blue accent becomes white: buttons, badges, links. */
51+ --axo-color-brand-primary: #ffffff;
52+ --axo-color-fill-accent: rgba(255, 255, 255, 0.2);
53+ --axo-color-fill-accent-pressed: rgba(255, 255, 255, 0.28);
54+ --axo-color-fill-accent-tint: rgba(255, 255, 255, 0.12);
55+ --axo-color-fill-accent-tint-pressed: rgba(255, 255, 255, 0.16);
56+ --axo-color-label-accent: #ffffff;
57+}
58+
59+/* The one layer of black behind everything. */
60+html {
61+ background: rgba(0, 0, 0, 0.1);
62+}
63+
64+/* The desktop's font. Signal's emoji fonts stay in the list. */
65+body,
66+button,
67+input,
68+textarea {
69+ font-family: "NotoSans Nerd Font", "Noto Sans", "Signal Emoji Large", "Signal Emoji Small", sans-serif;
70+}
71+
72+/* Your own bubbles ignore the per-chat colour (ultramarine, crimson, ...), which
73+ Signal sets through classes and inline styles of its own. */
74+.module-message__container--outgoing[class] {
75+ background: var(--axo-color-surface-message-outgoing) !important;
76+}
@@ -1,0 +1,3 @@
1+################### File Generated by Goverlay ###################
2+toggleKey = Home
3+enableOnLaunch = True
@@ -1,0 +1,30 @@
1+################### File Generated by Goverlay 1.8.2 stable ###################
2+# vkSumi color grading
3+#
4+enabled = true
5+toggle_keys = Shift_R+F9 # in-game hotkey, X11 + XWayland (Wine/Proton)
6+
7+PER_GAME_CONFIG_CREATION = false
8+
9+# tone
10+brightness = 0.0
11+contrast = 0.0
12+exposure = 0.0
13+gamma = 0.0
14+
15+# color
16+saturation = 0.0
17+vibrance = 0.0
18+hue_deg = 0.0
19+temperature = 0.0
20+tint = 0.0
21+
22+# per-channel gain
23+red_gain = 0.0
24+green_gain = 0.0
25+blue_gain = 0.0
26+
27+# 3-band
28+shadows = 0.0
29+midtones = 0.0
30+highlights = 0.0
@@ -1,0 +1,94 @@
1+[
2+ {
3+ "clock": {
4+ "format": "{:%H:%M}",
5+ "format-alt": "{:%A, %B %d, %Y (%R)}",
6+ "tooltip-format": "<tt><big>{calendar}</big></tt>",
7+ "calendar": {
8+ "mode" : "month",
9+ "iso8601" : true,
10+ "mode-mon-col" : 3,
11+ "weeks-pos" : "right",
12+ "on-scroll" : 1,
13+ "format": {
14+ "months": "<span color='#ffffff'><b>{}</b></span>",
15+ "days": "<span color='#ffffff'>{}</span>",
16+ "weeks": "<span color='#8a8a8a'>W{:%V}</span>",
17+ "weekdays": "<span color='#b0b0b0'><b>{}</b></span>",
18+ "today": "<span color='#aaffff'><b><u>{}</u></b></span>"
19+ }
20+ },
21+ "actions": {
22+ "on-click-right": "mode",
23+ "on-scroll-up": "shift_up",
24+ "on-scroll-down": "shift_down"
25+ }
26+ },
27+ "custom/notifications": {
28+ "format": "<span size='12288'>󰂚</span>",
29+ "tooltip": false,
30+ "on-click": "bash $HOME/.config/waybar/eww/scripts/toggle.sh notifications"
31+ },
32+ "custom/notifications-count": {
33+ "exec": "python3 $HOME/.config/waybar/scripts/notification-count.py",
34+ "return-type": "json",
35+ "hide-empty-text": true,
36+ "tooltip": false,
37+ "on-click": "bash $HOME/.config/waybar/eww/scripts/toggle.sh notifications"
38+ },
39+ "custom/control-center": {
40+ "format": " ",
41+ "tooltip": false,
42+ "on-click": "bash $HOME/.config/waybar/eww/scripts/toggle.sh"
43+ },
44+ "tray": {
45+ "icon-size": 16,
46+ "spacing": 10,
47+ "order": ["discord_status_icon_1"],
48+ "icons": {
49+ "Signal_status_icon_1": "/home/vizie/.config/waybar/icons/signal.svg",
50+ "bitwarden": "/home/vizie/.config/waybar/icons/bitwarden.svg",
51+ "wowup": "/home/vizie/.config/waybar/icons/wowup.svg",
52+ "Claude_status_icon_1": "/home/vizie/.config/waybar/icons/claude.svg",
53+ "steam_app_default": "/home/vizie/.config/waybar/icons/battlenet.svg",
54+ "faugus-launcher": "/home/vizie/.config/waybar/icons/faugus.svg",
55+ "steam": "/home/vizie/.config/waybar/icons/steam.svg",
56+ "tray-icon tray app opendeck": "/home/vizie/.config/waybar/icons/opendeck.svg"
57+ }
58+ },
59+ "custom/imsh-cast-monitor": {
60+ "format": "{text}",
61+ "exec": "imsh-cast-monitor",
62+ "return-type": "json",
63+ "hide-empty-text": true
64+ },
65+ "height": 36,
66+ "hyprland/window": {
67+ "max-length": 60
68+ },
69+ "hyprland/workspaces": {
70+ "format": "{name}",
71+ "on-click": "activate",
72+ "sort-by": "number"
73+ },
74+ "layer": "top",
75+ "modules-center": [
76+ "custom/imsh-cast-monitor",
77+ "clock"
78+ ],
79+ "modules-left": [
80+ "hyprland/workspaces",
81+ "hyprland/window"
82+ ],
83+ "modules-right": [
84+ "tray",
85+ "custom/notifications",
86+ "custom/notifications-count",
87+ "custom/control-center"
88+ ],
89+ "position": "top",
90+ "margin-left": 15,
91+ "margin-right": 15,
92+ "margin-top": 15
93+ }
94+]
@@ -1,0 +1,277 @@
1+// Control center styling. Monochrome to match the transparent Waybar.
2+
3+$fg: #ffffff;
4+$dim: rgba(255, 255, 255, 0.55);
5+// Black-based translucency (not white-tinted) to match the rest of the rice
6+// (Waybar's #waybar, fuzzel, dunst all use rgba(0, 0, 0, ...) over the
7+// compositor blur, rather than a lightened panel color).
8+$card: rgba(0, 0, 0, 0.16);
9+$card-hover: rgba(0, 0, 0, 0.26);
10+$track: rgba(255, 255, 255, 0.14);
11+$ink: #18181c;
12+$danger: #ff6b6b;
13+
14+* {
15+ all: unset;
16+ font-family: "NotoSans Nerd Font Regular", "NotoSans Nerd Font", "Noto Sans", sans-serif;
17+ font-size: 13px;
18+ color: $fg;
19+}
20+
21+// ---- panel -----------------------------------------------------------------
22+
23+.cc {
24+ // Same near-transparent black as #waybar / fuzzel's background; the
25+ // compositor's own blur (layerrule-4 in hyprland.lua) does the rest, no
26+ // border, same as the rest of the rice.
27+ background-color: rgba(0, 0, 0, 0.1);
28+ border-radius: 20px;
29+ padding: 14px;
30+}
31+
32+.card {
33+ background-color: $card;
34+ border-radius: 18px;
35+ padding: 12px 14px;
36+}
37+
38+.card-title {
39+ font-weight: bold;
40+}
41+
42+// ---- tiles (Bluetooth / mouse) ------------------------------------------------
43+
44+.tile {
45+ background-color: $card;
46+ border-radius: 18px;
47+ padding: 10px 12px;
48+}
49+
50+.tile-wrap:hover .tile {
51+ background-color: $card-hover;
52+}
53+
54+.icon-circle {
55+ min-width: 38px;
56+ min-height: 38px;
57+ border-radius: 19px;
58+ background-color: $track;
59+
60+ label {
61+ font-size: 18px;
62+ }
63+
64+ &.on {
65+ background-color: $fg;
66+
67+ label {
68+ color: $ink;
69+ }
70+ }
71+}
72+
73+.tile-title {
74+ font-weight: bold;
75+}
76+
77+.tile-sub {
78+ font-size: 11px;
79+ color: $dim;
80+}
81+
82+// ---- sliders ------------------------------------------------------------------
83+
84+.slider-icon {
85+ font-size: 17px;
86+ min-width: 24px;
87+
88+ &.muted {
89+ color: $dim;
90+ }
91+}
92+
93+.slider-val {
94+ font-size: 11px;
95+ color: $dim;
96+ min-width: 34px;
97+}
98+
99+scale trough {
100+ min-height: 24px;
101+ border-radius: 12px;
102+ background-color: $track;
103+}
104+
105+scale highlight {
106+ border-radius: 12px;
107+ background-color: $fg;
108+}
109+
110+scale slider {
111+ min-width: 0;
112+ min-height: 24px;
113+ background-color: transparent;
114+}
115+
116+// ---- output device switcher ------------------------------------------------------
117+
118+.device-row {
119+ padding: 4px 6px 0 6px;
120+ margin: 0 -6px;
121+ border-radius: 10px;
122+
123+ &:hover {
124+ background-color: $card-hover;
125+ }
126+}
127+
128+.device-current,
129+.chevron {
130+ font-size: 12px;
131+ color: $dim;
132+}
133+
134+.device-list {
135+ margin-top: 4px;
136+}
137+
138+.device {
139+ padding: 6px 8px;
140+ border-radius: 10px;
141+
142+ &:hover {
143+ background-color: $card-hover;
144+ }
145+
146+ &.active {
147+ font-weight: bold;
148+ }
149+}
150+
151+// ---- system rings -----------------------------------------------------------------
152+
153+.ring {
154+ color: $fg;
155+ background-color: $track;
156+
157+ &.hot {
158+ color: $danger;
159+ }
160+}
161+
162+.ring-value {
163+ font-weight: bold;
164+ font-size: 13px;
165+}
166+
167+.ring-name {
168+ font-size: 11px;
169+ color: $dim;
170+}
171+
172+// ---- power ------------------------------------------------------------------------
173+
174+.power-btn {
175+ padding: 8px 10px;
176+ border-radius: 14px;
177+
178+ &:hover {
179+ background-color: $card-hover;
180+ }
181+}
182+
183+.power-icon {
184+ font-size: 20px;
185+}
186+
187+.power-label {
188+ font-size: 10px;
189+ color: $dim;
190+}
191+
192+.confirm {
193+ margin-top: 4px;
194+ padding: 8px 10px;
195+ border-radius: 12px;
196+ background-color: rgba(0, 0, 0, 0.16);
197+}
198+
199+// .btn-wrap is the eventbox (hover/click); .btn is the inner box that is
200+// actually drawn, since GTK ignores padding on an eventbox.
201+.btn {
202+ padding: 5px 14px;
203+ border-radius: 10px;
204+ background-color: $card-hover;
205+
206+ &.danger {
207+ background-color: $danger;
208+
209+ // `*` sets color on every label directly, so it has to be overridden on
210+ // the label rather than inherited from the box.
211+ label {
212+ color: $ink;
213+ font-weight: bold;
214+ }
215+ }
216+}
217+
218+.btn-wrap:hover .btn {
219+ background-color: rgba(255, 255, 255, 0.14);
220+
221+ &.danger {
222+ background-color: #ff8787;
223+ }
224+}
225+
226+// ---- notifications --------------------------------------------------------------
227+
228+.notif-header {
229+ padding: 0 4px;
230+}
231+
232+// Each notification is a card of its own, like the control center's cards.
233+.notif {
234+ margin-top: 8px;
235+ padding: 10px 12px;
236+ border-radius: 16px;
237+ background-color: $card;
238+ transition: opacity 240ms ease-in, margin-left 280ms ease-in, margin-right 280ms ease-in;
239+
240+ // Clearing: fade while sliding out to the right. The opposite margins keep the
241+ // width constant, so the entry moves instead of being squeezed.
242+ &.out {
243+ opacity: 0;
244+ margin-left: 70px;
245+ margin-right: -70px;
246+ }
247+}
248+
249+.notif-wrap:hover .notif {
250+ background-color: $card-hover;
251+}
252+
253+// Stagger: each entry leaves 45ms after the one above it.
254+@for $i from 0 through 9 {
255+ .notif.d#{$i} {
256+ transition-delay: #{$i * 45}ms;
257+ }
258+}
259+
260+.notif-meta {
261+ font-size: 11px;
262+ color: $dim;
263+}
264+
265+.notif-summary {
266+ font-weight: bold;
267+}
268+
269+.notif-body,
270+.notif-empty {
271+ font-size: 12px;
272+ color: $dim;
273+}
274+
275+.notif-empty {
276+ padding: 6px 4px 2px 4px;
277+}
@@ -1,0 +1,235 @@
1+;; Flyout panels opened from the Waybar buttons (scripts/toggle.sh): the control
2+;; center and the notification list. `cc-closer` is an invisible click-catcher (click
3+;; outside = close), shared by both panels. All windows take a `monitor` argument (a
4+;; number), which scripts/toggle.sh fills in with the monitor you clicked on.
5+
6+;; ---------------------------------------------------------------------------
7+;; State
8+;; ---------------------------------------------------------------------------
9+
10+(defvar sinks_open false)
11+(defvar confirm "none") ;; none | logout | reboot | poweroff
12+
13+(deflisten audio
14+ :initial '{"vol":0,"muted":false,"mic":0,"mic_muted":false,"sink":"","sinks":[]}'
15+ "python3 $HOME/.config/waybar/eww/scripts/audio.py")
16+
17+(defpoll mouse
18+ :interval "30s"
19+ :initial '{"text":"","percentage":0,"class":"","tooltip":"","label":"Not connected","connected":false}'
20+ "bash $HOME/.config/waybar/scripts/mouse-battery.sh")
21+
22+(defpoll bt
23+ :interval "2s"
24+ :initial '{"available":false,"on":false,"device":"","label":"Unavailable"}'
25+ "bash $HOME/.config/waybar/eww/scripts/bluetooth.sh")
26+
27+(defpoll disk
28+ :interval "30s"
29+ :initial "0"
30+ "df --output=pcent / | tail -1 | tr -dc '0-9'")
31+
32+;; Last notifications from dunst's history. Only polled while the panel is open;
33+;; scripts/toggle.sh fills it once right before opening.
34+(defvar notifs_open false)
35+;; Clear animation (scripts/clear-notifications.sh): entries first slide out one after
36+;; another (notifs_clearing), then the emptied list folds up (notifs_folded).
37+(defvar notifs_clearing false)
38+(defvar notifs_folded false)
39+(defpoll notifs
40+ :interval "2s"
41+ :run-while notifs_open
42+ :initial "[]"
43+ "python3 $HOME/.config/waybar/eww/scripts/notifications.py")
44+
45+;; ---------------------------------------------------------------------------
46+;; Windows
47+;; ---------------------------------------------------------------------------
48+
49+(defwindow cc-closer [monitor]
50+ :monitor monitor
51+ :geometry (geometry :x "0px" :y "0px" :width "100%" :height "100%" :anchor "top left")
52+ :stacking "overlay"
53+ :namespace "eww-cc-closer"
54+ (eventbox :class "closer"
55+ :timeout "2s"
56+ :onclick "bash $HOME/.config/waybar/eww/scripts/toggle.sh close"))
57+
58+(defwindow control-center [monitor]
59+ :monitor monitor
60+ :geometry (geometry :x "15px" :y "8px" :width "360px" :anchor "top right")
61+ :stacking "overlay"
62+ :namespace "eww-control-center"
63+ (control-center))
64+
65+;; Same spot and namespace as the control center, so Hyprland's blur rule applies.
66+(defwindow notifications [monitor]
67+ :monitor monitor
68+ :geometry (geometry :x "15px" :y "8px" :width "360px" :anchor "top right")
69+ :stacking "overlay"
70+ :namespace "eww-control-center"
71+ (notifications))
72+
73+;; ---------------------------------------------------------------------------
74+;; Layout
75+;; ---------------------------------------------------------------------------
76+
77+(defwidget control-center []
78+ (box :class "cc" :orientation "v" :space-evenly false :spacing 10
79+ (box :orientation "h" :space-evenly true :spacing 10
80+ (tile :icon {bt.on ? "󰂯" : "󰂲"}
81+ :title "Bluetooth"
82+ :sub {bt.label}
83+ :active {bt.available && bt.on}
84+ :onclick "bash $HOME/.config/waybar/eww/scripts/bt-toggle.sh ${bt.on ? 'off' : 'on'}")
85+ (tile :icon {mouse.connected ? mouse.text : "󰍽"}
86+ :title "Superlight"
87+ :sub {mouse.label}
88+ :active {mouse.connected && mouse.class == "charging"}
89+ :onclick ":"))
90+ (sound)
91+ (stats)
92+ (power)))
93+
94+;; ---------------------------------------------------------------------------
95+;; Building blocks
96+;; ---------------------------------------------------------------------------
97+
98+(defwidget tile [icon title sub active onclick]
99+ (eventbox :class "tile-wrap" :cursor "pointer" :timeout "2s" :onclick onclick
100+ (box :class "tile" :orientation "h" :space-evenly false :spacing 10
101+ (box :class "icon-circle ${active ? 'on' : ''}" :space-evenly true
102+ (label :text icon))
103+ (box :orientation "v" :valign "center" :halign "start" :space-evenly false
104+ (label :class "tile-title" :halign "start" :limit-width 16 :text title)
105+ (label :class "tile-sub" :halign "start" :limit-width 18 :text sub)))))
106+
107+(defwidget slider-row [icon muted value onmute onchange]
108+ (box :class "slider-row" :orientation "h" :space-evenly false :spacing 10
109+ (eventbox :cursor "pointer" :timeout "1s" :onclick onmute
110+ (label :class "slider-icon ${muted ? 'muted' : ''}" :text icon))
111+ (scale :class "slider" :hexpand true :min 0 :max 100 :round-digits 0
112+ :timeout "1s" :value value :onchange onchange)
113+ (label :class "slider-val" :text "${value}%")))
114+
115+(defwidget ring [value name]
116+ (box :class "ring-wrap" :orientation "v" :space-evenly false :spacing 6 :hexpand true
117+ (overlay :halign "center"
118+ (circular-progress :class "ring ${value > 85 ? 'hot' : ''}"
119+ :value value :thickness 6 :start-at 75 :clockwise true
120+ :width 60 :height 60)
121+ (label :class "ring-value" :text "${round(value, 0)}%"))
122+ (label :class "ring-name" :text name)))
123+
124+(defwidget power-btn [icon label onclick]
125+ ;; :height is a hard GTK size request (unlike CSS padding, which the box/eventbox
126+ ;; layout here wasn't actually growing from -- the row just stayed shrink-wrapped
127+ ;; to the icon+label no matter how much padding was added). This is what actually
128+ ;; makes the hover rectangle taller.
129+ (eventbox :class "power-btn" :cursor "pointer" :timeout "3s" :onclick onclick :height 62
130+ (box :orientation "v" :space-evenly false :spacing 4 :valign "center" :halign "center"
131+ (label :class "power-icon" :text icon)
132+ (label :class "power-label" :text label))))
133+
134+;; ---------------------------------------------------------------------------
135+;; Cards
136+;; ---------------------------------------------------------------------------
137+
138+(defwidget sound []
139+ (box :class "card" :orientation "v" :space-evenly false :spacing 10
140+ (label :class "card-title" :halign "start" :text "Sound")
141+ (slider-row :icon {audio.muted ? "󰝟" : "󰕾"}
142+ :muted {audio.muted}
143+ :value {audio.vol}
144+ :onmute "pactl set-sink-mute @DEFAULT_SINK@ toggle"
145+ :onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%")
146+ (slider-row :icon {audio.mic_muted ? "󰍭" : "󰍬"}
147+ :muted {audio.mic_muted}
148+ :value {audio.mic}
149+ :onmute "pactl set-source-mute @DEFAULT_SOURCE@ toggle"
150+ :onchange "pactl set-source-volume @DEFAULT_SOURCE@ {}%")
151+ (eventbox :class "device-row" :cursor "pointer" :timeout "1s"
152+ :onclick "${EWW_CMD} update sinks_open=${!sinks_open}"
153+ (box :orientation "h" :space-evenly false
154+ (label :class "device-current" :hexpand true :halign "start" :limit-width 34
155+ :text "Output: ${audio.sink}")
156+ (label :class "chevron" :text {sinks_open ? "󰅃" : "󰅀"})))
157+ (revealer :transition "slidedown" :duration "200ms" :reveal sinks_open
158+ (box :class "device-list" :orientation "v" :space-evenly false
159+ (for s in {audio.sinks}
160+ (eventbox :class "device ${s.active ? 'active' : ''}" :cursor "pointer" :timeout "1s"
161+ :onclick "pactl set-default-sink '${s.name}'; ${EWW_CMD} update sinks_open=false"
162+ (box :orientation "h" :space-evenly false
163+ (label :hexpand true :halign "start" :limit-width 34 :text "${s.desc}")
164+ (label :text {s.active ? "󰄬" : ""}))))))))
165+
166+(defwidget stats []
167+ (box :class "card" :orientation "h" :space-evenly true
168+ (ring :value {EWW_CPU.avg} :name "CPU")
169+ (ring :value {EWW_RAM.used_mem_perc} :name "RAM")
170+ (ring :value disk :name "Disk")))
171+
172+(defwidget power []
173+ (box :class "card" :orientation "v" :space-evenly false :spacing 6
174+ (box :orientation "h" :space-evenly true
175+ (power-btn :icon "󰌾" :label "Lock"
176+ :onclick "${EWW_CMD} close control-center cc-closer; pidof hyprlock || setsid -f hyprlock")
177+ (power-btn :icon "󰒲" :label "Sleep"
178+ :onclick "${EWW_CMD} close control-center cc-closer; systemctl suspend")
179+ (power-btn :icon "󰍃" :label "Log out"
180+ :onclick "${EWW_CMD} update confirm=logout")
181+ (power-btn :icon "󰜉" :label "Restart"
182+ :onclick "${EWW_CMD} update confirm=reboot")
183+ (power-btn :icon "󰐥" :label "Shut down"
184+ :onclick "${EWW_CMD} update confirm=poweroff"))
185+ (revealer :transition "slidedown" :duration "150ms" :reveal {confirm != "none"}
186+ (box :class "confirm" :orientation "h" :space-evenly false :spacing 8
187+ (label :hexpand true :halign "start"
188+ :text {confirm == "reboot" ? "Restart now?" : (confirm == "poweroff" ? "Shut down now?" : "Log out now?")})
189+ ;; Padding/background live on the inner box: GTK ignores CSS padding on an
190+ ;; eventbox, so styling the eventbox itself left the pills shrink-wrapped.
191+ (eventbox :class "btn-wrap" :cursor "pointer" :onclick "${EWW_CMD} update confirm=none"
192+ (box :class "btn"
193+ (label :text "Cancel")))
194+ (eventbox :class "btn-wrap" :cursor "pointer" :timeout "3s"
195+ :onclick "${EWW_CMD} update confirm=none sinks_open=false; ${EWW_CMD} close control-center cc-closer; ${confirm == 'reboot' ? 'systemctl reboot' : (confirm == 'poweroff' ? 'systemctl poweroff' : 'hyprctl dispatch exit')}"
196+ (box :class "btn danger"
197+ (label :text "Confirm")))))))
198+
199+;; ---------------------------------------------------------------------------
200+;; Notifications panel
201+;; ---------------------------------------------------------------------------
202+
203+(defwidget notifications []
204+ ;; One panel, no inner card: the header sits straight on the panel and every
205+ ;; notification is its own card. Spacing comes from CSS margins rather than
206+ ;; :spacing, because the two revealers stay in the layout even when folded shut.
207+ (box :class "cc" :orientation "v" :space-evenly false
208+ ;; Fixed height, so the header doesn't shrink when the Clear button hides.
209+ (box :class "notif-header" :orientation "h" :space-evenly false :height 28
210+ (label :class "card-title" :hexpand true :halign "start" :valign "center" :text "Notifications")
211+ (eventbox :class "btn-wrap" :cursor "pointer" :timeout "2s" :valign "center"
212+ :visible {arraylength(notifs) > 0 && !notifs_clearing}
213+ :onclick "bash $HOME/.config/waybar/eww/scripts/clear-notifications.sh &"
214+ (box :class "btn"
215+ (label :text "Clear"))))
216+ ;; Slides in while the list folds up, so the panel goes straight to its empty size.
217+ (revealer :transition "slidedown" :duration "220ms"
218+ :reveal {arraylength(notifs) == 0 || notifs_folded}
219+ (label :class "notif-empty" :halign "start" :text "No notifications"))
220+ (revealer :transition "slideup" :duration "220ms" :reveal {!notifs_folded}
221+ (box :orientation "v" :space-evenly false
222+ (for n in notifs
223+ ;; Click: jump to the app that sent it (scripts/open-notification.py). Only the
224+ ;; numeric id is passed, never the notification's own text.
225+ (eventbox :class "notif-wrap" :cursor "pointer" :timeout "3s"
226+ :onclick "python3 $HOME/.config/waybar/eww/scripts/open-notification.py ${n.id} &"
227+ ;; `d<n>` delays each entry's slide-out a little more than the one above it
228+ (box :class "notif d${n.i} ${notifs_clearing ? 'out' : ''}"
229+ :orientation "v" :space-evenly false :spacing 2
230+ (box :orientation "h" :space-evenly false
231+ (label :class "notif-meta" :hexpand true :halign "start" :limit-width 30 :text {n.app})
232+ (label :class "notif-meta" :text {n.time}))
233+ (label :class "notif-summary" :halign "start" :xalign 0 :wrap true :text {n.summary})
234+ (label :class "notif-body" :halign "start" :xalign 0 :wrap true
235+ :visible {n.body != ""} :text {n.body}))))))))
@@ -1,0 +1,89 @@
1+#!/usr/bin/env python3
2+"""Streams PipeWire/PulseAudio state as one JSON line per change (Eww `deflisten`)."""
3+import json
4+import re
5+import select
6+import subprocess
7+import time
8+
9+
10+def run(*args):
11+ try:
12+ return subprocess.run(args, capture_output=True, text=True, timeout=3).stdout.strip()
13+ except Exception:
14+ return ""
15+
16+
17+def pactl_json(kind):
18+ try:
19+ return json.loads(run("pactl", "--format=json", "list", kind) or "[]")
20+ except ValueError:
21+ return []
22+
23+
24+def percent(dev):
25+ for ch in (dev.get("volume") or {}).values():
26+ m = re.match(r"\d+", str(ch.get("value_percent", "")))
27+ if m:
28+ return int(m.group())
29+ return 0
30+
31+
32+def snapshot():
33+ default_sink = run("pactl", "get-default-sink")
34+ default_source = run("pactl", "get-default-source")
35+ sinks = pactl_json("sinks")
36+ sources = pactl_json("sources")
37+ sink = next((s for s in sinks if s.get("name") == default_sink), None)
38+ src = next((s for s in sources if s.get("name") == default_source), None)
39+ return {
40+ "vol": percent(sink) if sink else 0,
41+ "muted": bool(sink and sink.get("mute")),
42+ "mic": percent(src) if src else 0,
43+ "mic_muted": bool(src and src.get("mute")),
44+ "sink": (sink or {}).get("description", "No output"),
45+ "sinks": [
46+ {
47+ "name": s.get("name", ""),
48+ "desc": s.get("description") or s.get("name", ""),
49+ "active": s.get("name") == default_sink,
50+ }
51+ for s in sinks
52+ ],
53+ }
54+
55+
56+last = None
57+
58+
59+def emit():
60+ global last
61+ line = json.dumps(snapshot(), separators=(",", ":"))
62+ if line != last:
63+ print(line, flush=True)
64+ last = line
65+
66+
67+while True:
68+ emit()
69+ try:
70+ proc = subprocess.Popen(
71+ ["pactl", "subscribe"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=0
72+ )
73+ except OSError:
74+ time.sleep(5)
75+ continue
76+ out = proc.stdout
77+ while True:
78+ line = out.readline()
79+ if not line:
80+ break
81+ if not re.search(rb"on (sink|source|server)", line):
82+ continue
83+ # Collapse bursts of events (e.g. while dragging a slider) into one update.
84+ while select.select([out], [], [], 0.08)[0]:
85+ if not out.readline():
86+ break
87+ emit()
88+ proc.wait()
89+ time.sleep(2)
@@ -1,0 +1,14 @@
1+#!/usr/bin/env bash
2+# Bluetooth state as one line of JSON for the Eww control center.
3+NONE='{"available":false,"on":false,"device":"","label":"Unavailable"}'
4+
5+command -v bluetoothctl >/dev/null 2>&1 || { echo "$NONE"; exit 0; }
6+show=$(timeout 2 bluetoothctl show 2>/dev/null)
7+grep -q 'Powered:' <<<"$show" || { echo "$NONE"; exit 0; }
8+
9+if grep -q 'Powered: yes' <<<"$show"; then
10+ dev=$(timeout 2 bluetoothctl devices Connected 2>/dev/null | head -1 | cut -d' ' -f3- | tr -d '"\\')
11+ printf '{"available":true,"on":true,"device":"%s","label":"%s"}\n' "$dev" "${dev:-On}"
12+else
13+ echo '{"available":true,"on":false,"device":"","label":"Off"}'
14+fi
@@ -1,0 +1,6 @@
1+#!/usr/bin/env bash
2+# Usage: bt-toggle.sh on|off -- switches Bluetooth and refreshes the tile right away.
3+DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
4+bluetoothctl power "$1" >/dev/null 2>&1
5+sleep 0.4
6+eww --config "$DIR" update bt="$(bash "$DIR/scripts/bluetooth.sh")" >/dev/null 2>&1
@@ -1,0 +1,22 @@
1+#!/usr/bin/env bash
2+# Clear button of the notifications panel: play the clear animation, then empty
3+# dunst's history. The timings follow eww.scss (.notif transitions, 45ms stagger).
4+
5+DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6+EWW=(eww --config "$DIR")
7+
8+count=$("${EWW[@]}" get notifs 2>/dev/null | python3 -c 'import json,sys; print(len(json.load(sys.stdin)))' 2>/dev/null)
9+count=${count:-0}
10+
11+# 1. entries slide out one after another: the last one starts after (count-1)*45ms
12+# and takes 280ms
13+"${EWW[@]}" update notifs_clearing=true
14+sleep "$(python3 -c "print((max($count, 1) - 1) * 0.045 + 0.30)")"
15+
16+# 2. the now invisible list folds up
17+"${EWW[@]}" update notifs_folded=true
18+sleep 0.24
19+
20+# 3. actually clear, and reset for the next time
21+dunstctl history-clear
22+"${EWW[@]}" update notifs='[]' notifs_clearing=false notifs_folded=false
@@ -1,0 +1,56 @@
1+#!/usr/bin/env python3
2+"""Prints dunst's most recent notifications as a JSON list for the Eww notifications panel.
3+
4+Reads `dunstctl history` (newest first) and keeps the last LIMIT entries. Dunst only
5+moves a notification into its history once it has left the screen.
6+"""
7+import html
8+import json
9+import re
10+import subprocess
11+import time
12+
13+LIMIT = 10
14+BODY_MAX = 160
15+
16+
17+def plain(text):
18+ """Dunst bodies may carry Pango/HTML markup; show them as plain text."""
19+ text = re.sub(r"<[^>]+>", "", text or "")
20+ return re.sub(r"\s+", " ", html.unescape(text)).strip()
21+
22+
23+def main():
24+ try:
25+ out = subprocess.run(["dunstctl", "history"], capture_output=True, text=True, timeout=3).stdout
26+ history = json.loads(out)["data"][0]
27+ except Exception:
28+ history = []
29+
30+ # Dunst timestamps are microseconds on the monotonic clock, not wall-clock time.
31+ offset = time.time() - time.monotonic()
32+ items = []
33+ for n in history[:LIMIT]:
34+ def field(name):
35+ return (n.get(name) or {}).get("data", "")
36+
37+ body = plain(field("body"))
38+ if len(body) > BODY_MAX:
39+ body = body[: BODY_MAX - 1].rstrip() + "…"
40+ try:
41+ when = time.strftime("%H:%M", time.localtime(offset + int(field("timestamp")) / 1e6))
42+ except (TypeError, ValueError):
43+ when = ""
44+ items.append({
45+ "i": len(items), # position in the list, staggers the clear animation
46+ "id": field("id"),
47+ "app": plain(field("appname")),
48+ "summary": plain(field("summary")),
49+ "body": body,
50+ "time": when,
51+ })
52+ print(json.dumps(items))
53+
54+
55+if __name__ == "__main__":
56+ main()
@@ -1,0 +1,88 @@
1+#!/usr/bin/env python3
2+"""Click on an entry in the Eww notifications panel: go to the app that sent it.
3+
4+Usage: open-notification.py <dunst notification id>
5+
6+Focuses the sending app's window (matched by app name against Hyprland's window
7+classes and titles), removes the entry from dunst's history and closes the panel.
8+If no window of that app is open, nothing happens and the entry stays.
9+
10+Dunst can't re-run the app's own action here: once a notification has left the
11+screen it has been reported to the app as closed, and dunst no longer signals
12+actions for it. That only works by clicking the pop-up while it is showing.
13+
14+Only the id comes from the panel; the app name is looked up in dunst's history, so
15+text chosen by whoever sent the notification never reaches a shell.
16+"""
17+import json
18+import os
19+import subprocess
20+import sys
21+
22+DIR = os.path.dirname(os.path.abspath(__file__))
23+
24+
25+def run(*args):
26+ try:
27+ return subprocess.run(args, capture_output=True, text=True, timeout=3).stdout
28+ except Exception:
29+ return ""
30+
31+
32+def app_of(notification_id):
33+ try:
34+ history = json.loads(run("dunstctl", "history"))["data"][0]
35+ except Exception:
36+ return ""
37+ for n in history:
38+ if (n.get("id") or {}).get("data") == notification_id:
39+ return ((n.get("appname") or {}).get("data") or "").strip()
40+ return ""
41+
42+
43+def find_window(app):
44+ """Best matching window for an app name, preferring the most recently focused."""
45+ app = app.lower()
46+ if not app:
47+ return None
48+ try:
49+ clients = json.loads(run("hyprctl", "clients", "-j"))
50+ except Exception:
51+ return None
52+
53+ def score(client):
54+ names = [(client.get("class") or "").lower(), (client.get("initialClass") or "").lower()]
55+ if app in names:
56+ return 3
57+ if any(name and (app in name or name in app) for name in names):
58+ return 2
59+ if app in (client.get("title") or "").lower():
60+ return 1
61+ return 0
62+
63+ matches = [c for c in clients if c.get("mapped", True) and score(c) > 0]
64+ if not matches:
65+ return None
66+ matches.sort(key=lambda c: (-score(c), c.get("focusHistoryID", 1 << 30)))
67+ return matches[0]["address"]
68+
69+
70+def main():
71+ try:
72+ notification_id = int(sys.argv[1])
73+ except (IndexError, ValueError):
74+ return 2
75+
76+ address = find_window(app_of(notification_id))
77+ if not address:
78+ return 1
79+
80+ subprocess.run(["bash", os.path.join(DIR, "toggle.sh"), "close"])
81+ subprocess.run(["hyprctl", "dispatch", f'hl.dsp.focus({{ window = "address:{address}" }})'],
82+ capture_output=True)
83+ subprocess.run(["dunstctl", "history-rm", str(notification_id)])
84+ return 0
85+
86+
87+if __name__ == "__main__":
88+ sys.exit(main())
@@ -1,0 +1,85 @@
1+#!/usr/bin/env bash
2+# Opens/closes the Waybar flyout panels. Called by the Waybar buttons.
3+# toggle.sh [control-center|notifications] toggle that panel (default: control-center)
4+# toggle.sh close close whichever panel is open
5+# Only one panel is open at a time. Errors are shown as a notification and written to
6+# $XDG_RUNTIME_DIR/control-center.log
7+
8+PANEL="${1:-control-center}"
9+
10+# Force a monitor by number (0, 1, ...). Leave empty to open on the monitor you clicked on.
11+MONITOR=""
12+
13+DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
14+EWW=(eww --config "$DIR")
15+LOG="${XDG_RUNTIME_DIR:-/tmp}/control-center.log"
16+
17+fail() {
18+ notify-send -u critical "Control center" "$1 (log: $LOG)" 2>/dev/null
19+ exit 1
20+}
21+
22+command -v eww >/dev/null 2>&1 || fail "eww is not installed"
23+
24+case "$PANEL" in
25+ control-center|notifications|close) ;;
26+ *) fail "Unknown panel: $PANEL" ;;
27+esac
28+
29+ACTIVE=$("${EWW[@]}" active-windows 2>/dev/null)
30+is_open() { grep -q "^$1:" <<<"$ACTIVE"; }
31+
32+close_all() {
33+ local open=()
34+ for w in control-center notifications cc-closer; do
35+ is_open "$w" && open+=("$w")
36+ done
37+ [ ${#open[@]} -gt 0 ] && "${EWW[@]}" close "${open[@]}"
38+ "${EWW[@]}" update sinks_open=false confirm=none notifs_open=false
39+}
40+
41+# Clicking the button of the panel that is open (or asking to close) just closes it;
42+# clicking the other button swaps panels.
43+if [ "$PANEL" = close ] || is_open "$PANEL"; then
44+ close_all
45+ exit 0
46+fi
47+close_all
48+
49+# The focused monitor is the one the bar button was clicked on. Hyprland numbers monitors
50+# (HDMI-A-2 = 1) but Eww uses GTK's own numbering, so translate by matching screen positions.
51+# If that isn't possible we fall back to Hyprland's number, and finally to monitor 1.
52+if [ -z "$MONITOR" ] && command -v hyprctl >/dev/null 2>&1; then
53+ MONITOR=$(hyprctl monitors -j 2>/dev/null | GDK_BACKEND=wayland python3 -c '
54+import sys, json
55+mons = json.load(sys.stdin)
56+cur = next((m for m in mons if m.get("focused")), mons[0])
57+idx = cur["id"]
58+try:
59+ import gi
60+ gi.require_version("Gdk", "3.0")
61+ from gi.repository import Gdk
62+ Gdk.init([])
63+ display = Gdk.Display.get_default()
64+ for i in range(display.get_n_monitors()):
65+ g = display.get_monitor(i).get_geometry()
66+ if (g.x, g.y) == (cur["x"], cur["y"]):
67+ idx = i
68+ break
69+except Exception:
70+ pass
71+print(idx)
72+' 2>/dev/null)
73+fi
74+MONITOR="${MONITOR:-1}"
75+
76+if [ "$PANEL" = notifications ]; then
77+ # Fill the list before the panel appears; Eww then keeps it fresh while it is open.
78+ "${EWW[@]}" update notifs="$(python3 "$DIR/scripts/notifications.py")" notifs_open=true
79+fi
80+
81+out=$("${EWW[@]}" open-many cc-closer "$PANEL" \
82+ --arg "cc-closer:monitor=$MONITOR" --arg "$PANEL:monitor=$MONITOR" 2>&1) || {
83+ printf '%s\n' "$out" > "$LOG"
84+ fail "Could not open the panel: $(printf '%s' "$out" | head -1)"
85+}
@@ -1,0 +1,3 @@
1+<!-- battlenet: Waybar tray icon. All tray icons share the same full-size white disc, with the
2+ app logo cut out of it (logo path from Simple Icons, simpleicons.org, CC0). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><mask id="m"><rect width="24" height="24" fill="#ffffff"/><g transform="translate(12 12) scale(0.74) translate(-12 -12)" fill="#000000" stroke="#000000" stroke-width="1.3" stroke-linejoin="round"><path d="M18.94 8.296C15.9 6.892 11.534 6 7.426 6.332c.206-1.36.714-2.308 1.548-2.508 1.148-.275 2.4.48 3.594 1.854.782.102 1.71.28 2.355.429C12.747 2.013 9.828-.282 7.607.565c-1.688.644-2.553 2.97-2.448 6.094-2.2.468-3.915 1.3-5.013 2.495-.056.065-.181.227-.137.305.034.058.146-.008.194-.04 1.274-.89 2.904-1.373 5.027-1.676.303 3.333 1.713 7.56 4.055 10.952-1.28.502-2.356.536-2.946-.087-.812-.856-.784-2.318-.19-4.04a26.764 26.764 0 0 1-.807-2.254c-2.459 3.934-2.986 7.61-1.143 9.11 1.402 1.14 3.847.725 6.502-.926 1.505 1.672 3.083 2.74 4.667 3.094.084.015.287.043.332-.034.034-.06-.08-.124-.131-.149-1.408-.657-2.64-1.828-3.964-3.515 2.735-1.929 5.691-5.263 7.457-8.988 1.076.86 1.64 1.773 1.398 2.595-.336 1.131-1.615 1.84-3.403 2.185a27.697 27.697 0 0 1-1.548 1.826c4.634.16 8.08-1.22 8.458-3.565.286-1.786-1.295-3.696-4.053-5.17.696-2.139.832-4.04.346-5.588-.029-.08-.106-.27-.196-.27-.068 0-.067.13-.063.187.135 1.547-.263 3.2-1.062 5.19zm-8.533 9.869c-1.96-3.145-3.09-6.849-3.082-10.594 3.702-.124 7.474.748 10.714 2.627-1.743 3.269-4.385 6.1-7.633 7.966h.001z"/></g></mask><circle cx="12" cy="12" r="12" fill="#ffffff" mask="url(#m)"/></svg>
@@ -1,0 +1,3 @@
1+<!-- bitwarden: Waybar tray icon. All tray icons share the same full-size white disc, with the
2+ app logo cut out of it (logo path from Simple Icons, simpleicons.org, CC0). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><mask id="m"><rect width="24" height="24" fill="#ffffff"/><g transform="translate(12 12) scale(0.62) translate(-12 -12)" fill="#000000" stroke="#000000" stroke-width="0" stroke-linejoin="round"><path d="M21.722.296A.964.964 0 0 0 21.018 0H2.982a.959.959 0 0 0-.703.296.96.96 0 0 0-.297.702v12c0 .895.174 1.783.523 2.665.349.88.783 1.66 1.3 2.345.517.68 1.132 1.346 1.848 1.993a21.807 21.807 0 0 0 1.98 1.609c.605.427 1.235.83 1.893 1.212.657.381 1.125.638 1.4.772.276.134.5.241.664.311a.916.916 0 0 0 .814 0c.168-.073.389-.177.667-.311.275-.134.743-.394 1.401-.772a25.305 25.305 0 0 0 1.894-1.212A21.891 21.891 0 0 0 18.348 20c.716-.647 1.33-1.31 1.847-1.993s.949-1.463 1.3-2.345c.35-.879.524-1.767.524-2.665V1.001a.95.95 0 0 0-.297-.705zm-2.325 12.815c0 4.344-7.397 8.087-7.397 8.087V2.57h7.397v10.54z"/></g></mask><circle cx="12" cy="12" r="12" fill="#ffffff" mask="url(#m)"/></svg>
@@ -1,0 +1,3 @@
1+<!-- claude: Waybar tray icon. All tray icons share the same full-size white disc, with the
2+ app logo cut out of it (logo path from Simple Icons, simpleicons.org, CC0). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><mask id="m"><rect width="24" height="24" fill="#ffffff"/><g transform="translate(12 12) scale(0.72) translate(-12 -12)" fill="#000000" stroke="#000000" stroke-width="0.5" stroke-linejoin="round"><path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z"/></g></mask><circle cx="12" cy="12" r="12" fill="#ffffff" mask="url(#m)"/></svg>
@@ -1,0 +1,3 @@
1+<!-- faugus: Waybar tray icon. All tray icons share the same full-size white disc, with the
2+ app logo cut out of it (hand-drawn gamepad). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><mask id="m"><rect width="24" height="24" fill="#ffffff"/><g transform="translate(12 12) scale(0.7) translate(-12 -12)" fill="#000000" stroke="#000000" stroke-width="0" stroke-linejoin="round"><path d="M6.5 4.5h11A5.5 5.5 0 0 1 23 10v6a3.5 3.5 0 0 1-6.4 2L15.2 16H8.8l-1.4 2A3.5 3.5 0 0 1 1 16v-6a5.5 5.5 0 0 1 5.5-5.5z"/></g><g transform="translate(12 12) scale(0.7) translate(-12 -12)" fill="#ffffff" stroke="#ffffff" stroke-width="0" stroke-linejoin="round"><rect x="4" y="9.3" width="7" height="3.4"/><rect x="5.8" y="7.5" width="3.4" height="7"/><circle cx="15.5" cy="12.6" r="2"/><circle cx="19" cy="9.2" r="2"/></g></mask><circle cx="12" cy="12" r="12" fill="#ffffff" mask="url(#m)"/></svg>
@@ -1,0 +1,3 @@
1+<!-- opendeck: Waybar tray icon. All tray icons share the same full-size white disc, with the
2+ app logo cut out of it (hand-drawn key grid). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><mask id="m"><rect width="24" height="24" fill="#ffffff"/><g transform="translate(12 12) scale(0.62) translate(-12 -12)" fill="#000000" stroke="#000000" stroke-width="0" stroke-linejoin="round"><rect x="1" y="4" width="6" height="7" rx="1.2"/><rect x="9" y="4" width="6" height="7" rx="1.2"/><rect x="17" y="4" width="6" height="7" rx="1.2"/><rect x="1" y="13" width="6" height="7" rx="1.2"/><rect x="9" y="13" width="6" height="7" rx="1.2"/><rect x="17" y="13" width="6" height="7" rx="1.2"/></g></mask><circle cx="12" cy="12" r="12" fill="#ffffff" mask="url(#m)"/></svg>
@@ -1,0 +1,2 @@
1+<!-- signal: logo path from Simple Icons (simpleicons.org, CC0), filled white for the Waybar tray -->
2+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><path fill="#ffffff" d="M12 0q-.934 0-1.83.139l.17 1.111a11 11 0 0 1 3.32 0l.172-1.111A12 12 0 0 0 12 0M9.152.34A12 12 0 0 0 5.77 1.742l.584.961a10.8 10.8 0 0 1 3.066-1.27zm5.696 0-.268 1.094a10.8 10.8 0 0 1 3.066 1.27l.584-.962A12 12 0 0 0 14.848.34M12 2.25a9.75 9.75 0 0 0-8.539 14.459c.074.134.1.292.064.441l-1.013 4.338 4.338-1.013a.62.62 0 0 1 .441.064A9.7 9.7 0 0 0 12 21.75c5.385 0 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25m-7.092.068a12 12 0 0 0-2.59 2.59l.909.664a11 11 0 0 1 2.345-2.345zm14.184 0-.664.909a11 11 0 0 1 2.345 2.345l.909-.664a12 12 0 0 0-2.59-2.59M1.742 5.77A12 12 0 0 0 .34 9.152l1.094.268a10.8 10.8 0 0 1 1.269-3.066zm20.516 0-.961.584a10.8 10.8 0 0 1 1.27 3.066l1.093-.268a12 12 0 0 0-1.402-3.383M.138 10.168A12 12 0 0 0 0 12q0 .934.139 1.83l1.111-.17A11 11 0 0 1 1.125 12q0-.848.125-1.66zm23.723.002-1.111.17q.125.812.125 1.66c0 .848-.042 1.12-.125 1.66l1.111.172a12.1 12.1 0 0 0 0-3.662M1.434 14.58l-1.094.268a12 12 0 0 0 .96 2.591l-.265 1.14 1.096.255.36-1.539-.188-.365a10.8 10.8 0 0 1-.87-2.35m21.133 0a10.8 10.8 0 0 1-1.27 3.067l.962.584a12 12 0 0 0 1.402-3.383zm-1.793 3.848a11 11 0 0 1-2.345 2.345l.664.909a12 12 0 0 0 2.59-2.59zm-19.959 1.1L.357 21.48a1.8 1.8 0 0 0 2.162 2.161l1.954-.455-.256-1.095-1.953.455a.675.675 0 0 1-.81-.81l.454-1.954zm16.832 1.769a10.8 10.8 0 0 1-3.066 1.27l.268 1.093a12 12 0 0 0 3.382-1.402zm-10.94.213-1.54.36.256 1.095 1.139-.266c.814.415 1.683.74 2.591.961l.268-1.094a10.8 10.8 0 0 1-2.35-.869zm3.634 1.24-.172 1.111a12.1 12.1 0 0 0 3.662 0l-.17-1.111q-.812.125-1.66.125a11 11 0 0 1-1.66-.125"/></svg>
@@ -1,0 +1,3 @@
1+<!-- steam: Waybar tray icon. All tray icons share the same full-size white disc; Steam's logo already
2+ is one (logo path from Simple Icons, simpleicons.org, CC0). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><path fill="#ffffff" d="M11.979 0C5.678 0 .511 4.86.022 11.037l6.432 2.658c.545-.371 1.203-.59 1.912-.59.063 0 .125.004.188.006l2.861-4.142V8.91c0-2.495 2.028-4.524 4.524-4.524 2.494 0 4.524 2.031 4.524 4.527s-2.03 4.525-4.524 4.525h-.105l-4.076 2.911c0 .052.004.105.004.159 0 1.875-1.515 3.396-3.39 3.396-1.635 0-3.016-1.173-3.331-2.727L.436 15.27C1.862 20.307 6.486 24 11.979 24c6.627 0 11.999-5.373 11.999-12S18.605 0 11.979 0zM7.54 18.21l-1.473-.61c.262.543.714.999 1.314 1.25 1.297.539 2.793-.076 3.332-1.375.263-.63.264-1.319.005-1.949s-.75-1.121-1.377-1.383c-.624-.26-1.29-.249-1.878-.03l1.523.63c.956.4 1.409 1.5 1.009 2.455-.397.957-1.497 1.41-2.454 1.012H7.54zm11.415-9.303c0-1.662-1.353-3.015-3.015-3.015-1.665 0-3.015 1.353-3.015 3.015 0 1.665 1.35 3.015 3.015 3.015 1.663 0 3.015-1.35 3.015-3.015zm-5.273-.005c0-1.252 1.013-2.266 2.265-2.266 1.249 0 2.266 1.014 2.266 2.266 0 1.251-1.017 2.265-2.266 2.265-1.253 0-2.265-1.014-2.265-2.265z"/></svg>
@@ -1,0 +1,3 @@
1+<!-- wowup: Waybar tray icon. All tray icons share the same full-size white disc, with the
2+ app logo cut out of it (hand-drawn after WowUp's logo). -->
3+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="32" height="32"><mask id="m"><rect width="24" height="24" fill="#ffffff"/><g transform="translate(12 12) scale(0.66) translate(-12 -12)" fill="#000000" stroke="#000000" stroke-width="0" stroke-linejoin="round"><path d="M2.4 8 L6.7 19.5 L10.9 9.5 L15.1 19.5 L19.2 8.5" fill="none" stroke-width="4" stroke-linejoin="miter" stroke-miterlimit="3"/><path d="M14.2 9.6 L19.2 2.4 L24.2 9.6 Z"/></g></mask><circle cx="12" cy="12" r="12" fill="#ffffff" mask="url(#m)"/></svg>
@@ -1,0 +1,33 @@
1+<svg xmlns="http://www.w3.org/2000/svg" xmlns:graphite="https://graphite.art" viewBox="0 0 14 14" width="14" height="14"><defs><clipPath id="artboard-2210588180667624318"><rect x="0" y="0" width="14" height="14" /></clipPath></defs><g>
2+<rect fill="#ffffff" fill-opacity="0" x="0" y="0" width="14" height="14"/>
3+<g clip-path="url(#artboard-2210588180667624318)">
4+ <g>
5+ <g>
6+ <path d="M7,0 C7,0 8,1 8,1 C8,1 7,2 7,2 C7,2 6,1 6,1 C6,1 7,0 7,0 Z" fill="#ffffff"/>
7+ </g>
8+ <g>
9+ <path d="M9,2 C9,2 10,3 10,3 C10,3 9,4 9,4 C9,4 8,3 8,3 C8,3 9,2 9,2 Z" fill="#ffffff"/>
10+ </g>
11+ <g>
12+ <path d="M5,2 C5,2 8,5 8,5 C8,5 7,6 7,6 C7,6 4,3 4,3 C4,3 5,2 5,2 Z" fill="#ffffff"/>
13+ </g>
14+ <g>
15+ <path d="M3,4 C3,4 4,5 4,5 C4,5 1,8 1,8 C1,8 0,7 0,7 C0,7 3,4 3,4 Z" fill="#ffffff"/>
16+ </g>
17+ <g>
18+ <path d="M5,6 C5,6 8,9 8,9 C8,9 7,10 7,10 C7,10 4,7 4,7 C4,7 5,6 5,6 Z" fill="#ffffff"/>
19+ </g>
20+ <g>
21+ <path d="M3,8 C3,8 4,9 4,9 C4,9 3,10 3,10 C3,10 2,9 2,9 C2,9 3,8 3,8 Z" fill="#ffffff"/>
22+ </g>
23+ <g>
24+ <path d="M5,10 C5,10 8,13 8,13 C8,13 7,14 7,14 C7,14 4,11 4,11 C4,11 5,10 5,10 Z" fill="#ffffff"/>
25+ </g>
26+ <g>
27+ <path d="M11,4 C11,4 12,5 12,5 C12,5 9,8 9,8 C9,8 8,7 8,7 C8,7 11,4 11,4 Z" fill="#ffffff"/>
28+ </g>
29+ <g>
30+ <path d="M13,6 C13,6 14,7 14,7 C14,7 9,12 9,12 C9,12 8,11 8,11 C8,11 13,6 13,6 Z" fill="#ffffff"/>
31+ </g>
32+ </g>
33+</g></g></svg>
@@ -1,0 +1,55 @@
1+#!/usr/bin/env bash
2+# Battery of a wireless Logitech mouse (G Pro X Superlight) as one line of JSON.
3+# Used by the Eww control center (and by a Waybar custom module if you re-add one).
4+# When the mouse isn't found: "connected":false and an empty "text" (Waybar hides the module).
5+#
6+# Source 1: the kernel's HID++ driver, which exposes the mouse under
7+# /sys/class/power_supply/hidpp_battery_N (the number changes between
8+# reconnects, so we glob instead of hardcoding it).
9+# Source 2: Solaar (`solaar show`), used only if source 1 finds nothing.
10+#
11+# MOUSE_MATCH is matched (case-insensitive) against the device name.
12+
13+MATCH="${MOUSE_MATCH:-superlight}"
14+cap=""; status=""; name=""
15+
16+for d in /sys/class/power_supply/hidpp_battery_*; do
17+ [ -d "$d" ] || continue
18+ model=$(tr -d '"\\' < "$d/model_name" 2>/dev/null)
19+ if [ -z "$cap" ] || echo "$model" | grep -qi "$MATCH"; then
20+ c=$(cat "$d/capacity" 2>/dev/null)
21+ if [ -n "$c" ]; then
22+ cap=$c
23+ status=$(cat "$d/status" 2>/dev/null)
24+ name=$model
25+ fi
26+ fi
27+done
28+
29+if [ -z "$cap" ] && command -v solaar >/dev/null 2>&1; then
30+ out=$(timeout 8 solaar show "$MATCH" 2>/dev/null)
31+ line=$(echo "$out" | grep -i 'Battery:' | head -1)
32+ cap=$(echo "$line" | grep -oE '[0-9]+%' | head -1 | tr -d '%')
33+ status=$(echo "$line" | grep -oiE 'discharging|charging|recharging|full' | head -1)
34+ name="Logitech mouse (via Solaar)"
35+fi
36+
37+if [ -z "$cap" ]; then
38+ echo '{"text":"","percentage":0,"class":"","tooltip":"","label":"Not connected","connected":false}'
39+ exit 0
40+fi
41+
42+# Icon: 5 steps from full to empty (charging gets its own icon).
43+if [ "${status,,}" = "charging" ] || [ "${status,,}" = "recharging" ]; then icon="󰂄"; class="charging"
44+elif [ "$cap" -gt 85 ]; then icon="󰁹"; class=""
45+elif [ "$cap" -gt 60 ]; then icon="󰂀"; class=""
46+elif [ "$cap" -gt 35 ]; then icon="󰁾"; class=""
47+elif [ "$cap" -gt 15 ]; then icon="󰁼"; class="warning"
48+else icon="󰂎"; class="critical"
49+fi
50+
51+label="${cap}%"
52+[ "$class" = "charging" ] && label="${cap}% · charging"
53+
54+printf '{"text":"%s","percentage":%s,"class":"%s","tooltip":"%s: %s%% %s","label":"%s","connected":true}\n' \
55+ "$icon" "$cap" "$class" "${name:-Mouse}" "$cap" "${status:+($status)}" "$label"
@@ -1,0 +1,54 @@
1+#!/usr/bin/env python3
2+"""Waybar module: number of notifications in dunst's history, as one JSON line per change.
3+
4+Prints an empty text when there are none, so Waybar hides the badge. Event-driven (dunst
5+announces changes to its history length over D-Bus), with a slow poll as a safety net.
6+The number is capped at what the Eww notifications panel shows.
7+"""
8+import json
9+
10+import gi
11+
12+gi.require_version("Gio", "2.0")
13+from gi.repository import Gio, GLib # noqa: E402
14+
15+LIMIT = 10 # keep in sync with eww/scripts/notifications.py
16+BUS = "org.freedesktop.Notifications"
17+PATH = "/org/freedesktop/Notifications"
18+IFACE = "org.dunstproject.cmd0"
19+
20+last = None
21+
22+
23+def emit(count):
24+ global last
25+ text = str(min(count, LIMIT)) if count > 0 else ""
26+ if text != last:
27+ last = text
28+ print(json.dumps({"text": text}), flush=True)
29+
30+
31+def read(connection):
32+ try:
33+ reply = connection.call_sync(
34+ BUS, PATH, "org.freedesktop.DBus.Properties", "Get",
35+ GLib.Variant("(ss)", (IFACE, "historyLength")),
36+ GLib.VariantType("(v)"), Gio.DBusCallFlags.NO_AUTO_START, 2000, None)
37+ emit(reply.unpack()[0])
38+ except GLib.Error:
39+ emit(0) # dunst not running
40+ return True
41+
42+
43+def main():
44+ connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
45+ connection.signal_subscribe(
46+ BUS, "org.freedesktop.DBus.Properties", "PropertiesChanged", PATH, None,
47+ Gio.DBusSignalFlags.NONE, lambda *args: read(connection))
48+ read(connection)
49+ GLib.timeout_add_seconds(5, read, connection)
50+ GLib.MainLoop().run()
51+
52+
53+if __name__ == "__main__":
54+ main()
@@ -1,0 +1,114 @@
1+* {
2+ border: none;
3+ border-radius: 17px;
4+ font-family: NotoSans Nerd Font Regular;
5+ font-size: 12px;
6+ font-weight: bolder;
7+ background: transparent;
8+ color: white;
9+ min-height: 0;
10+}
11+
12+window#waybar {
13+ background: rgba(0, 0, 0, 0.1);
14+}
15+
16+window#waybar #tray menu{
17+ background: rgba(0, 0, 0, 0.1);
18+}
19+
20+#tray menu :hover {
21+ background: rgba(0, 0, 0, 0.1);
22+}
23+
24+#tray menu :hover label {
25+ background: rgba(0, 0, 0, 0.1);
26+}
27+
28+tooltip {
29+ background: rgba(0, 0, 0, 0.1);
30+}
31+
32+/* Workspace buttons are pills inset 4px from the bar's edge, so their 13px radius
33+ is concentric with the bar's 17px one. Everything GTK's theme draws on a button
34+ (border, shadows, hover gradient) is reset here, as it otherwise shows up as a
35+ square box on hover. */
36+#workspaces button {
37+ margin: 4px 0 4px 4px;
38+ padding: 0 6px;
39+ min-width: 16px;
40+ border: none;
41+ border-radius: 13px;
42+ outline: none;
43+ box-shadow: none;
44+ text-shadow: none;
45+ background-color: transparent;
46+ /* Active-workspace underline: 2px line that grows from the centre (see .active). */
47+ background-image: linear-gradient(white, white);
48+ background-repeat: no-repeat;
49+ background-position: 50% 92%;
50+ background-size: 0 2px;
51+ transition: background-size 200ms ease-out, background-color 150ms ease-out;
52+}
53+
54+#workspaces button:hover {
55+ background-color: rgba(255, 255, 255, 0.12);
56+}
57+
58+#tray window decoration{
59+ border-style: none;
60+ box-shadow: none;
61+}
62+
63+#workspaces button.active {
64+ background-size: 10px 2px;
65+}
66+
67+/* Workspaces with no windows are dimmed, unless it's the one you're on. The colour
68+ goes on the label because the `*` rule above gives every label its own white. */
69+#workspaces button label {
70+ transition: color 150ms ease-out;
71+}
72+
73+#workspaces button.empty:not(.active) label {
74+ color: rgba(255, 255, 255, 0.4);
75+}
76+
77+#clock, #tray, #custom-notifications, #custom-control-center {
78+ padding: 0 7px;
79+}
80+
81+/* Notification count: a small white disc overlapping the bell's top right corner.
82+ The margins shrink it from the bar's full height to 12px; the negative left margin
83+ pulls it over the bell. Hidden by Waybar when there are no notifications. */
84+#custom-notifications-count {
85+ min-width: 6px;
86+ padding: 0 3px;
87+ margin: 7px 3px 17px -11px;
88+ border-radius: 6px;
89+ background: white;
90+ color: #18181c;
91+ font-size: 8px;
92+}
93+
94+.imsh-cast-monitor.recording {
95+ color: #f00;
96+}
97+
98+.imsh-cast-monitor.error {
99+ color: #ff0;
100+}
101+
102+/* Control-center button: your logo as a pre-rendered 18px image.
103+ GTK stretches SVG backgrounds as pixels (which makes them blurry), so logo-18.png is
104+ rendered at exactly 18px, and logo-36.png is used automatically on 2x-scaled monitors. */
105+#custom-control-center {
106+ min-width: 18px;
107+ min-height: 18px;
108+ background-image: -gtk-scaled(url("file:///home/vizie/.config/waybar/logo-18.png"),
109+ url("file:///home/vizie/.config/waybar/logo-36.png"));
110+ background-repeat: no-repeat;
111+ background-position: center;
112+ background-size: 18px 18px;
113+ margin-right: 6px; /* gap between the logo and the end of the bar */
114+}
@@ -1,0 +1,1844 @@
1+# Generated by Powerlevel10k configuration wizard on 2026-06-14 at 03:34 CEST.
2+# Based on romkatv/powerlevel10k/config/p10k-rainbow.zsh, checksum 49619.
3+# Wizard options: nerdfont-v3 + powerline, small icons, rainbow, unicode,
4+# slanted separators, slanted heads, sharp tails, 2 lines, dotted, full frame,
5+# dark-ornaments, compact, many icons, concise, transient_prompt,
6+# instant_prompt=verbose.
7+# Type `p10k configure` to generate another config.
8+#
9+# Config for Powerlevel10k with powerline prompt style with colorful background.
10+# Type `p10k configure` to generate your own config based on it.
11+#
12+# Tip: Looking for a nice color? Here's a one-liner to print colormap.
13+#
14+# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done
15+
16+# Temporarily change options.
17+'builtin' 'local' '-a' 'p10k_config_opts'
18+[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
19+[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
20+[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
21+'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
22+
23+() {
24+ emulate -L zsh -o extended_glob
25+
26+ # Unset all configuration options. This allows you to apply configuration changes without
27+ # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`.
28+ unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR'
29+
30+ # Zsh >= 5.1 is required.
31+ [[ $ZSH_VERSION == (5.<1->*|<6->.*) ]] || return
32+
33+ # The list of segments shown on the left. Fill it with the most important segments.
34+ typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
35+ # =========================[ Line #1 ]=========================
36+ os_icon # os identifier
37+ dir # current directory
38+ vcs # git status
39+ # =========================[ Line #2 ]=========================
40+ newline # \n
41+ # prompt_char # prompt symbol
42+ )
43+
44+ # The list of segments shown on the right. Fill it with less important segments.
45+ # Right prompt on the last prompt line (where you are typing your commands) gets
46+ # automatically hidden when the input line reaches it. Right prompt above the
47+ # last prompt line gets hidden if it would overlap with left prompt.
48+ typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
49+ # =========================[ Line #1 ]=========================
50+ status # exit code of the last command
51+ command_execution_time # duration of the last command
52+ background_jobs # presence of background jobs
53+ direnv # direnv status (https://direnv.net/)
54+ asdf # asdf version manager (https://github.com/asdf-vm/asdf)
55+ virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html)
56+ anaconda # conda environment (https://conda.io/)
57+ pyenv # python environment (https://github.com/pyenv/pyenv)
58+ goenv # go environment (https://github.com/syndbg/goenv)
59+ nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv)
60+ nvm # node.js version from nvm (https://github.com/nvm-sh/nvm)
61+ nodeenv # node.js environment (https://github.com/ekalinin/nodeenv)
62+ # node_version # node.js version
63+ # go_version # go version (https://golang.org)
64+ # rust_version # rustc version (https://www.rust-lang.org)
65+ # dotnet_version # .NET version (https://dotnet.microsoft.com)
66+ # php_version # php version (https://www.php.net/)
67+ # laravel_version # laravel php framework version (https://laravel.com/)
68+ # java_version # java version (https://www.java.com/)
69+ # package # name@version from package.json (https://docs.npmjs.com/files/package.json)
70+ rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv)
71+ rvm # ruby version from rvm (https://rvm.io)
72+ fvm # flutter version management (https://github.com/leoafarias/fvm)
73+ luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv)
74+ jenv # java version from jenv (https://github.com/jenv/jenv)
75+ plenv # perl version from plenv (https://github.com/tokuhirom/plenv)
76+ perlbrew # perl version from perlbrew (https://github.com/gugod/App-perlbrew)
77+ phpenv # php version from phpenv (https://github.com/phpenv/phpenv)
78+ scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv)
79+ haskell_stack # haskell version from stack (https://haskellstack.org/)
80+ kubecontext # current kubernetes context (https://kubernetes.io/)
81+ terraform # terraform workspace (https://www.terraform.io)
82+ # terraform_version # terraform version (https://www.terraform.io)
83+ aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
84+ aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/)
85+ azure # azure account name (https://docs.microsoft.com/en-us/cli/azure)
86+ gcloud # google cloud cli account and project (https://cloud.google.com/)
87+ google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production)
88+ toolbox # toolbox name (https://github.com/containers/toolbox)
89+ context # user@hostname
90+ nordvpn # nordvpn connection status, linux only (https://nordvpn.com/)
91+ ranger # ranger shell (https://github.com/ranger/ranger)
92+ yazi # yazi shell (https://github.com/sxyazi/yazi)
93+ nnn # nnn shell (https://github.com/jarun/nnn)
94+ lf # lf shell (https://github.com/gokcehan/lf)
95+ xplr # xplr shell (https://github.com/sayanarijit/xplr)
96+ vim_shell # vim shell indicator (:sh)
97+ midnight_commander # midnight commander shell (https://midnight-commander.org/)
98+ nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html)
99+ chezmoi_shell # chezmoi shell (https://www.chezmoi.io/)
100+ vi_mode # vi mode (you don't need this if you've enabled prompt_char)
101+ # vpn_ip # virtual private network indicator
102+ # load # CPU load
103+ # disk_usage # disk usage
104+ # ram # free RAM
105+ # swap # used swap
106+ todo # todo items (https://github.com/todotxt/todo.txt-cli)
107+ timewarrior # timewarrior tracking status (https://timewarrior.net/)
108+ taskwarrior # taskwarrior task count (https://taskwarrior.org/)
109+ per_directory_history # Oh My Zsh per-directory-history local/global indicator
110+ # cpu_arch # CPU architecture
111+ # time # current time
112+ # =========================[ Line #2 ]=========================
113+ newline
114+ # ip # ip address and bandwidth usage for a specified network interface
115+ # public_ip # public IP address
116+ # proxy # system-wide http/https/ftp proxy
117+ # battery # internal battery
118+ # wifi # wifi speed
119+ # example # example user-defined segment (see prompt_example function below)
120+ )
121+
122+ # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you.
123+ typeset -g POWERLEVEL9K_MODE=nerdfont-v3
124+ # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid
125+ # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added.
126+ typeset -g POWERLEVEL9K_ICON_PADDING=none
127+
128+ # When set to true, icons appear before content on both sides of the prompt. When set
129+ # to false, icons go after content. If empty or not set, icons go before content in the left
130+ # prompt and after content in the right prompt.
131+ #
132+ # You can also override it for a specific segment:
133+ #
134+ # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false
135+ #
136+ # Or for a specific segment in specific state:
137+ #
138+ # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false
139+ typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=
140+
141+ # Add an empty line before each prompt.
142+ typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false
143+
144+ # Connect left prompt lines with these symbols. You'll probably want to use the same color
145+ # as POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND below.
146+ typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX='%240F╭─'
147+ typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX='%240F├─'
148+ typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX='%240F╰─'
149+ # Connect right prompt lines with these symbols.
150+ typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX='%240F─╮'
151+ typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX='%240F─┤'
152+ typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX='%240F─╯'
153+
154+ # Filler between left and right prompt on the first prompt line. You can set it to ' ', '·' or
155+ # '─'. The last two make it easier to see the alignment between left and right prompt and to
156+ # separate prompt from command output. You might want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false
157+ # for more compact prompt if using this option.
158+ typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR='·'
159+ typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_BACKGROUND=
160+ typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_GAP_BACKGROUND=
161+ if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then
162+ # The color of the filler. You'll probably want to match the color of POWERLEVEL9K_MULTILINE
163+ # ornaments defined above.
164+ typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=240
165+ # Start filler from the edge of the screen if there are no left segments on the first line.
166+ typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}'
167+ # End filler on the edge of the screen if there are no right segments on the first line.
168+ typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}'
169+ fi
170+
171+ # Separator between same-color segments on the left.
172+ typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR='\u2571'
173+ # Separator between same-color segments on the right.
174+ typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR='\u2571'
175+ # Separator between different-color segments on the left.
176+ typeset -g POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR='\uE0BC'
177+ # Separator between different-color segments on the right.
178+ typeset -g POWERLEVEL9K_RIGHT_SEGMENT_SEPARATOR='\uE0BA'
179+ # To remove a separator between two segments, add "_joined" to the second segment name.
180+ # For example: POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(os_icon context_joined)
181+
182+ # The right end of left prompt.
183+ typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='\uE0BC'
184+ # The left end of right prompt.
185+ typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='\uE0BA'
186+ # The left end of left prompt.
187+ typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL='\uE0B2'
188+ # The right end of right prompt.
189+ typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL='\uE0B0'
190+ # Left prompt terminator for lines without any segments.
191+ typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=
192+
193+ #################################[ os_icon: os identifier ]##################################
194+ # OS identifier color.
195+ typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=232
196+ typeset -g POWERLEVEL9K_OS_ICON_BACKGROUND=7
197+ # Custom icon.
198+ # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐'
199+
200+ ################################[ prompt_char: prompt symbol ]################################
201+ # Transparent background.
202+ typeset -g POWERLEVEL9K_PROMPT_CHAR_BACKGROUND=
203+ # Green prompt symbol if the last command succeeded.
204+ typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76
205+ # Red prompt symbol if the last command failed.
206+ typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196
207+ # Default prompt symbol.
208+ typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯'
209+ # Prompt symbol in command vi mode.
210+ typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮'
211+ # Prompt symbol in visual vi mode.
212+ typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V'
213+ # Prompt symbol in overwrite vi mode.
214+ typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶'
215+ typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true
216+ # No line terminator if prompt_char is the last segment.
217+ typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=
218+ # No line introducer if prompt_char is the first segment.
219+ typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL=
220+ # No surrounding whitespace.
221+ typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE=
222+
223+ ##################################[ dir: current directory ]##################################
224+ # Current directory background color.
225+ typeset -g POWERLEVEL9K_DIR_BACKGROUND=4
226+ # Default current directory foreground color.
227+ typeset -g POWERLEVEL9K_DIR_FOREGROUND=254
228+ # If directory is too long, shorten some of its segments to the shortest possible unique
229+ # prefix. The shortened directory can be tab-completed to the original.
230+ typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique
231+ # Replace removed segment suffixes with this symbol.
232+ typeset -g POWERLEVEL9K_SHORTEN_DELIMITER=
233+ # Color of the shortened directory segments.
234+ typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=250
235+ # Color of the anchor directory segments. Anchor segments are never shortened. The first
236+ # segment is always an anchor.
237+ typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=255
238+ # Display anchor directory segments in bold.
239+ typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true
240+ # Don't shorten directories that contain any of these files. They are anchors.
241+ local anchor_files=(
242+ .bzr
243+ .citc
244+ .git
245+ .hg
246+ .node-version
247+ .python-version
248+ .go-version
249+ .ruby-version
250+ .lua-version
251+ .java-version
252+ .perl-version
253+ .php-version
254+ .tool-versions
255+ .mise.toml
256+ .shorten_folder_marker
257+ .svn
258+ .terraform
259+ CVS
260+ Cargo.toml
261+ composer.json
262+ go.mod
263+ package.json
264+ stack.yaml
265+ )
266+ typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})"
267+ # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains
268+ # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is
269+ # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first)
270+ # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers
271+ # and other directories don't.
272+ #
273+ # Optionally, "first" and "last" can be followed by ":<offset>" where <offset> is an integer.
274+ # This moves the truncation point to the right (positive offset) or to the left (negative offset)
275+ # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0"
276+ # respectively.
277+ typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false
278+ # Don't shorten this many last directory segments. They are anchors.
279+ typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1
280+ # Shorten directory if it's longer than this even if there is space for it. The value can
281+ # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty,
282+ # directory will be shortened only when prompt doesn't fit or when other parameters demand it
283+ # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below).
284+ # If set to `0`, directory will always be shortened to its minimum length.
285+ typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80
286+ # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this
287+ # many columns for typing commands.
288+ typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40
289+ # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least
290+ # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands.
291+ typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50
292+ # If set to true, embed a hyperlink into the directory. Useful for quickly
293+ # opening a directory in the file manager simply by clicking the link.
294+ # Can also be handy when the directory is shortened, as it allows you to see
295+ # the full directory that was used in previous commands.
296+ typeset -g POWERLEVEL9K_DIR_HYPERLINK=false
297+
298+ # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON
299+ # and POWERLEVEL9K_DIR_CLASSES below.
300+ typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3
301+
302+ # The default icon shown next to non-writable and non-existent directories when
303+ # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3.
304+ # typeset -g POWERLEVEL9K_LOCK_ICON='⭐'
305+
306+ # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different
307+ # directories. It must be an array with 3 * N elements. Each triplet consists of:
308+ #
309+ # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with
310+ # extended_glob option enabled.
311+ # 2. Directory class for the purpose of styling.
312+ # 3. An empty string.
313+ #
314+ # Triplets are tried in order. The first triplet whose pattern matches $PWD wins.
315+ #
316+ # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories
317+ # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively.
318+ #
319+ # For example, given these settings:
320+ #
321+ # typeset -g POWERLEVEL9K_DIR_CLASSES=(
322+ # '~/work(|/*)' WORK ''
323+ # '~(|/*)' HOME ''
324+ # '*' DEFAULT '')
325+ #
326+ # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one
327+ # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or
328+ # WORK_NON_EXISTENT.
329+ #
330+ # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an
331+ # option to define custom colors and icons for different directory classes.
332+ #
333+ # # Styling for WORK.
334+ # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐'
335+ # typeset -g POWERLEVEL9K_DIR_WORK_BACKGROUND=4
336+ # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=254
337+ # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=250
338+ # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=255
339+ #
340+ # # Styling for WORK_NOT_WRITABLE.
341+ # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐'
342+ # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_BACKGROUND=4
343+ # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=254
344+ # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=250
345+ # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=255
346+ #
347+ # # Styling for WORK_NON_EXISTENT.
348+ # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐'
349+ # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_BACKGROUND=4
350+ # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=254
351+ # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=250
352+ # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=255
353+ #
354+ # If a styling parameter isn't explicitly defined for some class, it falls back to the classless
355+ # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls
356+ # back to POWERLEVEL9K_DIR_FOREGROUND.
357+ #
358+ # typeset -g POWERLEVEL9K_DIR_CLASSES=()
359+
360+ # Custom prefix.
361+ # typeset -g POWERLEVEL9K_DIR_PREFIX='in '
362+
363+ #####################################[ vcs: git status ]######################################
364+ # Version control background colors.
365+ typeset -g POWERLEVEL9K_VCS_CLEAN_BACKGROUND=2
366+ typeset -g POWERLEVEL9K_VCS_MODIFIED_BACKGROUND=3
367+ typeset -g POWERLEVEL9K_VCS_UNTRACKED_BACKGROUND=2
368+ typeset -g POWERLEVEL9K_VCS_CONFLICTED_BACKGROUND=3
369+ typeset -g POWERLEVEL9K_VCS_LOADING_BACKGROUND=8
370+
371+ # Branch icon. Set this parameter to '\UE0A0 ' for the popular Powerline branch icon.
372+ typeset -g POWERLEVEL9K_VCS_BRANCH_ICON='\uF126 '
373+
374+ # Untracked files icon. It's really a question mark, your font isn't broken.
375+ # Change the value of this parameter to show a different icon.
376+ typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?'
377+
378+ # Formatter for Git status.
379+ #
380+ # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42.
381+ #
382+ # You can edit the function to customize how Git status looks.
383+ #
384+ # VCS_STATUS_* parameters are set by gitstatus plugin. See reference:
385+ # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh.
386+ function my_git_formatter() {
387+ emulate -L zsh
388+
389+ if [[ -n $P9K_CONTENT ]]; then
390+ # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from
391+ # gitstatus plugin). VCS_STATUS_* parameters are not available in this case.
392+ typeset -g my_git_format=$P9K_CONTENT
393+ return
394+ fi
395+
396+ # Styling for different parts of Git status.
397+ local meta='%7F' # white foreground
398+ local clean='%0F' # black foreground
399+ local modified='%0F' # black foreground
400+ local untracked='%0F' # black foreground
401+ local conflicted='%1F' # red foreground
402+
403+ local res
404+
405+ if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then
406+ local branch=${(V)VCS_STATUS_LOCAL_BRANCH}
407+ # If local branch name is at most 32 characters long, show it in full.
408+ # Otherwise show the first 12 … the last 12.
409+ # Tip: To always show local branch name in full without truncation, delete the next line.
410+ (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line
411+ res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}"
412+ fi
413+
414+ if [[ -n $VCS_STATUS_TAG
415+ # Show tag only if not on a branch.
416+ # Tip: To always show tag, delete the next line.
417+ && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line
418+ ]]; then
419+ local tag=${(V)VCS_STATUS_TAG}
420+ # If tag name is at most 32 characters long, show it in full.
421+ # Otherwise show the first 12 … the last 12.
422+ # Tip: To always show tag name in full without truncation, delete the next line.
423+ (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line
424+ res+="${meta}#${clean}${tag//\%/%%}"
425+ fi
426+
427+ # Display the current Git commit if there is no branch and no tag.
428+ # Tip: To always display the current Git commit, delete the next line.
429+ [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line
430+ res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}"
431+
432+ # Show tracking branch name if it differs from local branch.
433+ if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then
434+ res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}"
435+ fi
436+
437+ # Display "wip" if the latest commit's summary contains "wip" or "WIP".
438+ if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then
439+ res+=" ${modified}wip"
440+ fi
441+
442+ if (( VCS_STATUS_COMMITS_AHEAD || VCS_STATUS_COMMITS_BEHIND )); then
443+ # ⇣42 if behind the remote.
444+ (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
445+ # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42.
446+ (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" "
447+ (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
448+ elif [[ -n $VCS_STATUS_REMOTE_BRANCH ]]; then
449+ # Tip: Uncomment the next line to display '=' if up to date with the remote.
450+ # res+=" ${clean}="
451+ fi
452+
453+ # ⇠42 if behind the push remote.
454+ (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}"
455+ (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" "
456+ # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42.
457+ (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}"
458+ # *42 if have stashes.
459+ (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}"
460+ # 'merge' if the repo is in an unusual state.
461+ [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}"
462+ # ~42 if have merge conflicts.
463+ (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
464+ # +42 if have staged changes.
465+ (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
466+ # !42 if have unstaged changes.
467+ (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
468+ # ?42 if have untracked files. It's really a question mark, your font isn't broken.
469+ # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon.
470+ # Remove the next line if you don't want to see untracked files at all.
471+ (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}"
472+ # "─" if the number of unstaged files is unknown. This can happen due to
473+ # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower
474+ # than the number of files in the Git index, or due to bash.showDirtyState being set to false
475+ # in the repository config. The number of staged and untracked files may also be unknown
476+ # in this case.
477+ (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─"
478+
479+ typeset -g my_git_format=$res
480+ }
481+ functions -M my_git_formatter 2>/dev/null
482+
483+ # Don't count the number of unstaged, untracked and conflicted files in Git repositories with
484+ # more than this many files in the index. Negative value means infinity.
485+ #
486+ # If you are working in Git repositories with tens of millions of files and seeing performance
487+ # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output
488+ # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's
489+ # config: `git config bash.showDirtyState false`.
490+ typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1
491+
492+ # Don't show Git status in prompt for repositories whose workdir matches this pattern.
493+ # For example, if set to '~', the Git repository at $HOME/.git will be ignored.
494+ # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'.
495+ typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~'
496+
497+ # Disable the default Git status formatting.
498+ typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true
499+ # Install our own Git status formatter.
500+ typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter()))+${my_git_format}}'
501+ # Enable counters for staged, unstaged, etc.
502+ typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1
503+
504+ # Custom icon.
505+ # typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION='⭐'
506+ # Custom prefix.
507+ # typeset -g POWERLEVEL9K_VCS_PREFIX='on '
508+
509+ # Show status of repositories of these types. You can add svn and/or hg if you are
510+ # using them. If you do, your prompt may become slow even when your current directory
511+ # isn't in an svn or hg repository.
512+ typeset -g POWERLEVEL9K_VCS_BACKENDS=(git)
513+
514+ ##########################[ status: exit code of the last command ]###########################
515+ # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and
516+ # style them independently from the regular OK and ERROR state.
517+ typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true
518+
519+ # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as
520+ # it will signify success by turning green.
521+ typeset -g POWERLEVEL9K_STATUS_OK=true
522+ typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔'
523+ typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=2
524+ typeset -g POWERLEVEL9K_STATUS_OK_BACKGROUND=0
525+
526+ # Status when some part of a pipe command fails but the overall exit status is zero. It may look
527+ # like this: 1|0.
528+ typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true
529+ typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔'
530+ typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=2
531+ typeset -g POWERLEVEL9K_STATUS_OK_PIPE_BACKGROUND=0
532+
533+ # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as
534+ # it will signify error by turning red.
535+ typeset -g POWERLEVEL9K_STATUS_ERROR=true
536+ typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘'
537+ typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=3
538+ typeset -g POWERLEVEL9K_STATUS_ERROR_BACKGROUND=1
539+
540+ # Status when the last command was terminated by a signal.
541+ typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true
542+ # Use terse signal names: "INT" instead of "SIGINT(2)".
543+ typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false
544+ typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘'
545+ typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=3
546+ typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_BACKGROUND=1
547+
548+ # Status when some part of a pipe command fails and the overall exit status is also non-zero.
549+ # It may look like this: 1|0.
550+ typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true
551+ typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘'
552+ typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=3
553+ typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_BACKGROUND=1
554+
555+ ###################[ command_execution_time: duration of the last command ]###################
556+ # Execution time color.
557+ typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=0
558+ typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_BACKGROUND=3
559+ # Show duration of the last command if takes at least this many seconds.
560+ typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3
561+ # Show this many fractional digits. Zero means round to seconds.
562+ typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
563+ # Duration format: 1d 2h 3m 4s.
564+ typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
565+ # Custom icon.
566+ # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION='⭐'
567+ # Custom prefix.
568+ # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='took '
569+
570+ #######################[ background_jobs: presence of background jobs ]#######################
571+ # Background jobs color.
572+ typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=6
573+ typeset -g POWERLEVEL9K_BACKGROUND_JOBS_BACKGROUND=0
574+ # Don't show the number of background jobs.
575+ typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false
576+ # Custom icon.
577+ # typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='⭐'
578+
579+ #######################[ direnv: direnv status (https://direnv.net/) ]########################
580+ # Direnv color.
581+ typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=3
582+ typeset -g POWERLEVEL9K_DIRENV_BACKGROUND=0
583+ # Custom icon.
584+ # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
585+
586+ ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]###############
587+ # Default asdf color. Only used to display tools for which there is no color override (see below).
588+ # Tip: Override these parameters for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND and
589+ # POWERLEVEL9K_ASDF_${TOOL}_BACKGROUND.
590+ typeset -g POWERLEVEL9K_ASDF_FOREGROUND=0
591+ typeset -g POWERLEVEL9K_ASDF_BACKGROUND=7
592+
593+ # There are four parameters that can be used to hide asdf tools. Each parameter describes
594+ # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at
595+ # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to
596+ # hide a tool, it gets shown.
597+ #
598+ # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and
599+ # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands:
600+ #
601+ # asdf local python 3.8.1
602+ # asdf global python 3.8.1
603+ #
604+ # After running both commands the current python version is 3.8.1 and its source is "local" as
605+ # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false,
606+ # it'll hide python version in this case because 3.8.1 is the same as the global version.
607+ # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't
608+ # contain "local".
609+
610+ # Hide tool versions that don't come from one of these sources.
611+ #
612+ # Available sources:
613+ #
614+ # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable"
615+ # - local `asdf current` says "set by /some/not/home/directory/file"
616+ # - global `asdf current` says "set by /home/username/file"
617+ #
618+ # Note: If this parameter is set to (shell local global), it won't hide tools.
619+ # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES.
620+ typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global)
621+
622+ # If set to false, hide tool versions that are the same as global.
623+ #
624+ # Note: The name of this parameter doesn't reflect its meaning at all.
625+ # Note: If this parameter is set to true, it won't hide tools.
626+ # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW.
627+ typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false
628+
629+ # If set to false, hide tool versions that are equal to "system".
630+ #
631+ # Note: If this parameter is set to true, it won't hide tools.
632+ # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM.
633+ typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true
634+
635+ # If set to non-empty value, hide tools unless there is a file matching the specified file pattern
636+ # in the current directory, or its parent directory, or its grandparent directory, and so on.
637+ #
638+ # Note: If this parameter is set to empty value, it won't hide tools.
639+ # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments.
640+ # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB.
641+ #
642+ # Example: Hide nodejs version when there is no package.json and no *.js files in the current
643+ # directory, in `..`, in `../..` and so on.
644+ #
645+ # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json'
646+ typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB=
647+
648+ # Ruby version from asdf.
649+ typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=0
650+ typeset -g POWERLEVEL9K_ASDF_RUBY_BACKGROUND=1
651+ # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐'
652+ # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar'
653+
654+ # Python version from asdf.
655+ typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=0
656+ typeset -g POWERLEVEL9K_ASDF_PYTHON_BACKGROUND=4
657+ # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐'
658+ # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar'
659+
660+ # Go version from asdf.
661+ typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=0
662+ typeset -g POWERLEVEL9K_ASDF_GOLANG_BACKGROUND=4
663+ # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐'
664+ # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar'
665+
666+ # Node.js version from asdf.
667+ typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=0
668+ typeset -g POWERLEVEL9K_ASDF_NODEJS_BACKGROUND=2
669+ # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐'
670+ # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar'
671+
672+ # Rust version from asdf.
673+ typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=0
674+ typeset -g POWERLEVEL9K_ASDF_RUST_BACKGROUND=208
675+ # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐'
676+ # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar'
677+
678+ # .NET Core version from asdf.
679+ typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=0
680+ typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_BACKGROUND=5
681+ # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐'
682+ # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_SHOW_ON_UPGLOB='*.foo|*.bar'
683+
684+ # Flutter version from asdf.
685+ typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=0
686+ typeset -g POWERLEVEL9K_ASDF_FLUTTER_BACKGROUND=4
687+ # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐'
688+ # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar'
689+
690+ # Lua version from asdf.
691+ typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=0
692+ typeset -g POWERLEVEL9K_ASDF_LUA_BACKGROUND=4
693+ # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐'
694+ # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar'
695+
696+ # Java version from asdf.
697+ typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=1
698+ typeset -g POWERLEVEL9K_ASDF_JAVA_BACKGROUND=7
699+ # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐'
700+ # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar'
701+
702+ # Perl version from asdf.
703+ typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=0
704+ typeset -g POWERLEVEL9K_ASDF_PERL_BACKGROUND=4
705+ # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐'
706+ # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar'
707+
708+ # Erlang version from asdf.
709+ typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=0
710+ typeset -g POWERLEVEL9K_ASDF_ERLANG_BACKGROUND=1
711+ # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐'
712+ # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar'
713+
714+ # Elixir version from asdf.
715+ typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=0
716+ typeset -g POWERLEVEL9K_ASDF_ELIXIR_BACKGROUND=5
717+ # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐'
718+ # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar'
719+
720+ # Postgres version from asdf.
721+ typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=0
722+ typeset -g POWERLEVEL9K_ASDF_POSTGRES_BACKGROUND=6
723+ # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐'
724+ # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar'
725+
726+ # PHP version from asdf.
727+ typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=0
728+ typeset -g POWERLEVEL9K_ASDF_PHP_BACKGROUND=5
729+ # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐'
730+ # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar'
731+
732+ # Haskell version from asdf.
733+ typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=0
734+ typeset -g POWERLEVEL9K_ASDF_HASKELL_BACKGROUND=3
735+ # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
736+ # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar'
737+
738+ # Julia version from asdf.
739+ typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=0
740+ typeset -g POWERLEVEL9K_ASDF_JULIA_BACKGROUND=2
741+ # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐'
742+ # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar'
743+
744+ ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]###########
745+ # NordVPN connection indicator color.
746+ typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=7
747+ typeset -g POWERLEVEL9K_NORDVPN_BACKGROUND=4
748+ # Hide NordVPN connection indicator when not connected.
749+ typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION=
750+ typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION=
751+ # Custom icon.
752+ # typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='⭐'
753+
754+ #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]##################
755+ # Ranger shell color.
756+ typeset -g POWERLEVEL9K_RANGER_FOREGROUND=3
757+ typeset -g POWERLEVEL9K_RANGER_BACKGROUND=0
758+ # Custom icon.
759+ # typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='⭐'
760+
761+ ####################[ yazi: yazi shell (https://github.com/sxyazi/yazi) ]#####################
762+ # Yazi shell color.
763+ typeset -g POWERLEVEL9K_YAZI_FOREGROUND=3
764+ typeset -g POWERLEVEL9K_YAZI_BACKGROUND=0
765+ # Custom icon.
766+ # typeset -g POWERLEVEL9K_YAZI_VISUAL_IDENTIFIER_EXPANSION='⭐'
767+
768+ ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]#######################
769+ # Nnn shell color.
770+ typeset -g POWERLEVEL9K_NNN_FOREGROUND=0
771+ typeset -g POWERLEVEL9K_NNN_BACKGROUND=6
772+ # Custom icon.
773+ # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐'
774+
775+ ######################[ lf: lf shell (https://github.com/gokcehan/lf) ]#######################
776+ # lf shell color.
777+ typeset -g POWERLEVEL9K_LF_FOREGROUND=0
778+ typeset -g POWERLEVEL9K_LF_BACKGROUND=6
779+ # Custom icon.
780+ # typeset -g POWERLEVEL9K_LF_VISUAL_IDENTIFIER_EXPANSION='⭐'
781+
782+ ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]##################
783+ # xplr shell color.
784+ typeset -g POWERLEVEL9K_XPLR_FOREGROUND=0
785+ typeset -g POWERLEVEL9K_XPLR_BACKGROUND=6
786+ # Custom icon.
787+ # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐'
788+
789+ ###########################[ vim_shell: vim shell indicator (:sh) ]###########################
790+ # Vim shell indicator color.
791+ typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=0
792+ typeset -g POWERLEVEL9K_VIM_SHELL_BACKGROUND=2
793+ # Custom icon.
794+ # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
795+
796+ ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]######
797+ # Midnight Commander shell color.
798+ typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=3
799+ typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_BACKGROUND=0
800+ # Custom icon.
801+ # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐'
802+
803+ #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]##
804+ # Nix shell color.
805+ typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=0
806+ typeset -g POWERLEVEL9K_NIX_SHELL_BACKGROUND=4
807+
808+ # Display the icon of nix_shell if PATH contains a subdirectory of /nix/store.
809+ # typeset -g POWERLEVEL9K_NIX_SHELL_INFER_FROM_PATH=false
810+
811+ # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line.
812+ # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION=
813+
814+ # Custom icon.
815+ # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
816+
817+ ##################[ chezmoi_shell: chezmoi shell (https://www.chezmoi.io/) ]##################
818+ # chezmoi shell color.
819+ typeset -g POWERLEVEL9K_CHEZMOI_SHELL_FOREGROUND=0
820+ typeset -g POWERLEVEL9K_CHEZMOI_SHELL_BACKGROUND=4
821+ # Custom icon.
822+ # typeset -g POWERLEVEL9K_CHEZMOI_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐'
823+
824+ ##################################[ disk_usage: disk usage ]##################################
825+ # Colors for different levels of disk usage.
826+ typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=3
827+ typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_BACKGROUND=0
828+ typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=0
829+ typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_BACKGROUND=3
830+ typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=7
831+ typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_BACKGROUND=1
832+ # Thresholds for different levels of disk usage (percentage points).
833+ typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90
834+ typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95
835+ # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent.
836+ typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false
837+ # Custom icon.
838+ # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐'
839+
840+ ###########[ vi_mode: vi mode (you don't need this if you've enabled prompt_char) ]###########
841+ # Foreground color.
842+ typeset -g POWERLEVEL9K_VI_MODE_FOREGROUND=0
843+ # Text and color for normal (a.k.a. command) vi mode.
844+ typeset -g POWERLEVEL9K_VI_COMMAND_MODE_STRING=NORMAL
845+ typeset -g POWERLEVEL9K_VI_MODE_NORMAL_BACKGROUND=2
846+ # Text and color for visual vi mode.
847+ typeset -g POWERLEVEL9K_VI_VISUAL_MODE_STRING=VISUAL
848+ typeset -g POWERLEVEL9K_VI_MODE_VISUAL_BACKGROUND=4
849+ # Text and color for overtype (a.k.a. overwrite and replace) vi mode.
850+ typeset -g POWERLEVEL9K_VI_OVERWRITE_MODE_STRING=OVERTYPE
851+ typeset -g POWERLEVEL9K_VI_MODE_OVERWRITE_BACKGROUND=3
852+ # Text and color for insert vi mode.
853+ typeset -g POWERLEVEL9K_VI_INSERT_MODE_STRING=
854+ typeset -g POWERLEVEL9K_VI_MODE_INSERT_FOREGROUND=8
855+ # Custom icon.
856+ # typeset -g POWERLEVEL9K_VI_MODE_VISUAL_IDENTIFIER_EXPANSION='⭐'
857+
858+ ######################################[ ram: free RAM ]#######################################
859+ # RAM color.
860+ typeset -g POWERLEVEL9K_RAM_FOREGROUND=0
861+ typeset -g POWERLEVEL9K_RAM_BACKGROUND=3
862+ # Custom icon.
863+ # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐'
864+
865+ #####################################[ swap: used swap ]######################################
866+ # Swap color.
867+ typeset -g POWERLEVEL9K_SWAP_FOREGROUND=0
868+ typeset -g POWERLEVEL9K_SWAP_BACKGROUND=3
869+ # Custom icon.
870+ # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐'
871+
872+ ######################################[ load: CPU load ]######################################
873+ # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15.
874+ typeset -g POWERLEVEL9K_LOAD_WHICH=5
875+ # Load color when load is under 50%.
876+ typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=0
877+ typeset -g POWERLEVEL9K_LOAD_NORMAL_BACKGROUND=2
878+ # Load color when load is between 50% and 70%.
879+ typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=0
880+ typeset -g POWERLEVEL9K_LOAD_WARNING_BACKGROUND=3
881+ # Load color when load is over 70%.
882+ typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=0
883+ typeset -g POWERLEVEL9K_LOAD_CRITICAL_BACKGROUND=1
884+ # Custom icon.
885+ # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐'
886+
887+ ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################
888+ # Todo color.
889+ typeset -g POWERLEVEL9K_TODO_FOREGROUND=0
890+ typeset -g POWERLEVEL9K_TODO_BACKGROUND=8
891+ # Hide todo when the total number of tasks is zero.
892+ typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true
893+ # Hide todo when the number of tasks after filtering is zero.
894+ typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false
895+
896+ # Todo format. The following parameters are available within the expansion.
897+ #
898+ # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks.
899+ # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering.
900+ #
901+ # These variables correspond to the last line of the output of `todo.sh -p ls`:
902+ #
903+ # TODO: 24 of 42 tasks shown
904+ #
905+ # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT.
906+ #
907+ # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT'
908+
909+ # Custom icon.
910+ # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐'
911+
912+ ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############
913+ # Timewarrior color.
914+ typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=255
915+ typeset -g POWERLEVEL9K_TIMEWARRIOR_BACKGROUND=8
916+
917+ # If the tracked task is longer than 24 characters, truncate and append "…".
918+ # Tip: To always display tasks without truncation, delete the following parameter.
919+ # Tip: To hide task names and display just the icon when time tracking is enabled, set the
920+ # value of the following parameter to "".
921+ typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}'
922+
923+ # Custom icon.
924+ # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐'
925+
926+ ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]##############
927+ # Taskwarrior color.
928+ typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=0
929+ typeset -g POWERLEVEL9K_TASKWARRIOR_BACKGROUND=6
930+
931+ # Taskwarrior segment format. The following parameters are available within the expansion.
932+ #
933+ # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`.
934+ # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`.
935+ #
936+ # Zero values are represented as empty parameters.
937+ #
938+ # The default format:
939+ #
940+ # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT'
941+ #
942+ # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT'
943+
944+ # Custom icon.
945+ # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐'
946+
947+ ######[ per_directory_history: Oh My Zsh per-directory-history local/global indicator ]#######
948+ # Color when using local/global history.
949+ typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_FOREGROUND=0
950+ typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_BACKGROUND=5
951+ typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_FOREGROUND=0
952+ typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_BACKGROUND=3
953+
954+ # Tip: Uncomment the next two lines to hide "local"/"global" text and leave just the icon.
955+ # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_CONTENT_EXPANSION=''
956+ # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_CONTENT_EXPANSION=''
957+
958+ # Custom icon.
959+ # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_VISUAL_IDENTIFIER_EXPANSION='⭐'
960+ # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_VISUAL_IDENTIFIER_EXPANSION='⭐'
961+
962+ ################################[ cpu_arch: CPU architecture ]################################
963+ # CPU architecture color.
964+ typeset -g POWERLEVEL9K_CPU_ARCH_FOREGROUND=0
965+ typeset -g POWERLEVEL9K_CPU_ARCH_BACKGROUND=3
966+
967+ # Hide the segment when on a specific CPU architecture.
968+ # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_CONTENT_EXPANSION=
969+ # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_VISUAL_IDENTIFIER_EXPANSION=
970+
971+ # Custom icon.
972+ # typeset -g POWERLEVEL9K_CPU_ARCH_VISUAL_IDENTIFIER_EXPANSION='⭐'
973+
974+ ##################################[ context: user@hostname ]##################################
975+ # Context color when running with privileges.
976+ typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=1
977+ typeset -g POWERLEVEL9K_CONTEXT_ROOT_BACKGROUND=0
978+ # Context color in SSH without privileges.
979+ typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=3
980+ typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_BACKGROUND=0
981+ # Default context color (no privileges, no SSH).
982+ typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=3
983+ typeset -g POWERLEVEL9K_CONTEXT_BACKGROUND=0
984+
985+ # Context format when running with privileges: user@hostname.
986+ typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%n@%m'
987+ # Context format when in SSH without privileges: user@hostname.
988+ typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m'
989+ # Default context format (no privileges, no SSH): user@hostname.
990+ typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m'
991+
992+ # Don't show context unless running with privileges or in SSH.
993+ # Tip: Remove the next line to always show context.
994+ typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION=
995+
996+ # Custom icon.
997+ # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐'
998+ # Custom prefix.
999+ # typeset -g POWERLEVEL9K_CONTEXT_PREFIX='with '
1000+
1001+ ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]###
1002+ # Python virtual environment color.
1003+ typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=0
1004+ typeset -g POWERLEVEL9K_VIRTUALENV_BACKGROUND=4
1005+ # Don't show Python version next to the virtual environment name.
1006+ typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
1007+ # If set to "false", won't show virtualenv if pyenv is already shown.
1008+ # If set to "if-different", won't show virtualenv if it's the same as pyenv.
1009+ typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false
1010+ # Separate environment name from Python version only with a space.
1011+ typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
1012+ # Custom icon.
1013+ # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1014+
1015+ #####################[ anaconda: conda environment (https://conda.io/) ]######################
1016+ # Anaconda environment color.
1017+ typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=0
1018+ typeset -g POWERLEVEL9K_ANACONDA_BACKGROUND=4
1019+
1020+ # Anaconda segment format. The following parameters are available within the expansion.
1021+ #
1022+ # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment.
1023+ # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment.
1024+ # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below).
1025+ # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version).
1026+ #
1027+ # CONDA_PROMPT_MODIFIER can be configured with the following command:
1028+ #
1029+ # conda config --set env_prompt '({default_env}) '
1030+ #
1031+ # The last argument is a Python format string that can use the following variables:
1032+ #
1033+ # - prefix The same as CONDA_PREFIX.
1034+ # - default_env The same as CONDA_DEFAULT_ENV.
1035+ # - name The last segment of CONDA_PREFIX.
1036+ # - stacked_env Comma-separated list of names in the environment stack. The first element is
1037+ # always the same as default_env.
1038+ #
1039+ # Note: '({default_env}) ' is the default value of env_prompt.
1040+ #
1041+ # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER
1042+ # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former
1043+ # is empty.
1044+ typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}'
1045+
1046+ # Custom icon.
1047+ # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐'
1048+
1049+ ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################
1050+ # Pyenv color.
1051+ typeset -g POWERLEVEL9K_PYENV_FOREGROUND=0
1052+ typeset -g POWERLEVEL9K_PYENV_BACKGROUND=4
1053+ # Hide python version if it doesn't come from one of these sources.
1054+ typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global)
1055+ # If set to false, hide python version if it's the same as global:
1056+ # $(pyenv version-name) == $(pyenv global).
1057+ typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false
1058+ # If set to false, hide python version if it's equal to "system".
1059+ typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true
1060+
1061+ # Pyenv segment format. The following parameters are available within the expansion.
1062+ #
1063+ # - P9K_CONTENT Current pyenv environment (pyenv version-name).
1064+ # - P9K_PYENV_PYTHON_VERSION Current python version (python --version).
1065+ #
1066+ # The default format has the following logic:
1067+ #
1068+ # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or
1069+ # starts with "$P9K_PYENV_PYTHON_VERSION/".
1070+ # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION".
1071+ typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}'
1072+
1073+ # Custom icon.
1074+ # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1075+
1076+ ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################
1077+ # Goenv color.
1078+ typeset -g POWERLEVEL9K_GOENV_FOREGROUND=0
1079+ typeset -g POWERLEVEL9K_GOENV_BACKGROUND=4
1080+ # Hide go version if it doesn't come from one of these sources.
1081+ typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global)
1082+ # If set to false, hide go version if it's the same as global:
1083+ # $(goenv version-name) == $(goenv global).
1084+ typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false
1085+ # If set to false, hide go version if it's equal to "system".
1086+ typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true
1087+ # Custom icon.
1088+ # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1089+
1090+ ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]##########
1091+ # Nodenv color.
1092+ typeset -g POWERLEVEL9K_NODENV_FOREGROUND=2
1093+ typeset -g POWERLEVEL9K_NODENV_BACKGROUND=0
1094+ # Hide node version if it doesn't come from one of these sources.
1095+ typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global)
1096+ # If set to false, hide node version if it's the same as global:
1097+ # $(nodenv version-name) == $(nodenv global).
1098+ typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false
1099+ # If set to false, hide node version if it's equal to "system".
1100+ typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true
1101+ # Custom icon.
1102+ # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1103+
1104+ ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]###############
1105+ # Nvm color.
1106+ typeset -g POWERLEVEL9K_NVM_FOREGROUND=0
1107+ typeset -g POWERLEVEL9K_NVM_BACKGROUND=5
1108+ # If set to false, hide node version if it's the same as default:
1109+ # $(nvm version current) == $(nvm version default).
1110+ typeset -g POWERLEVEL9K_NVM_PROMPT_ALWAYS_SHOW=false
1111+ # If set to false, hide node version if it's equal to "system".
1112+ typeset -g POWERLEVEL9K_NVM_SHOW_SYSTEM=true
1113+ # Custom icon.
1114+ # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐'
1115+
1116+ ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############
1117+ # Nodeenv color.
1118+ typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=2
1119+ typeset -g POWERLEVEL9K_NODEENV_BACKGROUND=0
1120+ # Don't show Node version next to the environment name.
1121+ typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false
1122+ # Separate environment name from Node version only with a space.
1123+ typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER=
1124+ # Custom icon.
1125+ # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1126+
1127+ ##############################[ node_version: node.js version ]###############################
1128+ # Node version color.
1129+ typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=7
1130+ typeset -g POWERLEVEL9K_NODE_VERSION_BACKGROUND=2
1131+ # Show node version only when in a directory tree containing package.json.
1132+ typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true
1133+ # Custom icon.
1134+ # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1135+
1136+ #######################[ go_version: go version (https://golang.org) ]########################
1137+ # Go version color.
1138+ typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=255
1139+ typeset -g POWERLEVEL9K_GO_VERSION_BACKGROUND=2
1140+ # Show go version only when in a go project subdirectory.
1141+ typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true
1142+ # Custom icon.
1143+ # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1144+
1145+ #################[ rust_version: rustc version (https://www.rust-lang.org) ]##################
1146+ # Rust version color.
1147+ typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=0
1148+ typeset -g POWERLEVEL9K_RUST_VERSION_BACKGROUND=208
1149+ # Show rust version only when in a rust project subdirectory.
1150+ typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true
1151+ # Custom icon.
1152+ # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1153+
1154+ ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################
1155+ # .NET version color.
1156+ typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=7
1157+ typeset -g POWERLEVEL9K_DOTNET_VERSION_BACKGROUND=5
1158+ # Show .NET version only when in a .NET project subdirectory.
1159+ typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true
1160+ # Custom icon.
1161+ # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1162+
1163+ #####################[ php_version: php version (https://www.php.net/) ]######################
1164+ # PHP version color.
1165+ typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=0
1166+ typeset -g POWERLEVEL9K_PHP_VERSION_BACKGROUND=5
1167+ # Show PHP version only when in a PHP project subdirectory.
1168+ typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true
1169+ # Custom icon.
1170+ # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1171+
1172+ ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]###########
1173+ # Laravel version color.
1174+ typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=1
1175+ typeset -g POWERLEVEL9K_LARAVEL_VERSION_BACKGROUND=7
1176+ # Custom icon.
1177+ # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1178+
1179+ #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]##############
1180+ # Rbenv color.
1181+ typeset -g POWERLEVEL9K_RBENV_FOREGROUND=0
1182+ typeset -g POWERLEVEL9K_RBENV_BACKGROUND=1
1183+ # Hide ruby version if it doesn't come from one of these sources.
1184+ typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global)
1185+ # If set to false, hide ruby version if it's the same as global:
1186+ # $(rbenv version-name) == $(rbenv global).
1187+ typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false
1188+ # If set to false, hide ruby version if it's equal to "system".
1189+ typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true
1190+ # Custom icon.
1191+ # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1192+
1193+ ####################[ java_version: java version (https://www.java.com/) ]####################
1194+ # Java version color.
1195+ typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=1
1196+ typeset -g POWERLEVEL9K_JAVA_VERSION_BACKGROUND=7
1197+ # Show java version only when in a java project subdirectory.
1198+ typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true
1199+ # Show brief version.
1200+ typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false
1201+ # Custom icon.
1202+ # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1203+
1204+ ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]####
1205+ # Package color.
1206+ typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=0
1207+ typeset -g POWERLEVEL9K_PACKAGE_BACKGROUND=6
1208+
1209+ # Package format. The following parameters are available within the expansion.
1210+ #
1211+ # - P9K_PACKAGE_NAME The value of `name` field in package.json.
1212+ # - P9K_PACKAGE_VERSION The value of `version` field in package.json.
1213+ #
1214+ # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}'
1215+
1216+ # Custom icon.
1217+ # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐'
1218+
1219+ #######################[ rvm: ruby version from rvm (https://rvm.io) ]########################
1220+ # Rvm color.
1221+ typeset -g POWERLEVEL9K_RVM_FOREGROUND=0
1222+ typeset -g POWERLEVEL9K_RVM_BACKGROUND=240
1223+ # Don't show @gemset at the end.
1224+ typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false
1225+ # Don't show ruby- at the front.
1226+ typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false
1227+ # Custom icon.
1228+ # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐'
1229+
1230+ ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############
1231+ # Fvm color.
1232+ typeset -g POWERLEVEL9K_FVM_FOREGROUND=0
1233+ typeset -g POWERLEVEL9K_FVM_BACKGROUND=4
1234+ # Custom icon.
1235+ # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐'
1236+
1237+ ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]###########
1238+ # Lua color.
1239+ typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=0
1240+ typeset -g POWERLEVEL9K_LUAENV_BACKGROUND=4
1241+ # Hide lua version if it doesn't come from one of these sources.
1242+ typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global)
1243+ # If set to false, hide lua version if it's the same as global:
1244+ # $(luaenv version-name) == $(luaenv global).
1245+ typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false
1246+ # If set to false, hide lua version if it's equal to "system".
1247+ typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true
1248+ # Custom icon.
1249+ # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1250+
1251+ ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################
1252+ # Java color.
1253+ typeset -g POWERLEVEL9K_JENV_FOREGROUND=1
1254+ typeset -g POWERLEVEL9K_JENV_BACKGROUND=7
1255+ # Hide java version if it doesn't come from one of these sources.
1256+ typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global)
1257+ # If set to false, hide java version if it's the same as global:
1258+ # $(jenv version-name) == $(jenv global).
1259+ typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false
1260+ # If set to false, hide java version if it's equal to "system".
1261+ typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true
1262+ # Custom icon.
1263+ # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1264+
1265+ ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############
1266+ # Perl color.
1267+ typeset -g POWERLEVEL9K_PLENV_FOREGROUND=0
1268+ typeset -g POWERLEVEL9K_PLENV_BACKGROUND=4
1269+ # Hide perl version if it doesn't come from one of these sources.
1270+ typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global)
1271+ # If set to false, hide perl version if it's the same as global:
1272+ # $(plenv version-name) == $(plenv global).
1273+ typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false
1274+ # If set to false, hide perl version if it's equal to "system".
1275+ typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true
1276+ # Custom icon.
1277+ # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1278+
1279+ ###########[ perlbrew: perl version from perlbrew (https://github.com/gugod/App-perlbrew) ]############
1280+ # Perlbrew color.
1281+ typeset -g POWERLEVEL9K_PERLBREW_FOREGROUND=67
1282+ # Show perlbrew version only when in a perl project subdirectory.
1283+ typeset -g POWERLEVEL9K_PERLBREW_PROJECT_ONLY=true
1284+ # Don't show "perl-" at the front.
1285+ typeset -g POWERLEVEL9K_PERLBREW_SHOW_PREFIX=false
1286+ # Custom icon.
1287+ # typeset -g POWERLEVEL9K_PERLBREW_VISUAL_IDENTIFIER_EXPANSION='⭐'
1288+
1289+ ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############
1290+ # PHP color.
1291+ typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=0
1292+ typeset -g POWERLEVEL9K_PHPENV_BACKGROUND=5
1293+ # Hide php version if it doesn't come from one of these sources.
1294+ typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global)
1295+ # If set to false, hide php version if it's the same as global:
1296+ # $(phpenv version-name) == $(phpenv global).
1297+ typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false
1298+ # If set to false, hide PHP version if it's equal to "system".
1299+ typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true
1300+ # Custom icon.
1301+ # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1302+
1303+ #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]#######
1304+ # Scala color.
1305+ typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=0
1306+ typeset -g POWERLEVEL9K_SCALAENV_BACKGROUND=1
1307+ # Hide scala version if it doesn't come from one of these sources.
1308+ typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global)
1309+ # If set to false, hide scala version if it's the same as global:
1310+ # $(scalaenv version-name) == $(scalaenv global).
1311+ typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false
1312+ # If set to false, hide scala version if it's equal to "system".
1313+ typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true
1314+ # Custom icon.
1315+ # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1316+
1317+ ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]###########
1318+ # Haskell color.
1319+ typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=0
1320+ typeset -g POWERLEVEL9K_HASKELL_STACK_BACKGROUND=3
1321+
1322+ # Hide haskell version if it doesn't come from one of these sources.
1323+ #
1324+ # shell: version is set by STACK_YAML
1325+ # local: version is set by stack.yaml up the directory tree
1326+ # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml)
1327+ typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local)
1328+ # If set to false, hide haskell version if it's the same as in the implicit global project.
1329+ typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true
1330+ # Custom icon.
1331+ # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐'
1332+
1333+ ################[ terraform: terraform workspace (https://www.terraform.io) ]#################
1334+ # Don't show terraform workspace if it's literally "default".
1335+ typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false
1336+ # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element
1337+ # in each pair defines a pattern against which the current terraform workspace gets matched.
1338+ # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1339+ # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters,
1340+ # you'll see this value in your prompt. The second element of each pair in
1341+ # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The
1342+ # first match wins.
1343+ #
1344+ # For example, given these settings:
1345+ #
1346+ # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=(
1347+ # '*prod*' PROD
1348+ # '*test*' TEST
1349+ # '*' OTHER)
1350+ #
1351+ # If your current terraform workspace is "project_test", its class is TEST because "project_test"
1352+ # doesn't match the pattern '*prod*' but does match '*test*'.
1353+ #
1354+ # You can define different colors, icons and content expansions for different classes:
1355+ #
1356+ # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=2
1357+ # typeset -g POWERLEVEL9K_TERRAFORM_TEST_BACKGROUND=0
1358+ # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1359+ # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1360+ typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=(
1361+ # '*prod*' PROD # These values are examples that are unlikely
1362+ # '*test*' TEST # to match your needs. Customize them as needed.
1363+ '*' OTHER)
1364+ typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=4
1365+ typeset -g POWERLEVEL9K_TERRAFORM_OTHER_BACKGROUND=0
1366+ # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐'
1367+
1368+ #############[ terraform_version: terraform version (https://www.terraform.io) ]##############
1369+ # Terraform version color.
1370+ typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=4
1371+ typeset -g POWERLEVEL9K_TERRAFORM_VERSION_BACKGROUND=0
1372+ # Custom icon.
1373+ # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐'
1374+
1375+ ################[ terraform_version: It shows active terraform version (https://www.terraform.io) ]#################
1376+ typeset -g POWERLEVEL9K_TERRAFORM_VERSION_SHOW_ON_COMMAND='terraform|tf'
1377+
1378+ #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]#############
1379+ # Show kubecontext only when the command you are typing invokes one of these tools.
1380+ # Tip: Remove the next line to always show kubecontext.
1381+ typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern|kubeseal|skaffold|kubent|kubecolor|cmctl|sparkctl'
1382+
1383+ # Kubernetes context classes for the purpose of using different colors, icons and expansions with
1384+ # different contexts.
1385+ #
1386+ # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element
1387+ # in each pair defines a pattern against which the current kubernetes context gets matched.
1388+ # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1389+ # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters,
1390+ # you'll see this value in your prompt. The second element of each pair in
1391+ # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The
1392+ # first match wins.
1393+ #
1394+ # For example, given these settings:
1395+ #
1396+ # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=(
1397+ # '*prod*' PROD
1398+ # '*test*' TEST
1399+ # '*' DEFAULT)
1400+ #
1401+ # If your current kubernetes context is "deathray-testing/default", its class is TEST
1402+ # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'.
1403+ #
1404+ # You can define different colors, icons and content expansions for different classes:
1405+ #
1406+ # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=0
1407+ # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_BACKGROUND=2
1408+ # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1409+ # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1410+ typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=(
1411+ # '*prod*' PROD # These values are examples that are unlikely
1412+ # '*test*' TEST # to match your needs. Customize them as needed.
1413+ '*' DEFAULT)
1414+ typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=7
1415+ typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_BACKGROUND=5
1416+ # typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐'
1417+
1418+ # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext
1419+ # segment. Parameter expansions are very flexible and fast, too. See reference:
1420+ # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
1421+ #
1422+ # Within the expansion the following parameters are always available:
1423+ #
1424+ # - P9K_CONTENT The content that would've been displayed if there was no content
1425+ # expansion defined.
1426+ # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the
1427+ # output of `kubectl config get-contexts`.
1428+ # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the
1429+ # output of `kubectl config get-contexts`.
1430+ # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE
1431+ # in the output of `kubectl config get-contexts`. If there is no
1432+ # namespace, the parameter is set to "default".
1433+ # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the
1434+ # output of `kubectl config get-contexts`.
1435+ #
1436+ # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS),
1437+ # the following extra parameters are available:
1438+ #
1439+ # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks".
1440+ # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID.
1441+ # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone.
1442+ # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster.
1443+ #
1444+ # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example,
1445+ # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01":
1446+ #
1447+ # - P9K_KUBECONTEXT_CLOUD_NAME=gke
1448+ # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account
1449+ # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a
1450+ # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01
1451+ #
1452+ # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01":
1453+ #
1454+ # - P9K_KUBECONTEXT_CLOUD_NAME=eks
1455+ # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012
1456+ # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1
1457+ # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01
1458+ typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION=
1459+ # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME.
1460+ POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}'
1461+ # Append the current context's namespace if it's not "default".
1462+ POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}'
1463+
1464+ # Custom prefix.
1465+ # typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='at '
1466+
1467+ #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]#
1468+ # Show aws only when the command you are typing invokes one of these tools.
1469+ # Tip: Remove the next line to always show aws.
1470+ typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|cdk|terraform|pulumi|terragrunt'
1471+
1472+ # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element
1473+ # in each pair defines a pattern against which the current AWS profile gets matched.
1474+ # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1475+ # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters,
1476+ # you'll see this value in your prompt. The second element of each pair in
1477+ # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The
1478+ # first match wins.
1479+ #
1480+ # For example, given these settings:
1481+ #
1482+ # typeset -g POWERLEVEL9K_AWS_CLASSES=(
1483+ # '*prod*' PROD
1484+ # '*test*' TEST
1485+ # '*' DEFAULT)
1486+ #
1487+ # If your current AWS profile is "company_test", its class is TEST
1488+ # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'.
1489+ #
1490+ # You can define different colors, icons and content expansions for different classes:
1491+ #
1492+ # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28
1493+ # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1494+ # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1495+ typeset -g POWERLEVEL9K_AWS_CLASSES=(
1496+ # '*prod*' PROD # These values are examples that are unlikely
1497+ # '*test*' TEST # to match your needs. Customize them as needed.
1498+ '*' DEFAULT)
1499+ typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=7
1500+ typeset -g POWERLEVEL9K_AWS_DEFAULT_BACKGROUND=1
1501+ # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐'
1502+
1503+ # AWS segment format. The following parameters are available within the expansion.
1504+ #
1505+ # - P9K_AWS_PROFILE The name of the current AWS profile.
1506+ # - P9K_AWS_REGION The region associated with the current AWS profile.
1507+ typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}'
1508+
1509+ #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]#
1510+ # AWS Elastic Beanstalk environment color.
1511+ typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=2
1512+ typeset -g POWERLEVEL9K_AWS_EB_ENV_BACKGROUND=0
1513+ # Custom icon.
1514+ # typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='⭐'
1515+
1516+ ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]##########
1517+ # Show azure only when the command you are typing invokes one of these tools.
1518+ # Tip: Remove the next line to always show azure.
1519+ typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt'
1520+
1521+ # POWERLEVEL9K_AZURE_CLASSES is an array with even number of elements. The first element
1522+ # in each pair defines a pattern against which the current azure account name gets matched.
1523+ # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below)
1524+ # that gets matched. If you unset all POWERLEVEL9K_AZURE_*CONTENT_EXPANSION parameters,
1525+ # you'll see this value in your prompt. The second element of each pair in
1526+ # POWERLEVEL9K_AZURE_CLASSES defines the account class. Patterns are tried in order. The
1527+ # first match wins.
1528+ #
1529+ # For example, given these settings:
1530+ #
1531+ # typeset -g POWERLEVEL9K_AZURE_CLASSES=(
1532+ # '*prod*' PROD
1533+ # '*test*' TEST
1534+ # '*' OTHER)
1535+ #
1536+ # If your current azure account is "company_test", its class is TEST because "company_test"
1537+ # doesn't match the pattern '*prod*' but does match '*test*'.
1538+ #
1539+ # You can define different colors, icons and content expansions for different classes:
1540+ #
1541+ # typeset -g POWERLEVEL9K_AZURE_TEST_FOREGROUND=2
1542+ # typeset -g POWERLEVEL9K_AZURE_TEST_BACKGROUND=0
1543+ # typeset -g POWERLEVEL9K_AZURE_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1544+ # typeset -g POWERLEVEL9K_AZURE_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <'
1545+ typeset -g POWERLEVEL9K_AZURE_CLASSES=(
1546+ # '*prod*' PROD # These values are examples that are unlikely
1547+ # '*test*' TEST # to match your needs. Customize them as needed.
1548+ '*' OTHER)
1549+
1550+ # Azure account name color.
1551+ typeset -g POWERLEVEL9K_AZURE_OTHER_FOREGROUND=7
1552+ typeset -g POWERLEVEL9K_AZURE_OTHER_BACKGROUND=4
1553+ # Custom icon.
1554+ # typeset -g POWERLEVEL9K_AZURE_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐'
1555+
1556+ ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]###########
1557+ # Show gcloud only when the command you are typing invokes one of these tools.
1558+ # Tip: Remove the next line to always show gcloud.
1559+ typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs|gsutil'
1560+ # Google cloud color.
1561+ typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=7
1562+ typeset -g POWERLEVEL9K_GCLOUD_BACKGROUND=4
1563+
1564+ # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or
1565+ # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative
1566+ # enough. You can use the following parameters in the expansions. Each of them corresponds to the
1567+ # output of `gcloud` tool.
1568+ #
1569+ # Parameter | Source
1570+ # -------------------------|--------------------------------------------------------------------
1571+ # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)'
1572+ # P9K_GCLOUD_ACCOUNT | gcloud config get-value account
1573+ # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project
1574+ # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)'
1575+ #
1576+ # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'.
1577+ #
1578+ # Obtaining project name requires sending a request to Google servers. This can take a long time
1579+ # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud
1580+ # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets
1581+ # set and gcloud prompt segment transitions to state COMPLETE.
1582+ #
1583+ # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL
1584+ # and COMPLETE. You can also hide gcloud in state PARTIAL by setting
1585+ # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and
1586+ # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty.
1587+ typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}'
1588+ typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}'
1589+
1590+ # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name
1591+ # this often. Negative value disables periodic polling. In this mode project name is retrieved
1592+ # only when the current configuration, account or project id changes.
1593+ typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60
1594+
1595+ # Custom icon.
1596+ # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐'
1597+
1598+ #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]#
1599+ # Show google_app_cred only when the command you are typing invokes one of these tools.
1600+ # Tip: Remove the next line to always show google_app_cred.
1601+ typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt'
1602+
1603+ # Google application credentials classes for the purpose of using different colors, icons and
1604+ # expansions with different credentials.
1605+ #
1606+ # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first
1607+ # element in each pair defines a pattern against which the current kubernetes context gets
1608+ # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion
1609+ # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION
1610+ # parameters, you'll see this value in your prompt. The second element of each pair in
1611+ # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order.
1612+ # The first match wins.
1613+ #
1614+ # For example, given these settings:
1615+ #
1616+ # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=(
1617+ # '*:*prod*:*' PROD
1618+ # '*:*test*:*' TEST
1619+ # '*' DEFAULT)
1620+ #
1621+ # If your current Google application credentials is "service_account deathray-testing x@y.com",
1622+ # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'.
1623+ #
1624+ # You can define different colors, icons and content expansions for different classes:
1625+ #
1626+ # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28
1627+ # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐'
1628+ # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID'
1629+ typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=(
1630+ # '*:*prod*:*' PROD # These values are examples that are unlikely
1631+ # '*:*test*:*' TEST # to match your needs. Customize them as needed.
1632+ '*' DEFAULT)
1633+ typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=7
1634+ typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_BACKGROUND=4
1635+ # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐'
1636+
1637+ # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by
1638+ # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference:
1639+ # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
1640+ #
1641+ # You can use the following parameters in the expansion. Each of them corresponds to one of the
1642+ # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS.
1643+ #
1644+ # Parameter | JSON key file field
1645+ # ---------------------------------+---------------
1646+ # P9K_GOOGLE_APP_CRED_TYPE | type
1647+ # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id
1648+ # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email
1649+ #
1650+ # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'.
1651+ typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}'
1652+
1653+ ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]###############
1654+ # Toolbox color.
1655+ typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=0
1656+ typeset -g POWERLEVEL9K_TOOLBOX_BACKGROUND=3
1657+ # Don't display the name of the toolbox if it matches fedora-toolbox-*.
1658+ typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}'
1659+ # Custom icon.
1660+ # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐'
1661+ # Custom prefix.
1662+ # typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='in '
1663+
1664+ ###############################[ public_ip: public IP address ]###############################
1665+ # Public IP color.
1666+ typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=7
1667+ typeset -g POWERLEVEL9K_PUBLIC_IP_BACKGROUND=0
1668+ # Custom icon.
1669+ # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐'
1670+
1671+ ########################[ vpn_ip: virtual private network indicator ]#########################
1672+ # VPN IP color.
1673+ typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=0
1674+ typeset -g POWERLEVEL9K_VPN_IP_BACKGROUND=6
1675+ # When on VPN, show just an icon without the IP address.
1676+ # Tip: To display the private IP address when on VPN, remove the next line.
1677+ typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION=
1678+ # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN
1679+ # to see the name of the interface.
1680+ typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*|(zt.*)'
1681+ # If set to true, show one segment per matching network interface. If set to false, show only
1682+ # one segment corresponding to the first matching network interface.
1683+ # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION.
1684+ typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false
1685+ # Custom icon.
1686+ # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐'
1687+
1688+ ###########[ ip: ip address and bandwidth usage for a specified network interface ]###########
1689+ # IP color.
1690+ typeset -g POWERLEVEL9K_IP_BACKGROUND=4
1691+ typeset -g POWERLEVEL9K_IP_FOREGROUND=0
1692+ # The following parameters are accessible within the expansion:
1693+ #
1694+ # Parameter | Meaning
1695+ # ----------------------+-------------------------------------------
1696+ # P9K_IP_IP | IP address
1697+ # P9K_IP_INTERFACE | network interface
1698+ # P9K_IP_RX_BYTES | total number of bytes received
1699+ # P9K_IP_TX_BYTES | total number of bytes sent
1700+ # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt
1701+ # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt
1702+ # P9K_IP_RX_RATE | receive rate (since last prompt)
1703+ # P9K_IP_TX_RATE | send rate (since last prompt)
1704+ typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='${P9K_IP_RX_RATE:+⇣$P9K_IP_RX_RATE }${P9K_IP_TX_RATE:+⇡$P9K_IP_TX_RATE }$P9K_IP_IP'
1705+ # Show information for the first network interface whose name matches this regular expression.
1706+ # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces.
1707+ typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*'
1708+ # Custom icon.
1709+ # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐'
1710+
1711+ #########################[ proxy: system-wide http/https/ftp proxy ]##########################
1712+ # Proxy color.
1713+ typeset -g POWERLEVEL9K_PROXY_FOREGROUND=4
1714+ typeset -g POWERLEVEL9K_PROXY_BACKGROUND=0
1715+ # Custom icon.
1716+ # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐'
1717+
1718+ ################################[ battery: internal battery ]#################################
1719+ # Show battery in red when it's below this level and not connected to power supply.
1720+ typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20
1721+ typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=1
1722+ # Show battery in green when it's charging or fully charged.
1723+ typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=2
1724+ # Show battery in yellow when it's discharging.
1725+ typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=3
1726+ # Battery pictograms going from low to high level of charge.
1727+ typeset -g POWERLEVEL9K_BATTERY_STAGES='\UF008E\UF007A\UF007B\UF007C\UF007D\UF007E\UF007F\UF0080\UF0081\UF0082\UF0079'
1728+ # Don't show the remaining time to charge/discharge.
1729+ typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false
1730+ typeset -g POWERLEVEL9K_BATTERY_BACKGROUND=0
1731+
1732+ #####################################[ wifi: wifi speed ]#####################################
1733+ # WiFi color.
1734+ typeset -g POWERLEVEL9K_WIFI_FOREGROUND=0
1735+ typeset -g POWERLEVEL9K_WIFI_BACKGROUND=4
1736+ # Custom icon.
1737+ # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐'
1738+
1739+ # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS).
1740+ #
1741+ # # Wifi colors and icons for different signal strength levels (low to high).
1742+ # typeset -g my_wifi_fg=(0 0 0 0 0) # <-- change these values
1743+ # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values
1744+ #
1745+ # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps'
1746+ # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}'
1747+ #
1748+ # The following parameters are accessible within the expansions:
1749+ #
1750+ # Parameter | Meaning
1751+ # ----------------------+---------------
1752+ # P9K_WIFI_SSID | service set identifier, a.k.a. network name
1753+ # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown
1754+ # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second
1755+ # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0
1756+ # P9K_WIFI_NOISE | noise in dBm, from -120 to 0
1757+ # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE)
1758+
1759+ ####################################[ time: current time ]####################################
1760+ # Current time color.
1761+ typeset -g POWERLEVEL9K_TIME_FOREGROUND=0
1762+ typeset -g POWERLEVEL9K_TIME_BACKGROUND=7
1763+ # Format for the current time: 09:51:02. See `man 3 strftime`.
1764+ typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
1765+ # If set to true, time will update when you hit enter. This way prompts for the past
1766+ # commands will contain the start times of their commands as opposed to the default
1767+ # behavior where they contain the end times of their preceding commands.
1768+ typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
1769+ # Custom icon.
1770+ # typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION='⭐'
1771+ # Custom prefix.
1772+ # typeset -g POWERLEVEL9K_TIME_PREFIX='at '
1773+
1774+ # Example of a user-defined prompt segment. Function prompt_example will be called on every
1775+ # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or
1776+ # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and yellow text on red background
1777+ # greeting the user.
1778+ #
1779+ # Type `p10k help segment` for documentation and a more sophisticated example.
1780+ function prompt_example() {
1781+ p10k segment -b 1 -f 3 -i '⭐' -t 'hello, %n'
1782+ }
1783+
1784+ # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job
1785+ # is to generate the prompt segment for display in instant prompt. See
1786+ # https://github.com/romkatv/powerlevel10k#instant-prompt.
1787+ #
1788+ # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function
1789+ # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k
1790+ # will replay these calls without actually calling instant_prompt_*. It is imperative that
1791+ # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this
1792+ # rule is not observed, the content of instant prompt will be incorrect.
1793+ #
1794+ # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If
1795+ # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt.
1796+ function instant_prompt_example() {
1797+ # Since prompt_example always makes the same `p10k segment` calls, we can call it from
1798+ # instant_prompt_example. This will give us the same `example` prompt segment in the instant
1799+ # and regular prompts.
1800+ prompt_example
1801+ }
1802+
1803+ # User-defined prompt segments can be customized the same way as built-in segments.
1804+ typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=3
1805+ typeset -g POWERLEVEL9K_EXAMPLE_BACKGROUND=1
1806+ # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐'
1807+
1808+ # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt
1809+ # when accepting a command line. Supported values:
1810+ #
1811+ # - off: Don't change prompt when accepting a command line.
1812+ # - always: Trim down prompt when accepting a command line.
1813+ # - same-dir: Trim down prompt when accepting a command line unless this is the first command
1814+ # typed after changing current working directory.
1815+ typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always
1816+
1817+ # Instant prompt mode.
1818+ #
1819+ # - off: Disable instant prompt. Choose this if you've tried instant prompt and found
1820+ # it incompatible with your zsh configuration files.
1821+ # - quiet: Enable instant prompt and don't print warnings when detecting console output
1822+ # during zsh initialization. Choose this if you've read and understood
1823+ # https://github.com/romkatv/powerlevel10k#instant-prompt.
1824+ # - verbose: Enable instant prompt and print a warning when detecting console output during
1825+ # zsh initialization. Choose this if you've never tried instant prompt, haven't
1826+ # seen the warning, or if you are unsure what this all means.
1827+ typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose
1828+
1829+ # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized.
1830+ # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload
1831+ # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you
1832+ # really need it.
1833+ typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
1834+
1835+ # If p10k is already loaded, reload configuration.
1836+ # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
1837+ (( ! $+functions[p10k] )) || p10k reload
1838+}
1839+
1840+# Tell `p10k configure` which file it should overwrite.
1841+typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a}
1842+
1843+(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
1844+'builtin' 'unset' 'p10k_config_opts'
@@ -1,0 +1,178 @@
1+{
2+ "nodes": {
3+ "flake-compat": {
4+ "flake": false,
5+ "locked": {
6+ "lastModified": 1767039857,
7+ "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
8+ "owner": "NixOS",
9+ "repo": "flake-compat",
10+ "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
11+ "type": "github"
12+ },
13+ "original": {
14+ "owner": "NixOS",
15+ "repo": "flake-compat",
16+ "type": "github"
17+ }
18+ },
19+ "flake-parts": {
20+ "inputs": {
21+ "nixpkgs-lib": "nixpkgs-lib"
22+ },
23+ "locked": {
24+ "lastModified": 1788450739,
25+ "narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=",
26+ "owner": "hercules-ci",
27+ "repo": "flake-parts",
28+ "rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993",
29+ "type": "github"
30+ },
31+ "original": {
32+ "owner": "hercules-ci",
33+ "repo": "flake-parts",
34+ "type": "github"
35+ }
36+ },
37+ "home-manager": {
38+ "inputs": {
39+ "nixpkgs": [
40+ "nixpkgs"
41+ ]
42+ },
43+ "locked": {
44+ "lastModified": 1792098797,
45+ "narHash": "sha256-u4DIIZgznEGzByxXrxWCXK19VcMtTBmi/M9gZ7Jk9bM=",
46+ "owner": "nix-community",
47+ "repo": "home-manager",
48+ "rev": "640725978b99076e18e069f6e7d2959124584f8f",
49+ "type": "github"
50+ },
51+ "original": {
52+ "owner": "nix-community",
53+ "repo": "home-manager",
54+ "type": "github"
55+ }
56+ },
57+ "imsh-clients": {
58+ "inputs": {
59+ "nixpkgs": [
60+ "nixpkgs"
61+ ]
62+ },
63+ "locked": {
64+ "lastModified": 1763709007,
65+ "narHash": "sha256-IQVGDW9qysUoGRNE4VdxplT+TbTYesB1IBqK+t9s2iw=",
66+ "owner": "echozio",
67+ "repo": "imsh-clients",
68+ "rev": "c7064ac7fa6c3759cfab18db06eca15c4217bf7e",
69+ "type": "github"
70+ },
71+ "original": {
72+ "owner": "echozio",
73+ "repo": "imsh-clients",
74+ "type": "github"
75+ }
76+ },
77+ "nix-cachyos-kernel": {
78+ "inputs": {
79+ "flake-compat": "flake-compat",
80+ "flake-parts": "flake-parts",
81+ "nixpkgs": "nixpkgs"
82+ },
83+ "locked": {
84+ "lastModified": 1790564970,
85+ "narHash": "sha256-ANcKKTLB5oitjAYPp+iWts5clpbowVbDPVvt03P7Hes=",
86+ "owner": "xddxdd",
87+ "repo": "nix-cachyos-kernel",
88+ "rev": "b1332396df6e880d7e3b6b451c6a74132ce8cf66",
89+ "type": "github"
90+ },
91+ "original": {
92+ "owner": "xddxdd",
93+ "ref": "release",
94+ "repo": "nix-cachyos-kernel",
95+ "type": "github"
96+ }
97+ },
98+ "nixpkgs": {
99+ "locked": {
100+ "lastModified": 1790505892,
101+ "narHash": "sha256-/pAz2XfqHS//WRn2xRjoqes7Z92qZ0Q1gufZZ3PtuwA=",
102+ "owner": "NixOS",
103+ "repo": "nixpkgs",
104+ "rev": "545c226a9af7f59fba5c3873f7c6a65e014c8f5f",
105+ "type": "github"
106+ },
107+ "original": {
108+ "owner": "NixOS",
109+ "ref": "nixos-unstable-small",
110+ "repo": "nixpkgs",
111+ "type": "github"
112+ }
113+ },
114+ "nixpkgs-lib": {
115+ "locked": {
116+ "lastModified": 1788057806,
117+ "narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=",
118+ "owner": "nix-community",
119+ "repo": "nixpkgs.lib",
120+ "rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c",
121+ "type": "github"
122+ },
123+ "original": {
124+ "owner": "nix-community",
125+ "repo": "nixpkgs.lib",
126+ "type": "github"
127+ }
128+ },
129+ "nixpkgs_2": {
130+ "locked": {
131+ "lastModified": 1792094642,
132+ "narHash": "sha256-zp8ToDMnqZcu4o4IH9pdwXFXGp7yLm/M0dEnNWnyd7I=",
133+ "owner": "NixOS",
134+ "repo": "nixpkgs",
135+ "rev": "fe13242281a67641c0102cdb1087f0ebe586c7df",
136+ "type": "github"
137+ },
138+ "original": {
139+ "owner": "NixOS",
140+ "ref": "nixos-unstable",
141+ "repo": "nixpkgs",
142+ "type": "github"
143+ }
144+ },
145+ "philly": {
146+ "inputs": {
147+ "nixpkgs": [
148+ "nixpkgs"
149+ ]
150+ },
151+ "locked": {
152+ "lastModified": 1792282887,
153+ "narHash": "sha256-b5YCa1GRWutE7GyK4e/56lmYgUT7+0H9E/4NGs2MXgM=",
154+ "ref": "master",
155+ "rev": "9d9a7c483aaaa78a7309a48dc2aed1c0c7aed21d",
156+ "revCount": 21,
157+ "type": "git",
158+ "url": "https://github.com/Eiziv/philly"
159+ },
160+ "original": {
161+ "ref": "master",
162+ "type": "git",
163+ "url": "https://github.com/Eiziv/philly"
164+ }
165+ },
166+ "root": {
167+ "inputs": {
168+ "home-manager": "home-manager",
169+ "imsh-clients": "imsh-clients",
170+ "nix-cachyos-kernel": "nix-cachyos-kernel",
171+ "nixpkgs": "nixpkgs_2",
172+ "philly": "philly"
173+ }
174+ }
175+ },
176+ "root": "root",
177+ "version": 7
178+}
@@ -1,0 +1,70 @@
1+{
2+ description = "vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks";
3+
4+ inputs = {
5+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6+
7+ home-manager = {
8+ url = "github:nix-community/home-manager";
9+ inputs.nixpkgs.follows = "nixpkgs";
10+ };
11+
12+ # CachyOS kernels, built by the maintainer's CI. Its nixpkgs input is left
13+ # alone on purpose: overriding it changes the kernel's hash, so nothing comes
14+ # from the binary cache and the kernel compiles locally.
15+ nix-cachyos-kernel.url = "github:xddxdd/nix-cachyos-kernel/release";
16+
17+ imsh-clients = {
18+ url = "github:echozio/imsh-clients";
19+ inputs.nixpkgs.follows = "nixpkgs";
20+ };
21+
22+ # Private repo: fetched with git so the gh credential helper can sign in.
23+ philly = {
24+ url = "git+https://github.com/Eiziv/philly?ref=master";
25+ inputs.nixpkgs.follows = "nixpkgs";
26+ };
27+ };
28+
29+ nixConfig = {
30+ extra-substituters = [ "https://attic.xuyh0120.win/lantian" ];
31+ extra-trusted-public-keys = [ "lantian:EeAUQ+W+6r7EtwnmYjeVwx5kOGEBpjlBfPlzGlTNvHc=" ];
32+ };
33+
34+ outputs =
35+ { self, nixpkgs, ... }@inputs:
36+ let
37+ system = "x86_64-linux";
38+ pkgs = import nixpkgs {
39+ inherit system;
40+ config.allowUnfree = true;
41+ overlays = [ self.overlays.default ];
42+ };
43+ in
44+ {
45+ overlays.default = import ./pkgs;
46+
47+ # The packages this repo adds or patches, so each can be built on its own:
48+ # nix build .#waybar
49+ packages.${system} = {
50+ inherit (pkgs)
51+ waybar
52+ xembedsniproxy
53+ signal-desktop-themed
54+ claude-desktop
55+ opendeck
56+ breezex-cursor
57+ proton-cachyos
58+ python3-desktop
59+ ;
60+ };
61+
62+ nixosConfigurations.desktop = nixpkgs.lib.nixosSystem {
63+ inherit system;
64+ specialArgs = { inherit inputs; };
65+ modules = [ ./hosts/desktop ];
66+ };
67+
68+ formatter.${system} = pkgs.nixfmt;
69+ };
70+}
@@ -1,0 +1,26 @@
1+# The desktop: ASUS TUF B850-PLUS, Ryzen 7 9850X3D, RTX 3080 Ti.
2+{ inputs, ... }:
3+{
4+ imports = [
5+ ./hardware-configuration.nix
6+ ../../modules/nixos
7+ inputs.home-manager.nixosModules.home-manager
8+ ];
9+
10+ networking.hostName = "desktop";
11+
12+ home-manager = {
13+ useGlobalPkgs = true;
14+ useUserPackages = true;
15+ extraSpecialArgs = { inherit inputs; };
16+ # The first switch finds the old symlinks into ~/git/dotfiles where Home
17+ # Manager wants its own; they are moved aside with this suffix, not deleted.
18+ backupFileExtension = "pre-nixos";
19+ users.vizie = import ../../modules/home;
20+ };
21+
22+ # Leave this at the release the machine was installed with, even after
23+ # upgrading: it only controls the defaults of stateful things such as
24+ # database formats. It is not the NixOS version you run.
25+ system.stateVersion = "26.05";
26+}
@@ -1,0 +1,75 @@
1+# Written by hand from the CachyOS install, in the shape nixos-generate-config
2+# produces. During the install, compare it with the output of
3+# nixos-generate-config --show-hardware-config --root /mnt
4+# and keep the generated one if they differ.
5+#
6+# Disk layout: the existing btrfs partition is kept. NixOS gets subvolumes of
7+# its own (@nixos for /, @nix for /nix, @nixos-log for /var/log) next to
8+# CachyOS's, and /home is CachyOS's @home, so your files stay where they are.
9+# See MIGRATION.md.
10+{
11+ config,
12+ lib,
13+ modulesPath,
14+ ...
15+}:
16+let
17+ btrfs = "/dev/disk/by-uuid/6e57f546-3dd3-43f6-9d1b-1c57f3b489ad";
18+ subvol = name: [
19+ "subvol=${name}"
20+ "noatime"
21+ "compress=zstd"
22+ ];
23+in
24+{
25+ imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
26+
27+ boot.initrd.availableKernelModules = [
28+ "nvme"
29+ "xhci_pci"
30+ "ahci"
31+ "usbhid"
32+ "usb_storage"
33+ "sd_mod"
34+ ];
35+ boot.kernelModules = [ "kvm-amd" ];
36+
37+ fileSystems."/" = {
38+ device = btrfs;
39+ fsType = "btrfs";
40+ options = subvol "@nixos";
41+ };
42+ fileSystems."/nix" = {
43+ device = btrfs;
44+ fsType = "btrfs";
45+ options = subvol "@nix";
46+ };
47+ fileSystems."/var/log" = {
48+ device = btrfs;
49+ fsType = "btrfs";
50+ options = subvol "@nixos-log";
51+ neededForBoot = true;
52+ };
53+ fileSystems."/home" = {
54+ device = btrfs;
55+ fsType = "btrfs";
56+ options = subvol "@home";
57+ };
58+ fileSystems."/boot" = {
59+ device = "/dev/disk/by-uuid/DDCE-5209";
60+ fsType = "vfat";
61+ options = [
62+ "fmask=0077"
63+ "dmask=0077"
64+ ];
65+ };
66+
67+ # The second drive (sdb, ext4) was commented out in CachyOS's fstab too.
68+ # fileSystems."/home/vizie/Games" = {
69+ # device = "/dev/disk/by-uuid/deadbeef-b2a7-406c-9704-4d07e2f38996";
70+ # fsType = "ext4";
71+ # };
72+
73+ hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
74+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
75+}
@@ -1,0 +1,65 @@
1+<svg viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg">
2+ <path d="
3+ M 7 0
4+ l 1 1
5+ l -1 1
6+ l -1 -1
7+ Z
8+ " />
9+ <path d="
10+ M 9 2
11+ l 1 1
12+ l -1 1
13+ l -1 -1
14+ Z
15+ " />
16+ <path d="
17+ M 5 2
18+ l 3 3
19+ l -1 1
20+ l -3 -3
21+ Z
22+ " />
23+ <path d="
24+ M 3 4
25+ l 1 1
26+ l -3 3
27+ l -1 -1
28+ Z
29+ " />
30+ <path d="
31+ M 5 6
32+ l 3 3
33+ l -1 1
34+ l -3 -3
35+ Z
36+ " />
37+ <path d="
38+ M 3 8
39+ l 1 1
40+ l -1 1
41+ l -1 -1
42+ Z
43+ " />
44+ <path d="
45+ M 5 10
46+ l 3 3
47+ l -1 1
48+ l -3 -3
49+ Z
50+ " />
51+ <path d="
52+ M 11 4
53+ l 1 1
54+ l -3 3
55+ l -1 -1
56+ Z
57+ " />
58+ <path d="
59+ M 13 6
60+ l 1 1
61+ l -5 5
62+ l -1 -1
63+ Z
64+ " />
65+</svg>
@@ -1,0 +1,52 @@
1+# Home Manager: everything in your home directory.
2+{
3+ config,
4+ lib,
5+ ...
6+}:
7+{
8+ imports = [
9+ ./files.nix
10+ ./services.nix
11+ ./shell.nix
12+ ./theming.nix
13+ ./packages.nix
14+ ];
15+
16+ options.dot = {
17+ repoPath = lib.mkOption {
18+ type = lib.types.str;
19+ default = "${config.home.homeDirectory}/git/dot";
20+ description = "Where this repo is checked out.";
21+ };
22+
23+ liveConfig = lib.mkOption {
24+ type = lib.types.bool;
25+ default = true;
26+ description = ''
27+ Link the hand-edited configs (hypr, waybar, kitty, ...) straight to the
28+ checkout at repoPath, as the old symlink.sh did: an edit takes effect
29+ the next time the program reads it, with no rebuild. Off, they are
30+ copied into the Nix store and change only on a rebuild (the test VM
31+ does this, since it has no checkout).
32+ '';
33+ };
34+ };
35+
36+ config = {
37+ home = {
38+ username = "vizie";
39+ homeDirectory = "/home/vizie";
40+ stateVersion = "26.05";
41+ };
42+
43+ xdg.enable = true;
44+ xdg.userDirs = {
45+ enable = true;
46+ createDirectories = true;
47+ extraConfig.XDG_PROJECTS_DIR = "${config.home.homeDirectory}/Projects";
48+ };
49+
50+ programs.home-manager.enable = true;
51+ };
52+}
@@ -1,0 +1,95 @@
1+# The config files in ../../config, put where the programs read them. This
2+# replaces symlink.sh from the old dotfiles repo.
3+{
4+ config,
5+ lib,
6+ pkgs,
7+ ...
8+}:
9+let
10+ cfg = config.dot;
11+
12+ # A file or directory of config/: linked to the checkout when liveConfig is
13+ # on (edits apply at once), copied into the store otherwise.
14+ link =
15+ rel:
16+ if cfg.liveConfig then
17+ config.lib.file.mkOutOfStoreSymlink "${cfg.repoPath}/config/${rel}"
18+ else
19+ ../../config + "/${rel}";
20+
21+ # Configs the programs write themselves (GOverlay, btop, micro, apps that
22+ # register URL handlers): copied in only when missing, and never overwritten,
23+ # so changes made in the app stick. Your existing files in /home win.
24+ seeds = {
25+ ".config/btop/btop.conf" = "btop/btop.conf";
26+ ".config/MangoHud/MangoHud.conf" = "MangoHud/MangoHud.conf";
27+ ".config/vkBasalt/vkBasalt.conf" = "vkBasalt/vkBasalt.conf";
28+ ".config/vkSumi/vkSumi.conf" = "vkSumi/vkSumi.conf";
29+ ".config/micro/settings.json" = "micro/settings.json";
30+ ".config/micro/bindings.json" = "micro/bindings.json";
31+ ".config/mimeapps.list" = "mimeapps.list";
32+ };
33+in
34+{
35+ xdg.configFile = {
36+ "hypr".source = link "hypr";
37+ "waybar".source = link "waybar";
38+ "dunst".source = link "dunst";
39+ "kitty".source = link "kitty";
40+ "fuzzel".source = link "fuzzel";
41+ "nvim".source = link "nvim";
42+ "qt6ct".source = link "qt6ct";
43+ "kdeglobals".source = link "kde/kdeglobals";
44+ "philly/theme.css".source = link "philly/theme.css";
45+
46+ # Kvantum: the active theme is KvArcDark# (your customised copy of KvArcDark,
47+ # made with Kvantum Manager; it uses the stock theme's artwork). RiceDark is
48+ # the tweaked theme from the old repo, with the stock KvArcDark artwork
49+ # copied in under its name, as kvantum/install.sh did.
50+ "Kvantum/kvantum.kvconfig".source = link "kvantum/kvantum.kvconfig";
51+ "Kvantum/KvArcDark#/KvArcDark#.kvconfig".source = link "kvantum/KvArcDark#.kvconfig";
52+ "Kvantum/RiceDark/RiceDark.kvconfig".source = link "kvantum/RiceDark.kvconfig";
53+ "Kvantum/RiceDark/RiceDark.svg".source =
54+ "${pkgs.kdePackages.qtstyleplugin-kvantum}/share/Kvantum/KvArcDark/KvArcDark.svg";
55+
56+ "micro/colorschemes".source = ../../config/micro/colorschemes;
57+ };
58+
59+ home.file.".p10k.zsh".source = link "zsh/p10k.zsh";
60+
61+ home.activation = {
62+ seedConfigs = lib.hm.dag.entryAfter [ "writeBoundary" ] (
63+ lib.concatStrings (
64+ lib.mapAttrsToList (target: rel: ''
65+ if [ ! -e "$HOME/${target}" ]; then
66+ run mkdir -p "$(dirname "$HOME/${target}")"
67+ run install -m644 ${../../config + "/${rel}"} "$HOME/${target}"
68+ fi
69+ '') seeds
70+ )
71+ );
72+
73+ # Firefox: user.js and userChrome.css in the default profile, which Firefox
74+ # creates itself (Home Manager's own Firefox module would make a new profile
75+ # and take over profiles.ini). Same as symlink.sh did.
76+ firefoxProfile = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
77+ ini="$HOME/.config/mozilla/firefox/profiles.ini"
78+ if [ -f "$ini" ]; then
79+ profile=$(${pkgs.gawk}/bin/awk -F= '
80+ /^\[Install/ { install = 1; next }
81+ /^\[/ { install = 0 }
82+ install && $1 == "Default" { print $2; exit }
83+ ' "$ini")
84+ if [ -n "$profile" ]; then
85+ dir="$HOME/.config/mozilla/firefox/$profile"
86+ run mkdir -p "$dir/chrome"
87+ run ln -sfn ${link "firefox/user.js"} "$dir/user.js"
88+ run ln -sfn ${link "firefox/userChrome.css"} "$dir/chrome/userChrome.css"
89+ fi
90+ else
91+ warnEcho "Firefox has no profile yet: start Firefox once, then run home-manager switch (or nixos-rebuild switch) again."
92+ fi
93+ '';
94+ };
95+}
@@ -1,0 +1,31 @@
1+# Programs your configs and scripts call, and your own tools.
2+{ inputs, pkgs, ... }:
3+{
4+ home.packages = with pkgs; [
5+ # The Hyprland desktop: launcher, bar flyouts, notifications, idle/lock.
6+ fuzzel
7+ eww
8+ dunst
9+ libnotify
10+ hyprpaper
11+ hypridle
12+ wl-clipboard
13+ playerctl
14+
15+ # python3 with PyGObject and python-xlib, for the Waybar/Eww scripts.
16+ python3-desktop
17+
18+ # Called by the control centre and tray scripts.
19+ pulseaudio # pactl
20+ bluez # bluetoothctl
21+ solaar # mouse battery fallback (mouse-battery.sh)
22+
23+ # Screenshots and screen recording (imsh-shot, imsh-cast; Super+Shift+S/F/R).
24+ inputs.imsh-clients.packages.${pkgs.stdenv.hostPlatform.system}.default
25+ # Philly, the file browser (also the handler for folders, in mimeapps.list).
26+ inputs.philly.packages.${pkgs.stdenv.hostPlatform.system}.default
27+
28+ nodejs
29+ go
30+ ];
31+}
@@ -1,0 +1,161 @@
1+# The desktop's background programs, as systemd user services. On CachyOS most
2+# of these were started by hyprland.lua (hl.exec_cmd at hyprland.start).
3+#
4+# uwsm starts Hyprland and then graphical-session.target; every service here is
5+# wanted by that target and stopped with it, so they come up after the
6+# compositor is ready and go away at logout. They are restarted if they crash.
7+#
8+# systemctl --user status waybar is it running, and its last log lines
9+# journalctl --user -u waybar -f follow its log
10+# systemctl --user restart waybar e.g. after changing its config
11+#
12+# Slices are uwsm's: session-graphical for the shell itself (bar, idle, wallpaper,
13+# notifications), background-graphical for helpers.
14+{
15+ config,
16+ lib,
17+ pkgs,
18+ ...
19+}:
20+let
21+ graphical =
22+ {
23+ description,
24+ exec,
25+ slice ? "background-graphical.slice",
26+ after ? [ ],
27+ unit ? { },
28+ service ? { },
29+ }:
30+ {
31+ Unit = {
32+ Description = description;
33+ PartOf = [ "graphical-session.target" ];
34+ After = [ "graphical-session.target" ] ++ after;
35+ Requisite = [ "graphical-session.target" ];
36+ }
37+ // unit;
38+ Service = {
39+ ExecStart = exec;
40+ Restart = "on-failure";
41+ RestartSec = 2;
42+ Slice = slice;
43+ }
44+ // service;
45+ Install.WantedBy = [ "graphical-session.target" ];
46+ };
47+
48+ configDir = "${config.xdg.configHome}";
49+in
50+{
51+ systemd.user.services = {
52+ waybar = graphical {
53+ description = "Waybar";
54+ exec = lib.getExe pkgs.waybar;
55+ slice = "session-graphical.slice";
56+ service.ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR2 $MAINPID";
57+ };
58+
59+ hypridle = graphical {
60+ description = "Hyprland's idle daemon";
61+ exec = lib.getExe pkgs.hypridle;
62+ slice = "session-graphical.slice";
63+ unit.ConditionEnvironment = "WAYLAND_DISPLAY";
64+ };
65+
66+ hyprpaper = graphical {
67+ description = "Hyprland wallpaper daemon";
68+ exec = lib.getExe pkgs.hyprpaper;
69+ slice = "session-graphical.slice";
70+ };
71+
72+ dunst = graphical {
73+ description = "Dunst notification daemon";
74+ exec = lib.getExe pkgs.dunst;
75+ slice = "session-graphical.slice";
76+ service = {
77+ Type = "dbus";
78+ BusName = "org.freedesktop.Notifications";
79+ };
80+ };
81+
82+ # The Waybar flyouts (control centre, notifications). Started with the
83+ # session so the first click on the Waybar button opens instantly.
84+ eww = graphical {
85+ description = "Eww daemon for the Waybar flyouts";
86+ exec = "${lib.getExe pkgs.eww} --config ${configDir}/waybar/eww daemon --no-daemonize";
87+ };
88+
89+ hyprpolkitagent = graphical {
90+ description = "Hyprland polkit agent";
91+ exec = "${pkgs.hyprpolkitagent}/libexec/hyprpolkitagent";
92+ slice = "session-graphical.slice";
93+ };
94+
95+ nm-applet = graphical {
96+ description = "NetworkManager tray icon";
97+ exec = "${pkgs.networkmanagerapplet}/bin/nm-applet --indicator";
98+ after = [ "waybar.service" ];
99+ };
100+
101+ # Puts Wine/Proton tray icons (Battle.net) into Waybar's tray, so it needs
102+ # the tray to be there first.
103+ xembedsniproxy = graphical {
104+ description = "XEmbed to StatusNotifierItem tray bridge";
105+ exec = lib.getExe pkgs.xembedsniproxy;
106+ after = [ "waybar.service" ];
107+ # Restarting it while a Wine app is open breaks that app's tray icon until
108+ # the app is restarted, so a rebuild leaves the running one alone.
109+ unit.X-RestartIfChanged = false;
110+ };
111+
112+ # Forwards the X11 clipboard (Wine/Proton games) to Wayland whatever has
113+ # focus. Waits for Xwayland by itself.
114+ xwl-clipbridge = graphical {
115+ description = "X11 to Wayland clipboard bridge";
116+ exec = "${pkgs.python3-desktop}/bin/python3 ${configDir}/hypr/scripts/xwl-clipbridge";
117+ service.Environment = [ "PATH=${lib.makeBinPath [ pkgs.wl-clipboard ]}" ];
118+ };
119+
120+ # The apps opened at login (Signal, Discord, Faugus, OpenDeck, WowUp); see
121+ # config/hypr/scripts/autostart-apps. It starts each one as its own unit and
122+ # exits; the window placement rules for them are in hyprland.lua.
123+ autostart-apps = graphical {
124+ description = "Open the apps that start at login";
125+ exec = "${configDir}/hypr/scripts/autostart-apps";
126+ after = [ "waybar.service" ];
127+ service = {
128+ Type = "oneshot";
129+ Restart = "no";
130+ # The script finds the apps (and pgrep, uwsm, hyprctl, ...) on the
131+ # system and user profiles, whatever environment the session imported.
132+ Environment = [
133+ "PATH=${
134+ lib.makeBinPath [ pkgs.python3-desktop ]
135+ }:/etc/profiles/per-user/${config.home.username}/bin:/run/current-system/sw/bin"
136+ ];
137+ # It waits for the tray and for Signal and Discord to open, up to about
138+ # two minutes; more than the 15 s default the CachyOS settings set.
139+ TimeoutStartSec = "3min";
140+ };
141+ unit.X-RestartIfChanged = false;
142+ };
143+ };
144+
145+ # The Bluetooth tray icon (blueman-applet) comes from the autostart entry
146+ # services.blueman installs, which uwsm also runs as a systemd unit
147+ # (app-blueman@autostart.service).
148+
149+ # Bitwarden at login. This one stays an XDG autostart entry, as on CachyOS
150+ # (uwsm runs those as systemd units too: app-bitwarden@autostart.service); the
151+ # entry just needs NixOS's name for the program.
152+ xdg.configFile."autostart/bitwarden.desktop".text = ''
153+ [Desktop Entry]
154+ Type=Application
155+ Name=Bitwarden
156+ Comment=Bitwarden startup script
157+ Exec=${lib.getExe pkgs.bitwarden-desktop}
158+ StartupNotify=false
159+ Terminal=false
160+ '';
161+}
@@ -1,0 +1,130 @@
1+# zsh as CachyOS set it up (cachyos-zsh-config: oh-my-zsh, Powerlevel10k and the
2+# fish-like plugins), plus git, gh and ssh.
3+{
4+ config,
5+ lib,
6+ pkgs,
7+ ...
8+}:
9+{
10+ programs.zsh = {
11+ enable = true;
12+
13+ # Same history file and sizes as before (oh-my-zsh's defaults), so your
14+ # history carries over.
15+ history = {
16+ path = "${config.home.homeDirectory}/.zsh_history";
17+ size = 50000;
18+ save = 10000;
19+ ignoreSpace = true;
20+ ignoreDups = true;
21+ };
22+
23+ oh-my-zsh = {
24+ enable = true;
25+ plugins = [
26+ "git"
27+ "fzf"
28+ "extract"
29+ ];
30+ extraConfig = ''
31+ DISABLE_MAGIC_FUNCTIONS="true"
32+ ENABLE_CORRECTION="true"
33+ COMPLETION_WAITING_DOTS="true"
34+ '';
35+ };
36+
37+ syntaxHighlighting.enable = true;
38+ autosuggestion.enable = true;
39+ historySubstringSearch.enable = true;
40+
41+ plugins = [
42+ {
43+ name = "powerlevel10k";
44+ src = pkgs.zsh-powerlevel10k;
45+ file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme";
46+ }
47+ ];
48+
49+ shellAliases = {
50+ make = "make -j$(nproc)";
51+ ninja = "ninja -j$(nproc)";
52+ n = "ninja";
53+ c = "clear";
54+ please = "sudo";
55+ tb = "nc termbin.com 9999";
56+ jctl = "journalctl -p 3 -xb";
57+
58+ # The pacman ones, for NixOS.
59+ update = "nix flake update --flake ${config.dot.repoPath} && sudo nixos-rebuild switch --flake ${config.dot.repoPath}";
60+ rebuild = "sudo nixos-rebuild switch --flake ${config.dot.repoPath}";
61+ cleanup = "sudo nix-collect-garbage --delete-older-than 14d";
62+ };
63+
64+ initContent = lib.mkMerge [
65+ # Powerlevel10k instant prompt. Must stay at the very top: anything that
66+ # may ask for input goes above it, everything else below.
67+ (lib.mkOrder 500 ''
68+ if [[ -r "''${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-''${(%):-%n}.zsh" ]]; then
69+ source "''${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-''${(%):-%n}.zsh"
70+ fi
71+ '')
72+ ''
73+ export HISTCONTROL=ignoreboth
74+ export HISTORY_IGNORE="(\&|[bf]g|c|clear|history|exit|q|pwd|* --help)"
75+
76+ # Bold green headings in man pages.
77+ export LESS_TERMCAP_md="$(tput bold 2> /dev/null; tput setaf 2 2> /dev/null)"
78+ export LESS_TERMCAP_me="$(tput sgr0 2> /dev/null)"
79+
80+ [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
81+ POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true
82+ ''
83+ ];
84+ };
85+
86+ programs.fzf.enable = true;
87+
88+ # "command not found" suggests the package to install, like pkgfile did.
89+ programs.nix-index.enable = true;
90+ programs.nix-index.enableZshIntegration = true;
91+
92+ programs.bash.enable = true;
93+
94+ programs.git = {
95+ enable = true;
96+ settings.user = {
97+ name = "Gustaf Granström";
98+ email = "gustaf@granstrm.com";
99+ };
100+ };
101+
102+ # Also makes git use gh for GitHub credentials, as in your old .gitconfig.
103+ programs.gh = {
104+ enable = true;
105+ gitCredentialHelper.enable = true;
106+ };
107+
108+ # ~/.ssh/config. Your keys, known_hosts and authorized_keys stay where they are
109+ # in ~/.ssh and are not part of this repo.
110+ programs.ssh = {
111+ enable = true;
112+ enableDefaultConfig = false;
113+ settings = {
114+ granstrm = {
115+ HostName = "granstrm.com";
116+ User = "root";
117+ IdentityFile = "~/.ssh/id_ed25519";
118+ };
119+ # Media center laptop, behind the Google Wifi (10.0.0.100), which
120+ # forwards port 2222 to the laptop's SSH.
121+ tv = {
122+ HostName = "10.0.0.100";
123+ Port = 2222;
124+ User = "tv";
125+ IdentityFile = "~/.ssh/id_ed25519";
126+ };
127+ };
128+ };
129+
130+}
@@ -1,0 +1,68 @@
1+# GTK and cursor theme, as nwg-look had set them on CachyOS (settings.ini,
2+# .gtkrc-2.0, GSettings). Qt is themed by config/qt6ct and config/kvantum.
3+#
4+# These files are now written by Nix, so change the theme here rather than in
5+# nwg-look (it can still preview themes, but can't save over these files).
6+{ pkgs, ... }:
7+{
8+ home.pointerCursor = {
9+ enable = true;
10+ name = "BreezeX-Black";
11+ package = pkgs.breezex-cursor;
12+ size = 28;
13+ gtk.enable = true;
14+ x11.enable = true;
15+ };
16+
17+ gtk = {
18+ enable = true;
19+ theme = {
20+ name = "Adwaita-dark";
21+ package = pkgs.gnome-themes-extra;
22+ };
23+ iconTheme = {
24+ name = "breeze-dark";
25+ package = pkgs.kdePackages.breeze-icons;
26+ };
27+ font = {
28+ name = "Adwaita Sans";
29+ size = 11;
30+ package = pkgs.adwaita-fonts;
31+ };
32+ gtk2.extraConfig = ''
33+ gtk-toolbar-style=GTK_TOOLBAR_BOTH_HORIZ
34+ gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
35+ gtk-button-images=0
36+ gtk-menu-images=0
37+ gtk-enable-event-sounds=1
38+ gtk-enable-input-feedback-sounds=0
39+ gtk-xft-antialias=1
40+ gtk-xft-hinting=1
41+ gtk-xft-hintstyle="hintslight"
42+ gtk-xft-rgba="rgb"
43+ '';
44+ gtk3.extraConfig = {
45+ gtk-toolbar-style = "GTK_TOOLBAR_BOTH_HORIZ";
46+ gtk-toolbar-icon-size = "GTK_ICON_SIZE_LARGE_TOOLBAR";
47+ gtk-button-images = 0;
48+ gtk-menu-images = 0;
49+ gtk-enable-event-sounds = 1;
50+ gtk-enable-input-feedback-sounds = 0;
51+ gtk-xft-antialias = 1;
52+ gtk-xft-hinting = 1;
53+ gtk-xft-hintstyle = "hintslight";
54+ gtk-xft-rgba = "rgb";
55+ gtk-application-prefer-dark-theme = 1;
56+ };
57+ gtk4.extraConfig.gtk-application-prefer-dark-theme = 1;
58+ };
59+
60+ dconf.settings."org/gnome/desktop/interface" = {
61+ color-scheme = "prefer-dark";
62+ gtk-theme = "Adwaita-dark";
63+ icon-theme = "breeze-dark";
64+ cursor-theme = "BreezeX-Black";
65+ cursor-size = 28;
66+ font-name = "Adwaita Sans 11";
67+ };
68+}
@@ -1,0 +1,66 @@
1+# Desktop apps and what they need from the system.
2+{ pkgs, ... }:
3+{
4+ programs.firefox.enable = true;
5+
6+ environment.systemPackages = with pkgs; [
7+ claude-desktop
8+ signal-desktop-themed
9+ discord
10+ bitwarden-desktop
11+ opendeck
12+ kdePackages.dolphin
13+ kdePackages.ark
14+ kdePackages.kio-extras
15+ kdePackages.ffmpegthumbs
16+ cosmic-files
17+ kdePackages.partitionmanager
18+ meld
19+ mpv
20+ vlc
21+ pavucontrol
22+ pulsemixer
23+ playerctl
24+ nwg-look
25+ qalculate-gtk
26+ alacritty
27+ ];
28+
29+ # --- Claude Desktop's Cowork ----------------------------------------------------
30+
31+ # Cowork runs its tasks in a QEMU/KVM virtual machine. The app looks for the
32+ # UEFI firmware at the path Debian uses, so it is linked there; QEMU, socat
33+ # and virtiofsd come with the package (pkgs/claude-desktop).
34+ boot.kernelModules = [ "vhost_vsock" ];
35+ systemd.tmpfiles.rules = [
36+ "L+ /usr/share/OVMF/OVMF_CODE_4M.fd - - - - ${pkgs.OVMF.fd}/FV/OVMF_CODE.fd"
37+ ];
38+
39+ # --- OpenDeck -------------------------------------------------------------------
40+
41+ # Lets your user open the Stream Deck (40-streamdeck.rules on CachyOS).
42+ services.udev.packages = [ pkgs.opendeck ];
43+
44+ # --- Flatpak --------------------------------------------------------------------
45+
46+ # Bolt (RuneScape launcher) was a system Flatpak on CachyOS. System Flatpaks
47+ # live outside /home, so it is installed again here the first time the system
48+ # boots with a network; its data in ~/.var/app/com.adamcake.Bolt is kept.
49+ services.flatpak.enable = true;
50+ systemd.services.flatpak-bolt = {
51+ description = "Install Bolt from Flathub";
52+ wantedBy = [ "multi-user.target" ];
53+ wants = [ "network-online.target" ];
54+ after = [ "network-online.target" ];
55+ path = [ pkgs.flatpak ];
56+ serviceConfig = {
57+ Type = "oneshot";
58+ Restart = "on-failure";
59+ RestartSec = "1min";
60+ };
61+ script = ''
62+ flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
63+ flatpak info com.adamcake.Bolt >/dev/null 2>&1 || flatpak install --noninteractive flathub com.adamcake.Bolt
64+ '';
65+ };
66+}
@@ -1,0 +1,146 @@
1+# Nix itself, boot, locale, network and the user account.
2+{
3+ inputs,
4+ pkgs,
5+ ...
6+}:
7+{
8+ nixpkgs = {
9+ config.allowUnfree = true;
10+ overlays = [ (import ../../pkgs) ];
11+ };
12+
13+ nix = {
14+ settings = {
15+ experimental-features = [
16+ "nix-command"
17+ "flakes"
18+ ];
19+ # Lets you (as a wheel member, who can sudo anyway) use the binary caches
20+ # flakes ask for, such as the CachyOS kernel's.
21+ trusted-users = [
22+ "root"
23+ "@wheel"
24+ ];
25+ auto-optimise-store = true;
26+ };
27+ gc = {
28+ automatic = true;
29+ dates = "weekly";
30+ options = "--delete-older-than 14d";
31+ };
32+ # `nix shell nixpkgs#foo` uses the same nixpkgs as the system.
33+ registry.nixpkgs.flake = inputs.nixpkgs;
34+ nixPath = [ "nixpkgs=${inputs.nixpkgs}" ];
35+ };
36+
37+ boot = {
38+ loader = {
39+ systemd-boot = {
40+ enable = true;
41+ configurationLimit = 10;
42+ };
43+ efi.canTouchEfiVariables = true;
44+ timeout = 3;
45+ };
46+ initrd.systemd.enable = true;
47+ plymouth.enable = true;
48+ kernelParams = [
49+ "quiet"
50+ "splash"
51+ ];
52+ tmp.useTmpfs = true;
53+ };
54+
55+ zramSwap = {
56+ enable = true;
57+ algorithm = "zstd";
58+ memoryPercent = 100;
59+ priority = 100;
60+ };
61+
62+ time.timeZone = "Europe/Oslo";
63+ i18n.defaultLocale = "en_US.UTF-8";
64+ console.keyMap = "no";
65+
66+ networking = {
67+ networkmanager.enable = true;
68+ # CachyOS: /etc/sysctl.d/99-disable-ipv6.conf
69+ enableIPv6 = false;
70+ # ufw on CachyOS: drop incoming except SSH.
71+ firewall = {
72+ enable = true;
73+ allowedTCPPorts = [ 22 ];
74+ };
75+ };
76+ services.resolved.enable = true;
77+
78+ services.avahi = {
79+ enable = true;
80+ nssmdns4 = true;
81+ # ufw kept port 5353 closed on CachyOS too.
82+ openFirewall = false;
83+ };
84+
85+ services.openssh.enable = true;
86+ services.fstrim.enable = true;
87+
88+ users.users.vizie = {
89+ isNormalUser = true;
90+ description = "Gustaf Granström";
91+ shell = pkgs.zsh;
92+ extraGroups = [
93+ "wheel"
94+ "networkmanager"
95+ "video"
96+ "audio"
97+ "input"
98+ "lp"
99+ # Claude Desktop's Cowork VM needs /dev/kvm and /dev/vhost-vsock.
100+ "kvm"
101+ ];
102+ };
103+ programs.zsh.enable = true;
104+
105+ environment.systemPackages = with pkgs; [
106+ git
107+ gh
108+ wget
109+ curl
110+ rsync
111+ unzip
112+ unrar
113+ pv
114+ less
115+ file
116+ which
117+ vim
118+ nano
119+ micro
120+ neovim
121+ tmux
122+ btop
123+ duf
124+ glances
125+ fastfetch
126+ ripgrep
127+ fd
128+ jq
129+ yq-go
130+ usbutils
131+ pciutils
132+ lsof
133+ dmidecode
134+ hwinfo
135+ smartmontools
136+ hdparm
137+ ethtool
138+ btrfs-progs
139+ compsize
140+ exfatprogs
141+ dosfstools
142+ ntfs3g
143+ efibootmgr
144+ man-pages
145+ ];
146+}
@@ -1,0 +1,261 @@
1+# What CachyOS adds on top of plain Arch: its kernel and the cachyos-settings
2+# package (sysctl, udev rules, systemd defaults), ported file by file. The CachyOS
3+# file each part comes from is named next to it, so they can be compared when
4+# CachyOS changes something.
5+#
6+# Not ported, because it does nothing on this machine: the laptop audio power
7+# saving and touchpad rules, the old-Radeon (SI/CIK) amdgpu options, and
8+# debuginfod. Snapper root snapshots are replaced by NixOS generations: every
9+# rebuild is a boot menu entry you can go back to.
10+{
11+ inputs,
12+ lib,
13+ pkgs,
14+ ...
15+}:
16+let
17+ # cachyos-settings: /usr/bin/game-performance. Runs a game with the
18+ # "performance" power profile held, and the screensaver inhibited. Use it as
19+ # a Steam launch option: game-performance %command%
20+ game-performance = pkgs.writeShellApplication {
21+ name = "game-performance";
22+ runtimeInputs = [ pkgs.power-profiles-daemon pkgs.systemd ];
23+ text = ''
24+ if ! powerprofilesctl list | grep -q 'performance:'; then
25+ exec "$@"
26+ fi
27+ if [ -n "''${GAME_PERFORMANCE_SCREENSAVER_ON:-}" ]; then
28+ exec powerprofilesctl launch -p performance \
29+ -r "Launched with CachyOS game-performance utility" -- "$@"
30+ else
31+ exec systemd-inhibit --why "CachyOS game-performance is running" powerprofilesctl launch \
32+ -p performance -r "Launched with CachyOS game-performance utility" -- "$@"
33+ fi
34+ '';
35+ };
36+
37+ # cachyos-settings: /usr/bin/dlss-swapper. Steam launch option:
38+ # dlss-swapper %command%
39+ dlss-swapper = pkgs.writeShellScriptBin "dlss-swapper" ''
40+ export PROTON_ENABLE_NGX_UPDATER=1
41+ export DXVK_NVAPI_DRS_NGX_DLSS_RR_OVERRIDE=on
42+ export DXVK_NVAPI_DRS_NGX_DLSS_SR_OVERRIDE=on
43+ export DXVK_NVAPI_DRS_NGX_DLSS_FG_OVERRIDE=on
44+ export DXVK_NVAPI_DRS_NGX_DLSS_RR_OVERRIDE_RENDER_PRESET_SELECTION=render_preset_latest
45+ export DXVK_NVAPI_DRS_NGX_DLSS_SR_OVERRIDE_RENDER_PRESET_SELECTION=render_preset_latest
46+ exec "$@"
47+ '';
48+in
49+{
50+ # --- Kernel ----------------------------------------------------------------
51+
52+ # The overlay pins the kernel to the nixpkgs it was built with, so it comes
53+ # from the binary cache instead of compiling.
54+ nixpkgs.overlays = [ inputs.nix-cachyos-kernel.overlays.pinned ];
55+
56+ nix.settings = {
57+ substituters = [ "https://attic.xuyh0120.win/lantian" ];
58+ trusted-public-keys = [ "lantian:EeAUQ+W+6r7EtwnmYjeVwx5kOGEBpjlBfPlzGlTNvHc=" ];
59+ };
60+
61+ # Same build as linux-cachyos on CachyOS's znver4 repo: EEVDF scheduler,
62+ # 1000 Hz, full preemption, Clang ThinLTO, tuned for Zen 4 (also right for
63+ # this Zen 5 CPU, as on CachyOS).
64+ boot.kernelPackages = pkgs.cachyosKernels.linuxPackages-cachyos-latest-lto-zen4;
65+
66+ boot.kernelParams = [
67+ "nowatchdog"
68+ # cachyos-settings: udev/rules.d/30-zram.rules turns zswap off once zram is
69+ # up; it is always up here, so it is simply off from boot.
70+ "zswap.enabled=0"
71+ ];
72+
73+ # modprobe.d/blacklist.conf
74+ boot.blacklistedKernelModules = [
75+ "iTCO_wdt"
76+ "sp5100_tco"
77+ ];
78+
79+ # modules-load.d/ntsync.conf: Wine/Proton's NT synchronization primitives.
80+ boot.kernelModules = [ "ntsync" ];
81+
82+ boot.extraModprobeConfig = ''
83+ # modprobe.d/nvidia.conf
84+ options nvidia NVreg_InitializeSystemMemoryAllocations=0
85+ # cachyos-iw-set-regdomain: the Wi-Fi regulatory domain, from the time zone.
86+ options cfg80211 ieee80211_regdom="NO"
87+ '';
88+
89+ # --- sysctl.d/70-cachyos-settings.conf --------------------------------------
90+
91+ boot.kernel.sysctl = {
92+ # 150 rather than the file's 100: 30-zram.rules raises it to 150 as soon as
93+ # zram is active, which on CachyOS is always.
94+ "vm.swappiness" = 150;
95+ "vm.vfs_cache_pressure" = 50;
96+ "vm.dirty_bytes" = 268435456;
97+ "vm.page-cluster" = 0;
98+ "vm.dirty_background_bytes" = 67108864;
99+ "vm.dirty_writeback_centisecs" = 1500;
100+ "kernel.nmi_watchdog" = 0;
101+ "kernel.unprivileged_userns_clone" = 1;
102+ "kernel.printk" = "3 3 3 3";
103+ "kernel.kptr_restrict" = 2;
104+ "net.core.netdev_max_backlog" = 4096;
105+ "fs.file-max" = 2097152;
106+ };
107+
108+ # --- udev rules ---------------------------------------------------------------
109+
110+ services.udev.extraRules = ''
111+ # 60-ioschedulers.rules
112+ ACTION=="add|change", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
113+ ACTION=="add|change", KERNEL=="sd[a-z]*|mmcblk[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
114+ ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="kyber"
115+
116+ # 40-hpet-permissions.rules, 99-cpu-dma-latency.rules
117+ KERNEL=="rtc0", GROUP="audio"
118+ KERNEL=="hpet", GROUP="audio"
119+ DEVPATH=="/devices/virtual/misc/cpu_dma_latency", OWNER="root", GROUP="audio", MODE="0660"
120+
121+ # 50-sata.rules
122+ ACTION=="add", SUBSYSTEM=="scsi_host", KERNEL=="host*", ATTR{link_power_management_supported}=="1", ATTR{link_power_management_policy}=="*", ATTR{link_power_management_policy}="max_performance"
123+
124+ # 71-nvidia.rules: runtime power management for the NVIDIA GPU.
125+ ACTION=="add|bind", SUBSYSTEM=="pci", DRIVERS=="nvidia", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="auto"
126+ ACTION=="remove|unbind", SUBSYSTEM=="pci", DRIVERS=="nvidia", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", TEST=="power/control", ATTR{power/control}="on"
127+ '';
128+
129+ # --- tmpfiles.d ---------------------------------------------------------------
130+
131+ systemd.tmpfiles.rules = [
132+ # thp.conf, thp-shrinker.conf
133+ "w! /sys/kernel/mm/transparent_hugepage/defrag - - - - defer+madvise"
134+ "w! /sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none - - - - 409"
135+ # coredump.conf
136+ "e /var/lib/systemd/coredump - - - 3d"
137+ ];
138+
139+ # --- systemd ------------------------------------------------------------------
140+
141+ # system.conf.d/00-timeout.conf, 10-limits.conf
142+ systemd.settings.Manager = {
143+ DefaultTimeoutStartSec = "15s";
144+ DefaultTimeoutStopSec = "10s";
145+ DefaultLimitNOFILE = "2048:2097152";
146+ };
147+ # user.conf.d/00-timeout.conf, 10-limits.conf
148+ systemd.user.settings.Manager = {
149+ DefaultTimeoutStartSec = "15s";
150+ DefaultTimeoutStopSec = "10s";
151+ DefaultLimitNOFILE = "1024:1048576";
152+ };
153+
154+ # journald.conf.d/00-journal-size.conf
155+ services.journald.settings.Journal.SystemMaxUse = "50M";
156+
157+ # timesyncd.conf.d/10-timesyncd.conf
158+ services.timesyncd = {
159+ servers = [ "time.cloudflare.com" ];
160+ fallbackServers = [
161+ "time.google.com"
162+ "0.nixos.pool.ntp.org"
163+ "1.nixos.pool.ntp.org"
164+ ];
165+ };
166+
167+ # system/user@.service.d/delegate.conf: lets user services (and ananicy)
168+ # manage their own CPU, I/O and memory controllers.
169+ systemd.services."user@".serviceConfig.Delegate = "cpu cpuset io memory pids";
170+
171+ # system/rtkit-daemon.service.d/override.conf
172+ security.rtkit.enable = true;
173+ systemd.services.rtkit-daemon.serviceConfig.LogLevelMax = "info";
174+
175+ # systemd-oomd per-slice defaults (system.slice.d, user-.slice.d,
176+ # session.slice.d).
177+ systemd.oomd.enable = true;
178+ systemd.slices = {
179+ "system".sliceConfig = {
180+ ManagedOOMSwap = "kill";
181+ ManagedOOMMemoryPressure = "kill";
182+ ManagedOOMMemoryPressureLimit = "80%";
183+ };
184+ "user-".sliceConfig = {
185+ ManagedOOMSwap = "kill";
186+ ManagedOOMMemoryPressure = "kill";
187+ ManagedOOMMemoryPressureLimit = "80%";
188+ };
189+ };
190+ systemd.user.slices.session.sliceConfig.ManagedOOMPreference = "omit";
191+
192+ # system/pci-latency.service + /usr/bin/pci-latency
193+ systemd.services.pci-latency = {
194+ description = "Adjust latency timers for PCI peripherals";
195+ wantedBy = [ "multi-user.target" ];
196+ serviceConfig.Type = "oneshot";
197+ path = [ pkgs.pciutils ];
198+ script = ''
199+ setpci -v -s '*:*' latency_timer=20
200+ setpci -v -s '0:0' latency_timer=0
201+ setpci -v -d "*:*:04xx" latency_timer=80
202+ '';
203+ };
204+
205+ # security/limits.d/20-audio.conf
206+ security.pam.loginLimits = [
207+ {
208+ domain = "@audio";
209+ item = "rtprio";
210+ type = "-";
211+ value = "99";
212+ }
213+ {
214+ domain = "@audio";
215+ item = "nice";
216+ type = "-";
217+ value = "-11";
218+ }
219+ ];
220+
221+ hardware.wirelessRegulatoryDatabase = true;
222+
223+ # --- CachyOS services ---------------------------------------------------------
224+
225+ # ananicy-cpp with CachyOS's rules: renices games, compilers, browsers etc.
226+ services.ananicy = {
227+ enable = true;
228+ package = pkgs.ananicy-cpp;
229+ rulesProvider = pkgs.ananicy-rules-cachyos;
230+ };
231+
232+ # bpftune: auto-tunes network and other kernel settings (enabled on CachyOS).
233+ services.bpftune.enable = true;
234+
235+ # sched-ext schedulers, as CachyOS's scx-scheds. Off there too (scx_loader was
236+ # disabled); to try one:
237+ # services.scx = { enable = true; scheduler = "scx_lavd"; };
238+
239+ # power-profiles-daemon, with the profile you had set on CachyOS.
240+ services.power-profiles-daemon.enable = true;
241+ # Started with the daemon itself (wanting it from multi-user.target makes an
242+ # ordering cycle). Skipped where there is no performance profile, e.g. in a VM.
243+ systemd.services.power-profile-performance = {
244+ description = "Select the performance power profile";
245+ after = [ "power-profiles-daemon.service" ];
246+ bindsTo = [ "power-profiles-daemon.service" ];
247+ wantedBy = [ "power-profiles-daemon.service" ];
248+ serviceConfig.Type = "oneshot";
249+ path = [ pkgs.power-profiles-daemon ];
250+ script = ''
251+ if powerprofilesctl list | grep -q 'performance:'; then
252+ powerprofilesctl set performance
253+ fi
254+ '';
255+ };
256+
257+ environment.systemPackages = [
258+ game-performance
259+ dlss-swapper
260+ ];
261+}
@@ -1,0 +1,11 @@
1+{
2+ imports = [
3+ ./base.nix
4+ ./cachyos.nix
5+ ./nvidia.nix
6+ ./desktop.nix
7+ ./gaming.nix
8+ ./apps.nix
9+ ./vm.nix
10+ ];
11+}
@@ -1,0 +1,143 @@
1+# Hyprland under uwsm, the greetd login screen, audio, Bluetooth, fonts and the
2+# system side of theming.
3+{
4+ config,
5+ lib,
6+ pkgs,
7+ ...
8+}:
9+let
10+ greetdFiles = ../../config/greetd;
11+in
12+{
13+ programs.hyprland = {
14+ enable = true;
15+ # uwsm starts Hyprland as a systemd unit and brings up
16+ # graphical-session.target, which the user services in
17+ # modules/home/services.nix hang off.
18+ withUWSM = true;
19+ };
20+ programs.hyprlock.enable = true;
21+
22+ xdg.portal = {
23+ enable = true;
24+ # File pickers and settings for GTK apps; screen sharing comes from
25+ # xdg-desktop-portal-hyprland, which programs.hyprland adds.
26+ extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
27+ };
28+
29+ # --- Login screen -------------------------------------------------------------
30+
31+ # gtkgreet in a small Hyprland of its own, blurred over the wallpaper. Same
32+ # files as /etc/greetd on CachyOS; see config/greetd.
33+ services.greetd = {
34+ enable = true;
35+ settings = {
36+ terminal.vt = 1;
37+ default_session = {
38+ command = "${config.programs.hyprland.package}/bin/start-hyprland -- --config /etc/greetd/hyprland.lua";
39+ user = "greeter";
40+ };
41+ };
42+ };
43+ environment.etc = {
44+ "greetd/hyprland.lua".source = "${greetdFiles}/hyprland.lua";
45+ "greetd/hyprpaper.conf".source = "${greetdFiles}/hyprpaper.conf";
46+ "greetd/style.css".source = "${greetdFiles}/style.css";
47+ "greetd/session.sh".source = "${greetdFiles}/session.sh";
48+ "greetd/wallpapers/wallpaper.jpg".source = ../../config/hypr/wallpaper.jpg;
49+ };
50+
51+ # The greeter user has no settings of its own; these system-wide GSettings
52+ # defaults give gtkgreet the desktop's cursor. They replace
53+ # 99_greeter-cursor.gschema.override from CachyOS.
54+ programs.dconf = {
55+ enable = true;
56+ profiles.user.databases = [
57+ {
58+ settings."org/gnome/desktop/interface" = {
59+ cursor-theme = "BreezeX-Black";
60+ cursor-size = lib.gvariant.mkInt32 28;
61+ color-scheme = "prefer-dark";
62+ };
63+ }
64+ ];
65+ };
66+
67+ # --- Audio, Bluetooth, devices --------------------------------------------------
68+
69+ services.pipewire = {
70+ enable = true;
71+ alsa.enable = true;
72+ alsa.support32Bit = true;
73+ pulse.enable = true;
74+ jack.enable = true;
75+ };
76+ services.pulseaudio.enable = false;
77+
78+ hardware.bluetooth = {
79+ enable = true;
80+ powerOnBoot = true;
81+ };
82+ services.blueman.enable = true;
83+
84+ services.upower.enable = true;
85+ services.udisks2.enable = true;
86+ services.gvfs.enable = true;
87+ security.polkit.enable = true;
88+ # Secret Service (what Electron apps and libsecret store passwords in) from
89+ # KWallet's ksecretd, started on demand over D-Bus, as on CachyOS. Not GNOME
90+ # Keyring: it would claim the same D-Bus name.
91+ services.dbus.packages = [ pkgs.kdePackages.kwallet ];
92+
93+ # Battery of the Logitech mouse in the control centre (mouse-battery.sh).
94+ hardware.logitech.wireless.enable = true;
95+
96+ # --- Fonts -------------------------------------------------------------------
97+
98+ # The fonts installed on CachyOS. kitty.conf asks for JetBrains Mono, which
99+ # was never installed there, so kitty has been using Noto Sans Mono; add
100+ # nerd-fonts.jetbrains-mono here to get the font the config names.
101+ fonts.packages = with pkgs; [
102+ noto-fonts
103+ noto-fonts-cjk-sans
104+ noto-fonts-color-emoji
105+ nerd-fonts.noto
106+ nerd-fonts.meslo-lg
107+ nerd-fonts.fantasque-sans-mono
108+ dejavu_fonts
109+ liberation_ttf
110+ ttf-bitstream-vera
111+ open-sans
112+ cantarell-fonts
113+ adwaita-fonts
114+ font-awesome
115+ wqy_zenhei
116+ ];
117+
118+ # --- Theming (system side) ------------------------------------------------------
119+
120+ # Qt apps use qt6ct + Kvantum (QT_QPA_PLATFORMTHEME is set in hyprland.lua).
121+ qt = {
122+ enable = true;
123+ platformTheme = "qt5ct";
124+ style = "kvantum";
125+ };
126+
127+ environment.systemPackages = with pkgs; [
128+ # The greeter runs these from /etc/greetd/hyprland.lua.
129+ gtkgreet
130+ hyprpaper
131+ kitty
132+ procps
133+
134+ breezex-cursor
135+ papirus-icon-theme
136+ kdePackages.breeze-icons
137+ gnome-themes-extra
138+ adwaita-icon-theme
139+ kdePackages.qtstyleplugin-kvantum
140+ libsForQt5.qtstyleplugin-kvantum
141+ qt6Packages.qt6ct
142+ ];
143+}
@@ -1,0 +1,45 @@
1+# Gaming: what cachyos-gaming-meta and cachyos-gaming-applications install.
2+{ pkgs, ... }:
3+{
4+ programs.steam = {
5+ enable = true;
6+ # Your default compatibility tool on CachyOS; registers under the same
7+ # internal name, so Steam keeps using it (see pkgs/proton-cachyos).
8+ extraCompatPackages = [ pkgs.proton-cachyos ];
9+ protontricks.enable = true;
10+ # Steam's own CJK font, as wqy-zenhei on CachyOS.
11+ fontPackages = [ pkgs.wqy_zenhei ];
12+ };
13+
14+ # Run games with gamemoderun %command% (CPU governor, I/O priority, renice).
15+ programs.gamemode = {
16+ enable = true;
17+ enableRenice = true;
18+ };
19+
20+ programs.gamescope = {
21+ enable = true;
22+ capSysNice = true;
23+ };
24+
25+ # Xbox Wireless adapter (xone-dkms on CachyOS).
26+ hardware.xone.enable = true;
27+
28+ environment.systemPackages = with pkgs; [
29+ mangohud
30+ goverlay
31+ vkbasalt
32+ umu-launcher
33+ winetricks
34+ wineWow64Packages.staging
35+ lutris
36+ heroic
37+ faugus-launcher
38+ protonplus
39+ prismlauncher
40+ wowup-cf
41+ parsec-bin
42+ vulkan-tools
43+ mesa-demos
44+ ];
45+}
@@ -1,0 +1,43 @@
1+# RTX 3080 Ti with the open kernel module, as linux-cachyos-nvidia-open on
2+# CachyOS. Turned off in the test VM (see vm.nix).
3+{
4+ config,
5+ lib,
6+ pkgs,
7+ ...
8+}:
9+let
10+ cfg = config.dot.nvidia;
11+in
12+{
13+ options.dot.nvidia.enable = lib.mkEnableOption "the NVIDIA driver" // {
14+ default = true;
15+ };
16+
17+ config = lib.mkMerge [
18+ {
19+ hardware.graphics = {
20+ enable = true;
21+ enable32Bit = true;
22+ };
23+ }
24+
25+ (lib.mkIf cfg.enable {
26+ services.xserver.videoDrivers = [ "nvidia" ];
27+
28+ hardware.nvidia = {
29+ open = true;
30+ # 615.x, the same branch CachyOS has.
31+ package = config.boot.kernelPackages.nvidiaPackages.latest;
32+ modesetting.enable = true;
33+ nvidiaSettings = true;
34+ # Not set up on CachyOS either (no nvidia-suspend services enabled).
35+ powerManagement.enable = false;
36+ };
37+
38+ # libva-nvidia-driver: Firefox hardware video decoding (see the NVD_BACKEND
39+ # and LIBVA_DRIVER_NAME lines in hyprland.lua).
40+ hardware.graphics.extraPackages = [ pkgs.nvidia-vaapi-driver ];
41+ })
42+ ];
43+}
@@ -1,0 +1,60 @@
1+# The test VM: this exact system in QEMU, built with
2+# nixos-rebuild build-vm --flake .#desktop (or ./vm.sh)
3+# It shares the host's /nix/store, so it builds in seconds once the real
4+# system has. Only what can't work in a VM is changed: no NVIDIA, a virtual GPU,
5+# the configs copied in rather than linked to your checkout, and an automatic
6+# first login (log out, Super+Delete, to see the real login screen).
7+{ lib, ... }:
8+{
9+ virtualisation.vmVariant = {
10+ virtualisation = {
11+ memorySize = 8192;
12+ cores = 6;
13+ diskSize = 16384;
14+ resolution = {
15+ x = 1920;
16+ y = 1080;
17+ };
18+ # A virtual GPU with OpenGL passed through to the host (virgl); vm.sh
19+ # picks how it is displayed.
20+ qemu.options = [ "-device virtio-vga-gl" ];
21+
22+ # ssh -p 2222 vizie@localhost, with the key vm.sh hands in (see there).
23+ forwardPorts = [
24+ {
25+ from = "host";
26+ host.address = "127.0.0.1";
27+ host.port = 2222;
28+ guest.port = 22;
29+ }
30+ ];
31+ };
32+
33+ # /tmp/xchg is a folder shared with the host; vm.sh puts the public key
34+ # there. sshd won't read keys from under /tmp (anyone can write there), so
35+ # it is copied somewhere root owns at boot. Only the VM has this; the real
36+ # system's SSH logins are unchanged.
37+ services.openssh.authorizedKeysFiles = [ "/run/vm-ssh/authorized_keys" ];
38+ systemd.services.vm-ssh-key = {
39+ wantedBy = [ "sshd.service" ];
40+ before = [ "sshd.service" ];
41+ serviceConfig.Type = "oneshot";
42+ script = ''
43+ install -d -m755 /run/vm-ssh
44+ if [ -f /tmp/xchg/authorized_keys ]; then
45+ install -m644 /tmp/xchg/authorized_keys /run/vm-ssh/authorized_keys
46+ fi
47+ '';
48+ };
49+
50+ dot.nvidia.enable = false;
51+ home-manager.users.vizie.dot.liveConfig = lib.mkForce false;
52+
53+ # greetd's one-time initial session: straight into the desktop on boot; the
54+ # login screen comes up after you log out.
55+ services.greetd.settings.initial_session = {
56+ command = "/etc/greetd/session.sh";
57+ user = "vizie";
58+ };
59+ };
60+}
@@ -1,0 +1,32 @@
1+# BreezeX cursor theme (the BreezeX-Black variant used everywhere in this config).
2+{
3+ lib,
4+ stdenvNoCC,
5+ fetchurl,
6+}:
7+
8+stdenvNoCC.mkDerivation (finalAttrs: {
9+ pname = "breezex-cursor";
10+ version = "2.0.1";
11+
12+ src = fetchurl {
13+ url = "https://github.com/ful1e5/BreezeX_Cursor/releases/download/v${finalAttrs.version}/BreezeX-Black.tar.xz";
14+ hash = "sha256-dzt1UjdIFzQJ7mIoQbD3Sx6AYXpcWz3LtTp6w9BswjM=";
15+ };
16+
17+ sourceRoot = ".";
18+
19+ installPhase = ''
20+ runHook preInstall
21+ mkdir -p $out/share/icons
22+ cp -r BreezeX-Black $out/share/icons/
23+ runHook postInstall
24+ '';
25+
26+ meta = {
27+ description = "Extended KDE cursor theme (BreezeX-Black)";
28+ homepage = "https://github.com/ful1e5/BreezeX_Cursor";
29+ license = lib.licenses.gpl3Only;
30+ platforms = lib.platforms.all;
31+ };
32+})
@@ -1,0 +1,170 @@
1+# Anthropic's official Claude Desktop for Linux (beta), repackaged from the .deb in
2+# Anthropic's own apt repository: https://code.claude.com/docs/en/desktop-linux
3+#
4+# To update: pick the newest Filename/SHA256 pair from
5+# https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-amd64/Packages
6+# and change version and sha256 below (the index's SHA256 is in hex, as here).
7+#
8+# Cowork also needs QEMU/KVM, the vhost_vsock module and OVMF firmware at the
9+# Debian path it looks for; modules/nixos/apps.nix provides those.
10+{
11+ lib,
12+ stdenv,
13+ fetchurl,
14+ dpkg,
15+ autoPatchelfHook,
16+ makeWrapper,
17+ wrapGAppsHook3,
18+ alsa-lib,
19+ at-spi2-atk,
20+ at-spi2-core,
21+ cairo,
22+ cups,
23+ dbus,
24+ expat,
25+ glib,
26+ gtk3,
27+ libdrm,
28+ libgbm,
29+ libglvnd,
30+ libnotify,
31+ libpulseaudio,
32+ libsecret,
33+ libuuid,
34+ libxkbcommon,
35+ libayatana-appindicator,
36+ nspr,
37+ nss,
38+ pango,
39+ pipewire,
40+ systemd,
41+ vulkan-loader,
42+ libx11,
43+ libxcomposite,
44+ libxdamage,
45+ libxext,
46+ libxfixes,
47+ libxrandr,
48+ libxtst,
49+ libxshmfence,
50+ libxcb,
51+ libseccomp,
52+ libcap_ng,
53+ xdg-utils,
54+ qemu_kvm,
55+ socat,
56+ bubblewrap,
57+ openssh,
58+}:
59+
60+stdenv.mkDerivation (finalAttrs: {
61+ pname = "claude-desktop";
62+ version = "2.42304.0";
63+
64+ src = fetchurl {
65+ url = "https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${finalAttrs.version}_amd64.deb";
66+ sha256 = "74cd2ccc29d24165144e455cbc872c6cc2a24a263b83e73420e95f89166d4aa4";
67+ };
68+
69+ nativeBuildInputs = [
70+ dpkg
71+ autoPatchelfHook
72+ makeWrapper
73+ wrapGAppsHook3
74+ ];
75+
76+ buildInputs = [
77+ alsa-lib
78+ at-spi2-atk
79+ at-spi2-core
80+ cairo
81+ cups
82+ dbus
83+ expat
84+ glib
85+ gtk3
86+ libdrm
87+ libgbm
88+ libnotify
89+ libpulseaudio
90+ libsecret
91+ libuuid
92+ libxkbcommon
93+ nspr
94+ nss
95+ pango
96+ pipewire
97+ systemd
98+ libx11
99+ libxcomposite
100+ libxdamage
101+ libxext
102+ libxfixes
103+ libxrandr
104+ libxtst
105+ libxshmfence
106+ libxcb
107+ # The bundled virtiofsd (Cowork).
108+ libseccomp
109+ libcap_ng
110+ ];
111+
112+ # Loaded with dlopen at run time, so autoPatchelf can't see them.
113+ runtimeLibs = [
114+ libglvnd
115+ libnotify
116+ libpulseaudio
117+ libayatana-appindicator
118+ pipewire
119+ systemd
120+ vulkan-loader
121+ ];
122+
123+ dontWrapGApps = true;
124+
125+ unpackPhase = ''
126+ runHook preUnpack
127+ # Not dpkg-deb -x: the sandbox can't recreate chrome-sandbox's setuid bit.
128+ dpkg-deb --fsys-tarfile $src | tar -x --no-same-permissions --no-same-owner
129+ runHook postUnpack
130+ '';
131+
132+ installPhase = ''
133+ runHook preInstall
134+
135+ mkdir -p $out/lib $out/bin
136+ cp -r usr/lib/claude-desktop $out/lib/
137+ cp -r usr/share $out/
138+ rm -rf $out/share/lintian
139+
140+ # The setuid sandbox helper can't be setuid in the Nix store; Chromium uses
141+ # the user-namespace sandbox instead, which needs no helper.
142+ rm $out/lib/claude-desktop/chrome-sandbox
143+
144+ runHook postInstall
145+ '';
146+
147+ postFixup = ''
148+ makeWrapper $out/lib/claude-desktop/claude-desktop $out/bin/claude-desktop \
149+ "''${gappsWrapperArgs[@]}" \
150+ --prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath finalAttrs.runtimeLibs} \
151+ --prefix PATH : ${
152+ lib.makeBinPath [
153+ xdg-utils
154+ qemu_kvm
155+ socat
156+ bubblewrap
157+ openssh
158+ ]
159+ }
160+ '';
161+
162+ meta = {
163+ description = "Claude desktop app (official Linux beta build)";
164+ homepage = "https://claude.ai/download";
165+ license = lib.licenses.unfree;
166+ mainProgram = "claude-desktop";
167+ platforms = [ "x86_64-linux" ];
168+ sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
169+ };
170+})
@@ -1,0 +1,53 @@
1+# Packages this config adds to nixpkgs or patches. Used as an overlay, so the
2+# patched waybar replaces the stock one everywhere.
3+final: prev: {
4+ # Same three patches as the Arch PKGBUILD in the old dotfiles repo; see the
5+ # comments at the top of each patch.
6+ waybar = prev.waybar.overrideAttrs (old: {
7+ patches = (old.patches or [ ]) ++ [
8+ ./waybar/tray-sticky-custom-icons.patch
9+ ./waybar/tray-order.patch
10+ ./waybar/hyprland-lua-dispatch.patch
11+ ];
12+ });
13+
14+ xembedsniproxy = final.callPackage ./xembedsniproxy { };
15+ signal-desktop-themed = final.callPackage ./signal-desktop-themed { };
16+ claude-desktop = final.callPackage ./claude-desktop { };
17+ opendeck = final.callPackage ./opendeck { };
18+ breezex-cursor = final.callPackage ./breezex-cursor { };
19+ proton-cachyos = final.callPackage ./proton-cachyos { };
20+
21+ # python3 for the Waybar/Eww scripts and xwl-clipbridge: PyGObject (Gio, GLib,
22+ # Gdk) and python-xlib, with the typelibs those imports need on its own path,
23+ # since the scripts run from Waybar and systemd rather than a wrapped program.
24+ python3-desktop =
25+ let
26+ python = final.python3.withPackages (ps: [
27+ ps.pygobject3
28+ ps.xlib
29+ ]);
30+ in
31+ final.symlinkJoin {
32+ name = "python3-desktop";
33+ paths = [ python ];
34+ nativeBuildInputs = [ final.makeWrapper ];
35+ postBuild = ''
36+ for exe in $out/bin/python3 $out/bin/python; do
37+ wrapProgram "$exe" --prefix GI_TYPELIB_PATH : ${
38+ final.lib.makeSearchPath "lib/girepository-1.0" (
39+ map (p: p.out or p) [
40+ final.glib
41+ final.gobject-introspection
42+ final.gtk3
43+ final.pango
44+ final.gdk-pixbuf
45+ final.atk
46+ final.harfbuzz
47+ ]
48+ )
49+ }
50+ done
51+ '';
52+ };
53+}
@@ -1,0 +1,63 @@
1+# OpenDeck (Stream Deck software), repackaged from the upstream release .deb.
2+# Not in nixpkgs. It also ships the udev rules that let your user open the
3+# Stream Deck; modules/nixos/apps.nix installs them with services.udev.packages.
4+{
5+ lib,
6+ stdenv,
7+ fetchurl,
8+ dpkg,
9+ autoPatchelfHook,
10+ wrapGAppsHook3,
11+ gtk3,
12+ webkitgtk_4_1,
13+ libayatana-appindicator,
14+ libsoup_3,
15+ glib-networking,
16+}:
17+
18+stdenv.mkDerivation (finalAttrs: {
19+ pname = "opendeck";
20+ version = "2.14.1";
21+
22+ src = fetchurl {
23+ url = "https://github.com/nekename/OpenDeck/releases/download/v${finalAttrs.version}/opendeck_${finalAttrs.version}_amd64.deb";
24+ hash = "sha256-67C3lnjOD6Ra1TUsYDpiC89+k6l2toWVLYTutMLaEww=";
25+ };
26+
27+ nativeBuildInputs = [
28+ dpkg
29+ autoPatchelfHook
30+ wrapGAppsHook3
31+ ];
32+
33+ buildInputs = [
34+ gtk3
35+ webkitgtk_4_1
36+ libsoup_3
37+ glib-networking
38+ ];
39+
40+ unpackPhase = "dpkg-deb -x $src .";
41+
42+ installPhase = ''
43+ runHook preInstall
44+ mkdir -p $out
45+ cp -r usr/* $out/
46+ install -Dm644 etc/udev/rules.d/40-streamdeck.rules $out/lib/udev/rules.d/40-streamdeck.rules
47+ runHook postInstall
48+ '';
49+
50+ # The tray icon library is opened with dlopen.
51+ preFixup = ''
52+ gappsWrapperArgs+=(--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [ libayatana-appindicator ]})
53+ '';
54+
55+ meta = {
56+ description = "Linux software for the Elgato Stream Deck with Stream Deck plugin support";
57+ homepage = "https://github.com/nekename/OpenDeck";
58+ license = lib.licenses.gpl3Plus;
59+ mainProgram = "opendeck";
60+ platforms = [ "x86_64-linux" ];
61+ sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
62+ };
63+})
@@ -1,0 +1,58 @@
1+# CachyOS's Proton build (the proton-cachyos-slr package on CachyOS), from the
2+# upstream release, for programs.steam.extraCompatPackages. Same layout as
3+# nixpkgs' proton-ge-bin. The x86_64_v3 build is the one CachyOS installs on
4+# v3/v4/Zen 4 machines. It runs inside the Steam Linux Runtime, which Steam
5+# downloads by itself the first time a game uses it.
6+{
7+ lib,
8+ stdenvNoCC,
9+ fetchzip,
10+}:
11+
12+stdenvNoCC.mkDerivation (finalAttrs: {
13+ pname = "proton-cachyos";
14+ version = "11.0-20261014";
15+
16+ toolName = "proton-cachyos-${finalAttrs.version}-slr-x86_64_v3";
17+
18+ src = fetchzip {
19+ url = "https://github.com/CachyOS/proton-cachyos/releases/download/cachyos-${finalAttrs.version}-slr/${finalAttrs.toolName}.tar.xz";
20+ hash = "sha256-fHi/LfyvPj2qm8ma6JH+wEJiJO1Htwp6zkbdRSg6Xic=";
21+ };
22+
23+ dontUnpack = true;
24+ dontConfigure = true;
25+ dontBuild = true;
26+
27+ outputs = [
28+ "out"
29+ "steamcompattool"
30+ ];
31+
32+ installPhase = ''
33+ runHook preInstall
34+ echo "${finalAttrs.pname} should not be installed into environments. Use programs.steam.extraCompatPackages instead." > $out
35+ mkdir $steamcompattool
36+ ln -s $src/* $steamcompattool
37+ rm $steamcompattool/compatibilitytool.vdf
38+ cp $src/compatibilitytool.vdf $steamcompattool
39+ runHook postInstall
40+ '';
41+
42+ # Register under CachyOS's own internal name, "proton-cachyos-slr": Steam stores
43+ # the default and per-game compatibility tool by that name, so the choices made
44+ # on CachyOS keep working, and they survive updates (no version in the name).
45+ preFixup = ''
46+ substituteInPlace "$steamcompattool/compatibilitytool.vdf" \
47+ --replace-fail '"display_name" "${finalAttrs.toolName}"' '"display_name" "proton-cachyos-${finalAttrs.version} (steam linux runtime)"' \
48+ --replace-fail '"${finalAttrs.toolName}"' '"proton-cachyos-slr"'
49+ '';
50+
51+ meta = {
52+ description = "CachyOS's Proton build, for programs.steam.extraCompatPackages";
53+ homepage = "https://github.com/CachyOS/proton-cachyos";
54+ license = lib.licenses.bsd3;
55+ platforms = [ "x86_64-linux" ];
56+ sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
57+ };
58+})
@@ -1,0 +1,62 @@
1+# Signal Desktop with config/signal/theme.css applied. Signal has no user
2+# stylesheet, so this repacks the app.asar of nixpkgs' signal-desktop so that it
3+# 1. loads theme.css as stylesheets/custom.css after Signal's own stylesheets, and
4+# 2. creates the main window transparent, so Hyprland's blur shows through.
5+# Nothing else is changed; the electron, flags and profile (~/.config/Signal) are
6+# those of the stock package.
7+#
8+# This replaces signal/launch from the old dotfiles: the copy is built here, once,
9+# instead of at start-up. Edit theme.css, rebuild, and run config/signal/restart.
10+# If a Signal update moves one of the two places being patched, the build fails
11+# rather than shipping an unthemed or half-patched Signal.
12+{
13+ lib,
14+ runCommand,
15+ signal-desktop,
16+ asar,
17+ theme ? ../../config/signal/theme.css,
18+}:
19+
20+runCommand "signal-desktop-themed-${signal-desktop.version}"
21+ {
22+ nativeBuildInputs = [ asar ];
23+ inherit (signal-desktop) meta;
24+ passthru.unthemed = signal-desktop;
25+ }
26+ ''
27+ src=${signal-desktop}
28+ mkdir -p $out/bin $out/share
29+ for d in $src/share/*; do
30+ [ "$(basename "$d")" = signal-desktop ] || [ "$(basename "$d")" = applications ] || ln -s "$d" $out/share/
31+ done
32+
33+ mkdir -p $out/share/signal-desktop
34+ for f in $src/share/signal-desktop/*; do
35+ case "$(basename "$f")" in app.asar|app.asar.unpacked) ;; *) ln -s "$f" $out/share/signal-desktop/ ;; esac
36+ done
37+
38+ asar extract $src/share/signal-desktop/app.asar app
39+ cp ${theme} app/stylesheets/custom.css
40+
41+ # 1. Load the theme last, so it wins over Signal's own stylesheets.
42+ anchor='<link href="stylesheets/tailwind.css" rel="stylesheet" type="text/css" />'
43+ grep -qF "$anchor" app/background.html
44+ sed -i "s|$anchor|&\n <link href=\"stylesheets/custom.css\" rel=\"stylesheet\" type=\"text/css\" />|" app/background.html
45+ grep -qF 'href="stylesheets/custom.css"' app/background.html
46+
47+ # 2. Transparent main window. It is the only BrowserWindow created with
48+ # sandbox:false, which is what the pattern keys on; exactly one match or stop.
49+ sed -i 's/backgroundColor:[A-Za-z0-9_$]*,\(webPreferences:{[^}]*sandbox:!1\)/transparent:!0,backgroundColor:`#00000000`,\1/' app/bundles/main.js
50+ [ "$(grep -o 'transparent:!0,backgroundColor:`#00000000`' app/bundles/main.js | wc -l)" = 1 ]
51+
52+ asar pack --unpack '*.node' app $out/share/signal-desktop/app.asar
53+
54+ # Same launcher as the stock package, pointed at the themed app.asar.
55+ substitute $src/bin/signal-desktop $out/bin/signal-desktop \
56+ --replace-fail "$src/share/signal-desktop/app.asar" "$out/share/signal-desktop/app.asar"
57+ chmod +x $out/bin/signal-desktop
58+
59+ mkdir -p $out/share/applications
60+ substitute $src/share/applications/signal.desktop $out/share/applications/signal.desktop \
61+ --replace-quiet "$src/bin/signal-desktop" "$out/bin/signal-desktop"
62+ ''
@@ -1,0 +1,188 @@
1+diff --color -ruN a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp
2+--- a/include/modules/hyprland/backend.hpp 2026-10-04 00:13:00.535259516 +0200
3++++ b/include/modules/hyprland/backend.hpp 2026-10-04 00:13:24.130275037 +0200
4+@@ -3,6 +3,7 @@

Diff not shown: too large.

Diff not shown: too large.

Diff not shown: too large.

Diff not shown: too large.

Diff not shown: too large.

Diff not shown: too large.

addedvm.sh

Diff not shown: too large.