66 Commits

Author SHA1 Message Date
Hadi 56341489e8 Launchers are now toggles
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 12:30:52 +02:00
Hadi 0846223efd update flake
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 12:01:08 +02:00
Hadi e58bb947d3 Autologin, unlock the keyring with the luks password
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 10:43:40 +02:00
Hadi 9baaaa2bfe Less GUI, more TUI
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 10:32:14 +02:00
Hadi a7100f6ed5 fix shell path
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 00:15:12 +02:00
Hadi 828fe95bd4 Add "Hardened" in the readme
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 00:04:20 +02:00
Hadi be9ffa94c6 add obsidian to impermanence
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-14 00:01:34 +02:00
Hadi 7fd44889a5 update comments
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 23:53:14 +02:00
Hadi 3b32f061da Don't want to be lectured
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 23:49:27 +02:00
Hadi 2234f167ef set the password
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 23:47:08 +02:00
Hadi 07df6754de fix: waybar osd call
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 23:46:58 +02:00
Your Name 8e093d75ef Add disko with LUKS encryption and impermanence setup 2026-08-13 20:03:18 +00:00
Hadi ac0a706f1c Init impermanence
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 20:03:07 +02:00
Hadi b0a3843bcc Minimise the steam's games
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 17:33:41 +02:00
Hadi 6c5210cf66 add steam
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 14:57:34 +02:00
Hadi f704a5e9c4 Change pinned folders
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-08-13 14:42:07 +02:00
Hadi 0cef4320fd Waybar here i come (#58) 2026-08-13 13:55:30 +02:00
Hadi f1f22de7b7 feat: new zsh config
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-12 20:31:52 +02:00
Hadi f73f992540 change theme
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 21:20:44 +02:00
Hadi d29e164c99 remove spicetify
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 21:20:37 +02:00
Hadi 9787f677de remove docker from dev pkgs
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 21:12:21 +02:00
Hadi 16eb64de6e pin docker version
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 21:10:28 +02:00
Hadi f3d9a4b428 edit yazi config
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 20:50:05 +02:00
Hadi 30e14103d1 add sheets, pom & slides
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 20:24:21 +02:00
Hadi 23d7c0b460 Change themes
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-09 20:19:27 +02:00
Hadi 075f8c4a11 add chafa to nvim
Signed-off-by: Hadi <hadi@example.com>
2026-06-09 15:51:49 +02:00
Hadi 0c89a3d7e5 Edit dashboard, colors & add yazi
Signed-off-by: Hadi <hadi@example.com>
2026-06-09 15:19:37 +02:00
Hadi 9000ed8e28 remove full border
Signed-off-by: Hadi <hadi@example.com>
2026-06-09 15:19:07 +02:00
Hadi 9365de4822 Execute shell commands, surrounds.nvim, ...
Signed-off-by: Hadi <hadi@example.com>
2026-06-09 13:59:52 +02:00
Hadi 207c49fff8 edit yazi config
Signed-off-by: Hadi <hadi@example.com>
2026-06-09 13:29:26 +02:00
Hadi 120a6df4ed add chafa
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-08 20:51:27 +02:00
Hadi f036f367da add gh & gh-dash
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-08 20:28:40 +02:00
Hadi 6fc06f8616 add yazi plugins
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-08 20:04:48 +02:00
Hadi 9880cbeb0f impala -> wifitui
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-08 20:04:22 +02:00
Hadi bb6cb8c00e remove deadcode
Signed-off-by: Hadi <hadi@example.com>
2026-06-08 15:18:48 +02:00
Hadi beeb29276c nur-anotherhadi
Signed-off-by: Hadi <hadi@example.com>
2026-06-08 14:47:28 +02:00
Hadi a2aa6c3d08 add yazi, wiremix, impala, ..
Signed-off-by: Hadi <hadi@example.com>
2026-06-05 11:43:09 +02:00
Hadi 83b0a34256 update & add packages
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-04 22:29:02 +02:00
Hadi 1459743717 run in background
Signed-off-by: Hadi <hadi@example.com>
2026-06-04 16:35:15 +02:00
Hadi c76f503dcc add TUIs
Signed-off-by: Hadi <hadi@example.com>
2026-06-04 16:20:41 +02:00
Hadi 7931f68e41 init spotatui
Signed-off-by: Hadi <hadi@example.com>
2026-06-04 16:12:55 +02:00
Hadi 5f74119742 Merge branch 'main' of github.com:anotherhadi/nixy 2026-06-04 11:12:07 +02:00
Hadi e593974327 disable rounded_frame
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-01 22:23:04 +02:00
Hadi b245cfacf9 woops
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-01 22:22:50 +02:00
Hadi 38c00b16f1 change tuigreet config
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-06-01 20:36:40 +02:00
Hadi 5a0bf2e2ac Merge branch 'main' of github.com:anotherhadi/nixy 2026-06-01 20:36:18 +02:00
Hadi 66758b8c24 add clear alias
Signed-off-by: Hadi <hadi@example.com>
2026-06-01 16:08:35 +02:00
Hadi 4c4035acf3 add bookmark
Signed-off-by: Hadi <hadi@example.com>
2026-06-01 16:08:31 +02:00
Hadi cced6d4f3a remove hardtime
Signed-off-by: Hadi <hadi@example.com>
2026-06-01 11:39:21 +02:00
Hadi 8aa58a2d0d Merge branch 'main' of github.com:anotherhadi/nixy 2026-06-01 10:02:40 +02:00
Hadi 94903f7b8a add apps
Signed-off-by: Hadi <hadi@example.com>
2026-06-01 10:02:28 +02:00
Hadi bf44f38fa0 Change tuigreet bin
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-30 23:32:47 +02:00
Hadi dbc68cdfc5 fix keyboards
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-30 22:02:32 +02:00
Hadi d25bc65534 remove hardtime.nvim
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-30 20:25:15 +02:00
Hadi 8d4f4cfd41 getting back to tuigreet
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-30 20:25:08 +02:00
Hadi ff31020187 Add ilovetui colors
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
2026-05-29 20:08:20 +02:00
Hadi 93971606b9 use theme opacity
Signed-off-by: Hadi <hadi@example.com>
2026-05-28 11:58:35 +02:00
Hadi 092cf1cbd7 solve warnings
Signed-off-by: Hadi <hadi@example.com>
2026-05-28 11:50:53 +02:00
Hadi 37c6211cb8 windowrules for helium
Signed-off-by: Hadi <hadi@example.com>
2026-05-28 11:43:17 +02:00
Hadi 7793ac81c4 edit spf config
Signed-off-by: Hadi <hadi@example.com>
2026-05-28 10:10:56 +02:00
Hadi bc6dd4e7a2 add a new usb key
Signed-off-by: Hadi <hadi@example.com>
2026-05-28 09:22:49 +02:00
Hadi 517465799f transparent backgroud
Signed-off-by: Hadi <hadi@example.com>
2026-05-27 16:57:10 +02:00
Hadi e5dc149b99 add hardtime.nvim
Signed-off-by: Hadi <hadi@example.com>
2026-05-27 15:09:52 +02:00
Hadi bae9ba2206 add superfile
Signed-off-by: Hadi <hadi@example.com>
2026-05-27 15:02:23 +02:00
Hadi f578134eda add nvf bookmark
Signed-off-by: Hadi <hadi@example.com>
2026-05-27 15:02:18 +02:00
Hadi c082fd0e34 update flakes
Signed-off-by: Hadi <hadi@example.com>
2026-05-27 14:13:43 +02:00
152 changed files with 4227 additions and 3767 deletions
+1
View File
@@ -0,0 +1 @@
use flake
-134
View File
@@ -1,134 +0,0 @@
<div align="center">
<img alt="nixy logo" src="./.github/assets/logo.png" width="120px" />
</div>
<br>
# Nixy
<br>
<div align="center">
<a href="https://github.com/anotherhadi/nixy/stargazers">
<img src="https://img.shields.io/github/stars/anotherhadi/nixy?color={primarycolor}&labelColor={backgroundcolor}&style=for-the-badge&logo=starship&logoColor={primarycolor}">
</a>
<a href="https://github.com/anotherhadi/nixy/">
<img src="https://img.shields.io/github/repo-size/anotherhadi/nixy?color={primarycolor}&labelColor={backgroundcolor}&style=for-the-badge&logo=github&logoColor={primarycolor}">
</a>
<a href="https://nixos.org">
<img src="https://img.shields.io/badge/NixOS-unstable-blue.svg?style=for-the-badge&labelColor={backgroundcolor}&logo=NixOS&logoColor={primarycolor}&color={primarycolor}">
</a>
<a href="https://github.com/anotherhadi/nixy/blob/main/LICENSE">
<img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&colorA={backgroundcolor}&colorB={primarycolor}&logo=unlicense&logoColor={primarycolor}"/>
</a>
</div>
<br>
**Nixy simplifies and unifies** the Hyprland ecosystem with a modular, easily
customizable setup. It provides a structured way to manage your system
configuration and dotfiles with minimal effort. It includes _home-manager_,
_secrets_, and _custom theming_ all in one place.
**Features:**
- 💻 Hyprland & Caelestia: Preconfigured Hyprland ecosystem with Caelestia-shell (Ty to both projects!)
- 🎨 Consistent Theming: Base16 & Stylix-powered themes
- ⌨️ Vim-like Everywhere: Unified keybindings (Hyprland, nvim, vimium, etc.)
## Table of Content
{md_table_of_content}
## Screenshots
![Home](.github/assets/rose-pine/home.png)
![Flake & Spotify](.github/assets/rose-pine/flake-spotify.png)
![Browser and notification center](.github/assets/rose-pine/browser-and-notifications.png)
## Architecture
### 🏠 /home (User-level configuration)
Contains **dotfiles and settings** that apply to your user environment.
**Subfolders:**
- `programs` is a collection of apps configured with home-manager
- `system` is some "desktop environment" configuration
### 🐧 /nixos
Those are the system-level configurations. (audio, bluetooth, gpu, bootloader,
...)
### 🎨 /themes
This folder contains all system themes. Mainly
[stylix](https://stylix.danth.me/) configurations. Check out the available
themes and learn how to create your own in [THEMES.md](docs/THEMES.md)
### 💻 /hosts
This directory contains host-specific configurations. Each host includes:
- `configuration.nix` for system-wide settings
- `home.nix` for user-level configuration
- `variables.nix` for global variables
- `secrets/` for sensitive data
### 🖥️ /server-modules
This folder contains server-related nixos modules. (bitwarden, nextcloud, ...)
## Installation
1. [Fork](https://github.com/anotherhadi/nixy/fork) this repo and clone it to
your system:
```sh
git clone https://github.com/anotherhadi/nixy ~/.config/nixos
```
2. Copy the `hosts/laptop` folder, rename it to match your systems hostname,
and update `variables.nix` with your machines settings.
3. Copy your `hardware-configuration.nix` into your new host's folder to ensure
proper hardware support.
4. Register your new host in `flake.nix` by adding it under nixosConfigurations.
> [!IMPORTANT]
> `# CHANGEME` comments are placed throughout the config to
> indicate necessary modifications. Use the following command to quickly locate
> them:
>
> ```sh
> rg "CHANGEME" ~/.config/nixos
> ```
> [!TIP]
> When you add new files, don't forget to run `git add .` to add them to the git
> repository
5. Build the system
```sh
sudo nixos-rebuild switch --flake ~/.config/nixos#yourhostname
```
## Documentation
- [SERVER](docs/SERVER.md): Check out the server documentation
- [THEMES](docs/THEMES.md): How themes work and how to create your own
- [WALLPAPERS](https://github.com/anotherhadi/awesome-wallpapers): An awesome
collection of wallpapers
- [NEOVIM](docs/NEOVIM.md): How to use the Neovim config in your own flake
- [GROUPS](docs/GROUPS.md): Reusable package groups (dev, cybersecurity)
- [CONTRIBUTING](docs/CONTRIBUTING.md): How to contribute
- [LICENSE](LICENSE): MIT License
---
<div align="center">
<a href="https://github.com/anotherhadi/nixy">github</a> |
<a href="https://gitlab.com/anotherhadi_mirror/nixy">gitlab (mirror)</a> |
<a href="https://git.hadi.icu/anotherhadi/nixy">gitea (mirror)</a>
</div
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env bash
# Create the README.md file from the .github/assets/README_template.md file
[[ -d "./docs" ]] || (echo "Folder ./docs not found" && exit 1)
README_FILE="./README.md"
table_of_content=$(markdown-table-of-contents --start-by 2 ./.github/assets/README_template.md) # https://github.com/anotherhadi/markdown-table-of-contents
readme_content=$(cat "./.github/assets/README_template.md")
# Replace variables
readme_content=${readme_content//\{primarycolor\}/A89AD1}
readme_content=${readme_content//\{backgroundcolor\}/0b0b0b}
readme_content=${readme_content//\{md_table_of_content\}/$table_of_content}
echo "[//]: # (This file is autogenerated)" >"$README_FILE"
echo "$readme_content" >>"$README_FILE"
sed 's/\r//' "$README_FILE" >"/tmp/readme.md"
mv "/tmp/readme.md" "$README_FILE"
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
import re
import subprocess
import sys
from pathlib import Path
PATTERN = re.compile(r"<!-- exec: (.+?) -->.*?<!-- endexec -->", re.DOTALL)
def replace(match):
cmd = match.group(1).strip()
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
output = result.stdout
if result.returncode != 0:
print(
f"[inject-exec] command failed ({result.returncode}): {cmd}",
file=sys.stderr,
)
print(result.stderr, file=sys.stderr)
sys.exit(1)
output = re.sub(r"<!-- exec: .+? -->\n?|<!-- endexec -->\n?", "", output)
if output and not output.endswith("\n"):
output += "\n"
return f"<!-- exec: {cmd} -->\n{output}<!-- endexec -->"
def process(path):
content = Path(path).read_text()
new_content = PATTERN.sub(replace, content)
if new_content != content:
Path(path).write_text(new_content)
print(f"[inject-exec] updated {path}")
for p in sys.argv[1:]:
process(p)
-31
View File
@@ -1,31 +0,0 @@
on:
push:
paths:
- '.github/assets/README_template.md'
permissions:
contents: write
jobs:
update-readme:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./
steps:
- name: Check out the repository to the runner
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '^1.23'
- name: Install required dependencies
run: |
go install github.com/anotherhadi/markdown-table-of-contents@latest
- name: Run a script
run: |
chmod +x ./.github/scripts/create_readme.sh
./.github/scripts/create_readme.sh
git config user.name github-actions
git config user.email github-actions@github.com
git add .
git commit -m "Update README.md (auto)" && git push
exit 0
+5 -1
View File
@@ -1,3 +1,7 @@
.sops.yaml
.claude/
old/
docs/superpowers/
.superpowers/
result/
.pre-commit-config.yaml
.direnv/
+12
View File
@@ -0,0 +1,12 @@
keys:
- &primary age12yvtj49pfh3fqzqflscm0ek4yzrjhr6cqhn7x89gdxnlykq0xudq5c7334
- &work age1c8pawdsxptfslgrz2c56s39mrtnjzc5mm3hfzgr2wdwu2v6vfsdsupjsq6
creation_rules:
- path_regex: hosts/laptop/secrets/secrets.yaml$
key_groups:
- age:
- *primary
- path_regex: hosts/server/secrets/secrets.yaml$
key_groups:
- age:
- *primary
+12 -9
View File
@@ -1,4 +1,3 @@
[//]: # (This file is autogenerated)
<div align="center">
<img alt="nixy logo" src="./.github/assets/logo.png" width="120px" />
</div>
@@ -24,20 +23,23 @@
</div>
<br>
**Nixy simplifies and unifies** the Hyprland ecosystem with a modular, easily
**Nixy simplifies and unifies** the Hyprland ecosystem with a modular, minimalist & easily
customizable setup. It provides a structured way to manage your system
configuration and dotfiles with minimal effort. It includes _home-manager_,
_secrets_, and _custom theming_ all in one place.
**Features:**
- 💻 Hyprland & Caelestia: Preconfigured Hyprland ecosystem with Caelestia-shell (Ty to both projects!)
- 💻 Hyprland & TUIs: Preconfigured Hyprland ecosystem with a lot of cool TUIs
- 🎨 Consistent Theming: Base16 & Stylix-powered themes
- ⌨️ Vim-like Everywhere: Unified keybindings (Hyprland, nvim, vimium, etc.)
- 🔒 Hardened by Default: LUKS encryption, sops-nix secrets, impermanence and more
## Table of Content
- [Table of Content](#table-of-content)
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Screenshots](#screenshots)
- [Architecture](#architecture)
- [🏠 /home (User-level configuration)](#-home-user-level-configuration)
@@ -48,11 +50,13 @@ _secrets_, and _custom theming_ all in one place.
- [Installation](#installation)
- [Documentation](#documentation)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Screenshots
![Home](.github/assets/rose-pine/home.png)
![Flake & Spotify](.github/assets/rose-pine/flake-spotify.png)
![Browser and notification center](.github/assets/rose-pine/browser-and-notifications.png)
![Home](.github/assets/screenshots/1.png)
![Neovim and spotify](.github/assets/screenshots/2.png)
![Browser and file explorer](.github/assets/screenshots/3.png)
## Architecture
@@ -87,7 +91,7 @@ This directory contains host-specific configurations. Each host includes:
### 🖥️ /server-modules
This folder contains server-related nixos modules. (bitwarden, nextcloud, ...)
This folder contains server-related nixos modules. (gitea, cloudflared, self-hosted apps, ...)
## Installation
@@ -130,7 +134,6 @@ sudo nixos-rebuild switch --flake ~/.config/nixos#yourhostname
- [WALLPAPERS](https://github.com/anotherhadi/awesome-wallpapers): An awesome
collection of wallpapers
- [NEOVIM](docs/NEOVIM.md): How to use the Neovim config in your own flake
- [GROUPS](docs/GROUPS.md): Reusable package groups (dev, cybersecurity)
- [CONTRIBUTING](docs/CONTRIBUTING.md): How to contribute
- [LICENSE](LICENSE): MIT License
-3
View File
@@ -9,8 +9,5 @@ The process is straight-forward.
- Write your changes (new theme, bug fixes, issues fix, ...).
- Create a Pull Request against the main branch of Nixy.
> [!TIP]
> To update the README, change the `./.github/assets/README_template.md`
> [!IMPORTANT]
> Don't push your host's folder
-44
View File
@@ -1,44 +0,0 @@
# Groups
Groups are curated sets of packages exposed as flake outputs. Each group has two forms:
- **`homeManagerModules.<group>`** — full home-manager module (packages + files + systemd units)
- **`packages.<group>`** — standalone environment for `nix shell` (packages only)
## Available groups
- dev (go, bun, air, ...)
- cybersecurity (nmap, john, dirb, ffuf, ...)
For the Cybersecurity group, the home-manager module also sets up:
- `~/Cyber/wordlists/` with SecLists, fuzz4bounty, and hashcat rules
- `~/Cyber/tmp/` as a temporary workspace
## Use in another flake
Add this repo as an input:
```nix
inputs.nixy.url = "github:anotherhadi/nixy";
```
Import the home-manager module in your home configuration:
```nix
{ inputs, ... }: {
imports = [
inputs.nixy.homeManagerModules.cybersecurity
# inputs.nixy.homeManagerModules.dev
];
}
```
## Quick shell without installing
```sh
nix shell github:anotherhadi/nixy#cybersecurity
nix shell github:anotherhadi/nixy#dev
```
This drops you into a shell with all tools in `PATH`. No home-manager required, no wordlists or systemd units.
+1 -1
View File
@@ -37,4 +37,4 @@ Then import the home-manager module in your home configuration:
| `picker.nix` | Snacks picker + oil.nvim |
| `snacks.nix` | Snacks extras: image preview, zen mode, git signs, statuscolumn |
| `utils.nix` | Bufferline, lualine, copilot, lazygit, toggleterm, autocomplete |
| `mini.nix` | Mini.nvim suite: pairs, comment, icons, indentscope, diff, git |
| `mini.nix` | Mini.nvim suite: ai, clue, comment, diff, git, icons, indentscope, jump2d, pairs, starter, statusline, surround |
+8 -4
View File
@@ -5,7 +5,7 @@
This document describes the architecture and setup of the self-hosted **NixOS server**, which is securely accessible via **a Cloudflare Tunnel**.
The server is designed for private, secure, and easily manageable self-hosting of various services.
![server dashboard](../.github/assets/server_dashboard.png)
![server dashboard](../.github/assets/screenshots/server_dashboard.png)
## **Why This Setup?**
@@ -17,13 +17,17 @@ The server is designed for private, secure, and easily manageable self-hosting o
The server hosts several key applications:
- **NGINX**: Reverse proxy for routing traffic to services via my domain name.
- **AdGuard Home**: A self-hosted DNS ad blocker for network-wide ad and tracker filtering.
- **Glance**: An awesome dashboard! (See the screenshot above)
- **Arr Stack (Radarr, Sonarr, etc.)**: Automated media management tools for handling movies and TV shows. (legaly ofc)
- **Arr Stack (Radarr, Sonarr, etc.)**: Automated media management tools for handling movies and TV shows. (legally ofc)
- **Mealie**: A self-hosted recipe manager and meal planner with a clean user interface.
- **Stirling-PDF**: A powerful, locally hosted web application for editing, merging, and converting PDF files.
- **CyberChef**: The "Cyber Swiss Army Knife" for data analysis, decoding, and encryption tasks.
- **Mazanoke**: A utility service for image processing, specialized in format conversion and downgrading/optimization.
- **Umami**: Privacy-focused web analytics.
- **Gitea**: Self-hosted git forge.
- **Blog**: Personal website/blog.
- **iknowyou**: Personal project hosted in two containers (prod + demo).
- **Awesome Wallpapers**: Wallpaper browser served via NGINX inside a container.
- **SSH**: Secure remote access configuration for server management. (via browser too)
- **Security related stuff**: Cloudflared, Fail2Ban, Firewall
- **Security related stuff**: Cloudflared, Fail2Ban, Firewall, Kernel Hardening
+4 -12
View File
@@ -1,17 +1,9 @@
# Themes
Themes are defined in `themes`. Those themes define the colors, fonts, icons, etc, used by Hyprland and the apps/programs installed.
You can change the selected theme by changing the import statement of your host's variables.nix file.
Themes are defined in `themes/`. They define the colors, fonts, icons, wallpaper, and other visual settings used by Hyprland and all apps/programs.
You can change the selected theme by changing the import statement in your host's `variables.nix` file.
## Create your theme
Create a copy of one of the existing themes and change the variables.
Wallpapers are loaded from the [hadi's awesome-wallpapers](https://github.com/anotherhadi/awesome-wallpapers) repo.
## Gallery
### Rose-pine
![Home](../.github/assets/rose-pine/home.png)
![Flake & Spotify](../.github/assets/rose-pine/flake-spotify.png)
![Browser and notification center](../.github/assets/rose-pine/browser-and-notifications.png)
Create a copy of `themes/nixy.nix` and change the variables.
Wallpapers are loaded from my [awesome-wallpapers](https://github.com/anotherhadi/awesome-wallpapers) repo.
Generated
+296 -1147
View File
File diff suppressed because it is too large Load Diff
+50 -27
View File
@@ -6,78 +6,101 @@
'';
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11";
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
hyprland.url = "git+https://github.com/hyprwm/Hyprland?submodules=1";
stylix.url = "github:danth/stylix";
sops-nix.url = "github:Mic92/sops-nix";
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-hardware.url = "github:NixOS/nixos-hardware";
nvf.url = "github:notashelf/nvf";
bun2nix.url = "github:nix-community/bun2nix";
usbguard-tui.url = "github:anotherhadi/usbguard-tui";
jwt-tui.url = "github:anotherhadi/jwt-tui";
nvf-config = {
url = "path:./home/programs/tui/nvf";
inputs.nixpkgs.follows = "nixpkgs";
inputs.nvf.follows = "nvf";
};
nur = {
url = "github:nix-community/nur";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-index-database = {
url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
url = "github:nix-community/home-manager/release-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
caelestia-shell = {
url = "github:caelestia-dots/shell";
stylix = {
url = "github:nix-community/stylix/release-26.05";
inputs.nixpkgs.follows = "nixpkgs";
};
caelestia-cli = {
url = "github:caelestia-dots/cli";
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
spicetify-nix = {
url = "github:Gerg-L/spicetify-nix";
impermanence.url = "github:nix-community/impermanence";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
helium-browser = {
url = "github:oxcl/nix-flake-helium-browser";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
git-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
# Server
# FIXME: Deleted repo for now
# eleakxir.url = "github:anotherhadi/eleakxir";
nixarr.url = "github:rasmus-kirk/nixarr";
default-creds.url = "github:anotherhadi/default-creds";
blog.url = "github:anotherhadi/blog";
awesome-wallpapers.url = "github:anotherhadi/awesome-wallpapers";
iknowyou.url = "github:anotherhadi/iknowyou";
spilltea.url = "github:anotherhadi/spilltea";
helium-browser.url = "github:oxcl/nix-flake-helium-browser";
};
outputs = inputs @ {
nixpkgs,
nixpkgs-stable,
nixpkgs-unstable,
git-hooks,
...
}: let
system = "x86_64-linux";
pkgs-unstable = import nixpkgs-unstable {
inherit system;
config.allowUnfree = true;
};
pkgs = nixpkgs.legacyPackages.${system};
args = {
inherit
inputs
nixpkgs
system
pkgs-unstable
pkgs
;
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
};
merge = nixpkgs.lib.foldl nixpkgs.lib.recursiveUpdate {};
supportedSystems = ["x86_64-linux" "aarch64-linux"];
forAllSystems = f:
nixpkgs.lib.genAttrs supportedSystems
(system: f system (import nixpkgs {inherit system;}));
in
merge [
(import ./home/programs/nvf/flake.nix args)
(import ./home/programs/group/flake.nix args)
(import ./home/programs/nixy/flake.nix args)
(import ./home/programs/tui/nixy/flake.nix args)
{
formatter.${system} = pkgs.alejandra;
packages.${system}.nvim = inputs.nvf-config.packages.${system}.nvim;
apps.${system}.nvim = inputs.nvf-config.apps.${system}.nvim;
nixosConfigurations = {
h-laptop = import ./hosts/laptop/flake.nix args;
h-work = import ./hosts/work/flake.nix args;
jack = import ./hosts/server/flake.nix args;
};
devShells = forAllSystems (system: pkgs: {
default = import ./shell.nix {
inherit pkgs;
gitHooksLib = git-hooks.lib.${system};
};
});
}
];
}
-16
View File
@@ -1,16 +0,0 @@
{pkgs-stable, ...}: {
home.packages = with pkgs-stable; [
vlc # Video player
obsidian # Note taking app
textpieces # Manipulate texts
resources # Ressource monitor
gnome-clocks # Clocks app
gnome-text-editor # Basic graphic text editor
ticktick # Todo app
pinta # Image editor
onlyoffice-desktopeditors # Office suite
signal-desktop # Messaging app
librewolf
];
}
+34 -33
View File
@@ -1,39 +1,40 @@
{
pkgs,
pkgs-stable,
inputs,
system,
}:
(with pkgs-stable; [
httpie
trufflehog
sttr
wireshark
nmap
john
hashcat
metasploit
haiti
hydra
dnsrecon
whois
dig
nosqli
jwt-cli
nuclei
caido
gobuster
{pkgs}: (with pkgs; [
# Web
dirb
ffuf
sqlmap
katana
inetutils
samba
openvpn
whatweb
sqlmap
nosqli
# Hashes
hashcat
haiti
john
# Databases
mariadb
redis
# Network
inetutils
termshark # wireshark in TUI
whois
dig
nmap
samba
hydra
# Misc
metasploit
nuclei
openvpn
# Secrets
trufflehog
# Forensics
binwalk
pkgs.nur.repos.anotherhadi.spilltea
pkgs.nur.repos.anotherhadi.jwt-tui
])
++ [
inputs.spilltea.packages.${system}.default
inputs.jwt-tui.packages.${system}.default
]
+6 -8
View File
@@ -1,12 +1,6 @@
{
inputs,
pkgs-stable,
pkgs,
...
}: {
{pkgs, ...}: {
home.packages = import ./cybersecurity-packages.nix {
inherit pkgs pkgs-stable inputs;
system = pkgs.stdenv.hostPlatform.system;
inherit pkgs;
};
systemd.user.tmpfiles.rules = [
@@ -43,4 +37,8 @@
hash = "sha256-T0XkMHJZQiy63/j25nGTkaiDFjc+blmgEVGTAFgVylU=";
};
};
home.persistence."/persist".directories = [
"Cyber"
];
}
-22
View File
@@ -1,22 +0,0 @@
{
pkgs,
pkgs-stable,
inputs,
system,
}:
(with pkgs; [
go
bun
nodejs
claude-code
inputs.bun2nix.packages.${system}.default
])
++ (with pkgs-stable; [
air
duckdb
docker
python3
jq
just
nix-prefetch-github
])
+26 -5
View File
@@ -1,11 +1,32 @@
{
pkgs,
pkgs-stable,
inputs,
pkgs-unstable,
...
}: {
home.packages = import ./dev-packages.nix {
inherit pkgs pkgs-stable inputs;
system = pkgs.stdenv.hostPlatform.system;
home = {
packages = with pkgs-unstable;
[
go
claude-code
]
++ (with pkgs; [
nodejs
air
duckdb
python3
jq
nix-prefetch-github
rsync
]);
persistence."/persist" = {
directories = [
".claude"
];
files = [".claude.json"];
};
sessionPath = ["$HOME/.local/share/go/bin"];
sessionVariables.GOPATH = "$HOME/.local/share/go";
};
}
-21
View File
@@ -1,21 +0,0 @@
{
inputs,
pkgs,
pkgs-stable,
system,
...
}: let
devPackages = import ./dev-packages.nix {inherit pkgs pkgs-stable inputs system;};
cyberPackages = import ./cybersecurity-packages.nix {inherit pkgs pkgs-stable inputs system;};
in {
packages.${system} = {
dev = pkgs.buildEnv {
name = "dev-tools";
paths = devPackages;
};
cybersecurity = pkgs.buildEnv {
name = "cybersecurity-tools";
paths = cyberPackages;
};
};
}
-14
View File
@@ -1,14 +0,0 @@
{
pkgs-stable,
inputs,
...
}: {
home.packages = with pkgs-stable; [
peaclock
cbonsai
pipes
cmatrix
fastfetch
inputs.usbguard-tui.packages.${system}.default
];
}
@@ -7,6 +7,10 @@
name = "Nix 4 Cyber";
url = "https://n4c.hadi.icu";
}
{
name = "Infosec Notes - Anotherhadi";
url = "https://hadi.icu/notes";
}
{
name = "Cyberchef";
url = "https://cyberchef.hadi.icu";
@@ -31,6 +35,10 @@
name = "Exploit-DB";
url = "https://exploit-db.com";
}
{
name = "Reverse Shell Generator";
url = "https://revshells.com";
}
{
name = "GTFOBins";
url = "https://gtfobins.org/";
@@ -11,6 +11,18 @@
name = "Hyprland Wiki";
url = "https://wiki.hypr.land";
}
{
name = "Nerdfont Cheatsheet";
url = "https://www.nerdfonts.com/cheat-sheet";
}
{
name = "Helium Bangs";
url = "https://helium.computer/bangs";
}
{
name = "NVF Options";
url = "https://nvf.notashelf.dev/";
}
{
name = "Stylix Wiki";
url = "https://nix-community.github.io/stylix/";
@@ -43,6 +43,10 @@
name = "Gemini";
url = "https://gemini.google.com";
}
{
name = "Lumo";
url = "https://lumo.proton.me";
}
];
}
]
@@ -1,7 +1,7 @@
{
inputs,
config,
pkgs,
pkgs-unstable,
lib,
...
}: let
@@ -44,11 +44,11 @@
"--show-avatar-button=never"
];
originalPkg = (inputs.helium-browser.packages.${pkgs.stdenv.hostPlatform.system}.helium).override {
originalPkg = (inputs.helium-browser.packages.${pkgs-unstable.stdenv.hostPlatform.system}.helium).override {
flags = heliumFlags;
};
patchScript = pkgs.writeShellScript "helium-patch-prefs" ''
patchScript = pkgs-unstable.writeShellScript "helium-patch-prefs" ''
PROFILE="$HOME/.config/net.imput.helium/Default"
THEME_DIR="$PROFILE/Extensions/${themeId}/1.0_0"
PREFS="$PROFILE/Preferences"
@@ -59,13 +59,14 @@
if [ -f "$PREFS" ]; then
tmp=$(mktemp)
${lib.getExe pkgs.jq} \
${lib.getExe pkgs-unstable.jq} \
--arg id "${themeId}" \
--slurpfile manifest "$THEME_DIR/manifest.json" \
'
.helium.browser.layout = 3 |
.helium.browser.minimal_location_bar = true |
.helium.browser.show_dynamic_new_tab_button = false |
.helium.browser.rounded_frame = false |
.bookmark_bar.show_tab_groups = false |
.extensions.settings[$id] = {
"active_permissions": {"api": [], "explicit_host": [], "manifest_permissions": [], "scriptable_host": []},
@@ -84,12 +85,13 @@
fi
'';
baseWrapped = pkgs.symlinkJoin {
baseWrapped = pkgs-unstable.symlinkJoin {
name = "helium-wrapped";
paths = [originalPkg];
nativeBuildInputs = [pkgs.makeWrapper];
nativeBuildInputs = [pkgs-unstable.makeWrapper];
postBuild = ''
wrapProgram $out/bin/helium \
--set-default GTK_USE_PORTAL 1 \
--run ${lib.escapeShellArg (toString patchScript)}
'';
};
@@ -110,7 +112,7 @@ in {
xdg.desktopEntries.helium = {
name = "Helium";
genericName = "Navigateur Web";
genericName = "Web Browser";
exec = "${config.programs.helium.package}/bin/helium %U";
icon = "${config.programs.helium.package}/share/icons/hicolor/256x256/apps/helium.png";
terminal = false;
@@ -127,11 +129,15 @@ in {
xdg.desktopEntries.helium-private = {
name = "Helium (Private window)";
genericName = "Navigateur Web";
genericName = "Web Browser";
exec = "${config.programs.helium.package}/bin/helium --incognito %U";
icon = "${config.programs.helium.package}/share/icons/hicolor/256x256/apps/helium.png";
terminal = false;
categories = ["Network" "WebBrowser"];
mimeType = ["text/html" "text/xml" "application/xhtml+xml"];
};
home.persistence."/persist".directories = [
".config/net.imput.helium"
];
}
@@ -41,7 +41,7 @@ in {
DefaultBrowserSettingEnabled = false;
DeveloperToolsAvailability = 1;
DnsOverHttpsMode = "secure";
DnsOverHttpsMode = "automatic";
DnsOverHttpsTemplates = "https://dns.quad9.net/dns-query";
DefaultSearchProviderEnabled = true;
+21
View File
@@ -0,0 +1,21 @@
{pkgs, ...}: {
home.packages = with pkgs; [
vlc # Video player
obsidian # Note taking app
pinta # Image editor
onlyoffice-desktopeditors # Office suite
blanket # Listen to different sounds
signal-desktop # Messaging app
ticktick # Todo app
# Backup
thunar
gnome-text-editor
];
home.persistence."/persist".directories = [
".config/ticktick"
".config/Signal"
".config/obsidian"
];
}
@@ -6,19 +6,10 @@
home.packages = with pkgs; [
proton-vpn
proton-pass
proton-authenticator
];
# Fix Proton Authenticator desktop entry
xdg.desktopEntries = {
"Proton Authenticator" = {
name = "Proton Authenticator";
exec = "env WEBKIT_DISABLE_COMPOSITING_MODE=1 ${pkgs.proton-authenticator}/bin/proton-authenticator";
icon = "proton-authenticator";
type = "Application";
categories = ["Utility"];
terminal = false;
};
"Proton Calendar" = {
name = "Proton Calendar";
exec = ''${config.programs.helium.package}/bin/helium "https://calendar.proton.me"'';
@@ -36,4 +27,9 @@
terminal = false;
};
};
home.persistence."/persist".directories = [
".config/protonvpn"
".config/Proton Pass"
];
}
-19
View File
@@ -1,19 +0,0 @@
{pkgs, ...}: let
nightshift-toggle =
pkgs.writeShellScriptBin "nightshift-toggle"
# bash
''
if pidof "hyprsunset"; then
pkill hyprsunset
${pkgs.libnotify}/bin/notify-send "Night Shift Disabled" "Returning to full spectrum light. Filter disabled."
else
${pkgs.hyprsunset}/bin/hyprsunset -t 4500 &
${pkgs.libnotify}/bin/notify-send "Night Shift Activated" "Welcome to the warm side. Blue light filtering is now active."
fi
'';
in {
home.packages = with pkgs; [
hyprsunset
nightshift-toggle
];
}
-21
View File
@@ -1,21 +0,0 @@
{
inputs,
pkgs,
...
}: {
imports = [inputs.nvf.homeManagerModules.default];
programs.nvf = {
enable = true;
settings = {
imports = [
./options.nix
./languages.nix
./picker.nix
./snacks.nix
./keymaps.nix
./utils.nix
./mini.nix
];
};
};
}
-37
View File
@@ -1,37 +0,0 @@
{
inputs,
pkgs,
system,
...
}: let
nvimConfig = inputs.nvf.lib.neovimConfiguration {
inherit pkgs;
modules = [
./options.nix
./languages.nix
./picker.nix
./snacks.nix
./keymaps.nix
./utils.nix
./mini.nix
];
};
in {
packages.${system}.nvim = nvimConfig.neovim;
apps.${system}.nvim = {
type = "app";
program = "${nvimConfig.neovim}/bin/nvim";
};
defaultApp.${system} = {
type = "app";
program = "${nvimConfig.neovim}/bin/nvim";
};
homeManagerModules.nvim = {
imports = [
inputs.nvf.homeManagerModules.default
./default.nix
];
};
}
-48
View File
@@ -1,48 +0,0 @@
{
vim.luaConfigRC.mini-clue = ''
local miniclue = require('mini.clue')
miniclue.setup({
triggers = {
{ mode = 'n', keys = '<Leader>' },
{ mode = 'x', keys = '<Leader>' },
{ mode = 'n', keys = 'g' },
{ mode = 'x', keys = 'g' },
{ mode = 'n', keys = "'" },
{ mode = 'n', keys = '`' },
{ mode = 'n', keys = '"' },
{ mode = 'i', keys = '<C-r>' },
{ mode = 'c', keys = '<C-r>' },
{ mode = 'n', keys = '<C-w>' },
{ mode = 'n', keys = 'z' },
},
clues = {
miniclue.gen_clues.g(),
miniclue.gen_clues.marks(),
miniclue.gen_clues.registers(),
miniclue.gen_clues.windows({ submode_resize = true }),
miniclue.gen_clues.z(),
{ mode = 'n', keys = '<Leader>f', desc = '+find' },
{ mode = 'n', keys = '<Leader>s', desc = '+search' },
{ mode = 'n', keys = '<Leader>t', desc = '+terminal' },
{ mode = 'n', keys = '<Leader>u', desc = '+ui' },
{ mode = 'n', keys = '<Leader>l', desc = '+lsp' },
{ mode = 'n', keys = '<Leader>x', desc = '+trouble' },
},
window = { delay = 300 },
})
'';
vim.mini = {
ai.enable = true;
starter.enable = true;
comment.enable = true;
icons.enable = true;
indentscope.enable = true;
pairs.enable = true;
diff.enable = true;
git.enable = true;
jump2d.enable = true;
statusline.enable = true;
clue.enable = true;
};
}
-5
View File
@@ -1,5 +0,0 @@
{
wayland.windowManager.hyprland.settings.exec-once = [
"protonvpn-app --start-minimized &"
];
}
-33
View File
@@ -1,33 +0,0 @@
# Fzf is a general-purpose command-line fuzzy finder.
{
config,
lib,
...
}: let
accent = "#" + config.lib.stylix.colors.base0D;
foreground = "#" + config.lib.stylix.colors.base05;
muted = "#" + config.lib.stylix.colors.base03;
in {
programs.fzf = {
enable = true;
enableZshIntegration = true;
colors = lib.mkForce {
"fg+" = accent;
"bg+" = "-1";
"fg" = foreground;
"bg" = "-1";
"prompt" = muted;
"pointer" = accent;
};
defaultOptions = [
"--margin=1"
"--layout=reverse"
"--border=none"
"--info='hidden'"
"--header=''"
"--prompt='/ '"
"-i"
"--no-bold"
];
};
}
-188
View File
@@ -1,188 +0,0 @@
# My shell configuration
{
pkgs,
lib,
config,
...
}: let
fetch = config.theme.fetch; # neofetch, nerdfetch, pfetch
in {
home.packages = with pkgs; [
bat
ripgrep
tldr
witr
];
# Add go binaries to the PATH
home.sessionPath = ["$HOME/go/bin"];
home.sessionVariables = {
COLORTERM = "truecolor";
};
programs.zsh = {
enable = true;
enableCompletion = true;
autosuggestion.enable = true;
syntaxHighlighting = {
enable = true;
highlighters = [
"main"
"brackets"
"pattern"
"regexp"
"root"
"line"
];
};
historySubstringSearch.enable = true;
history = {
ignoreDups = true;
save = 10000;
size = 10000;
};
profileExtra = lib.optionalString (config.home.sessionPath != []) ''
export PATH="$PATH''${PATH:+:}${lib.concatStringsSep ":" config.home.sessionPath}"
'';
shellAliases = {
vim = "nvim";
vi = "nvim";
v = "nvim";
c = "clear";
clera = "clear";
celar = "clear";
e = "exit";
cd = "z";
ls = "eza --icons=always --no-quotes";
tree = "eza --icons=always --tree --no-quotes";
sl = "ls";
open = "${pkgs.xdg-utils}/bin/xdg-open";
cat = "bat --theme=base16 --color=always --paging=never --tabs=2 --wrap=never --plain";
mkdir = "mkdir -p";
obsidian-no-gpu = "env ELECTRON_OZONE_PLATFORM_HINT=auto obsidian --ozone-platform=x11";
wireguard-import = "nmcli connection import type wireguard file";
notes = "nvim ~/notes/index.md --cmd 'cd ~/notes' -c ':lua Snacks.picker.smart()'";
note = "notes";
tmp = "nvim /tmp/$(date | sed 's/ //g;s/\\.//g').md";
nix-shell = "nix-shell --command zsh";
# git
g = "lazygit";
ga = "git add";
gc = "git commit";
gcu = "git add . && git commit -m 'Update'";
gp = "git push";
gpl = "git pull";
gs = "git status";
gd = "git diff";
gco = "git checkout";
gcb = "git checkout -b";
gbr = "git branch";
grs = "git reset HEAD~1";
grh = "git reset --hard HEAD~1";
gaa = "git add .";
gcm = "git commit -m";
};
initContent =
# bash
''
bindkey -e
${
if fetch == "neofetch"
then pkgs.neofetch + "/bin/neofetch"
else if fetch == "nerdfetch"
then "nerdfetch"
else if fetch == "pfetch"
then "echo; ${pkgs.pfetch}/bin/pfetch"
else ""
}
function n4c() {
category=''${1:-all}
shift
args=''${*}
nix develop "github:nix4cyber/n4c#''${category}" ''${args} -c zsh
}
# search history based on what's typed in the prompt
autoload -U history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[OA" history-beginning-search-backward-end
bindkey "^[OB" history-beginning-search-forward-end
# General completion behavior
zstyle ':completion:*' completer _extensions _complete _approximate
# Use cache
zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path "$XDG_CACHE_HOME/zsh/.zcompcache"
# Complete the alias
zstyle ':completion:*' complete true
# Autocomplete options
zstyle ':completion:*' complete-options true
# Completion matching control
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
zstyle ':completion:*' keep-prefix true
# Group matches and describe
zstyle ':completion:*' menu select
zstyle ':completion:*' list-grouped false
zstyle ':completion:*' list-separator '''
zstyle ':completion:*' group-name '''
zstyle ':completion:*' verbose yes
zstyle ':completion:*:matches' group 'yes'
zstyle ':completion:*:warnings' format '%F{red}%B-- No match for: %d --%b%f'
zstyle ':completion:*:messages' format '%d'
zstyle ':completion:*:corrections' format '%B%d (errors: %e)%b'
zstyle ':completion:*:descriptions' format '[%d]'
# Colors
zstyle ':completion:*' list-colors ''${(s.:.)LS_COLORS}
# case insensitive tab completion
zstyle ':completion:*:*:cd:*' tag-order local-directories directory-stack path-directories
zstyle ':completion:*:*:cd:*:directory-stack' menu yes select
zstyle ':completion:*:-tilde-:*' group-order 'named-directories' 'path-directories' 'users' 'expand'
zstyle ':completion:*:*:-command-:*:*' group-order aliases builtins functions commands
zstyle ':completion:*' special-dirs true
zstyle ':completion:*' squeeze-slashes true
# Sort
zstyle ':completion:*' sort false
zstyle ":completion:*:git-checkout:*" sort false
zstyle ':completion:*' file-sort modification
zstyle ':completion:*:eza' sort false
zstyle ':completion:complete:*:options' sort false
zstyle ':completion:files' sort false
${lib.optionalString config.services.gpg-agent.enable ''
gnupg_path=$(ls $XDG_RUNTIME_DIR/gnupg)
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/gnupg/$gnupg_path/S.gpg-agent.ssh"
''}
# Allow foot to pipe command output
function precmd {
if ! builtin zle; then
print -n "\e]133;D\e\\"
fi
}
function preexec {
print -n "\e]133;C\e\\"
}
'';
};
}
-43
View File
@@ -1,43 +0,0 @@
# Spicetify is a spotify client customizer
{
pkgs,
pkgs-stable,
config,
lib,
inputs,
...
}: let
spicePkgs = inputs.spicetify-nix.legacyPackages.${pkgs.stdenv.hostPlatform.system};
accent = "${config.lib.stylix.colors.base0D}";
background = "${config.lib.stylix.colors.base00}";
in {
imports = [inputs.spicetify-nix.homeManagerModules.default];
stylix.targets.spicetify.enable = false;
programs.spicetify = {
enable = true;
spotifyPackage = pkgs-stable.spotify;
theme = lib.mkForce spicePkgs.themes.dribbblish;
colorScheme = "custom";
customColorScheme = {
button = accent;
button-active = accent;
tab-active = accent;
player = background;
main = background;
sidebar = background;
};
enabledExtensions = with spicePkgs.extensions; [
playlistIcons
historyShortcut
hidePodcasts
adblock
fullAppDisplay
keyboardShortcut
];
};
}
-178
View File
@@ -1,178 +0,0 @@
# Thunar is a file explorer
{
pkgs,
pkgs-stable,
config,
lib,
...
}: let
user = config.var.username;
in {
# ctrl + m to toggle the menubar
home.packages =
(with pkgs-stable; [
xfce.thunar
xfce.xfconf
xfce.tumbler
xfce.thunar-archive-plugin
xfce.thunar-volman
xfce.thunar-media-tags-plugin
p7zip
xarchiver
])
++ (with pkgs; [
# Icon themes: keep on global pkgs to avoid conflicts with other modules
papirus-icon-theme
material-icons
material-design-icons
material-symbols
]);
gtk = {
enable = true;
iconTheme = {
name = "Papirus-Dark";
package = pkgs.papirus-icon-theme;
};
gtk4.theme = null;
# bookmarks for the side pane
gtk3.bookmarks = [
"file:///home/${user}/Downloads Downloads"
"file:///home/${user}/Pictures Pictures"
"file:///home/${user}/.config/nixos NixOS"
"file:///home/${user}/dev Development"
];
};
qt.enable = true;
home.sessionVariables = {
XDG_ICON_DIR = "${pkgs.papirus-icon-theme}/share/icons/Papirus";
QS_ICON_THEME = "Papirus";
QT_STYLE_OVERRIDE = lib.mkForce "Fusion";
};
home.file.".config/xarchiver/xarchiverrc".text = ''
[xarchiver]
preferred_format=0
prefer_unzip=true
confirm_deletion=true
sort_filename_content=false
advanced_isearch=true
auto_expand=true
store_output=false
icon_size=2
show_archive_comment=false
show_sidebar=true
show_location_bar=true
show_toolbar=true
preferred_custom_cmd=
preferred_temp_dir=/tmp
preferred_extract_dir=./
allow_sub_dir=0
ensure_directory=true
overwrite=false
full_path=2
touch=false
fresh=false
update=false
store_path=false
updadd=true
freshen=false
recurse=true
solid_archive=false
remove_files=false
'';
home.file.".config/xfce4/xfconf/xfce-perchannel-xml/thunar.xml" = {
text = ''
<?xml version="1.1" encoding="UTF-8"?>
<channel name="thunar" version="1.0">
<property name="last-view" type="string" value="ThunarIconView"/>
<property name="last-icon-view-zoom-level" type="string" value="THUNAR_ZOOM_LEVEL_100_PERCENT"/>
<property name="last-window-maximized" type="bool" value="true"/>
<property name="last-separator-position" type="int" value="230"/>
<property name="last-statusbar-visible" type="bool" value="false"/>
<property name="last-menubar-visible" type="bool" value="false"/>
<property name="misc-single-click" type="bool" value="false"/>
<property name="shortcuts-icon-emblems" type="bool" value="true"/>
<property name="tree-icon-emblems" type="bool" value="true"/>
<property name="misc-file-size-binary" type="bool" value="false"/>
<property name="misc-thumbnail-draw-frames" type="bool" value="true"/>
<property name="misc-text-beside-icons" type="bool" value="false"/>
<property name="misc-change-window-icon" type="bool" value="false"/>
<property name="hidden-bookmarks" type="array">
<value type="string" value="computer:///"/>
<value type="string" value="recent:///"/>
<value type="string" value="network:///"/>
</property>
<property name="hidden-devices" type="array">
<value type="string" value="52FEA905FEA8E309"/>
</property>
<property name="last-toolbar-item-order" type="string" value="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17"/>
<property name="last-toolbar-visible-buttons" type="string" value="0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0"/>
<property name="last-location-bar" type="string" value="ThunarLocationButtons"/>
<property name="last-show-hidden" type="bool" value="false"/>
<property name="last-details-view-zoom-level" type="string" value="THUNAR_ZOOM_LEVEL_38_PERCENT"/>
<property name="last-details-view-column-widths" type="string" value="50,50,118,111,50,50,50,50,993,50,50,84,50,151"/>
<property name="last-toolbar-items" type="string" value="menu:1,undo:1,back:1,forward:1,open-parent:0,open-home:0,redo:0,zoom-in:0,zoom-out:0,zoom-reset:0,location-bar:1,view-switcher:1,search:1,view-as-icons:0,view-as-detailed-list:0,view-as-compact-list:0,toggle-split-view:0,reload:0,new-tab:0,new-window:0,uca-action-1700000000000001:0"/>
<property name="last-side-pane" type="string" value="THUNAR_SIDEPANE_TYPE_SHORTCUTS"/>
<property name="last-image-preview-visible" type="bool" value="false"/>
<property name="misc-use-csd" type="bool" value="true"/>
<property name="default-view" type="string" value="ThunarIconView"/>
<property name="misc-thumbnail-max-file-size" type="uint64" value="1073741824"/>
<property name="misc-symbolic-icons-in-toolbar" type="bool" value="true"/>
<property name="misc-date-style" type="string" value="THUNAR_DATE_STYLE_SIMPLE"/>
<property name="shortcuts-icon-size" type="string" value="THUNAR_ICON_SIZE_16"/>
<property name="tree-icon-size" type="string" value="THUNAR_ICON_SIZE_16"/>
<property name="misc-symbolic-icons-in-sidepane" type="bool" value="true"/>
<property name="misc-open-new-window-as-tab" type="bool" value="false"/>
<property name="misc-full-path-in-tab-title" type="bool" value="true"/>
<property name="misc-show-delete-action" type="bool" value="false"/>
</channel>
'';
force = true;
};
xdg.configFile."Thunar/uca.xml".text = ''
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<icon>utilities-terminal</icon>
<name>Open Terminal Here</name>
<unique-id>1700000000000001</unique-id>
<command>ghostty -d %f</command>
<description>Opens terminal in the selected folder</description>
<patterns>*</patterns>
<startup-notify/>
<directories/>
</action>
<action>
<icon></icon>
<name>Extract here</name>
<submenu></submenu>
<unique-id>1689618425925956-3</unique-id>
<command>xarchiver -x . %f</command>
<description>Extracts the archive into the directory it is located in.</description>
<range>*</range>
<patterns>*.tar.bz2;*.tar.gz;*.tar.xz;*.tar.Z;*.tar;*.taz;*.tb2;*.tbz;*.tbz2;*.tgz;*.txz;*.zip;*.bz2;*.docx;*.apk;*.gz;*.odt;</patterns>
<other-files/>
</action>
<action>
<icon>package-x-generic</icon>
<name>Compress here (tar.gz)</name>
<submenu></submenu>
<unique-id>1700000000000003</unique-id>
<command>tar -czvf %n.tar.gz %N</command>
<description>Creates a compressed archive (.tar.gz) of selected files/folders.</description>
<range>*</range>
<patterns>*</patterns>
<directories/>
<other-files/>
</action>
</actions>
'';
}
+134
View File
@@ -0,0 +1,134 @@
{
pkgs,
config,
...
}: let
c = config.lib.stylix.colors;
# TODO: Waiting for a version in nixpkgs
version = "1.11.2";
sources = {
"x86_64-linux" = {
url = "https://github.com/elio-fm/elio/releases/download/v${version}/elio-${version}-x86_64-unknown-linux-gnu.tar.gz";
hash = "sha256-9cW/xUA9p0LEYI3/j4MGPVZvcyJpV1JYKYBuHur4sTM=";
};
"aarch64-linux" = {
url = "https://github.com/elio-fm/elio/releases/download/v${version}/elio-${version}-aarch64-unknown-linux-gnu.tar.gz";
hash = "sha256-zg141Dp+jHwAqzoaCNvOoC7ZFeAX+cO8HZF5W7wz1Qs=";
};
};
source =
sources.${pkgs.stdenv.hostPlatform.system}
or (throw "elio: unsupported platform ${pkgs.stdenv.hostPlatform.system}");
elio = pkgs.stdenv.mkDerivation {
pname = "elio";
inherit version;
src = pkgs.fetchurl {inherit (source) url hash;};
nativeBuildInputs = [pkgs.autoPatchelfHook];
buildInputs = [pkgs.stdenv.cc.cc.lib];
installPhase = ''
runHook preInstall
install -Dm755 elio -t $out/bin
for size in 48 128 256 512; do
install -Dm644 \
"packaging/linux/icons/hicolor/''${size}x''${size}/apps/elio.png" \
"$out/share/icons/hicolor/''${size}x''${size}/apps/elio.png"
done
install -Dm644 packaging/linux/elio.desktop -t $out/share/applications
runHook postInstall
'';
meta = {
description = "Terminal file manager";
homepage = "https://github.com/elio-fm/elio";
license = pkgs.lib.licenses.mit;
mainProgram = "elio";
platforms = builtins.attrNames sources;
sourceProvenance = [pkgs.lib.sourceTypes.binaryNativeCode];
};
};
in {
home.packages = [elio];
xdg.desktopEntries.elio = {
name = "elio";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${elio}/bin/elio %f";
terminal = false;
icon = "elio";
mimeType = ["inode/directory"];
categories = ["System" "FileManager" "FileTools" "ConsoleOnly"];
};
xdg.configFile."elio/config.toml".text = ''
[places]
entries = [
"home",
"documents",
"downloads",
"pictures",
{ title = "Notes", path = "~/Notes" },
{ title = "Cyber", path = "~/Cyber" },
{ title = "Projects", path = "~/Projects" },
{ title = "NixOS Config", path = "~/.config/nixos" },
"trash",
]
'';
xdg.configFile."elio/theme.toml".text = ''
[palette]
bg = "#${c.base00}"
chrome = "#${c.base01}"
chrome_alt = "#${c.base01}"
chip_text = "#${c.base00}"
panel = "#${c.base00}"
panel_alt = "#${c.base00}"
surface = "#${c.base02}"
elevated = "#${c.base02}"
border = "#${c.base03}"
text = "#${c.base05}"
muted = "#${c.base04}"
accent = "#${c.base0D}"
accent_soft = "#${c.base02}"
accent_text = "#${c.base06}"
selected_bg = "#${c.base02}"
selected_border = "#${c.base0D}"
selection_bar = "#${c.base09}"
yank_bar = "#${c.base0B}"
cut_bar = "#${c.base08}"
progress_bar = "#${c.base0D}"
grid_selection_band = "#${c.base02}"
grid_yank_band = "#${c.base01}"
grid_cut_band = "#${c.base01}"
sidebar_active = "#${c.base02}"
button_bg = "#${c.base01}"
button_disabled_bg = "#${c.base01}"
path_bg = "#${c.base00}"
[preview.code]
fg = "#${c.base05}"
bg = "#${c.base01}"
selection_bg = "#${c.base02}"
selection_fg = "#${c.base06}"
caret = "#${c.base0C}"
line_highlight = "#${c.base01}"
line_number = "#${c.base03}"
comment = "#${c.base03}"
string = "#${c.base0B}"
constant = "#${c.base09}"
keyword = "#${c.base0E}"
function = "#${c.base0D}"
type = "#${c.base0A}"
parameter = "#${c.base08}"
tag = "#${c.base0C}"
operator = "#${c.base0C}"
macro = "#${c.base0F}"
invalid = "#${c.base08}"
'';
}
@@ -1,16 +1,20 @@
{pkgs, ...}: let
# Ghostty is a terminal emulator
{
pkgs,
config,
...
}: let
cursorShaders = pkgs.fetchFromGitHub {
owner = "sahaj-b";
repo = "ghostty-cursor-shaders";
rev = "06d4e90fb5410e9c4d0b3131584060adddf89406";
hash = "sha256-G/UIr1bKnxn1AcHl/4FL/jou6b7M2VeREslYVELxdmw=";
};
c = config.lib.stylix.colors;
in {
home.sessionVariables = {
TERMINAL = "ghostty";
TERM = "ghostty";
XMODIFIERS = "@im=none";
GTK_IM_MODULE = "simple";
};
programs.ghostty = {
@@ -21,6 +25,7 @@ in {
window-padding-x = 10;
confirm-close-surface = false;
window-padding-y = 10;
gtk-single-instance = true;
clipboard-read = "allow";
clipboard-write = "allow";
copy-on-select = "clipboard";
@@ -28,14 +33,6 @@ in {
custom-shader = "${cursorShaders}/cursor_warp.glsl";
custom-shader-animation = "always";
keybind = [
# "ctrl+j=goto_split:left"
# "ctrl+i=goto_split:up"
# "ctrl+k=goto_split:down"
# "ctrl+l=goto_split:right"
# "shift+ctrl+h=new_split:left"
# "shift+ctrl+j=new_split:down"
# "shift+ctrl+k=new_split:up"
# "shift+ctrl+l=new_split:right"
"shift+ctrl+tab=new_tab"
];
};
@@ -16,6 +16,9 @@ in {
"node_modules"
"result"
"result-*"
".superpowers/"
".claude/"
"docs/superpowers/"
];
settings = {
user.name = username;
@@ -40,13 +43,10 @@ in {
d = "diff";
ps = "!git push origin $(git rev-parse --abbrev-ref HEAD)";
pl = "!git pull origin $(git rev-parse --abbrev-ref HEAD)";
af = "!git add $(git ls-files -m -o --exclude-standard | sk -m)";
st = "status";
br = "branch";
df = "!git hist | peco | awk '{print $2}' | xargs -I {} git diff {}^ {}";
hist = ''log --pretty=format:"%Cgreen%h %Creset%cd %Cblue[%cn] %Creset%s%C(yellow)%d%C(reset)" --graph --date=relative --decorate --all'';
llog = ''log --graph --name-status --pretty=format:"%C(red)%h %C(reset)(%cd) %C(green)%an %Creset%s %C(yellow)%d%Creset" --date=relative'';
edit-unmerged = "!f() { git ls-files --unmerged | cut -f2 | sort -u ; }; hx `f`";
};
};
};
+23
View File
@@ -0,0 +1,23 @@
{config, ...}: let
c = config.lib.stylix.colors;
in {
home.file.".config/ilovetui/config.yaml".text = ''
colors:
base00: "#${c.base00}" # Background
base01: "#${c.base01}" # Lighter Background / Status Bars
base02: "#${c.base02}" # Selection Background
base03: "#${c.base03}" # Comments / Invisibles
base04: "#${c.base04}" # Dark Foreground / Status Bars
base05: "#${c.base05}" # Default Foreground
base06: "#${c.base06}" # Light Foreground
base07: "#${c.base07}" # Light Background
base08: "#${c.base08}" # Variables / Errors / Diff Deleted
base09: "#${c.base09}" # Integers / Constants / Booleans
base0a: "#${c.base0A}" # Classes / Warnings / Search Background
base0b: "#${c.base0B}" # Strings / Success / Diff Inserted
base0c: "#${c.base0C}" # Support / Regex / Escape Characters
base0d: "#${c.base0D}" # Functions / Methods / Headings / Accent
base0e: "#${c.base0E}" # Keywords / Storage / Diff Changed
base0f: "#${c.base0F}" # Embedded / Misc
'';
}
+38
View File
@@ -0,0 +1,38 @@
{
config,
lib,
...
}: {
home.sessionVariables = {
EDITOR = "nvim";
VISUAL = "nvim";
};
programs.nvf = {
enable = true;
settings = {
vim.luaConfigRC = lib.mkIf (config ? stylix && config.stylix.enable) {
hl-overrides = let
c = config.lib.stylix.colors;
in ''
local function apply_hl()
vim.api.nvim_set_hl(0, "MiniStarterHeader", { fg = "#${c.base0D}", bold = true })
vim.api.nvim_set_hl(0, "SnacksPickerBorder", { fg = "#${c.base0D}" })
vim.api.nvim_set_hl(0, "SnacksPickerTitle", { fg = "#${c.base0D}", bold = true })
end
apply_hl()
vim.api.nvim_create_autocmd("ColorScheme", { pattern = "*", callback = apply_hl })
'';
};
imports = [
./options.nix
./languages.nix
./picker.nix
./snacks.nix
./keymaps.nix
./utils.nix
./mini.nix
];
};
};
}
+133
View File
@@ -0,0 +1,133 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1777699697,
"narHash": "sha256-Eg9b/rq/ECYwNwEXs5i9wHyhxNI0JrYx2srdI2uZMaQ=",
"ref": "refs/heads/main",
"rev": "382052b74656a369c5408822af3f2501e9b1af81",
"revCount": 94,
"type": "git",
"url": "https://git.lix.systems/lix-project/flake-compat.git"
},
"original": {
"type": "git",
"url": "https://git.lix.systems/lix-project/flake-compat.git"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nvf",
"nixpkgs"
]
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"mnw": {
"locked": {
"lastModified": 1780772958,
"narHash": "sha256-VKKe8r4pwCGWZ3Yr9CPN129R4S3CKLSrlYqdYz3vKpM=",
"owner": "Gerg-L",
"repo": "mnw",
"rev": "0871dbf63a53610c95db04439ed8ea4d6ec9c160",
"type": "github"
},
"original": {
"owner": "Gerg-L",
"repo": "mnw",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1785133411,
"narHash": "sha256-Yjv0WEg39KRYS0rBdTbu6Fc/or/ihAKk13W9sQ6VWd0=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2f5a153c270b70cb0f8c11f46d96d6d3bc39f4e3",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1781216227,
"narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nvf": {
"inputs": {
"flake-compat": "flake-compat",
"flake-parts": "flake-parts",
"mnw": "mnw",
"nixpkgs": "nixpkgs_2",
"systems": "systems"
},
"locked": {
"lastModified": 1781997110,
"narHash": "sha256-6D6xtYN5t1kZGNd69eMZsRBkgX5Df1roErn/kFR1C+A=",
"owner": "notashelf",
"repo": "nvf",
"rev": "320f60b97075a58d38b90fc5e39f478421dbd38a",
"type": "github"
},
"original": {
"owner": "notashelf",
"repo": "nvf",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"nvf": "nvf"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+57
View File
@@ -0,0 +1,57 @@
{
description = "Hadi's NVF Neovim configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
nvf.url = "github:notashelf/nvf";
};
outputs = {
self,
nixpkgs,
nvf,
...
}: let
systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"];
forAllSystems = f: nixpkgs.lib.genAttrs systems f;
modules = [
./options.nix
./languages.nix
./picker.nix
./snacks.nix
./keymaps.nix
./utils.nix
./mini.nix
];
in {
packages = forAllSystems (system: let
pkgs = nixpkgs.legacyPackages.${system};
nvim = (nvf.lib.neovimConfiguration {inherit pkgs modules;}).neovim;
in {
inherit nvim;
default = nvim;
});
apps = forAllSystems (system: let
nvim = {
type = "app";
program = "${self.packages.${system}.nvim}/bin/nvim";
};
in {
inherit nvim;
default = nvim;
});
homeManagerModules = rec {
nvim = {
imports = [
nvf.homeManagerModules.default
./default.nix
];
};
default = nvim;
};
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.alejandra);
};
}
@@ -1,6 +1,7 @@
{
vim = {
globals.mapleader = " ";
keymaps = [
{
key = "s";
@@ -97,7 +98,7 @@
mode = "n";
silent = true;
action = "<cmd>set spell!<cr>";
desc = "Toggle spellLazyGitcheck";
desc = "Toggle spell check";
}
{
key = "<leader>uc";
@@ -144,6 +145,52 @@
desc = "Todo (Trouble)";
}
# Shell
{
key = "<leader>!";
mode = "n";
silent = true;
lua = true;
desc = "Insert command output";
action = ''
function()
local cmd = vim.fn.input("Command: ")
if cmd == "" then return end
local lines = vim.fn.systemlist(cmd)
while #lines > 0 and lines[#lines] == "" do
table.remove(lines)
end
if #lines == 0 then return end
vim.api.nvim_put(lines, "l", true, true)
end
'';
}
{
key = "<leader>!";
mode = "v";
silent = true;
lua = true;
desc = "Run command with selection";
action = ''
function()
local start_line = vim.fn.line("'<")
local end_line = vim.fn.line("'>")
local lines = vim.api.nvim_buf_get_lines(0, start_line - 1, end_line, false)
local input_text = table.concat(lines, "\n")
local cmd = vim.fn.input("$ ")
if cmd == "" then return end
local result = vim.fn.system({"bash", "-c", cmd}, input_text)
local output = vim.split(result, "\n", { plain = true })
if output[#output] == "" then table.remove(output) end
if #output == 0 then return end
vim.api.nvim_buf_set_lines(0, start_line - 1, end_line, false, output)
end
'';
}
# QOL
{
key = ">";
@@ -1,4 +1,4 @@
{...}: {
{pkgs, ...}: {
vim = {
diagnostics = {
enable = true;
@@ -24,6 +24,8 @@
autotagHtml = true;
context.enable = true;
highlight.enable = true;
textobjects.enable = true;
grammars = [pkgs.vimPlugins.nvim-treesitter.grammarPlugins.http];
};
lsp = {
enable = true;
@@ -82,7 +84,7 @@
go.enable = true;
markdown = {
enable = true;
format.type = ["prettierd"];
format.type = ["prettier"];
extensions = {
markview-nvim = {
enable = true;
+99
View File
@@ -0,0 +1,99 @@
{lib, ...}: {
vim.luaConfigRC.mini-clue = ''
local miniclue = require('mini.clue')
miniclue.setup({
triggers = {
{ mode = 'n', keys = '<Leader>' },
{ mode = 'x', keys = '<Leader>' },
{ mode = 'n', keys = 'g' },
{ mode = 'x', keys = 'g' },
{ mode = 'n', keys = "'" },
{ mode = 'n', keys = '`' },
{ mode = 'n', keys = '"' },
{ mode = 'i', keys = '<C-r>' },
{ mode = 'c', keys = '<C-r>' },
{ mode = 'n', keys = '<C-w>' },
{ mode = 'n', keys = 'z' },
},
clues = {
miniclue.gen_clues.g(),
miniclue.gen_clues.marks(),
miniclue.gen_clues.registers(),
miniclue.gen_clues.windows({ submode_resize = true }),
miniclue.gen_clues.z(),
{ mode = 'n', keys = '<Leader>f', desc = '+find' },
{ mode = 'n', keys = '<Leader>s', desc = '+search' },
{ mode = 'n', keys = '<Leader>t', desc = '+tools' },
{ mode = 'n', keys = '<Leader>u', desc = '+ui' },
{ mode = 'n', keys = '<Leader>l', desc = '+lsp' },
{ mode = 'n', keys = '<Leader>x', desc = '+trouble' },
{ mode = 'n', keys = '<Leader>!', desc = 'shell' },
{ mode = 'v', keys = '<Leader>!', desc = 'shell' },
},
window = { delay = 300 },
})
'';
vim.mini = {
ai.enable = true;
starter = {
enable = true;
setupOpts = {
header = ''
'';
footer = "";
items = [
{
name = "files";
action = lib.mkLuaInline "function() Snacks.picker.files() end";
section = "";
}
{
name = "directories";
action = lib.mkLuaInline "function() Snacks.explorer() end";
section = "";
}
{
name = "tmp";
action = lib.mkLuaInline ''
function()
local uuid = vim.fn.system("uuidgen"):match("(%S+)")
vim.cmd("edit /tmp/" .. uuid .. ".md")
end
'';
section = "";
}
{
name = "recent";
action = lib.mkLuaInline "function() Snacks.picker.recent() end";
section = "";
}
];
};
};
comment.enable = true;
icons.enable = true;
indentscope.enable = true;
pairs.enable = true;
diff.enable = true;
git.enable = true;
jump2d.enable = true;
statusline.enable = true;
clue.enable = true;
surround.enable = true;
};
}
@@ -6,10 +6,6 @@
'';
undoFile.enable = true;
notes.todo-comments.enable = true;
assistant.copilot = {
enable = true;
cmp.enable = true;
};
autocomplete = {
nvim-cmp = {
+42
View File
@@ -0,0 +1,42 @@
{
pkgs,
pkgs-unstable,
...
}: {
home.packages = with pkgs; [
# I love TUIs
caligula # User-friendly, lightweight TUI for disk imaging (ISO, USB BOOT)
tealdeer # Fast tldr client
sttr # A minimalist CyberChef-like, cross-platform CLI app for performing various operations on strings
slides # A terminal-based presentation tool that allows you to create and deliver presentations directly from the command line
pom # Pomodoro timer in your terminal
tabiew # A terminal-based CSV viewer and manipulator
jless # A pager for JSON
pkgs.nur.repos.anotherhadi.usbguard-tui # TUI for managing USBGuard rules
pkgs.nur.repos.anotherhadi.settuings # Terminal based settings (wifi, bluetooth, audio, ...)
pkgs.nur.repos.anotherhadi.fztea # Flipper Zero from the terminal
pkgs.nur.repos.anotherhadi.monitui # Edit hyprland's monitor configuration
dua # Tool to conveniently learn about the disk usage of directories
mcat # Cat command for documents / images / videos and more
lazyjournal
httpie # Command-line HTTP client, a user-friendly cURL replacement
gh # GitHub
figlet # Transform text into ASCII art
pastel # Command-line tool to generate, analyze, convert and manipulate colors
imagemagick # Image manipulation tool
chafa # Image to ANSI/Unicode converter and more.
# Just cool
tty-solitaire
bastet
peaclock
cbonsai
pipes
cmatrix
fastfetch
smassh # typing test, 10fastfinger like
];
home.persistence."/persist".directories = [".config/gh" ".config/gh-dash"];
}
@@ -1,6 +1,8 @@
{
programs.direnv = {
enable = true;
nix-direnv.enable = true;
nix-direnv = {
enable = true;
};
};
}
@@ -1,5 +1,8 @@
# Eza is a ls replacement
{
programs.zsh.initContent = ''
compdef eza=ls
'';
programs.eza = {
enable = true;
icons = "auto";
+50
View File
@@ -0,0 +1,50 @@
# Fzf is a general-purpose command-line fuzzy finder.
{
config,
lib,
...
}: let
accent = "#" + config.lib.stylix.colors.base0D;
foreground = "#" + config.lib.stylix.colors.base05;
muted = "#" + config.lib.stylix.colors.base03;
previewCmd = "bat --color=always --style=plain,numbers --line-range=:500 {}";
in {
programs.fzf = {
enable = true;
enableZshIntegration = true;
defaultCommand = "fd --type f --hidden --strip-cwd-prefix";
fileWidgetCommand = "fd --type f --hidden --strip-cwd-prefix";
fileWidgetOptions = ["--preview '${previewCmd}'"];
colors = lib.mkForce {
"fg+" = accent;
"bg+" = "-1";
"fg" = foreground;
"bg" = "-1";
"prompt" = muted;
"pointer" = accent;
};
defaultOptions = [
"--height=60%"
"--layout=reverse"
"--border=none"
"--prompt='/ '"
"--preview-window=right:65%:wrap:border-left"
"-i"
"--no-bold"
];
};
programs.zsh.initContent = lib.mkAfter ''
_fzf_file_no_hidden() {
local cmd result
cmd="''${FZF_DEFAULT_COMMAND/--hidden /}"
result=$(eval "''${cmd:-find . -type f}" | fzf --preview "${previewCmd}") \
&& LBUFFER+="$result"
zle reset-prompt
}
zle -N _fzf_file_no_hidden
'';
}
+152
View File
@@ -0,0 +1,152 @@
# My shell configuration
{
pkgs,
lib,
config,
...
}: {
home.packages = with pkgs; [
bat
ripgrep
];
home = {
sessionVariables = {
COLORTERM = "truecolor";
MANPAGER = "bat -l man -p";
};
persistence."/persist".files = [".zsh_history"];
};
programs.zsh = {
enable = true;
autocd = true;
enableCompletion = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
historySubstringSearch.enable = true;
plugins = [
{
name = "zsh-vi-mode";
src = pkgs.zsh-vi-mode;
file = "share/zsh-vi-mode/zsh-vi-mode.plugin.zsh";
}
];
history = {
ignoreDups = true;
findNoDups = true;
expireDuplicatesFirst = true;
ignoreSpace = true;
append = true;
save = 10000;
size = 10000;
};
setOptions = [
"NOBEEP"
"AUTOCD"
"NUMERIC_GLOB_SORT"
];
profileExtra = lib.optionalString (config.home.sessionPath != []) ''
export PATH="$PATH''${PATH:+:}${lib.concatStringsSep ":" config.home.sessionPath}"
'';
shellAliases = {
# Change default
vim = "nvim";
vi = "nvim";
cd = "z";
ls = "eza --icons=always --no-quotes";
tree = "eza --icons=always --tree --no-quotes";
cat = "bat --theme=base16 --color=always --paging=never --tabs=2 --wrap=never --plain";
mkdir = "mkdir -p";
nix-shell = "nix-shell --command zsh";
grep = "rg --color=auto";
diff = "diff --color=auto";
df = "df -h";
# Shortcuts
spt = "spotatui";
open = "${pkgs.xdg-utils}/bin/xdg-open";
notes = "nvim ~/Notes/index.md --cmd 'cd ~/notes' -c ':lua Snacks.picker.smart()'";
# git
g = "lazygit";
ga = "git add";
gc = "git commit";
gp = "git push";
gpl = "git pull";
gs = "git status";
gd = "git diff";
gco = "git checkout";
gcb = "git checkout -b";
gbr = "git branch";
grs = "git reset HEAD~1";
grh = "git reset --hard HEAD~1";
gaa = "git add .";
gcm = "git commit -m";
# Original binaries
ocat = "/run/current-system/sw/bin/cat";
ols = "/run/current-system/sw/bin/ls";
ocd = "builtin cd";
# Typo
clera = "clear";
celar = "clear";
claer = "clear";
sl = "ls";
};
initContent = lib.mkMerge [
(lib.mkOrder 550 ''
function zvm_config() {
ZVM_INSERT_MODE_CURSOR=$ZVM_CURSOR_BEAM
ZVM_NORMAL_MODE_CURSOR=$ZVM_CURSOR_BLOCK
ZVM_VISUAL_MODE_CURSOR=$ZVM_CURSOR_BLOCK
ZVM_VI_HIGHLIGHT_BACKGROUND=none
ZVM_VI_HIGHLIGHT_FOREGROUND=none
ZVM_VI_HIGHLIGHT_EXTRASTYLE=none
}
'')
# bash
''
# Suffix Aliases
alias -s {nix,md,txt,yml,yaml,go}=nvim
alias -s {json,jsonl}=jless
alias -s {csv,tsv,parquet,pqt,arrow,db,sqlite,xls,xlsx,xlsm,xlsb,fwf}=tw
alias -s {png,jpg,jpeg,gif,pdf}=xdg-open
# Global Aliases
alias -g G="| rg"
alias -g L="| less"
alias -g V="| nvim"
alias -g H="| head"
alias -g T="| tail"
alias -g JQ="| jq"
alias -g C="| wl-copy"
alias -g NE="2>/dev/null"
alias -g ND=">/dev/null"
alias -g NUL=">/dev/null 2>&1"
autoload zmv # Mv for multiple files
zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
zvm_after_init() {
bindkey '^[[1;5C' forward-word
bindkey '^[[1;5D' backward-word
bindkey '^F' _fzf_file_no_hidden
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
}
''
];
};
}
+106
View File
@@ -0,0 +1,106 @@
# Spotatui is a terminal user interface for Spotify, written in Rust. It allows you to control your Spotify playback and manage your playlists directly from the terminal.
{
config,
pkgs,
...
}: let
c = config.lib.stylix.colors;
rgb = base: "${c."${base}-rgb-r"}, ${c."${base}-rgb-g"}, ${c."${base}-rgb-b"}";
in {
home.packages = with pkgs; [
spotatui
];
home.persistence."/persist".directories = [".config/spotatui" ".config/spotify"];
home.file.".config/spotatui/config.yml".text = ''
keybindings:
back: q
next_page: ctrl-d
previous_page: ctrl-u
jump_to_start: ctrl-a
jump_to_end: ctrl-e
jump_to_album: a
jump_to_artist_album: A
jump_to_context: o
manage_devices: d
decrease_volume: '-'
increase_volume: +
toggle_playback: space
seek_backwards: <
seek_forwards: '>'
next_track: n
previous_track: p
force_previous_track: P
help: '?'
shuffle: ctrl-s
repeat: ctrl-r
search: /
submit: enter
copy_song_url: c
copy_album_url: C
audio_analysis: v
lyrics_view: B
cover_art_view: G
add_item_to_queue: z
show_queue: Q
open_settings: alt-,
save_settings: alt-s
listening_party: ctrl-p
like_track: F
behavior:
seek_milliseconds: 5000
volume_increment: 10
volume_percent: 100
tick_rate_milliseconds: 16
enable_text_emphasis: true
show_loading_indicator: true
enforce_wide_search_bar: true
enable_global_song_count: false
disable_mouse_inputs: false
enable_discord_rpc: false
discord_rpc_client_id: null
enable_announcements: false
announcement_feed_url: null
seen_announcement_ids:
- 2026-05-19-sonos-help-wanted
- 2026-02-27-major-refactor-complete
shuffle_enabled: false
liked_icon:
shuffle_icon: 🔀
repeat_track_icon: 🔂
repeat_context_icon: 🔁
playing_icon:
paused_icon:
set_window_title: true
visualizer_style: Equalizer
dismissed_announcements: []
relay_server_url: wss://spotatui-party.spotatui.workers.dev/ws
stop_after_current_track: false
sidebar_width_percent: 20
playbar_height_rows: 6
library_height_percent: 30
startup_behavior: continue
disable_auto_update: true
auto_update_delay: '0'
keepawake_enabled: true
theme:
preset: Custom
active: ${rgb "base0D"}
banner: ${rgb "base0C"}
error_border: ${rgb "base08"}
error_text: ${rgb "base08"}
hint: ${rgb "base0A"}
hovered: ${rgb "base0E"}
inactive: ${rgb "base03"}
playbar_background: Reset
playbar_progress: ${rgb "base0D"}
playbar_progress_text: ${rgb "base05"}
playbar_text: Reset
selected: ${rgb "base0D"}
text: Reset
background: Reset
header: Reset
highlighted_lyrics: ${rgb "base0B"}
'';
}
+49
View File
@@ -0,0 +1,49 @@
{
pkgs,
lib,
...
}: let
# wikiman ships without the ArchWiki/tldr-pages databases (too heavy to
# bundle). These are fetched declaratively from wikiman's own release
# snapshots and baked into a wrapped `wikiman` so `-S` shows them as
# installed offline, no manual `make source-*` step required.
# To refresh: bump the release tag/date below and update the hashes
# (`nix hash file --sri <downloaded file>`).
#
# Download size (compressed): ~17M ArchWiki + ~3.4M tldr-pages.
# Store size (unpacked): ~239M ArchWiki + ~147M tldr-pages (~385M total).
archWikiSrc = pkgs.fetchurl {
url = "https://github.com/filiparag/wikiman/releases/download/2.14.1/arch-wiki_20260810.source.tar.xz";
hash = "sha256-82aiDvwZ07m9CfBVYIDLSUpWds4QxUbCbCPmQ26j1XI=";
};
tldrPagesSrc = pkgs.fetchurl {
url = "https://github.com/filiparag/wikiman/releases/download/2.14.1/tldr-pages_20260810.source.tar.xz";
hash = "sha256-2vdszVMImSN1UF+OepKdj1v+lsG2jM68gcpnUJyqddw=";
};
# Real files (not symlinks): wikiman's source-detection does `find -type f`
# on this dir, which doesn't match symlinks, so `pkgs.symlinkJoin` alone
# won't work here.
data = pkgs.runCommand "wikiman-data" {nativeBuildInputs = [pkgs.xz];} ''
mkdir -p $out
cp -r ${pkgs.wikiman}/share $out/share
chmod -R u+w $out/share
tar xf ${archWikiSrc} -C $out --strip-components=1
tar xf ${tldrPagesSrc} -C $out --strip-components=1
'';
wikiman-full = pkgs.symlinkJoin {
name = "wikiman-full";
paths = [pkgs.wikiman];
nativeBuildInputs = [pkgs.makeWrapper];
postBuild = ''
rm $out/bin/wikiman
makeWrapper ${pkgs.wikiman}/bin/.wikiman-wrapped $out/bin/wikiman \
--prefix PATH : "${lib.makeBinPath [pkgs.fzf pkgs.ripgrep pkgs.gawk pkgs.w3m pkgs.coreutils pkgs.parallel]}" \
--set conf_sys_usr "${data}"
'';
meta.mainProgram = "wikiman";
};
in {
home.packages = [wikiman-full]; # Offline search engine for manual pages, ArchWiki and tldr
}
@@ -1,50 +0,0 @@
{
pkgs,
config,
...
}: let
rounding = config.theme.rounding + config.theme.gaps-out;
inherit (config.stylix) fonts;
in {
home.packages = with pkgs; [
papirus-icon-theme
];
programs.caelestia.settings = {
paths.sessionGif = ./session-gif-hacker-cat.gif;
background.enabled = false;
appearance = {
transparency = {
enabled = true;
base = 0.85;
layers = 0.4;
};
font.family = {
material = "Material Symbols Rounded";
mono = fonts.monospace.name;
sans = fonts.sansSerif.name;
};
};
utilities = {
enabled = true;
maxToasts = 4;
toasts = {
audioInputChanged = false;
audioOutputChanged = false;
capsLockChanged = false;
chargingChanged = true;
configLoaded = false;
dndChanged = true;
gameModeChanged = true;
numLockChanged = false;
nowPlaying = false;
kbLayoutChanged = false;
};
};
dashboard.showOnHover = false;
border = {
inherit rounding;
thickness = 8;
};
};
}
-68
View File
@@ -1,68 +0,0 @@
{
programs.caelestia.settings.bar = {
clock.showIcon = false;
popouts.activeWindow = false;
persistent = true;
status = {
showBattery = true;
showMicrophone = false;
showLockStatus = false;
showBluetooth = true;
showAudio = true;
showKbLayout = false;
showNetwork = true;
};
workspaces = {
activeIndicator = true;
activeLabel = "󰪥 ";
activeTrail = false;
label = " ";
occupiedBg = true;
occupiedLabel = "󰪥 ";
showWindows = false;
shown = 5;
};
entries = [
{
id = "logo";
enabled = true;
}
{
id = "workspaces";
enabled = true;
}
{
id = "spacer";
enabled = true;
}
{
id = "activeWindow";
enabled = true;
}
{
id = "spacer";
enabled = true;
}
{
id = "tray";
enabled = true;
}
{
id = "clock";
enabled = true;
}
{
id = "statusIcons";
enabled = true;
}
{
id = "power";
enabled = true;
}
];
tray = {
background = false;
recolour = false;
};
};
}
-74
View File
@@ -1,74 +0,0 @@
# Caelestia Shell Home Manager Configuration
# See https://github.com/caelestia-dots/shell
{
pkgs,
inputs,
lib,
...
}: {
imports = [
inputs.caelestia-shell.homeManagerModules.default
./bar.nix
./launcher.nix
./appearance.nix
./scheme.nix
./swappy.nix
];
programs.caelestia = {
enable = true;
systemd.enable = false;
settings = {
services.weatherLocation = "Paris";
general = {
apps = {
terminal = ["ghostty"];
audio = ["pavucontrol"];
explorer = ["thunar"];
};
idle = {
timeouts = [];
};
};
};
cli = {
enable = true;
settings.theme = {
enableTerm = false;
enableDiscord = false;
enableSpicetify = false;
enableBtop = false;
enableCava = false;
enableHypr = false;
enableGtk = false;
enableQt = false;
};
};
};
home.packages = with pkgs; [
gpu-screen-recorder
];
wayland.windowManager.hyprland.settings.exec-once = [
"uwsm app -- caelestia resizer -d"
"uwsm app -- caelestia shell -d"
"caelestia scheme set -n onedark"
"caelestia scheme set -n custom"
];
# shell.json is managed by home-manager (read-only symlink) but caelestia
# needs to write to it at runtime: replace the symlink with a mutable copy
home.activation.caelestiaWritableShellConfig = lib.hm.dag.entryAfter ["linkGeneration"] ''
if [ -L "$HOME/.config/caelestia/shell.json" ]; then
$DRY_RUN_CMD cp --remove-destination \
"$(readlink -f "$HOME/.config/caelestia/shell.json")" \
"$HOME/.config/caelestia/shell.json"
fi
'';
services.cliphist = {
enable = true;
allowImages = true;
};
}
-173
View File
@@ -1,173 +0,0 @@
{
programs.caelestia.settings = {
session.commands = {
shutdown = [
"systemctl"
"poweroff"
];
logout = [
"loginctl"
"lock-session"
];
hibernate = [
"systemctl"
"hibernate"
];
reboot = [
"systemctl"
"reboot"
];
};
launcher = {
actionPrefix = "/";
actions = [
{
name = "Calculator";
icon = "calculate";
description = "Do simple math equations (powered by Qalc)";
command = [
"autocomplete"
"calc"
];
enabled = true;
dangerous = false;
}
{
name = "Shutdown";
icon = "power_settings_new";
description = "Shutdown the system";
command = [
"systemctl"
"poweroff"
];
enabled = true;
dangerous = true;
}
{
name = "Reboot";
icon = "cached";
description = "Reboot the system";
command = [
"systemctl"
"reboot"
];
enabled = true;
dangerous = true;
}
{
name = "Logout";
icon = "exit_to_app";
description = "Log out of the current session";
command = [
"loginctl"
"terminate-user"
""
];
enabled = true;
dangerous = true;
}
{
name = "Lock";
icon = "lock";
description = "Lock the current session";
command = [
"loginctl"
"lock-session"
];
enabled = true;
dangerous = false;
}
{
name = "Sleep";
icon = "bedtime";
description = "Suspend then hibernate";
command = [
"systemctl"
"suspend-then-hibernate"
];
enabled = true;
dangerous = false;
}
{
name = "Restart caelestia";
icon = "cached";
description = "Restart caelestia";
command = [
"hyprctl"
"dispatch"
"exec"
"caelestia-shell kill | sleep 1 | caelestia-shell"
];
enabled = true;
dangerous = false;
}
{
name = "Emoji Picker";
icon = "mood";
description = "Toggle the emoji picker";
command = [
"caelestia"
"emoji"
"-p"
];
enabled = true;
dangerous = false;
}
{
name = "Clipboard History";
icon = "content_paste";
description = "Toggle the clipboard history";
command = [
"caelestia"
"clipboard"
];
enabled = true;
dangerous = false;
}
{
name = "Delete from Clipboard History";
icon = "content_paste_off";
description = "Delete a line from the clipboard history";
command = [
"caelestia"
"clipboard"
];
enabled = true;
dangerous = false;
}
{
name = "Night Shift";
icon = "moon_stars";
description = "Toggle Night Shift, the blue light filter";
command = ["nightshift-toggle"];
enabled = true;
dangerous = false;
}
{
name = "Hyprpicker";
icon = "colorize";
description = "Pick an hex color";
command = [
"hyprpicker"
"-a"
];
enabled = true;
dangerous = false;
}
];
dragThreshold = 50;
enableDangerousActions = false;
maxShown = 7;
specialPrefix = "@";
useFuzzy = {
apps = false;
actions = false;
schemes = false;
variants = false;
wallpapers = false;
};
showOnHover = false;
hiddenApps = [];
};
};
}
-137
View File
@@ -1,137 +0,0 @@
# This Nix module customizes the color scheme of the Caelestia CLI application using the Stylix base16 color palette.
{
pkgs,
inputs,
config,
...
}: let
colors = config.lib.stylix.colors;
customSchemeFile = pkgs.writeText "custom-dark.txt" ''
primary_paletteKeyColor ${colors.base0D}
secondary_paletteKeyColor ${colors.base04}
tertiary_paletteKeyColor ${colors.base08}
neutral_paletteKeyColor ${colors.base03}
neutral_variant_paletteKeyColor ${colors.base03}
background ${colors.base00}
onBackground ${colors.base05}
surface ${colors.base00}
surfaceDim ${colors.base00}
surfaceBright ${colors.base02}
surfaceContainerLowest ${colors.base01}
surfaceContainerLow ${colors.base01}
surfaceContainer ${colors.base01}
surfaceContainerHigh ${colors.base02}
surfaceContainerHighest ${colors.base02}
onSurface ${colors.base05}
surfaceVariant ${colors.base02}
onSurfaceVariant ${colors.base04}
inverseSurface ${colors.base05}
inverseOnSurface ${colors.base00}
outline ${colors.base03}
outlineVariant ${colors.base02}
shadow ${colors.base00}
scrim ${colors.base00}
surfaceTint ${colors.base0D}
primary ${colors.base0D}
onPrimary ${colors.base00}
primaryContainer ${colors.base0D}
onPrimaryContainer ${colors.base05}
inversePrimary ${colors.base0D}
secondary ${colors.base0C}
onSecondary ${colors.base00}
secondaryContainer ${colors.base02}
onSecondaryContainer ${colors.base0C}
tertiary ${colors.base08}
onTertiary ${colors.base00}
tertiaryContainer ${colors.base08}
onTertiaryContainer ${colors.base00}
error ${colors.base08}
onError ${colors.base00}
errorContainer ${colors.base08}
onErrorContainer ${colors.base05}
primaryFixed ${colors.base0D}
primaryFixedDim ${colors.base0D}
onPrimaryFixed ${colors.base00}
onPrimaryFixedVariant ${colors.base00}
secondaryFixed ${colors.base0C}
secondaryFixedDim ${colors.base0C}
onSecondaryFixed ${colors.base00}
onSecondaryFixedVariant ${colors.base02}
tertiaryFixed ${colors.base08}
tertiaryFixedDim ${colors.base08}
onTertiaryFixed ${colors.base00}
onTertiaryFixedVariant ${colors.base00}
term0 ${colors.base00}
term1 ${colors.base08}
term2 ${colors.base0B}
term3 ${colors.base0A}
term4 ${colors.base0D}
term5 ${colors.base0E}
term6 ${colors.base0C}
term7 ${colors.base05}
term8 ${colors.base03}
term9 ${colors.base08}
term10 ${colors.base0B}
term11 ${colors.base0A}
term12 ${colors.base0D}
term13 ${colors.base0E}
term14 ${colors.base0C}
term15 ${colors.base07}
rosewater ${colors.base06}
flamingo ${colors.base0F}
pink ${colors.base0E}
mauve ${colors.base0D}
red ${colors.base08}
maroon ${colors.base08}
peach ${colors.base09}
yellow ${colors.base0A}
green ${colors.base0B}
teal ${colors.base0C}
sky ${colors.base0C}
sapphire ${colors.base0C}
blue ${colors.base0D}
lavender ${colors.base0D}
klink ${colors.base0D}
klinkSelection ${colors.base0D}
kvisited ${colors.base0E}
kvisitedSelection ${colors.base0E}
knegative ${colors.base08}
knegativeSelection ${colors.base08}
kneutral ${colors.base0A}
kneutralSelection ${colors.base0A}
kpositive ${colors.base0B}
kpositiveSelection ${colors.base0B}
text ${colors.base05}
subtext1 ${colors.base04}
subtext0 ${colors.base03}
overlay2 ${colors.base03}
overlay1 ${colors.base03}
overlay0 ${colors.base02}
surface2 ${colors.base02}
surface1 ${colors.base01}
surface0 ${colors.base01}
base ${colors.base00}
mantle ${colors.base00}
crust ${colors.base00}
success ${colors.base0B}
onSuccess ${colors.base00}
successContainer ${colors.base0B}
onSuccessContainer ${colors.base05}
'';
colorsHash = builtins.hashString "sha256" (builtins.toJSON colors);
customCli =
inputs.caelestia-cli.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs
(oldAttrs: {
name = "${oldAttrs.name or "caelestia-cli"}-themed-${colorsHash}";
postUnpack = ''
mkdir -p $sourceRoot/src/caelestia/data/schemes/custom/main
cp ${customSchemeFile} $sourceRoot/src/caelestia/data/schemes/custom/main/dark.txt
echo "Custom scheme added to source"
'';
});
in {
programs.caelestia.cli.package = customCli;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

-11
View File
@@ -1,11 +0,0 @@
{
xdg.configFile."swappy/config".text = ''
[Default]
save_dir=~/Pictures/Screenshots
save_filename_format=screenshot-%Y%m%d-%H%M%S.png
show_panel=false
line_size=5
text_size=20
text_font=sans-serif
'';
}
+24
View File
@@ -0,0 +1,24 @@
# Clipboard history: cliphist watches the Wayland clipboard and stores every
# entry (text + images) so it can be recalled later via the `clipboard-menu`
# script (tofi). Uses the home-manager module so the watcher services are
# managed the same way as swaync/hypridle.
{pkgs, ...}: {
services.cliphist = {
enable = true;
allowImages = true;
};
systemd.user.services.cliphist-wipe = {
Unit = {
Description = "Wipe cliphist history on login and logout";
After = ["cliphist.service"];
};
Service = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${pkgs.cliphist}/bin/cliphist wipe";
ExecStop = "${pkgs.cliphist}/bin/cliphist wipe";
};
Install.WantedBy = ["default.target"];
};
}
+31
View File
@@ -0,0 +1,31 @@
# hypridle handles idle management: lock the screen, turn the display off, and
# suspend after periods of inactivity. The `caffeine-toggle` script pauses it.
{pkgs, ...}: {
services.hypridle = {
enable = true;
settings = {
general = {
# Avoid starting multiple hyprlock instances.
lock_cmd = "${pkgs.procps}/bin/pidof ${pkgs.hyprlock}/bin/hyprlock || ${pkgs.hyprlock}/bin/hyprlock";
before_sleep_cmd = "loginctl lock-session";
after_sleep_cmd = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on";
};
listener = [
{
timeout = 300; # 5 min → lock
on-timeout = "loginctl lock-session";
}
{
timeout = 360; # 6 min → screen off
on-timeout = "${pkgs.hyprland}/bin/hyprctl dispatch dpms off";
on-resume = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on";
}
{
timeout = 1800; # 30 min → suspend
on-timeout = "systemctl suspend";
}
];
};
};
}
+4 -8
View File
@@ -6,13 +6,9 @@
then "4"
else if animationSpeed == "medium"
then "2.5"
else "1.5";
borderDuration =
if animationSpeed == "slow"
then "10"
else if animationSpeed == "medium"
then "6"
else "3";
else if animationSpeed == "fast"
then "1.5"
else "0.5";
in {
wayland.windowManager.hyprland.settings = {
animations = {
@@ -38,7 +34,7 @@ in {
"windows, 1, ${animationDuration}, md3_decel, popin 60%"
"windowsIn, 1, ${animationDuration}, md3_decel, popin 60%"
"windowsOut, 1, ${animationDuration}, md3_accel, popin 60%"
"border, 1, ${borderDuration}, default"
"border, 1, ${animationDuration}, default"
"fade, 1, ${animationDuration}, md3_decel"
"layersIn, 1, ${animationDuration}, menu_decel, slide"
"layersOut, 1, ${animationDuration}, menu_accel"
+44 -50
View File
@@ -5,16 +5,18 @@
...
}: let
colors = config.lib.stylix.colors;
scripts = import ../waybar/scripts.nix {inherit pkgs config;};
border-size = config.theme.border-size;
mkMenu = menu: let
configFile = pkgs.writeText "config.yaml" (
lib.generators.toYAML {} {
anchor = "bottom-right";
border = "#${colors.base0D}80";
background = "#${colors.base01}EE";
anchor = "top";
border = "#${colors.base0D}EE";
border_width = border-size;
background = "#${colors.base01}FF";
color = "#${colors.base05}";
margin_right = 15;
margin_bottom = 15;
margin_top = 0;
rows_per_column = 5;
inherit menu;
@@ -22,8 +24,21 @@
);
in
pkgs.writeShellScriptBin "menu" ''
# Toggle: if wlr-which-key is already open, closing it is all this run should do.
if ${pkgs.procps}/bin/pkill -x wlr-which-key; then
exit 0
fi
exec ${lib.getExe pkgs.wlr-which-key} ${configFile}
'';
tofi-drun-toggle = pkgs.writeShellScriptBin "tofi-drun-toggle" ''
# tofi-drun is a distinct process name from the plain "tofi" binary
# used by the emoji/icon/clipboard pickers, so this can't close those.
if ${pkgs.procps}/bin/pkill -x tofi-drun; then
exit 0
fi
exec ${pkgs.tofi}/bin/tofi-drun
'';
in {
wayland.windowManager.hyprland.settings = {
"$mod" = "SUPER";
@@ -91,14 +106,13 @@ in {
"$mod,B, exec, uwsm app -- ${config.programs.helium.package}/bin/helium" # Browser
# Power
"$mod, X, global, caelestia:session" # Powermenu
(
"$shiftMod, X, exec, "
"$mod, X, exec, "
+ lib.getExe (mkMenu [
{
key = "l";
desc = "Lock";
cmd = "hyprctl dispatch global caelestia:lock";
cmd = "${pkgs.hyprlock}/bin/hyprlock";
}
{
key = "s";
@@ -115,31 +129,20 @@ in {
desc = "Power Off";
cmd = "systemctl poweroff";
}
{
key = "n";
desc = "Nightshift";
cmd = "nightshift-toggle";
}
{
key = "c";
desc = "Restart caelestia";
cmd = "hyprctl dispatch exec 'caelestia-shell kill | sleep 1 | caelestia-shell'";
}
])
)
# Quick launch
"$mod,RETURN, exec, uwsm app -- ${pkgs.ghostty}/bin/ghostty" # Ghostty (terminal)
"$mod,E, exec, uwsm app -- ${pkgs.thunar}/bin/thunar" # Thunar
"$shiftMod, E, exec, pkill fuzzel || caelestia emoji -p" # Emoji picker
"$mod, SPACE, global, caelestia:launcher" # Launcher
"$mod, N, exec, caelestia shell drawers toggle sidebar" # Sidebar (Notifications, quick actions)
"$mod, D, exec, caelestia shell drawers toggle dashboard" # Dashboard
"$mod,RETURN, exec, ${pkgs.ghostty}/bin/ghostty +new-window" # Ghostty (terminal, via daemon D-Bus)
"$mod,E, exec, ${pkgs.ghostty}/bin/ghostty +new-window -e elio" # Elio
"$mod, SPACE, exec, ${lib.getExe tofi-drun-toggle}" # Launcher (toggle)
"$mod, N, exec, ${pkgs.swaynotificationcenter}/bin/swaync-client -t" # Notification center
# Windows
"$mod,Q, killactive," # Close window
"$mod,F, fullscreen" # Toggle Fullscreen
"$shiftMod,F, togglefloating," # Toggle Floating
"$shiftMod, SPACE, exec, ${scripts.focus-toggle}/bin/focus-toggle" # Toggle focus mode
# Focus Windows
"$mod,H, movefocus, l" # Move focus left
@@ -151,11 +154,13 @@ in {
"$shiftMod,K, layoutmsg, addmaster" # Add to master
"$shiftMod,L, focusmonitor, 1" # Focus next monitor
# Special workspaces
"$mod, S, togglespecialworkspace, scratch" # Toggle scratch workspace
"$shiftMod, S, movetoworkspace, special:scratch" # Move to scratch workspace
# Utilities
"$shiftMod, SPACE, exec, caelestia shell gameMode toggle" # Toggle Focus/Game mode
"$shiftMod, S, global, caelestia:screenshotFreeze" # Capture region (freeze)
", Print, global, caelestia:screenshotFreeze" # Capture region (freeze)
"$shiftMod+Alt, S, global, caelestia:screenshot" # Capture region
", Print, exec, ${pkgs.hyprshot}/bin/hyprshot -m region" # Capture region
"$shiftMod, Print, exec, ${pkgs.hyprshot}/bin/hyprshot -m output" # Capture screen
]
++ (builtins.concatLists (
builtins.genList (
@@ -176,32 +181,21 @@ in {
bindl = [
# Brightness
", XF86MonBrightnessUp, global, caelestia:brightnessUp"
", XF86MonBrightnessDown, global, caelestia:brightnessDown"
", XF86MonBrightnessUp, exec, ${scripts.bright-up}/bin/bright-up"
", XF86MonBrightnessDown, exec, ${scripts.bright-down}/bin/bright-down"
# Media
", XF86AudioPlay, global, caelestia:mediaToggle"
", XF86AudioPause, global, caelestia:mediaToggle"
", XF86AudioNext, global, caelestia:mediaNext"
", XF86AudioPrev, global, caelestia:mediaPrev"
", XF86AudioStop, global, caelestia:mediaStop"
", XF86AudioPlay, exec, ${pkgs.playerctl}/bin/playerctl play-pause"
", XF86AudioPause, exec, ${pkgs.playerctl}/bin/playerctl play-pause"
", XF86AudioNext, exec, ${pkgs.playerctl}/bin/playerctl next"
", XF86AudioPrev, exec, ${pkgs.playerctl}/bin/playerctl previous"
", XF86AudioStop, exec, ${pkgs.playerctl}/bin/playerctl stop"
# Sound
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
", XF86AudioRaiseVolume, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ 0; wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"
", XF86AudioLowerVolume, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ 0; wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
];
bindin = [
# Launcher
"$mod, mouse:272, global, caelestia:launcherInterrupt"
"$mod, mouse:273, global, caelestia:launcherInterrupt"
"$mod, mouse:274, global, caelestia:launcherInterrupt"
"$mod, mouse:275, global, caelestia:launcherInterrupt"
"$mod, mouse:276, global, caelestia:launcherInterrupt"
"$mod, mouse:277, global, caelestia:launcherInterrupt"
"$mod, mouse_up, global, caelestia:launcherInterrupt"
"$mod, mouse_down, global, caelestia:launcherInterrupt"
", XF86AudioMute, exec, ${scripts.vol-mute}/bin/vol-mute"
", XF86AudioRaiseVolume, exec, ${scripts.vol-up}/bin/vol-up"
", XF86AudioLowerVolume, exec, ${scripts.vol-down}/bin/vol-down"
", XF86AudioMicMute, exec, ${scripts.mic-mute}/bin/mic-mute"
];
};
}
+49 -17
View File
@@ -1,5 +1,6 @@
# Hyprland is a dynamic tiling Wayland compositor that is highly customizable and performant.
{
pkgs-unstable,
pkgs,
config,
lib,
@@ -19,50 +20,52 @@ in {
./animations.nix
./bindings.nix
./polkitagent.nix
./keyboard-backlight.nix # CHANGEME: This is for omen laptop only
./hyprpaper.nix
];
home.packages = with pkgs; [
home.packages =
(with pkgs-unstable; [
qt5.qtwayland
qt6.qtwayland
hyprland-qtutils
])
++ (with pkgs; [
libsForQt5.qt5ct
qt6Packages.qt6ct
xcb-util-cursor
papirus-icon-theme
material-icons
material-design-icons
material-symbols
libxcb
hyprland-qtutils
adw-gtk3
hyprshot
hyprpicker
swappy
satty
imv
wf-recorder
wlr-randr
brightnessctl
gnome-themes-extra
dconf
wayland-utils
wayland-protocols
];
]);
wayland.windowManager.hyprland = {
enable = true;
configType = "hyprlang";
xwayland.enable = true;
systemd = {
enable = false;
variables = [
"--all"
]; # https://wiki.hyprland.org/Nix/Hyprland-on-Home-Manager/#programs-dont-work-in-systemd-services-but-do-on-the-terminal
};
systemd.enable = false;
package = null;
portalPackage = null;
settings = {
exec-once = [
"dbus-update-activation-environment --systemd --all &"
monitor = [
",preferred,auto,1" # default
];
monitor = [
",prefered,auto,1" # default
exec-once = [
"systemctl --user start app-com.mitchellh.ghostty.service"
];
env = [
@@ -83,7 +86,6 @@ in {
cursor = {
no_hardware_cursors = true;
default_monitor = "eDP-2";
};
general = {
@@ -121,8 +123,18 @@ in {
gesture = "3, horizontal, workspace";
layerrule = [
"match:namespace launcher, animation popin 70%"
"match:namespace swaync-control-center, animation slide right"
];
windowrule = [
"match:class .*, suppress_event maximize"
"match:class helium, suppress_event fullscreen"
"match:class helium, sync_fullscreen false"
"match:class ^(steam_app_.*)$, suppress_event fullscreen"
"match:class ^(steam_app_.*)$, sync_fullscreen false"
"match:class proton-authenticator, float on"
"match:class proton-authenticator, center on"
@@ -131,6 +143,10 @@ in {
"match:class protonvpn-app, float on"
"match:class protonvpn-app, center on"
"match:class protonvpn-app, size 500 400"
"match:title run-bg, float on"
"match:title run-bg, center on"
"match:title run-bg, size 700 80"
];
misc = {
@@ -161,4 +177,20 @@ in {
};
};
};
qt.enable = true;
gtk = {
enable = true;
iconTheme = {
name = "Papirus-Dark";
package = pkgs.papirus-icon-theme;
};
};
home.sessionVariables = {
XDG_ICON_DIR = "${pkgs.papirus-icon-theme}/share/icons/Papirus";
QS_ICON_THEME = "Papirus";
QT_STYLE_OVERRIDE = lib.mkForce "Fusion";
};
}
@@ -1,44 +0,0 @@
# Turn the keyboard red/off when the battery is low
# Include this one only if you have a omen laptop with RGB keyboard
{
pkgs,
config,
...
}: let
alert-when-low-battery = false;
keyboard-backlight = pkgs.writeShellScriptBin "keyboard-backlight" ''
function set_keyboard_backlight {
local color=$1
echo $color > /sys/devices/platform/hp-wmi/rgb_zones/zone00
echo $color > /sys/devices/platform/hp-wmi/rgb_zones/zone01
echo $color > /sys/devices/platform/hp-wmi/rgb_zones/zone02
echo $color > /sys/devices/platform/hp-wmi/rgb_zones/zone03
}
state="white"
set_keyboard_backlight ${config.lib.stylix.colors.base0D}
if [ "${toString alert-when-low-battery}" = "false" ]; then
exit 0
fi
while true; do
BATTERY_LEVEL=$(cat /sys/class/power_supply/BAT*/capacity)
IS_CHARGING=$(cat /sys/class/power_supply/BAT*/status)
if [[ $BATTERY_LEVEL -le 10 && $IS_CHARGING != "Charging" ]]; then
if [[ $state == "red" ]];then
state="white"
set_keyboard_backlight "000000"
else
state="red"
set_keyboard_backlight "FF0000"
fi
else
state="white"
set_keyboard_backlight ${config.lib.stylix.colors.base0D}
fi
sleep 2
done
'';
command = "bash ${keyboard-backlight}/bin/keyboard-backlight &";
in {
wayland.windowManager.hyprland.settings.exec-once = [command];
}
+82
View File
@@ -0,0 +1,82 @@
{config, ...}: let
c = config.lib.stylix.colors;
in {
stylix.targets.hyprlock.enable = false;
programs.hyprlock = {
enable = true;
settings = {
background = [
{
monitor = "";
color = "rgba(${c.base00}FF)";
}
];
label = [
{
monitor = "";
text = ''cmd[update:0] echo "[$USER@$(hostname) ~]"'';
color = "rgb(${c.base0D})";
font_size = 13;
font_family = "Maple Mono NF";
position = "48, -48";
halign = "left";
valign = "top";
}
{
monitor = "";
text = ''cmd[update:1000] echo "$(date +'%H:%M:%S')"'';
color = "rgb(${c.base05})";
font_size = 13;
font_family = "Maple Mono NF";
position = "48, -70";
halign = "left";
valign = "top";
}
{
monitor = "";
text = ''cmd[update:60000] echo "$(date +'%A %d %B %Y')"'';
color = "rgb(${c.base04})";
font_size = 13;
font_family = "Maple Mono NF";
position = "48, -92";
halign = "left";
valign = "top";
}
{
monitor = "";
text = "passwd:";
color = "rgb(${c.base0D})";
font_size = 13;
font_family = "Maple Mono NF";
position = "48, -141";
halign = "left";
valign = "top";
}
];
"input-field" = [
{
monitor = "";
size = "200, 20";
outline_thickness = 0;
outer_color = "rgba(00000000)";
inner_color = "rgba(00000000)";
font_color = "rgb(${c.base05})";
fade_on_empty = false;
placeholder_text = "";
hide_input = true;
rounding = 0;
check_color = "rgb(${c.base0B})";
fail_color = "rgb(${c.base08})";
fail_text = ''<span font_desc="Maple Mono NF 13">auth failed</span>'';
capslock_color = "rgb(${c.base0A})";
position = "116, -138";
halign = "left";
valign = "top";
}
];
};
};
}
+40 -33
View File
@@ -1,25 +1,19 @@
# Mime type associations for the system.
{
config,
lib,
pkgs,
...
}:
with lib; let
}: let
defaultApps = {
# check desktop files here: `ls $(echo $XDG_DATA_DIRS| sed "s/:/ /g")`
text = [
# "org.gnome.TextEditor.desktop"
"nvim-ghostty.desktop"
];
text = ["nvim-ghostty.desktop"];
code = ["nvim-ghostty.desktop"];
image = ["imv-dir.desktop"];
audio = ["mpv.desktop"];
video = ["mpv.desktop"];
directory = ["thunar.desktop"];
office = ["libreoffice.desktop"];
pdf = ["zathura.desktop"];
terminal = ["ghostty.desktop"];
archive = ["xarchiver.desktop"];
audio = ["vlc.desktop"];
video = ["vlc.desktop"];
directory = ["elio.desktop"];
office = ["onlyoffice-desktopeditors.desktop"];
pdf = ["onlyoffice-desktopeditors.desktop"];
archive = ["elio.desktop"];
browser = ["helium.desktop"];
};
@@ -34,7 +28,6 @@ with lib; let
"text/x-go"
"text/x-java"
"text/x-csharp"
"text/x-python"
"application/x-shellscript"
"text/javascript"
@@ -42,7 +35,6 @@ with lib; let
"text/css"
"text/x-php"
"text/x-ruby"
"application/json"
"application/xml"
"text/xml"
@@ -51,11 +43,8 @@ with lib; let
"application/toml"
"text/x-nix"
"text/markdown"
"text/x-dockerfile"
"application/x-yaml"
"text/x-terraform"
"application/x-perl"
"text/x-lua"
"text/x-haskell"
@@ -104,12 +93,17 @@ with lib; let
"application/rtf"
];
pdf = ["application/pdf"];
terminal = ["terminal"];
archive = [
"application/zip"
"application/rar"
"application/7z"
"application/*tar"
"application/x-rar-compressed"
"application/vnd.rar"
"application/x-7z-compressed"
"application/x-tar"
"application/gzip"
"application/x-bzip2"
"application/x-xz"
"application/x-zstd"
"application/x-compressed-tar"
];
browser = [
"text/html"
@@ -123,18 +117,18 @@ with lib; let
nvim-ghostty = pkgs.makeDesktopItem {
name = "nvim-ghostty";
desktopName = "Neovim (Ghostty)";
exec = ''ghostty --title="Neovim Editor" -e nvim %F'';
exec = ''${pkgs.ghostty}/bin/ghostty +new-window --title="Neovim Editor" -e nvim %F'';
terminal = false;
categories = [
"Development"
"TextEditor"
];
categories = ["Development" "TextEditor"];
mimeTypes = mimeMap.code ++ mimeMap.text;
};
associations = with lists;
listToAttrs (
flatten (mapAttrsToList (key: map (type: attrsets.nameValuePair type defaultApps."${key}")) mimeMap)
associations = lib.listToAttrs (
lib.flatten (
lib.mapAttrsToList
(key: map (type: lib.nameValuePair type defaultApps.${key}))
mimeMap
)
);
in {
home.packages = [nvim-ghostty];
@@ -144,7 +138,20 @@ in {
mimeApps = {
enable = true;
associations.added = associations;
defaultApplications = associations;
defaultApplications = lib.mapAttrs (_: lib.mkForce) associations;
};
userDirs = {
enable = true;
createDirectories = true;
desktop = null;
music = null;
publicShare = null;
templates = null;
extraConfig = {
NOTES = "${config.home.homeDirectory}/Notes";
CYBER = "${config.home.homeDirectory}/Cyber";
};
};
};
}
+421
View File
@@ -0,0 +1,421 @@
{
config,
pkgs,
...
}: let
scripts = import ../waybar/scripts.nix {inherit pkgs config;};
c = config.lib.stylix.colors;
font = config.stylix.fonts.sansSerif.name;
monofont = config.stylix.fonts.monospace.name;
rounding = config.theme.rounding;
border-size = config.theme.border-size;
gaps-in = config.theme.gaps-in;
opacityToHex = opacity: let
val = builtins.floor (opacity * 255);
high = val / 16;
low = val - high * 16;
digits = "0123456789abcdef";
in "${builtins.substring high 1 digits}${builtins.substring low 1 digits}";
alphaHex = opacityToHex config.theme.inactive-opacity;
in {
services.swaync = {
enable = true;
settings = {
positionX = "right";
positionY = "top";
layer = "overlay";
"control-center-layer" = "overlay";
cssPriority = "user";
"control-center-width" = 450;
"control-center-margin-top" = config.theme.gaps-out * 2;
"control-center-margin-bottom" = config.theme.gaps-out * 2;
"control-center-margin-right" = 0;
"fit-to-screen" = true;
"notification-window-width" = 400;
"notification-icon-size" = 40;
"notification-body-image-height" = 500;
"notification-body-image-width" = 500;
"notification-inline-replies" = true;
"notification-2fa-action" = false;
timeout = 3;
"timeout-low" = 2;
"timeout-critical" = 6;
"keyboard-shortcuts" = true;
"image-visibility" = "when-available";
"transition-time" = 200;
"hide-on-clear" = false;
"hide-on-action" = false;
"script-fail-notify" = true;
widgets = ["mpris" "volume" "backlight" "dnd" "buttons-grid" "notifications"];
"widget-config" = {
dnd.text = "Do not disturb";
volume = {
label = "󰕾";
"show-per-app" = false;
};
backlight = {
label = "󰃠";
"min" = 0;
};
mpris = {
"image-size" = 100;
"image-radius" = 10;
autohide = false;
blacklist = ["org.mpris.MediaPlayer2.playerctld"];
};
"buttons-grid" = {
actions = [
{
label = "󰖔";
type = "toggle";
command = "${scripts.nightshift-toggle}/bin/nightshift-toggle";
"update-command" = "${pkgs.procps}/bin/pidof ${pkgs.hyprsunset}/bin/hyprsunset > /dev/null && echo true || echo false";
}
{
label = "󰈈";
type = "toggle";
command = "${scripts.focus-toggle}/bin/focus-toggle";
"update-command" = "test -f /tmp/hypr-focus-mode && echo true || echo false";
}
{
label = "󰍭";
type = "toggle";
command = "${scripts.mic-mute}/bin/mic-mute";
"update-command" = "${scripts.mic-status}/bin/mic-status";
}
{
label = "󰌾";
command = "${pkgs.swaynotificationcenter}/bin/swaync-client -cp ; ${pkgs.hyprlock}/bin/hyprlock";
}
{
label = "󰐥";
command = "${pkgs.swaynotificationcenter}/bin/swaync-client -cp ; systemctl poweroff";
}
];
};
};
};
style = ''
@define-color center-bg #${c.base01}${alphaHex};
@define-color notification-bg #${c.base00}${alphaHex};
@define-color center-notification-bg #${c.base01};
@define-color background #${c.base00}${alphaHex};
@define-color background-alt #${c.base01};
@define-color text #${c.base05};
@define-color text-alt #${c.base03};
@define-color selected #${c.base03}66;
@define-color hover #${c.base03}28;
@define-color urgent #${c.base08};
* {
all: unset;
font-family: "${font}";
font-weight: 700;
--border-radius: ${toString rounding}px;
--border-radius-s: ${toString gaps-in}px;
--border-radius-xl: ${toString (rounding - 2)}px;
}
.notification { padding: 5px; }
.notification-row { background: transparent; border: none; box-shadow: none; }
.control-center .notification { padding: 0; }
.notification-background {
background: @notification-bg;
border-radius: var(--border-radius);
border: ${toString border-size}px solid @selected;
box-shadow: none;
outline: none;
margin: ${toString gaps-in}px;
padding: 0;
}
.notification-background .close-button {
margin: 6px;
padding: 2px;
border-radius: ${toString (gaps-in - 2)}px;
background: transparent;
color: @text;
}
.notification-background .close-button:hover { background: @hover; }
.notification.low,
.notification.normal { border: none; }
.notification.critical {
border: ${toString border-size}px solid @urgent;
border-radius: var(--border-radius);
}
.notification-content {
color: @text;
margin: 10px;
font-weight: 700;
border: none;
}
.notification-content .text-box { margin: 0 0 0 15px; }
.notification-content .time {
padding: 2px;
font-size: 0.95rem;
font-weight: 800;
}
.notification-content .summary {
font-size: 1rem;
font-weight: 800;
padding: 2px 0;
margin-bottom: 2px;
}
.notification-content .body {
color: @text-alt;
font-size: 0.8rem;
}
.notification-row .inline-reply-entry,
.notification-row .inline-reply-button {
margin: 5px -5px -5px -5px;
padding: 10px 15px;
border-radius: var(--border-radius);
background: @background-alt;
}
.notification-row .inline-reply-entry { margin-right: 10px; }
.notification-row .inline-reply-button:hover { background: @selected; }
.notification-alt-actions { padding: 5px 0; }
.notification-action {
font-weight: 700;
border-radius: var(--border-radius);
background: @hover;
color: @text;
margin: 0 5px;
padding: 10px 0;
}
.notification-action:hover,
.notification-action:active { background: @selected; }
.control-center {
background: @center-bg;
border-radius: var(--border-radius) 0 0 var(--border-radius);
border-left: ${toString border-size}px solid @selected;
margin: 0;
padding: 5px 5px 0 5px;
}
.control-center .notification-background {
background: @center-notification-bg;
}
.control-center .notification-background .close-button,
.notification-group-close-button { opacity: 0; }
.notification-group { transition: 200ms; }
.notification-group:not(.collapsed) { margin: ${toString gaps-in}px; }
.notification-group-headers,
.notification-group-buttons {
color: @text;
font-weight: bold;
background: @background;
border-radius: var(--border-radius);
padding: 4px 8px;
}
.notification-group-headers > label {
margin: 0 3px;
font-size: 1rem;
}
.notification-group-icon { color: @text; }
.notification-group-buttons .image-button {
background: transparent;
color: @text;
padding: 4px;
border-radius: var(--border-radius);
}
.notification-group-buttons .image-button:hover { background: @hover; }
.widget {
margin: 5px;
color: @text;
}
.widget-mpris-album-art {
border-radius: var(--border-radius);
margin: 6px 4px;
}
.widget-mpris-title {
font-weight: 700;
font-size: 1.2rem;
margin: 0 3px;
}
.widget-mpris-subtitle {
color: @text-alt;
font-weight: 700;
font-size: 0.9rem;
margin: 0 3px;
}
.mpris-background { border-radius: 14px; margin: 3px; }
.widget-mpris {
border-radius: var(--border-radius);
}
.mpris-overlay {
border-radius: var(--border-radius);
padding: 10px;
background: @background;
}
.mpris-overlay > box:last-child {
border-radius: var(--border-radius-xl);
padding: 0 5px;
background: alpha(@hover, .6);
}
.widget-mpris-player button {
padding: 5px;
margin: 0 2.5px;
}
.widget-mpris-player button:hover {
border-radius: var(--border-radius-s);
background: @hover;
}
.widget-volume,
.widget-backlight {
border-radius: var(--border-radius);
padding: 10px 15px;
background: @background-alt;
color: @text;
}
.widget-volume > box,
.widget-backlight > box {
color: @text;
}
.widget-volume label,
.widget-backlight label {
font-size: 1rem;
font-weight: 700;
min-width: 1.2rem;
}
.widget-volume slider,
.widget-backlight slider {
background: @text;
border-radius: var(--border-radius-s);
min-height: 12px;
min-width: 12px;
}
.widget-volume trough,
.widget-backlight trough {
background: @hover;
border-radius: var(--border-radius-s);
min-height: 4px;
}
.widget-volume highlight,
.widget-backlight highlight {
background: @text;
border-radius: var(--border-radius-s);
}
.widget-dnd {
padding: 10px 15px;
border-radius: var(--border-radius);
background: @background-alt;
color: @text;
}
.widget-dnd > label {
font-size: 1rem;
font-weight: 700;
}
.widget-dnd switch {
background: @hover;
border-radius: var(--border-radius-s);
box-shadow: none;
padding: 2px;
transition: 200ms;
}
.widget-dnd switch slider {
background: @text;
border-radius: var(--border-radius-s);
}
.widget-dnd switch:hover,
.widget-dnd switch:checked { background-color: @selected; }
.widget-notifications { margin: 0; }
.widget-buttons-grid {
border-radius: var(--border-radius);
padding: 5px 15px;
margin-bottom: 5px;
background: @background-alt;
}
.widget-buttons-grid button {
padding: 10px;
margin: 3px;
min-width: 2rem;
font-family: "${monofont}";
background: @hover;
border-radius: var(--border-radius);
transition: 200ms;
}
.widget-buttons-grid button > label {
font-family: "${monofont}";
font-size: 18px;
font-weight: normal;
font-style: normal;
color: @text;
}
.widget-buttons-grid button:hover { background: @selected; }
.widget-buttons-grid button:checked { background-color: @text; }
.widget-buttons-grid button:checked > label { color: @background; }
.control-center-list-placeholder { color: @text; }
.blank-window { background: transparent; }
'';
};
stylix.targets.swaync.enable = false;
# GTK 4.22 defaults to the Vulkan renderer, which compiles its graphics
# pipelines lazily on the first frame. The control center has many distinct
# visual elements, so the first open triggers a big burst of pipeline
# compilation (a multi-second stall on a cold boot) while later opens reuse
# the in-process cache. The GL (ngl) renderer avoids this: it stays
# GPU-accelerated and benefits from Mesa's on-disk shader cache, so the
# first open is as fast as the rest. Scoped to swaync so other GTK4 apps
# keep the Vulkan renderer.
systemd.user.services.swaync.Service.Environment = ["GSK_RENDERER=ngl"];
}
+65
View File
@@ -0,0 +1,65 @@
# Use elio as the file picker for portal dialogs (browser "select file(s)" and
# "save download to..."). The backend is xdg-desktop-portal-termfilechooser,
# enabled system-wide in nixos/utils.nix (extraPortals + FileChooser routing).
{
config,
pkgs,
...
}: let
wrapper = pkgs.writeShellScript "elio-wrapper" ''
multiple="$1"
directory="$2"
save="$3"
path="$4"
out="$5"
debug="$6"
set -e
if [[ "$debug" = 1 ]]; then
set -x
fi
cmd="elio"
termcmd="''${TERMCMD:-ghostty --gtk-single-instance=false --title=termfilechooser -e}"
if [[ "$save" = "1" ]]; then
set -- --chooser-file="$out" "$path"
elif [[ "$directory" = "1" ]]; then
set -- --chooser-file="$out" --cwd-file="$out.1" "$path"
else
set -- --chooser-file="$out" "$path"
fi
command="$termcmd $cmd"
for arg in "$@"; do
escaped=$(printf "%s" "$arg" | sed 's/"/\\"/g')
command="$command \"$escaped\""
done
sh -c "$command"
if [[ "$directory" = "1" ]]; then
if [[ ! -s "$out" ]] && [[ -s "$out.1" ]]; then
cat "$out.1" > "$out"
rm "$out.1"
else
rm "$out.1"
fi
fi
'';
# ghostty must run in the foreground instead of handing off to the
# single-instance daemon, otherwise the wrapper returns before you've picked
# anything and the portal reads an empty selection.
termcmd = "ghostty --gtk-single-instance=false --title=termfilechooser -e";
in {
xdg.configFile."xdg-desktop-portal-termfilechooser/config".text = ''
[filechooser]
cmd=${wrapper}
default_dir=$HOME
open_mode=suggested
save_mode=suggested
env=TERMCMD=${termcmd}
PATH=${config.home.profileDirectory}/bin:/run/current-system/sw/bin:/run/wrappers/bin
'';
}
+47
View File
@@ -0,0 +1,47 @@
{
config,
lib,
...
}: let
c = config.lib.stylix.colors;
font = config.stylix.fonts.serif.name;
gaps = config.theme.gaps-out;
barHeight = config.theme.bar-height;
rounding =
if config.theme.rounding > barHeight / 2
then barHeight / 2
else config.theme.rounding;
in {
imports = [./desktop-actions.nix];
programs.tofi = {
enable = true;
settings = {
anchor = "top";
height = barHeight;
margin-top = gaps;
margin-left = 350;
margin-right = 350;
horizontal = true;
num-results = 8;
result-spacing = 20;
padding-top = 2;
padding-bottom = 2;
padding-left = 14;
padding-right = 14;
font = lib.mkForce font;
prompt-color = lib.mkForce "#${c.base0D}ff";
selection-color = lib.mkForce "#${c.base0D}ff";
outline-width = 0;
border-width = 0;
corner-radius = rounding;
prompt-text = " ";
placeholder-text = "Search...";
drun-launch = true;
};
};
}
+333
View File
@@ -0,0 +1,333 @@
{
pkgs,
config,
...
}: let
scripts = import ../waybar/scripts.nix {inherit pkgs config;};
in {
xdg.desktopEntries = {
focus-toggle = {
name = "Focus Mode";
exec = "${scripts.focus-toggle}/bin/focus-toggle";
icon = "do-not-disturb-symbolic";
comment = "Toggle focus mode";
categories = ["System"];
terminal = false;
};
nightshift-toggle = {
name = "Night Shift";
exec = "${scripts.nightshift-toggle}/bin/nightshift-toggle";
icon = "night-light-symbolic";
comment = "Toggle night shift";
categories = ["System"];
terminal = false;
};
mic-mute = {
name = "Mute Microphone";
exec = "${scripts.mic-mute}/bin/mic-mute";
icon = "microphone-sensitivity-muted-symbolic";
comment = "Toggle microphone mute";
categories = ["System"];
terminal = false;
};
lock = {
name = "Lock";
exec = "${pkgs.hyprlock}/bin/hyprlock";
icon = "system-lock-screen-symbolic";
comment = "Lock the screen";
categories = ["System"];
terminal = false;
};
reboot = {
name = "Reboot";
exec = "systemctl reboot";
icon = "system-restart-symbolic";
comment = "Restart the system";
categories = ["System"];
terminal = false;
};
shutdown = {
name = "Shutdown";
exec = "systemctl poweroff";
icon = "system-shutdown-symbolic";
comment = "Power off the system";
categories = ["System"];
terminal = false;
};
hibernate = {
name = "Hibernate";
exec = "systemctl hibernate";
icon = "drive-harddisk-symbolic";
comment = "Hibernate the system";
categories = ["System"];
terminal = false;
};
waybar-toggle = {
name = "Toggle Waybar";
exec = "${scripts.waybar-toggle}/bin/waybar-toggle";
icon = "panel-applets-symbolic";
comment = "Show or hide the status bar";
categories = ["System"];
terminal = false;
};
wifi-toggle = {
name = "Toggle Wi-Fi";
exec = "${scripts.wifi-toggle}/bin/wifi-toggle";
icon = "network-wireless-symbolic";
comment = "Enable or disable Wi-Fi";
categories = ["System"];
terminal = false;
};
bluetooth-toggle = {
name = "Toggle Bluetooth";
exec = "${scripts.bluetooth-toggle}/bin/bluetooth-toggle";
icon = "bluetooth-symbolic";
comment = "Enable or disable Bluetooth";
categories = ["System"];
terminal = false;
};
color-pick = {
name = "Pick Color";
exec = "${scripts.color-pick}/bin/color-pick";
icon = "color-select-symbolic";
comment = "Pick a screen color to the clipboard";
categories = ["Utility"];
terminal = false;
settings.Keywords = "color;picker;hex;eyedropper;";
};
screenshot-region = {
name = "Screenshot Region";
exec = "${pkgs.hyprshot}/bin/hyprshot -m region";
icon = "screenshot-recorded-symbolic";
comment = "Capture a selected region";
categories = ["Utility"];
terminal = false;
settings.Keywords = "screenshot;capture;region;";
};
screenshot-window = {
name = "Screenshot Window";
exec = "${pkgs.hyprshot}/bin/hyprshot -m window";
icon = "screenshot-recorded-symbolic";
comment = "Capture the active window";
categories = ["Utility"];
terminal = false;
settings.Keywords = "screenshot;capture;window;";
};
screenshot-screen = {
name = "Screenshot Screen";
exec = "${pkgs.hyprshot}/bin/hyprshot -m output";
icon = "screenshot-recorded-symbolic";
comment = "Capture the whole screen";
categories = ["Utility"];
terminal = false;
settings.Keywords = "screenshot;capture;screen;monitor;";
};
screenshot-edit = {
name = "Screenshot & Annotate";
exec = "${scripts.screenshot-edit}/bin/screenshot-edit";
icon = "edit-symbolic";
comment = "Capture a region and open it in Satty";
categories = ["Utility"];
terminal = false;
settings.Keywords = "screenshot;annotate;satty;edit;";
};
record-toggle = {
name = "Screen Recording";
exec = "${scripts.record-toggle}/bin/record-toggle";
icon = "media-record-symbolic";
comment = "Start or stop screen recording";
categories = ["Utility" "AudioVideo"];
terminal = false;
settings.Keywords = "record;recording;screencast;video;";
};
power-cycle = {
name = "Power Profile";
exec = "${scripts.power-cycle}/bin/power-cycle";
icon = "power-profile-balanced-symbolic";
comment = "Cycle power-saver / balanced / performance";
categories = ["System"];
terminal = false;
settings.Keywords = "power;profile;performance;battery;";
};
airplane-toggle = {
name = "Airplane Mode";
exec = "${scripts.airplane-toggle}/bin/airplane-toggle";
icon = "airplane-mode-symbolic";
comment = "Toggle Wi-Fi and Bluetooth together";
categories = ["System"];
terminal = false;
settings.Keywords = "airplane;flight;wifi;bluetooth;offline;";
};
clipboard-menu = {
name = "Clipboard History";
exec = "${scripts.clipboard-menu}/bin/clipboard-menu";
icon = "edit-paste-symbolic";
comment = "Pick a past clipboard entry";
categories = ["Utility"];
terminal = false;
settings.Keywords = "clipboard;history;paste;cliphist;";
};
clipboard-wipe = {
name = "Clear Clipboard History";
exec = "${pkgs.cliphist}/bin/cliphist wipe";
icon = "edit-clear-all-symbolic";
comment = "Delete all clipboard history entries";
categories = ["Utility"];
terminal = false;
settings.Keywords = "clipboard;history;clear;wipe;delete;cliphist;";
};
emoji-picker = {
name = "Emoji Picker";
exec = "${scripts.emoji-picker}/bin/emoji-picker";
icon = "face-smile-symbolic";
comment = "Pick an emoji and type it";
categories = ["Utility"];
terminal = false;
settings.Keywords = "emoji;emoticon;smiley;symbol;";
};
icon-picker = {
name = "Icon Picker";
exec = "${scripts.icon-picker}/bin/icon-picker";
icon = "character-map-symbolic";
comment = "Pick a Nerd Font icon and type it";
categories = ["Utility"];
terminal = false;
settings.Keywords = "icon;nerdfont;glyph;symbol;";
};
caffeine-toggle = {
name = "Keep Awake";
exec = "${scripts.caffeine-toggle}/bin/caffeine-toggle";
icon = "my-caffeine-on-symbolic";
comment = "Pause or resume idle locking and suspend";
categories = ["System"];
terminal = false;
settings.Keywords = "caffeine;idle;awake;inhibit;suspend;";
};
logout = {
name = "Logout";
exec = "${pkgs.hyprland}/bin/hyprctl dispatch exit";
icon = "system-log-out-symbolic";
comment = "End the current session";
categories = ["System"];
terminal = false;
};
dnd-toggle = {
name = "Do Not Disturb";
exec = "${scripts.dnd-toggle}/bin/dnd-toggle";
icon = "notifications-disabled-symbolic";
comment = "Toggle Do Not Disturb";
categories = ["System"];
terminal = false;
settings.Keywords = "dnd;notifications;disturb;swaync;silence;";
};
vol-mute = {
name = "Mute Volume";
exec = "${scripts.vol-mute}/bin/vol-mute";
icon = "audio-volume-muted-symbolic";
comment = "Toggle output mute";
categories = ["System"];
terminal = false;
};
vol-up = {
name = "Volume Up";
exec = "${scripts.vol-up}/bin/vol-up";
icon = "audio-volume-high-symbolic";
comment = "Raise the volume by 5%";
categories = ["System"];
terminal = false;
};
vol-down = {
name = "Volume Down";
exec = "${scripts.vol-down}/bin/vol-down";
icon = "audio-volume-low-symbolic";
comment = "Lower the volume by 5%";
categories = ["System"];
terminal = false;
};
bright-up = {
name = "Brightness Up";
exec = "${scripts.bright-up}/bin/bright-up";
icon = "display-brightness-high-symbolic";
comment = "Raise the brightness by 5%";
categories = ["System"];
terminal = false;
};
bright-down = {
name = "Brightness Down";
exec = "${scripts.bright-down}/bin/bright-down";
icon = "display-brightness-low-symbolic";
comment = "Lower the brightness by 5%";
categories = ["System"];
terminal = false;
};
output-cycle = {
name = "Cycle Audio Output";
exec = "${scripts.output-cycle}/bin/output-cycle";
icon = "audio-speakers-symbolic";
comment = "Switch to the next audio output";
categories = ["System"];
terminal = false;
settings.Keywords = "audio;output;sink;speaker;headphones;";
};
input-cycle = {
name = "Cycle Audio Input";
exec = "${scripts.input-cycle}/bin/input-cycle";
icon = "audio-input-microphone-symbolic";
comment = "Switch to the next audio input";
categories = ["System"];
terminal = false;
settings.Keywords = "audio;input;source;microphone;mic;";
};
spotatui = {
name = "Spotify";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs.spotatui}/bin/spotatui";
icon = "spotify";
comment = "Control Spotify from the terminal";
categories = ["Audio" "Music"];
terminal = false;
settings.Keywords = "spotify;spotatui;music;";
};
settuings = {
name = "Settings";
exec = "${pkgs.ghostty}/bin/ghostty +new-window -e ${pkgs.nur.repos.anotherhadi.settuings}/bin/settuings";
icon = "preferences-system-symbolic";
comment = "Terminal based settings (wifi, bluetooth, audio, ...)";
categories = ["Settings"];
terminal = false;
settings.Keywords = "settings;wifi;bluetooth;audio;settuings;";
};
};
}
+1
View File
@@ -4,5 +4,6 @@
enable = true;
notify = true;
automount = true;
tray = "never";
};
}
+103
View File
@@ -0,0 +1,103 @@
# battery-monitor: low-battery alerts for waybar/swaync.
# Runs main once (triggered by the battery-monitor systemd timer).
POWER_SUPPLY_DIR="${POWER_SUPPLY_DIR:-/sys/class/power_supply}"
STATE_FILE="${BATTERY_STATE_FILE:-/tmp/battery-alert-state}"
CRIT_ID_FILE="${BATTERY_CRIT_ID_FILE:-/tmp/battery-crit-id}"
WARN_THRESHOLD=10
CRIT_THRESHOLD=5
# Pure decision. Prints "<action>:<newstate>".
# action ∈ noop | osd_warn | crit_show | crit_clear ; state ∈ none | warn10 | crit5
battery_decide() {
status=$1
cap=$2
prev=$3
if [ "$status" = "Discharging" ]; then
if [ "$cap" -le "$CRIT_THRESHOLD" ]; then
echo "crit_show:crit5"
elif [ "$cap" -le "$WARN_THRESHOLD" ]; then
if [ "$prev" = "crit5" ]; then
echo "crit_clear:warn10"
elif [ "$prev" = "warn10" ]; then
echo "noop:warn10"
else
echo "osd_warn:warn10"
fi
else
if [ "$prev" = "crit5" ]; then
echo "crit_clear:none"
else
echo "noop:none"
fi
fi
else
if [ "$prev" = "crit5" ]; then
echo "crit_clear:none"
else
echo "noop:none"
fi
fi
}
# First real battery (type == Battery, with status + capacity).
resolve_battery() {
for d in "$POWER_SUPPLY_DIR"/*; do
[ -r "$d/type" ] || continue
[ "$(cat "$d/type")" = "Battery" ] || continue
[ -r "$d/status" ] && [ -r "$d/capacity" ] || continue
printf '%s' "$d"
return 0
done
return 1
}
show_warn_osd() {
waybar-osd "󰂃 Low battery · $1%"
}
show_crit_notification() {
cap=$1
id=$(cat "$CRIT_ID_FILE" 2>/dev/null || echo 0)
newid=$(notify-send --print-id --replace-id="$id" \
--urgency=critical --expire-time=0 \
"󰂃 Critical battery" "Battery at $cap%: plug in the charger")
printf '%s' "$newid" >"$CRIT_ID_FILE"
}
clear_crit_notification() {
id=$(cat "$CRIT_ID_FILE" 2>/dev/null || echo 0)
if [ "$id" != "0" ]; then
gdbus call --session \
--dest org.freedesktop.Notifications \
--object-path /org/freedesktop/Notifications \
--method org.freedesktop.Notifications.CloseNotification "$id" \
>/dev/null 2>&1 || true
fi
rm -f "$CRIT_ID_FILE"
}
main() {
dir=$(resolve_battery) || exit 0
status=$(cat "$dir/status")
cap=$(cat "$dir/capacity")
prev=$(cat "$STATE_FILE" 2>/dev/null || echo none)
result=$(battery_decide "$status" "$cap" "$prev")
action=${result%%:*}
newstate=${result##*:}
case "$action" in
osd_warn) show_warn_osd "$cap" ;;
crit_show) show_crit_notification "$cap" ;;
crit_clear) clear_crit_notification ;;
noop) : ;;
esac
printf '%s' "$newstate" >"$STATE_FILE"
}
main "$@"
+59
View File
@@ -0,0 +1,59 @@
{
pkgs,
config,
...
}: let
scripts = import ./scripts.nix {inherit pkgs config;};
in {
imports = [
./settings.nix
./style.nix
];
_module.args.networkScript = scripts.networkScript;
_module.args.bluetoothScript = scripts.bluetoothScript;
_module.args.volMuteScript = scripts.vol-mute;
_module.args.caffeineToggleScript = scripts.caffeine-toggle;
_module.args.osdStatusScript = scripts.waybar-osd-status;
_module.args.nightshiftToggleScript = scripts.nightshift-toggle;
programs.waybar.enable = true;
stylix.targets.waybar.enable = false;
home.packages = with pkgs;
[
playerctl
pavucontrol
blueman
hyprsunset
]
++ (with scripts; [waybar-osd waybar-osd-status battery-monitor vol-up vol-down vol-mute mic-mute bright-up bright-down nightshift-toggle focus-toggle waybar-toggle wifi-toggle bluetooth-toggle dnd-toggle output-cycle input-cycle color-pick screenshot-edit record-toggle power-cycle airplane-toggle clipboard-menu emoji-picker caffeine-toggle mic-status]);
# Poll battery level every 30s; battery-monitor no-ops when no battery exists.
systemd.user.services.battery-monitor = {
Unit.Description = "Low-battery OSD and critical notification monitor";
Service = {
Type = "oneshot";
ExecStart = "${scripts.battery-monitor}/bin/battery-monitor";
};
};
systemd.user.timers.battery-monitor = {
Unit.Description = "Poll battery level for low-battery alerts";
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "30s";
};
Install.WantedBy = ["timers.target"];
};
# Prevent blueman-applet from auto-starting via XDG autostart (we use custom/bluetooth module instead)
xdg.configFile."autostart/blueman.desktop".text = ''
[Desktop Entry]
Hidden=true
'';
wayland.windowManager.hyprland.settings.exec-once = [
"waybar"
];
}
+455
View File
@@ -0,0 +1,455 @@
{
pkgs,
config,
}: let
waybar-osd = pkgs.writeShellApplication {
name = "waybar-osd";
runtimeInputs = with pkgs; [procps coreutils];
text = ''
printf '%s' "$1" > /tmp/waybar-osd
pkill -f -RTMIN+8 '^waybar$' 2>/dev/null || true
'';
};
waybar-osd-status = pkgs.writeShellApplication {
name = "waybar-osd-status";
runtimeInputs = with pkgs; [coreutils];
text = ''
file=/tmp/waybar-osd
[ -f "$file" ] || exit 1
mtime=$(stat -c %Y "$file" 2>/dev/null) || exit 1
age=$(( $(date +%s) - mtime ))
if [ "$age" -ge 3 ]; then
rm -f "$file"
exit 1
fi
'';
};
# battery-monitor: low-battery alerts, run by the systemd user timer.
battery-monitor = pkgs.writeShellApplication {
name = "battery-monitor";
runtimeInputs = with pkgs; [waybar-osd libnotify glib coreutils];
text = builtins.readFile ./battery-monitor.sh;
};
updateOsd = ''
${waybar-osd}/bin/waybar-osd "$OSD_TEXT"
'';
volGetText = ''
VOL_RAW=$(${pkgs.wireplumber}/bin/wpctl get-volume @DEFAULT_SINK@)
VOL=$(printf '%s' "$VOL_RAW" | awk '{printf "%d", $2*100}')
if printf '%s' "$VOL_RAW" | grep -q MUTED; then
OSD_TEXT="󰝟 $VOL%"
elif [ "$VOL" -lt 33 ]; then
OSD_TEXT="󰕿 $VOL%"
elif [ "$VOL" -lt 66 ]; then
OSD_TEXT="󰖀 $VOL%"
else
OSD_TEXT="󰕾 $VOL%"
fi
'';
brightGetText = ''
BRIGHT=$(${pkgs.brightnessctl}/bin/brightnessctl -m | awk -F, '{print int($5)}')
if [ "$BRIGHT" -lt 33 ]; then
OSD_TEXT="󰃞 $BRIGHT%"
elif [ "$BRIGHT" -lt 66 ]; then
OSD_TEXT="󰃟 $BRIGHT%"
else
OSD_TEXT="󰃠 $BRIGHT%"
fi
'';
# Resolve $src: the default source, otherwise the first available source.
# Some machines have no default audio source (@DEFAULT_AUDIO_SOURCE@ unresolved).
micSource = ''
src=$(${pkgs.wireplumber}/bin/wpctl inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
if [ -z "$src" ]; then
src=$(${pkgs.pipewire}/bin/pw-dump | ${pkgs.jq}/bin/jq -r \
'[.[] | select(.info.props."media.class"=="Audio/Source") | .id] | .[0] // empty')
fi
'';
# tofi in vertical dmenu mode: the global config is a 36px-tall horizontal
# launcher, unusable for a list, so override the geometry here.
tofiMenu = "${pkgs.tofi}/bin/tofi --horizontal false --anchor center --width 700 --height 500 --margin-top 0 --margin-left 0 --margin-right 0 --num-results 10";
# Name -> glyph table maintained by the nerd-fonts project itself, pinned to
# the same release as the nerd-fonts packages in nixos/fonts.nix.
nerdFontGlyphnames = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/ryanoasis/nerd-fonts/v3.4.0/glyphnames.json";
sha256 = "sha256-4tENI/W/8L1vBnbpsB2XifzcZW3ntJiilVwncW6kQ5w=";
};
nerdFontIconList = pkgs.runCommand "nerd-font-icons.txt" {nativeBuildInputs = [pkgs.jq];} ''
jq -r 'to_entries[] | select(.key != "METADATA") | "\(.value.char) \(.key)"' \
${nerdFontGlyphnames} > "$out"
'';
in {
inherit waybar-osd waybar-osd-status battery-monitor;
bluetoothScript = pkgs.writeShellScript "waybar-bluetooth" ''
jq=${pkgs.jq}/bin/jq
nl=$'\n'
# bluetoothctl hangs without a controller by default always bounded by timeout.
bt() { timeout 3 ${pkgs.bluez}/bin/bluetoothctl "$@" 2>/dev/null; }
powered=$(bt show | awk '/Powered:/ { print $2; exit }')
if [ "$powered" != "yes" ]; then
"$jq" -cn '{ text: "󰂲", class: "off", tooltip: "Bluetooth off" }'
exit 0
fi
tip=""
add_tip() { tip="''${tip:+$tip$nl}$1"; }
count=0
for mac in $(bt devices Connected | awk '{ print $2 }'); do
count=$((count + 1))
info=$(bt info "$mac")
name=$(printf '%s' "$info" | sed -n 's/^[[:space:]]*Name: //p' | head -1)
[ -z "$name" ] && name="$mac"
batt=$(printf '%s' "$info" | sed -n 's/.*Battery Percentage:.*(\([0-9][0-9]*\)).*/\1/p' | head -1)
add_tip "󰂱 $name''${batt:+ · $batt%}"
done
if [ "$count" -gt 0 ]; then
"$jq" -cn --arg tooltip "$tip" '{ text: "󰂰", class: "connected", tooltip: $tooltip }'
else
"$jq" -cn '{ text: "󰂰", class: "on", tooltip: "Bluetooth on" }'
fi
'';
networkScript = pkgs.writeShellScript "waybar-network" ''
nmcli=${pkgs.networkmanager}/bin/nmcli
ip=${pkgs.iproute2}/bin/ip
jq=${pkgs.jq}/bin/jq
nl=$'\n'
text=""
tip=""
class="disconnected"
add_seg() { text="''${text:+$text }$1"; }
add_tip() { tip="''${tip:+$tip$nl}$1"; }
# Ethernet
eth=$("$nmcli" -t -f DEVICE,TYPE,STATE device status 2>/dev/null \
| awk -F: '$2 == "ethernet" && $3 == "connected" { print $1; exit }')
if [ -n "$eth" ]; then
eth_ip=$("$ip" -4 -br addr show "$eth" 2>/dev/null | awk '{ print $3 }' | cut -d/ -f1)
add_seg "󰛳"
class="ethernet"
add_tip "󰛳 Ethernet''${eth_ip:+ · $eth_ip}"
fi
# Wi-Fi
wifi=$("$nmcli" -t -f ACTIVE,SSID,SIGNAL device wifi 2>/dev/null | grep -m1 '^yes:')
if [ -n "$wifi" ]; then
ssid=$(printf '%s' "$wifi" | cut -d: -f2)
signal=$(printf '%s' "$wifi" | cut -d: -f3)
if [ "''${signal:-0}" -ge 80 ]; then icon="󰤨"
elif [ "''${signal:-0}" -ge 60 ]; then icon="󰤥"
elif [ "''${signal:-0}" -ge 40 ]; then icon="󰤢"
elif [ "''${signal:-0}" -ge 20 ]; then icon="󰤟"
else icon="󰤯"
fi
add_seg "$icon"
[ "$class" = "disconnected" ] && class="wifi"
add_tip "$icon ''${ssid:-Wi-Fi}''${signal:+ · $signal%}"
fi
# VPN (nmcli, fallback interfaces)
vpn=$("$nmcli" -t -f TYPE,NAME connection show --active 2>/dev/null \
| awk -F: '$1 == "vpn" || $1 == "wireguard" { print $2; exit }')
if [ -z "$vpn" ]; then
vpn=$("$ip" -br link show 2>/dev/null \
| awk '($2 == "UP" || $2 == "UNKNOWN") && $1 ~ /^(tun|wg|proton|ppp)/ { sub(/@.*/, "", $1); print $1; exit }')
fi
if [ -n "$vpn" ]; then
add_seg "󰦝"
add_tip "󰦝 VPN · $vpn"
fi
if [ -z "$text" ]; then
text="󰤭"
tip="Disconnected"
fi
"$jq" -cn --arg text "$text" --arg class "$class" --arg tooltip "$tip" \
'{ text: $text, class: $class, tooltip: $tooltip }'
'';
vol-up = pkgs.writeShellScriptBin "vol-up" ''
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_SINK@ 5%+ --limit 1.0
${volGetText}
${updateOsd}
'';
vol-down = pkgs.writeShellScriptBin "vol-down" ''
${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_SINK@ 5%-
${volGetText}
${updateOsd}
'';
vol-mute = pkgs.writeShellScriptBin "vol-mute" ''
${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_SINK@ toggle
${volGetText}
${updateOsd}
'';
mic-mute = pkgs.writeShellScriptBin "mic-mute" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
${micSource}
[ -z "$src" ] && exit 0
"$wpctl" set-mute "$src" toggle
if "$wpctl" get-volume "$src" | grep -q MUTED; then
OSD_TEXT="󰍭 Muted"
else
OSD_TEXT="󰍬 Live"
fi
${updateOsd}
'';
mic-status = pkgs.writeShellScriptBin "mic-status" ''
${micSource}
if [ -n "$src" ] && ${pkgs.wireplumber}/bin/wpctl get-volume "$src" | grep -q MUTED; then
echo true
else
echo false
fi
'';
bright-up = pkgs.writeShellScriptBin "bright-up" ''
${pkgs.brightnessctl}/bin/brightnessctl set 5%+
${brightGetText}
${updateOsd}
'';
bright-down = pkgs.writeShellScriptBin "bright-down" ''
${pkgs.brightnessctl}/bin/brightnessctl set 5%-
${brightGetText}
${updateOsd}
'';
wifi-toggle = pkgs.writeShellScriptBin "wifi-toggle" ''
if nmcli radio wifi | grep -q enabled; then
nmcli radio wifi off
else
nmcli radio wifi on
fi
${updateOsd}
'';
bluetooth-toggle = pkgs.writeShellScriptBin "bluetooth-toggle" ''
if bluetoothctl show | grep -q "Powered: yes"; then
bluetoothctl power off
else
bluetoothctl power on
fi
${updateOsd}
'';
waybar-toggle = pkgs.writeShellScriptBin "waybar-toggle" ''
if pidof waybar > /dev/null; then
pkill waybar
else
hyprctl dispatch exec waybar
fi
'';
nightshift-toggle = pkgs.writeShellScriptBin "nightshift-toggle" ''
if pidof "hyprsunset" > /dev/null; then
pkill hyprsunset
OSD_TEXT="󰖔 Night Shift Off"
else
${pkgs.hyprsunset}/bin/hyprsunset -t 4500 &
OSD_TEXT="󰖔 Night Shift On"
fi
${updateOsd}
'';
focus-toggle = pkgs.writeShellScriptBin "focus-toggle" ''
if test -f /tmp/hypr-focus-mode; then
rm /tmp/hypr-focus-mode
OSD_TEXT="󰈈 Focus Off"
${updateOsd}
hyprctl reload
hyprctl dispatch exec waybar
else
touch /tmp/hypr-focus-mode
OSD_TEXT="󰈈 Focus On"
${updateOsd}
pkill waybar || true
hyprctl keyword animations:enabled false
hyprctl keyword general:gaps_in 0
hyprctl keyword general:gaps_out 0
hyprctl keyword decoration:active_opacity 1
hyprctl keyword decoration:inactive_opacity 1
hyprctl keyword decoration:rounding 0
fi
'';
dnd-toggle = pkgs.writeShellScriptBin "dnd-toggle" ''
state=$(${pkgs.swaynotificationcenter}/bin/swaync-client -d)
if [ "$state" = "true" ]; then
OSD_TEXT="󰂛 Do Not Disturb On"
else
OSD_TEXT="󰂚 Do Not Disturb Off"
fi
${updateOsd}
'';
output-cycle = pkgs.writeShellScriptBin "output-cycle" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
jq=${pkgs.jq}/bin/jq
pwdump=${pkgs.pipewire}/bin/pw-dump
# Sink IDs in pw-dump order.
ids=($("$pwdump" | "$jq" -r '.[] | select(.info.props."media.class"=="Audio/Sink") | .id'))
n=''${#ids[@]}
[ "$n" -eq 0 ] && exit 0
cur=$("$wpctl" inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
next_index=0
for i in "''${!ids[@]}"; do
if [ "''${ids[$i]}" = "$cur" ]; then
next_index=$(( (i + 1) % n ))
break
fi
done
next=''${ids[$next_index]}
"$wpctl" set-default "$next"
desc=$("$pwdump" | "$jq" -r --argjson id "$next" '.[] | select(.id==$id) | .info.props."node.description"')
OSD_TEXT="󰓃 ''${desc:-Output}"
${updateOsd}
'';
input-cycle = pkgs.writeShellScriptBin "input-cycle" ''
wpctl=${pkgs.wireplumber}/bin/wpctl
jq=${pkgs.jq}/bin/jq
pwdump=${pkgs.pipewire}/bin/pw-dump
# Source IDs in pw-dump order.
ids=($("$pwdump" | "$jq" -r '.[] | select(.info.props."media.class"=="Audio/Source") | .id'))
n=''${#ids[@]}
[ "$n" -eq 0 ] && exit 0
cur=$("$wpctl" inspect @DEFAULT_AUDIO_SOURCE@ 2>/dev/null | sed -n 's/^id \([0-9]*\),.*/\1/p')
next_index=0
for i in "''${!ids[@]}"; do
if [ "''${ids[$i]}" = "$cur" ]; then
next_index=$(( (i + 1) % n ))
break
fi
done
next=''${ids[$next_index]}
"$wpctl" set-default "$next"
desc=$("$pwdump" | "$jq" -r --argjson id "$next" '.[] | select(.id==$id) | .info.props."node.description"')
OSD_TEXT="󰍬 ''${desc:-Input}"
${updateOsd}
'';
color-pick = pkgs.writeShellScriptBin "color-pick" ''
# hyprpicker -a already copies the color to the clipboard.
color=$(${pkgs.hyprpicker}/bin/hyprpicker -a 2>/dev/null)
if [ -n "$color" ]; then
OSD_TEXT="󰈊 $color"
else
OSD_TEXT="󰈊 Cancelled"
fi
${updateOsd}
'';
screenshot-edit = pkgs.writeShellScriptBin "screenshot-edit" ''
${pkgs.hyprshot}/bin/hyprshot -m region --raw 2>/dev/null \
| ${pkgs.satty}/bin/satty --filename -
'';
record-toggle = pkgs.writeShellScriptBin "record-toggle" ''
if pgrep -x wf-recorder >/dev/null; then
# -INT lets wf-recorder finalize the file cleanly.
pkill -INT -x wf-recorder
OSD_TEXT="󰕧 Recording saved"
else
dir="$HOME/Videos"
mkdir -p "$dir"
file="$dir/rec-$(date +%Y%m%d-%H%M%S).mp4"
${pkgs.wf-recorder}/bin/wf-recorder -f "$file" >/dev/null 2>&1 &
OSD_TEXT="󰑊 Recording"
fi
${updateOsd}
'';
power-cycle = pkgs.writeShellScriptBin "power-cycle" ''
ppd=${pkgs.power-profiles-daemon}/bin/powerprofilesctl
cur=$("$ppd" get)
case "$cur" in
power-saver) next=balanced; icon="󰾅" ;;
balanced) next=performance; icon="󰓅" ;;
performance) next=power-saver; icon="󰌪" ;;
*) next=balanced; icon="󰾅" ;;
esac
"$ppd" set "$next"
OSD_TEXT="$icon ''${next^}"
${updateOsd}
'';
airplane-toggle = pkgs.writeShellScriptBin "airplane-toggle" ''
# Reuse nmcli + bluetoothctl (unprivileged) instead of rfkill.
on=false
nmcli radio wifi 2>/dev/null | grep -q enabled && on=true
bluetoothctl show 2>/dev/null | grep -q "Powered: yes" && on=true
if $on; then
nmcli radio wifi off 2>/dev/null
bluetoothctl power off >/dev/null 2>&1 || true
OSD_TEXT="󰀝 Airplane On"
else
nmcli radio wifi on 2>/dev/null
bluetoothctl power on >/dev/null 2>&1 || true
OSD_TEXT="󰀞 Airplane Off"
fi
${updateOsd}
'';
clipboard-menu = pkgs.writeShellScriptBin "clipboard-menu" ''
list=$(${pkgs.cliphist}/bin/cliphist list)
selected=$(printf '%s\n' "$list" | cut -f2- | ${tofiMenu} --prompt-text "Clipboard: ")
[ -n "$selected" ] && printf '%s\n' "$list" \
| awk -F'\t' -v s="$selected" '$2 == s {print; exit}' \
| ${pkgs.cliphist}/bin/cliphist decode \
| ${pkgs.wl-clipboard}/bin/wl-copy
'';
emoji-picker = pkgs.writeShellScriptBin "emoji-picker" ''
export PATH="${pkgs.wtype}/bin:${pkgs.wl-clipboard}/bin:$PATH"
export BEMOJI_PICKER_CMD="${tofiMenu} --prompt-text 'emoji: '"
# -t types the emoji (wtype), -c also copies it.
exec ${pkgs.bemoji}/bin/bemoji -t -c
'';
icon-picker = pkgs.writeShellScriptBin "icon-picker" ''
selected=$(${tofiMenu} --font "${config.stylix.fonts.monospace.name}" --prompt-text 'icon: ' < ${nerdFontIconList})
[ -z "$selected" ] || icon=$(printf '%s\n' "$selected" | awk '{print $1}')
[ -n "''${icon:-}" ] || exit 0
${pkgs.wtype}/bin/wtype "$icon"
printf '%s' "$icon" | ${pkgs.wl-clipboard}/bin/wl-copy
'';
caffeine-toggle = pkgs.writeShellScriptBin "caffeine-toggle" ''
# Pause hypridle (stay awake) or resume it.
if systemctl --user is-active --quiet hypridle; then
systemctl --user stop hypridle
OSD_TEXT="󰅶 Keep Awake On"
else
systemctl --user start hypridle
OSD_TEXT="󰾫 Keep Awake Off"
fi
${updateOsd}
'';
}

Some files were not shown because too many files have changed in this diff Show More