From b7987c6a6e19feecfe08734fbf84f59556f7bba5 Mon Sep 17 00:00:00 2001 From: Adam0 Date: Sat, 9 May 2026 23:53:03 +0200 Subject: [PATCH] dendritic migration 2026-05-09 --- flake.lock | 347 +++++++++++++++--- flake.nix | 11 + keys/{ => users}/adam0.asc | 0 modules/desktop/xdg/applications/audio.nix | 29 ++ modules/desktop/xdg/applications/code.nix | 25 ++ modules/desktop/xdg/applications/default.nix | 5 + .../xdg/applications/document-viewers.nix | 101 +++++ modules/desktop/xdg/applications/files.nix | 10 + .../xdg/applications/image-editors.nix | 40 ++ .../xdg/applications/image-viewers.nix | 53 +++ .../desktop/xdg/applications/protocols.nix | 51 +++ modules/desktop/xdg/applications/video.nix | 43 +++ modules/desktop/xdg/cleanup.nix | 57 +++ modules/desktop/xdg/default.nix | 5 + modules/desktop/xdg/dirs.nix | 12 + modules/desktop/xdg/polkit.nix | 9 + modules/desktop/xdg/portal.nix | 96 +++++ modules/desktop/xdg/terminal.nix | 12 + modules/nix/lib.nix | 8 +- modules/pkgs/default.nix | 2 + modules/pkgs/neovim/nocheatsheet-nvim.nix | 31 ++ .../pkgs/neovim/telescope-all-recent-nvim.nix | 43 +++ modules/programs/cli/starship/build.nix | 27 ++ modules/programs/cli/starship/container.nix | 21 ++ modules/programs/cli/starship/default.nix | 5 + modules/programs/cli/starship/disabled.nix | 54 +++ modules/programs/cli/starship/format.nix | 74 ++++ modules/programs/cli/starship/general.nix | 170 +++++++++ modules/programs/cli/starship/git.nix | 47 +++ modules/programs/cli/starship/languages.nix | 123 +++++++ modules/programs/cli/starship/runtimes.nix | 34 ++ modules/programs/gui/beeper.nix | 52 ++- modules/programs/gui/discord/autostart.nix | 37 ++ modules/programs/gui/discord/default.nix | 34 ++ .../gui/discord/plugins/communication.nix | 210 +++++++++++ modules/programs/gui/discord/plugins/core.nix | 57 +++ .../gui/discord/plugins/integrations.nix | 29 ++ .../gui/discord/plugins/interface.nix | 159 ++++++++ .../programs/gui/discord/plugins/media.nix | 57 +++ .../programs/gui/discord/plugins/social.nix | 61 +++ modules/programs/gui/discord/settings.nix | 28 ++ .../programs/gui/discord/themes/default.nix | 78 ++++ .../programs/gui/discord/themes/snippets.css | 111 ++++++ .../programs/gui/discord/themes/system24.css | 231 ++++++++++++ modules/programs/gui/ghostty/cursor.glsl | 216 +++++++++++ modules/programs/gui/ghostty/default.nix | 32 ++ modules/programs/gui/ghostty/keybinds.nix | 32 ++ modules/programs/gui/ghostty/settings.nix | 40 ++ modules/programs/gui/steam.nix | 13 + modules/programs/tui/neovim/keymap.nix | 6 +- modules/programs/tui/opencode/default.nix | 106 ++++++ modules/programs/tui/opencode/env.nix | 35 ++ modules/programs/tui/opencode/formatter.nix | 162 ++++++++ modules/programs/tui/opencode/instructions.md | 67 ++++ modules/programs/tui/opencode/lsp.nix | 150 ++++++++ .../programs/tui/opencode/mcp-wrappers.nix | 59 +++ modules/programs/tui/opencode/plugins.nix | 35 ++ modules/programs/tui/opencode/settings.nix | 43 +++ .../tui/opencode/skills/create-readme.md | 28 ++ .../programs/tui/opencode/skills/default.nix | 39 ++ .../tui/opencode/skills/find-skills.md | 143 ++++++++ .../tui/opencode/skills/lazy-mcp/context7.md | 35 ++ .../tui/opencode/skills/lazy-mcp/github.md | 34 ++ .../tui/opencode/skills/lazy-mcp/grep-app.md | 34 ++ .../programs/tui/opencode/skills/nix/debug.md | 103 ++++++ .../programs/tui/opencode/skills/nix/index.md | 103 ++++++ .../tui/opencode/skills/nix/search.md | 146 ++++++++ .../tui/opencode/skills/nvf-config.md | 327 +++++++++++++++++ .../programs/tui/opencode/skills/uncodixfy.md | 157 ++++++++ modules/programs/tui/wiremix.nix | 4 +- .../programs/tui/yazi/plugins/recycle-bin.nix | 4 +- .../tui/yazi/plugins/relative-motions.nix | 4 +- modules/services/envfs.nix | 23 +- todo-stats.sh | 95 ++++- todo.md | 128 +++---- 75 files changed, 4963 insertions(+), 129 deletions(-) rename keys/{ => users}/adam0.asc (100%) create mode 100644 modules/desktop/xdg/applications/audio.nix create mode 100644 modules/desktop/xdg/applications/code.nix create mode 100644 modules/desktop/xdg/applications/default.nix create mode 100644 modules/desktop/xdg/applications/document-viewers.nix create mode 100644 modules/desktop/xdg/applications/files.nix create mode 100644 modules/desktop/xdg/applications/image-editors.nix create mode 100644 modules/desktop/xdg/applications/image-viewers.nix create mode 100644 modules/desktop/xdg/applications/protocols.nix create mode 100644 modules/desktop/xdg/applications/video.nix create mode 100644 modules/desktop/xdg/cleanup.nix create mode 100644 modules/desktop/xdg/default.nix create mode 100644 modules/desktop/xdg/dirs.nix create mode 100644 modules/desktop/xdg/polkit.nix create mode 100644 modules/desktop/xdg/portal.nix create mode 100644 modules/desktop/xdg/terminal.nix create mode 100644 modules/pkgs/neovim/nocheatsheet-nvim.nix create mode 100644 modules/pkgs/neovim/telescope-all-recent-nvim.nix create mode 100644 modules/programs/cli/starship/build.nix create mode 100644 modules/programs/cli/starship/container.nix create mode 100644 modules/programs/cli/starship/default.nix create mode 100644 modules/programs/cli/starship/disabled.nix create mode 100644 modules/programs/cli/starship/format.nix create mode 100644 modules/programs/cli/starship/general.nix create mode 100644 modules/programs/cli/starship/git.nix create mode 100644 modules/programs/cli/starship/languages.nix create mode 100644 modules/programs/cli/starship/runtimes.nix create mode 100644 modules/programs/gui/discord/autostart.nix create mode 100644 modules/programs/gui/discord/default.nix create mode 100644 modules/programs/gui/discord/plugins/communication.nix create mode 100644 modules/programs/gui/discord/plugins/core.nix create mode 100644 modules/programs/gui/discord/plugins/integrations.nix create mode 100644 modules/programs/gui/discord/plugins/interface.nix create mode 100644 modules/programs/gui/discord/plugins/media.nix create mode 100644 modules/programs/gui/discord/plugins/social.nix create mode 100644 modules/programs/gui/discord/settings.nix create mode 100644 modules/programs/gui/discord/themes/default.nix create mode 100644 modules/programs/gui/discord/themes/snippets.css create mode 100644 modules/programs/gui/discord/themes/system24.css create mode 100644 modules/programs/gui/ghostty/cursor.glsl create mode 100644 modules/programs/gui/ghostty/default.nix create mode 100644 modules/programs/gui/ghostty/keybinds.nix create mode 100644 modules/programs/gui/ghostty/settings.nix create mode 100644 modules/programs/tui/opencode/default.nix create mode 100644 modules/programs/tui/opencode/env.nix create mode 100644 modules/programs/tui/opencode/formatter.nix create mode 100644 modules/programs/tui/opencode/instructions.md create mode 100644 modules/programs/tui/opencode/lsp.nix create mode 100644 modules/programs/tui/opencode/mcp-wrappers.nix create mode 100644 modules/programs/tui/opencode/plugins.nix create mode 100644 modules/programs/tui/opencode/settings.nix create mode 100644 modules/programs/tui/opencode/skills/create-readme.md create mode 100644 modules/programs/tui/opencode/skills/default.nix create mode 100644 modules/programs/tui/opencode/skills/find-skills.md create mode 100644 modules/programs/tui/opencode/skills/lazy-mcp/context7.md create mode 100644 modules/programs/tui/opencode/skills/lazy-mcp/github.md create mode 100644 modules/programs/tui/opencode/skills/lazy-mcp/grep-app.md create mode 100644 modules/programs/tui/opencode/skills/nix/debug.md create mode 100644 modules/programs/tui/opencode/skills/nix/index.md create mode 100644 modules/programs/tui/opencode/skills/nix/search.md create mode 100644 modules/programs/tui/opencode/skills/nvf-config.md create mode 100644 modules/programs/tui/opencode/skills/uncodixfy.md diff --git a/flake.lock b/flake.lock index d959ba4..848b04a 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,39 @@ { "nodes": { + "abseil-src": { + "flake": false, + "locked": { + "lastModified": 1722535511, + "narHash": "sha256-51jpDhdZ0n+KLmxh8KVaTz53pZAB0dHjmILFX+OLud4=", + "owner": "abseil", + "repo": "abseil-cpp", + "rev": "4447c7562e3bc702ade25105912dce503f0c4010", + "type": "github" + }, + "original": { + "owner": "abseil", + "ref": "20240722.0", + "repo": "abseil-cpp", + "type": "github" + } + }, + "asio-src": { + "flake": false, + "locked": { + "lastModified": 1702548444, + "narHash": "sha256-8Xo6J6+dTvDtsthjbLMMTInHMDnMMM0rQwzbZu70J/s=", + "owner": "chriskohlhoff", + "repo": "asio", + "rev": "22ccfc94fc77356f7820601f9f33b9129a337d2d", + "type": "github" + }, + "original": { + "owner": "chriskohlhoff", + "ref": "asio-1-30-0", + "repo": "asio", + "type": "github" + } + }, "crane": { "locked": { "lastModified": 1777830388, @@ -15,6 +49,23 @@ "type": "github" } }, + "curl-src": { + "flake": false, + "locked": { + "lastModified": 1743572790, + "narHash": "sha256-huAGWNm2rYBmgzUuYQ21IYp2skyQECelEkXPMBJY3cE=", + "owner": "curl", + "repo": "curl", + "rev": "1c3149881769e7bd79b072e48374e4c2b3678b2f", + "type": "github" + }, + "original": { + "owner": "curl", + "ref": "curl-8_13_0", + "repo": "curl", + "type": "github" + } + }, "determinate": { "inputs": { "determinate-nixd-aarch64-darwin": "determinate-nixd-aarch64-darwin", @@ -24,12 +75,12 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1777920462, - "narHash": "sha256-LcwRttYxf2u6WUQC53alL0mdEoAJjOZ3AVMVXj2SdgI=", - "rev": "f3e0ac2fd392912b6af164b61b9aeadb7a4c1ef1", - "revCount": 415, + "lastModified": 1778179392, + "narHash": "sha256-W6zorvjBYbzMNvqKIqCdpDF4rq3gj50Xximl56YM9/I=", + "rev": "efd54faa68be8cd777b5c28cab11e638998a0853", + "revCount": 416, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/determinate/3.19.1/019df455-18df-7300-905f-4cd973ae848d/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/determinate/3.20.0/019e03c3-7dc0-7873-a333-9ab02d6f690c/source.tar.gz" }, "original": { "type": "tarball", @@ -39,37 +90,37 @@ "determinate-nixd-aarch64-darwin": { "flake": false, "locked": { - "narHash": "sha256-+d0XZPSqqTSPrSMSf1Vz/ipsZhnW+p27RH0d6XywOMU=", + "narHash": "sha256-z4mCqKI3Qd6weuHrlfzGccJG0giym/VJhKv20ijRSs0=", "type": "file", - "url": "https://install.determinate.systems/determinate-nixd/tag/v3.19.1/macOS" + "url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/macOS" }, "original": { "type": "file", - "url": "https://install.determinate.systems/determinate-nixd/tag/v3.19.1/macOS" + "url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/macOS" } }, "determinate-nixd-aarch64-linux": { "flake": false, "locked": { - "narHash": "sha256-oCCfgSQZBdiNBRolKQHap9En1JU+62pZd1M7kkvpqQ8=", + "narHash": "sha256-yW+VNepSRytzfanSssPMJPvwioCcmlZYaBX8++UFkAk=", "type": "file", - "url": "https://install.determinate.systems/determinate-nixd/tag/v3.19.1/aarch64-linux" + "url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/aarch64-linux" }, "original": { "type": "file", - "url": "https://install.determinate.systems/determinate-nixd/tag/v3.19.1/aarch64-linux" + "url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/aarch64-linux" } }, "determinate-nixd-x86_64-linux": { "flake": false, "locked": { - "narHash": "sha256-F/Csmk0KQas8anb9nepMyqMptMHMoShmOljomLEK5Wo=", + "narHash": "sha256-+L102C3Hhkd1GlXmRm2eLTLsZKBxEvooiQZFqQRlBf0=", "type": "file", - "url": "https://install.determinate.systems/determinate-nixd/tag/v3.19.1/x86_64-linux" + "url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/x86_64-linux" }, "original": { "type": "file", - "url": "https://install.determinate.systems/determinate-nixd/tag/v3.19.1/x86_64-linux" + "url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/x86_64-linux" } }, "flake-compat": { @@ -89,6 +140,20 @@ } }, "flake-compat_2": { + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "revCount": 69, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" + } + }, + "flake-compat_3": { "flake": false, "locked": { "lastModified": 1751685974, @@ -193,11 +258,11 @@ ] }, "locked": { - "lastModified": 1778009629, - "narHash": "sha256-nUoQtf4Zq7DRYJrfv904hjrxjAlWVP6a1pNNFKx3FCg=", + "lastModified": 1778248595, + "narHash": "sha256-dhFgEjoeJMYN/7OY6xfxS799YB4IjbbYXTjyGIJyLpc=", "owner": "nix-community", "repo": "home-manager", - "rev": "00ed86e58bb6979a7921859fd1615d19382eac5c", + "rev": "fdb2ccba9d5e1238d32e0c4a3ec1a277efa80c1d", "type": "github" }, "original": { @@ -221,13 +286,113 @@ "type": "github" } }, + "json-src": { + "flake": false, + "locked": { + "lastModified": 1744360948, + "narHash": "sha256-cECvDOLxgX7Q9R3IE86Hj9JJUxraDQvhoyPDF03B2CY=", + "owner": "nlohmann", + "repo": "json", + "rev": "55f93686c01528224f448c19128836e7df245f72", + "type": "github" + }, + "original": { + "owner": "nlohmann", + "ref": "v3.12.0", + "repo": "json", + "type": "github" + } + }, + "luajit-src": { + "flake": false, + "locked": { + "lastModified": 1763177035, + "narHash": "sha256-oYD86MqmlJpiCuEs4LwVtxvarPtz1RPWm8nJqNE0sBs=", + "owner": "SteamClientHomebrew", + "repo": "LuaJIT", + "rev": "89550023569c3e195e75e12951c067fe5591e0d2", + "type": "github" + }, + "original": { + "owner": "SteamClientHomebrew", + "ref": "v2.1", + "repo": "LuaJIT", + "type": "github" + } + }, + "millennium": { + "inputs": { + "abseil-src": "abseil-src", + "asio-src": "asio-src", + "curl-src": "curl-src", + "json-src": "json-src", + "luajit-src": "luajit-src", + "millennium-src": "millennium-src", + "minizip-src": "minizip-src", + "nixpkgs": [ + "nixpkgs" + ], + "re2-src": "re2-src", + "websocketpp-src": "websocketpp-src", + "zlib-src": "zlib-src" + }, + "locked": { + "dir": "packages/nix", + "lastModified": 1778348033, + "narHash": "sha256-TxCHaR1RFQ3de2E5RqeyqAWd4GmpQQpeeS4Xa4Ur3L4=", + "owner": "SteamClientHomebrew", + "repo": "Millennium", + "rev": "5df61f9d58e6944f5dfe73f0f8e25e654b5de261", + "type": "github" + }, + "original": { + "dir": "packages/nix", + "owner": "SteamClientHomebrew", + "repo": "Millennium", + "type": "github" + } + }, + "millennium-src": { + "flake": false, + "locked": { + "lastModified": 1770463863, + "narHash": "sha256-MceGTpXobCAh5Ll/1iTWcEf6/nHY3Ll1t06JxbYc+Co=", + "owner": "SteamClientHomebrew", + "repo": "Millennium", + "rev": "1bc62c94a06f25f7e8d7e269f11cd968cf576bff", + "type": "github" + }, + "original": { + "owner": "SteamClientHomebrew", + "repo": "Millennium", + "rev": "1bc62c94a06f25f7e8d7e269f11cd968cf576bff", + "type": "github" + } + }, + "minizip-src": { + "flake": false, + "locked": { + "lastModified": 1746408966, + "narHash": "sha256-I3CZwtfJMsZYQOMdGhooUN8vssnQj6rCzv8F+rne3vg=", + "owner": "zlib-ng", + "repo": "minizip-ng", + "rev": "f3ed731e27a97e30dffe076ed5e0537daae5c1bd", + "type": "github" + }, + "original": { + "owner": "zlib-ng", + "ref": "4.0.10", + "repo": "minizip-ng", + "type": "github" + } + }, "mnw": { "locked": { - "lastModified": 1770419553, - "narHash": "sha256-b1XqsH7AtVf2dXmq2iyRr2NC1yG7skY7Z6N2MpWHlK4=", + "lastModified": 1777828893, + "narHash": "sha256-gVWVnmyNr74BVKfhMMZDWkhx2699dhmZ2g0W8TTHtkk=", "owner": "Gerg-L", "repo": "mnw", - "rev": "2aaffa8030d0b262176146adbb6b0e6374ce2957", + "rev": "c1c0b544bfabe6669b5a6a0383ccb475fe60258b", "type": "github" }, "original": { @@ -267,12 +432,12 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1777918043, - "narHash": "sha256-Zl229Ynd484malKv6UPbbjIgRZHThLH3NugvUD63M6M=", - "rev": "35185ec4d4dcdfe34e08f0e48f6a66afd3b95007", - "revCount": 25846, + "lastModified": 1778177425, + "narHash": "sha256-oyHvP5HDRe59opmjTrq2ED9lh+R9FrHyaCGPPNfBqWM=", + "rev": "f0ccb960d3ad5bff28acd9cabf8bdef885b5d52f", + "revCount": 25858, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.19.1/019df44d-015e-7013-9fce-a0a4d4a95c7b/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.20.0/019e03bc-3f83-7833-aba3-b691ef4956c7/source.tar.gz" }, "original": { "type": "tarball", @@ -301,11 +466,11 @@ ] }, "locked": { - "lastModified": 1777787189, - "narHash": "sha256-2KUbS/HhzWW3kkkY1+RiWj9mJ76VEXw8lBJzcCFKzfY=", + "lastModified": 1778240325, + "narHash": "sha256-d2HIS7LpfI0lgxiXCXLjxrHl3eIdNvAVexOu0xiM488=", "owner": "nix-community", "repo": "nix-index-database", - "rev": "2dea2b920e7127b3afa8506713f23536651de312", + "rev": "dd2d0e3f6ba00af01b9498f5697173bdc2524bee", "type": "github" }, "original": { @@ -314,6 +479,31 @@ "type": "github" } }, + "nixcord": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-parts": [ + "flake-parts" + ], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-nixcord": "nixpkgs-nixcord" + }, + "locked": { + "lastModified": 1778346777, + "narHash": "sha256-7gpzB8MDCMcRE0wDr7jjxK7/obfeiyAulsfQEsK9pi4=", + "owner": "kaylorben", + "repo": "nixcord", + "rev": "43930a83206bcdcf3ba4fc10aa4fb9c5f7a677e6", + "type": "github" + }, + "original": { + "owner": "kaylorben", + "repo": "nixcord", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1773222311, @@ -344,6 +534,22 @@ "type": "github" } }, + "nixpkgs-nixcord": { + "locked": { + "lastModified": 1777428379, + "narHash": "sha256-ypxFOeDz+CqADEQNL72haqGjvZQdBR5Vc7pyx2JDttI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "755f5aa91337890c432639c60b6064bb7fe67769", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -362,12 +568,12 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1776949667, - "narHash": "sha256-GMSVw35Q+294GlrTUKlx087E31z7KurReQ1YHSKp5iw=", - "rev": "01fbdeef22b76df85ea168fbfe1bfd9e63681b30", - "revCount": 985930, + "lastModified": 1777826146, + "narHash": "sha256-wQ/iN5Zp5VIa3ebBibijPnLyKhor+xEbDy4d0goa9Zs=", + "rev": "73c703c22422b8951895a960959dbbaca7296492", + "revCount": 991389, "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.985930%2Brev-01fbdeef22b76df85ea168fbfe1bfd9e63681b30/019dd2c0-665a-7740-8096-1a0c0d7a8d1f/source.tar.gz" + "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.991389%2Brev-73c703c22422b8951895a960959dbbaca7296492/019df6c8-934b-7d40-b402-027bb5def30f/source.tar.gz" }, "original": { "type": "tarball", @@ -376,11 +582,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1778036283, - "narHash": "sha256-GW2cEd/cLcVbbCes8iQuoY2qGIeCA7UiaD351hpkXfI=", - "rev": "ed67bc86e84e51d4a88e73c7fd36006dc876476f", + "lastModified": 1778124196, + "narHash": "sha256-Z5mLDoR8p0d7psIY4LnyaHHRykXngMcWXTZ9JWvtvPc=", + "rev": "68a8af93ff4297686cb68880845e61e5e2e41d92", "type": "tarball", - "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre993032.ed67bc86e84e/nixexprs.tar.xz" + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre993588.68a8af93ff42/nixexprs.tar.xz" }, "original": { "type": "tarball", @@ -389,7 +595,7 @@ }, "nvf": { "inputs": { - "flake-compat": "flake-compat_2", + "flake-compat": "flake-compat_3", "flake-parts": [ "flake-parts" ], @@ -401,11 +607,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1777810151, - "narHash": "sha256-NZK6I0HmmiwE/vT0eUetke8VWX1WGGeMKRW+ki2kqDU=", + "lastModified": 1778347034, + "narHash": "sha256-iS2+fFwP5QL3KlFxmv07RypfOUa6tOpXX4vt9BSiZO8=", "owner": "adam01110", "repo": "nvf", - "rev": "de9c81e012b38dfc54ca373a0416eacab1c48f0b", + "rev": "78a1e46f6d651e4d5f5fac6bd72e57602114d915", "type": "github" }, "original": { @@ -415,6 +621,23 @@ "type": "github" } }, + "re2-src": { + "flake": false, + "locked": { + "lastModified": 1762353507, + "narHash": "sha256-0J1HVk+eR7VN0ymucW9dNlT36j16XIfCzcs1EVyEIEU=", + "owner": "google", + "repo": "re2", + "rev": "927f5d53caf8111721e734cf24724686bb745f55", + "type": "github" + }, + "original": { + "owner": "google", + "ref": "2025-11-05", + "repo": "re2", + "type": "github" + } + }, "root": { "inputs": { "determinate": "determinate", @@ -422,8 +645,10 @@ "flake-parts": "flake-parts_2", "home-manager": "home-manager", "import-tree": "import-tree", + "millennium": "millennium", "nix-flatpak": "nix-flatpak", "nix-index-database": "nix-index-database", + "nixcord": "nixcord", "nixpkgs": "nixpkgs_3", "nvf": "nvf", "sops-nix": "sops-nix", @@ -530,6 +755,23 @@ "type": "github" } }, + "websocketpp-src": { + "flake": false, + "locked": { + "lastModified": 1587320717, + "narHash": "sha256-9fIwouthv2GcmBe/UPvV7Xn9P2o0Kmn2hCI4jCh0hPM=", + "owner": "zaphoyd", + "repo": "websocketpp", + "rev": "56123c87598f8b1dd471be83ca841ceae07f95ba", + "type": "github" + }, + "original": { + "owner": "zaphoyd", + "ref": "0.8.2", + "repo": "websocketpp", + "type": "github" + } + }, "zen-browser": { "inputs": { "home-manager": [ @@ -540,11 +782,11 @@ ] }, "locked": { - "lastModified": 1777564084, - "narHash": "sha256-O9VRkxg+2j+sh+c73wi4VeIBECoqW2PlnCR9Qe1nQKA=", + "lastModified": 1778343639, + "narHash": "sha256-HHRwiRJlbVLkj5lu/0m1+bczp8Oe0BsC87lpdDnbJOw=", "owner": "0xc000022070", "repo": "zen-browser-flake", - "rev": "d93443c0f6fdb3b179bed68856f322dba4842612", + "rev": "e6bdec08f8b1836c9f241c08ae3f03bbec34dbf0", "type": "github" }, "original": { @@ -553,6 +795,23 @@ "repo": "zen-browser-flake", "type": "github" } + }, + "zlib-src": { + "flake": false, + "locked": { + "lastModified": 1754565515, + "narHash": "sha256-c2RYqHi3hj/ViBzJcYWoNib27GAbq/B1SJUfvG7CPG4=", + "owner": "zlib-ng", + "repo": "zlib-ng", + "rev": "425439062b114a0f6cf625022c41d929c7e879f9", + "type": "github" + }, + "original": { + "owner": "zlib-ng", + "ref": "2.2.5", + "repo": "zlib-ng", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 4ce9471..c3442b0 100644 --- a/flake.nix +++ b/flake.nix @@ -15,11 +15,22 @@ inputs.nixpkgs.follows = "nixpkgs"; }; import-tree.url = "github:vic/import-tree"; + millennium = { + url = "github:SteamClientHomebrew/Millennium?dir=packages/nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; nix-flatpak.url = "github:gmodena/nix-flatpak"; nix-index-database = { url = "github:nix-community/nix-index-database"; inputs.nixpkgs.follows = "nixpkgs"; }; + nixcord = { + url = "github:kaylorben/nixcord"; + inputs = { + flake-parts.follows = "flake-parts"; + nixpkgs.follows = "nixpkgs"; + }; + }; nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; nvf = { url = "github:adam01110/nvf?ref=personal"; diff --git a/keys/adam0.asc b/keys/users/adam0.asc similarity index 100% rename from keys/adam0.asc rename to keys/users/adam0.asc diff --git a/modules/desktop/xdg/applications/audio.nix b/modules/desktop/xdg/applications/audio.nix new file mode 100644 index 0000000..5632c2c --- /dev/null +++ b/modules/desktop/xdg/applications/audio.nix @@ -0,0 +1,29 @@ +{ + flake.modules.homeManager.xdgApplications = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + mimeAppDefaults + mimeAppEntries + # keep-sorted end + ; + in { + xdg.mimeApps.defaultApplications = mimeAppDefaults (mimeAppEntries "audio" "org.gnome.Decibels.desktop" [ + # keep-sorted start + "aac" + "flac" + "mp2" + "mp4" + "mpeg" + "vnd.wave" + "x-aiff" + "x-ape" + "x-m4b" + "x-opus+ogg" + "x-speex" + "x-vorbis+ogg" + "x-wavpack" + # keep-sorted end + ]); + }; +} diff --git a/modules/desktop/xdg/applications/code.nix b/modules/desktop/xdg/applications/code.nix new file mode 100644 index 0000000..65d7e38 --- /dev/null +++ b/modules/desktop/xdg/applications/code.nix @@ -0,0 +1,25 @@ +{ + flake.modules.homeManager.xdgApplications = _: { + xdg.mimeApps.defaultApplications = { + # keep-sorted start + "application/sql" = "nvim.desktop"; + "application/x-cmakecache" = "nvim.desktop"; + "application/x-perl" = "nvim.desktop"; + "application/x-php" = "nvim.desktop"; + "application/x-shellscript" = "nvim.desktop"; + "application/x-zerosize" = "nvim.desktop"; + "application/xml" = "nvim.desktop"; + "text/css" = "nvim.desktop"; + "text/markdown" = "nvim.desktop"; + "text/plain" = "nvim.desktop"; + "text/tcl" = "nvim.desktop"; + "text/x-csrc" = "nvim.desktop"; + "text/x-makefile" = "nvim.desktop"; + "text/x-moc" = "nvim.desktop"; + "text/x-pascal" = "nvim.desktop"; + "text/x-patch" = "nvim.desktop"; + "text/x-python" = "nvim.desktop"; + # keep-sorted end + }; + }; +} diff --git a/modules/desktop/xdg/applications/default.nix b/modules/desktop/xdg/applications/default.nix new file mode 100644 index 0000000..95e310b --- /dev/null +++ b/modules/desktop/xdg/applications/default.nix @@ -0,0 +1,5 @@ +{ + flake.modules.homeManager.xdgApplications = _: { + xdg.mimeApps.enable = true; + }; +} diff --git a/modules/desktop/xdg/applications/document-viewers.nix b/modules/desktop/xdg/applications/document-viewers.nix new file mode 100644 index 0000000..fa33f76 --- /dev/null +++ b/modules/desktop/xdg/applications/document-viewers.nix @@ -0,0 +1,101 @@ +{ + flake.modules.homeManager.xdgApplications = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + mimeAppDefaults + mimeAppEntries + # keep-sorted end + ; + in { + xdg.mimeApps.defaultApplications = mimeAppDefaults ( + # keep-sorted start block=yes + (mimeAppEntries "application" "org.pwmt.zathura-pdf-mupdf.desktop" [ + # keep-sorted start + "epub+zip" + "oxps" + "pdf" + "vnd.ms-xpsdocument" + # keep-sorted end + ]) + ++ (mimeAppEntries "application" "org.pwmt.zathura-cb.desktop" [ + # keep-sorted start + "vnd.comicbook+zip" + "vnd.comicbook-rar" + "x-cb7" + "x-cbt" + # keep-sorted end + ]) + ++ (mimeAppEntries "application" "org.pwmt.zathura-ps.desktop" [ + "postscript" + ]) + ++ (mimeAppEntries "application" "zaread.desktop" [ + # keep-sorted start + "msword" + "msword-template" + "rtf" + "vnd.apple.keynote" + "vnd.apple.numbers" + "vnd.apple.pages" + "vnd.ms-excel" + "vnd.ms-excel.sheet.binary.macroEnabled.12" + "vnd.ms-excel.sheet.macroEnabled.12" + "vnd.ms-excel.template.macroEnabled.12" + "vnd.ms-powerpoint" + "vnd.ms-powerpoint.presentation.macroEnabled.12" + "vnd.ms-powerpoint.slideshow.macroEnabled.12" + "vnd.ms-powerpoint.template.macroEnabled.12" + "vnd.ms-visio.drawing.macroEnabled.main+xml" + "vnd.ms-visio.drawing.main+xml" + "vnd.ms-visio.stencil.macroEnabled.main+xml" + "vnd.ms-visio.stencil.main+xml" + "vnd.ms-visio.template.macroEnabled.main+xml" + "vnd.ms-visio.template.main+xml" + "vnd.ms-word.document.macroEnabled.12" + "vnd.ms-word.template.macroEnabled.12" + "vnd.oasis.opendocument.graphics" + "vnd.oasis.opendocument.presentation" + "vnd.oasis.opendocument.presentation-flat-xml" + "vnd.oasis.opendocument.presentation-template" + "vnd.oasis.opendocument.spreadsheet" + "vnd.oasis.opendocument.spreadsheet-flat-xml" + "vnd.oasis.opendocument.spreadsheet-template" + "vnd.oasis.opendocument.text" + "vnd.oasis.opendocument.text-flat-xml" + "vnd.oasis.opendocument.text-template" + "vnd.openxmlformats-officedocument.presentationml.presentation" + "vnd.openxmlformats-officedocument.presentationml.slideshow" + "vnd.openxmlformats-officedocument.presentationml.template" + "vnd.openxmlformats-officedocument.spreadsheetml.sheet" + "vnd.openxmlformats-officedocument.spreadsheetml.template" + "vnd.openxmlformats-officedocument.wordprocessingml.document" + "vnd.openxmlformats-officedocument.wordprocessingml.template" + "vnd.sun.xml.calc" + "vnd.sun.xml.impress" + "vnd.sun.xml.writer" + "vnd.sun.xml.writer.template" + "x-hwp" + "x-mobipocket-ebook" + # keep-sorted end + ]) + ++ (mimeAppEntries "image" "org.pwmt.zathura-djvu.desktop" [ + # keep-sorted start + "vnd.djvu" + "vnd.djvu+multipage" + # keep-sorted end + ]) + ++ (mimeAppEntries "image" "org.pwmt.zathura-ps.desktop" [ + "x-eps" + ]) + ++ (mimeAppEntries "text" "zaread.desktop" [ + # keep-sorted start + "csv" + "markdown" + "x-markdown" + "x-typst" + # keep-sorted end + ]) + # keep-sorted end + ); + }; +} diff --git a/modules/desktop/xdg/applications/files.nix b/modules/desktop/xdg/applications/files.nix new file mode 100644 index 0000000..c68312a --- /dev/null +++ b/modules/desktop/xdg/applications/files.nix @@ -0,0 +1,10 @@ +{ + flake.modules.homeManager.xdgApplications = _: { + xdg.mimeApps.defaultApplications = { + # keep-sorted start + "application/x-modrinth-modpack+zip" = "org.prismlauncher.PrismLauncher.desktop"; + "inode/directory" = "yazi.desktop"; + # keep-sorted end + }; + }; +} diff --git a/modules/desktop/xdg/applications/image-editors.nix b/modules/desktop/xdg/applications/image-editors.nix new file mode 100644 index 0000000..dc1dd46 --- /dev/null +++ b/modules/desktop/xdg/applications/image-editors.nix @@ -0,0 +1,40 @@ +{ + flake.modules.homeManager.xdgApplications = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + mimeAppDefaults + mimeAppEntries + # keep-sorted end + ; + in { + xdg.mimeApps.defaultApplications = mimeAppDefaults ( + # keep-sorted start block=yes + (mimeAppEntries "image" "gimp.desktop" [ + # keep-sorted start + "jp2" + "openraster" + "vnd.adobe.photoshop" + "vnd.wap.wbmp" + "vnd.zbrush.pcx" + "wmf" + "x-icns" + "x-ilbm" + "x-jp2-codestream" + "x-sgi" + "x-sun-raster" + "x-xbitmap" + "x-xcf" + "x-xwindowdump" + # keep-sorted end + ]) + ++ (mimeAppEntries "application" "gimp.desktop" [ + # keep-sorted start + "fits" + "x-navi-animation" + # keep-sorted end + ]) + # keep-sorted end + ); + }; +} diff --git a/modules/desktop/xdg/applications/image-viewers.nix b/modules/desktop/xdg/applications/image-viewers.nix new file mode 100644 index 0000000..6b6cd3d --- /dev/null +++ b/modules/desktop/xdg/applications/image-viewers.nix @@ -0,0 +1,53 @@ +{ + flake.modules.homeManager.xdgApplications = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + mimeAppDefaults + mimeAppEntries + # keep-sorted end + ; + in { + xdg.mimeApps.defaultApplications = mimeAppDefaults ( + # keep-sorted start block=yes + (mimeAppEntries "image" "org.gnome.Loupe.desktop" [ + # keep-sorted start + "avif" + "bmp" + "gif" + "heif" + "jpeg" + "jxl" + "png" + "qoi" + "svg+xml" + "svg+xml-compressed" + "tiff" + "vnd.microsoft.icon" + "webp" + "x-adobe-dng" + "x-canon-cr2" + "x-dds" + "x-exr" + "x-kodak-dcr" + "x-kodak-k25" + "x-kodak-kdc" + "x-nikon-nef" + "x-pentax-pef" + "x-portable-anymap" + "x-portable-bitmap" + "x-portable-graymap" + "x-portable-pixmap" + "x-sony-arw" + "x-sony-sr2" + "x-sony-srf" + "x-tga" + # keep-sorted end + ]) + ++ (mimeAppEntries "video" "org.gnome.Loupe.desktop" [ + "x-mjpeg" + ]) + # keep-sorted end + ); + }; +} diff --git a/modules/desktop/xdg/applications/protocols.nix b/modules/desktop/xdg/applications/protocols.nix new file mode 100644 index 0000000..5b668ec --- /dev/null +++ b/modules/desktop/xdg/applications/protocols.nix @@ -0,0 +1,51 @@ +{ + flake.modules.homeManager.xdgApplications = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + mimeAppDefaults + mimeAppEntries + # keep-sorted end + ; + in { + # Register handlers for URL schemes. + xdg.mimeApps.defaultApplications = mimeAppDefaults ( + # keep-sorted start block=yes + (mimeAppEntries "x-scheme-handler" "zen-beta.desktop" [ + "http" + "https" + ]) + ++ (mimeAppEntries "x-scheme-handler" "beepertexts.desktop" [ + "beeper" + ]) + ++ (mimeAppEntries "x-scheme-handler" "bitwarden.desktop" [ + "bitwarden" + ]) + ++ (mimeAppEntries "x-scheme-handler" "obsidian.desktop" [ + "obsidian" + ]) + ++ (mimeAppEntries "x-scheme-handler" "onlyoffice-desktopeditors.desktop" [ + "oo-office" + ]) + ++ (mimeAppEntries "x-scheme-handler" "org.prismlauncher.PrismLauncher.desktop" [ + # keep-sorted start + "curseforge" + "prismlauncher" + # keep-sorted end + ]) + ++ (mimeAppEntries "x-scheme-handler" "org.vinegarhq.Sober.desktop" [ + # keep-sorted start + "roblox" + "roblox-player" + # keep-sorted end + ]) + ++ (mimeAppEntries "x-scheme-handler" "steam.desktop" [ + # keep-sorted start + "steam" + "steamlink" + # keep-sorted end + ]) + # keep-sorted end + ); + }; +} diff --git a/modules/desktop/xdg/applications/video.nix b/modules/desktop/xdg/applications/video.nix new file mode 100644 index 0000000..7396b04 --- /dev/null +++ b/modules/desktop/xdg/applications/video.nix @@ -0,0 +1,43 @@ +{ + flake.modules.homeManager.xdgApplications = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + mimeAppDefaults + mimeAppEntries + # keep-sorted end + ; + in { + xdg.mimeApps.defaultApplications = mimeAppDefaults ( + # keep-sorted start block=yes + (mimeAppEntries "video" "org.gnome.Showtime.desktop" [ + # keep-sorted start + "3gpp" + "3gpp2" + "dv" + "mp2t" + "mp4" + "mpeg" + "ogg" + "quicktime" + "vnd.avi" + "vnd.mpegurl" + "vnd.rn-realvideo" + "vnd.vivo" + "webm" + "x-anim" + "x-flic" + "x-flv" + "x-matroska" + "x-nsv" + "x-ogm+ogg" + "x-theora+ogg" + # keep-sorted end + ]) + ++ (mimeAppEntries "application" "org.gnome.Showtime.desktop" [ + "vnd.ms-asf" + ]) + # keep-sorted end + ); + }; +} diff --git a/modules/desktop/xdg/cleanup.nix b/modules/desktop/xdg/cleanup.nix new file mode 100644 index 0000000..fe78ee6 --- /dev/null +++ b/modules/desktop/xdg/cleanup.nix @@ -0,0 +1,57 @@ +{ + flake.modules.homeManager.xdgCleanup = {config, ...}: let + inherit (config.lib.file) mkOutOfStoreSymlink; + + inherit + (config.xdg) + # keep-sorted start + configHome + dataHome + stateHome + # keep-sorted end + ; + in { + # keep-sorted start block=yes newline_separated=yes + # Keep renpy saves under xdg data and expose legacy path. + home.file.".renpy".source = mkOutOfStoreSymlink "${dataHome}/renpy"; + + # GET OUT OF MY $HOME! + home.sessionVariables = { + # History. + # keep-sorted start + HISTFILE = "${stateHome}/bash/history"; + LESSHISTFILE = "${stateHome}/less/history"; + # keep-sorted end + + # Keep ffmpeg config under xdg config home. + FFMPEG_DATADIR = "${configHome}/ffmpeg"; + + # Rust toolchains. + # keep-sorted start + CARGO_HOME = "${dataHome}/cargo"; + RUSTUP_HOME = "${dataHome}/rustup"; + # keep-sorted end + + # Javascript toolchains. + # keep-sorted start + BUN_INSTALL = "${dataHome}/bun"; + NODE_REPL_HISTORY = "${dataHome}/node_repl_history"; + # keep-sorted end + + # Java toolschains. + # keep-sorted start + GRADLE_USER_HOME = "${dataHome}/gradle"; + MAVEN_ARGS = "--settings ${configHome}/maven/settings.xml"; + MAVEN_OPTS = "-Dmaven.repo.local=${dataHome}/maven/repository"; + # keep-sorted end + + # Gaming. + # keep-sorted start + RENPY_MULTIPERSISTENT = "${dataHome}/renpy_shared"; + RENPY_PATH_TO_SAVES = "${dataHome}/renpy"; + WINEPREFIX = "${dataHome}/wineprefixes/default"; + # keep-sorted end + }; + # keep-sorted end + }; +} diff --git a/modules/desktop/xdg/default.nix b/modules/desktop/xdg/default.nix new file mode 100644 index 0000000..2a108fb --- /dev/null +++ b/modules/desktop/xdg/default.nix @@ -0,0 +1,5 @@ +{ + flake.modules.homeManager.xdg = _: { + xdg.enable = true; + }; +} diff --git a/modules/desktop/xdg/dirs.nix b/modules/desktop/xdg/dirs.nix new file mode 100644 index 0000000..9474693 --- /dev/null +++ b/modules/desktop/xdg/dirs.nix @@ -0,0 +1,12 @@ +{ + flake.modules.homeManager.xdgDirs = _: { + xdg.userDirs = { + enable = true; + + # keep-sorted start + createDirectories = true; + setSessionVariables = true; + # keep-sorted end + }; + }; +} diff --git a/modules/desktop/xdg/polkit.nix b/modules/desktop/xdg/polkit.nix new file mode 100644 index 0000000..550a2cd --- /dev/null +++ b/modules/desktop/xdg/polkit.nix @@ -0,0 +1,9 @@ +{ + flake.modules.homeManager.polkit = _: { + services.polkit-gnome.enable = true; + }; + + flake.modules.nixos.polkit = _: { + security.polkit.enable = true; + }; +} diff --git a/modules/desktop/xdg/portal.nix b/modules/desktop/xdg/portal.nix new file mode 100644 index 0000000..d1c1f4f --- /dev/null +++ b/modules/desktop/xdg/portal.nix @@ -0,0 +1,96 @@ +{ + flake.modules.homeManager.xdgPortal = { + # keep-sorted start + config, + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (lib) getExe; + in { + xdg.configFile."xdg-desktop-portal-termfilechooser/config".text = let + terminalCommand = getExe config.xdg.terminal-exec.package; + in '' + [filechooser] + cmd=${pkgs.xdg-desktop-portal-termfilechooser}/share/xdg-desktop-portal-termfilechooser/yazi-wrapper.sh + env=TERMCMD=${terminalCommand} --title=Termfilechooser + ''; + }; + + flake.modules.nixos.xdgPortal = { + # keep-sorted start + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (builtins) attrValues; + inherit (lib) mkAfter; + in { + # keep-sorted start block=yes newline_separated=yes + systemd.user.services.xdg-desktop-portal = { + after = mkAfter ["graphical-session.target"]; + wantedBy = ["graphical-session.target"]; + + # Keep the portal on the session PATH imported into the user manager. + enableDefaultPath = false; + }; + + xdg = { + portal = { + enable = true; + + # Route `xdg-open` calls through the portal. + xdgOpenUsePortal = true; + + # keep-sorted start block=yes newline_separated=yes + config = { + # Defaults used by any desktop. + common = { + default = ["gtk"]; + + # keep-sorted start + + # Use termfilechooser for file picking. + "org.freedesktop.impl.portal.FileChooser" = ["termfilechooser"]; + # Use gnome-keyring for the secret portal backend. + "org.freedesktop.impl.portal.Secret" = ["gnome-keyring"]; + # keep-sorted end + }; + + # Desktop-specific overrides for hyprland. + hyprland = { + default = [ + # keep-sorted start + "gtk" + "hyprland" + # keep-sorted end + ]; + + # keep-sorted start + + # Use termfilechooser for file picking. + "org.freedesktop.impl.portal.FileChooser" = ["termfilechooser"]; + # Use gnome-keyring for the secret portal backend. + "org.freedesktop.impl.portal.Secret" = ["gnome-keyring"]; + # keep-sorted end + }; + }; + + # Provide the gtk and terminal file chooser portals. + extraPortals = attrValues { + inherit + (pkgs) + # keep-sorted start + xdg-desktop-portal-gtk + xdg-desktop-portal-termfilechooser + # keep-sorted end + ; + }; + # keep-sorted end + }; + }; + # keep-sorted end + }; +} diff --git a/modules/desktop/xdg/terminal.nix b/modules/desktop/xdg/terminal.nix new file mode 100644 index 0000000..0dc0569 --- /dev/null +++ b/modules/desktop/xdg/terminal.nix @@ -0,0 +1,12 @@ +{self, ...}: { + flake.modules.homeManager.xdgTerminal = { + imports = [self.modules.homeManager.ghostty]; + + xdg.terminal-exec = { + enable = true; + + # Register Ghostty as the terminal for terminal-exec. + settings.default = ["com.mitchellh.ghostty.desktop"]; + }; + }; +} diff --git a/modules/nix/lib.nix b/modules/nix/lib.nix index c094235..d77d705 100644 --- a/modules/nix/lib.nix +++ b/modules/nix/lib.nix @@ -1,6 +1,10 @@ -{inputs, ...}: let +{ + inputs, + self, + ... +}: let lib = inputs.nixpkgs.lib.extend (_final: prev: { - self = import ../lib {lib = prev;}; + self = import "${self}/lib" {lib = prev;}; }); in { flake.lib = lib; diff --git a/modules/pkgs/default.nix b/modules/pkgs/default.nix index fa923ff..e7f3cc3 100644 --- a/modules/pkgs/default.nix +++ b/modules/pkgs/default.nix @@ -8,9 +8,11 @@ # keep-sorted start djvutorga-adapter lutris + nocheatsheet-nvim os-age pptx2md-adapter systemd-status-preview + telescope-all-recent-nvim zaread # keep-sorted end ; diff --git a/modules/pkgs/neovim/nocheatsheet-nvim.nix b/modules/pkgs/neovim/nocheatsheet-nvim.nix new file mode 100644 index 0000000..9a8af91 --- /dev/null +++ b/modules/pkgs/neovim/nocheatsheet-nvim.nix @@ -0,0 +1,31 @@ +{ + perSystem = { + # keep-sorted start + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (pkgs) fetchFromGitHub; + inherit (pkgs.vimUtils) buildVimPlugin; + in { + packages.nocheatsheet-nvim = buildVimPlugin { + pname = "noCheatSheet.nvim"; + version = "0-unstable-2026-05-03"; + + src = fetchFromGitHub { + owner = "adam01110"; + repo = "noCheatSheet.nvim"; + rev = "c5365c0ebf1ca8d2ec47be99706926b8e0c2947d"; + hash = "sha256-aQJ/Dk8DoWtK8q7arD7MA1jSe/3l2n02h6CD78lLSvI="; + }; + + meta = with lib; { + description = "Standalone NvChad-style cheatsheet for Neovim"; + homepage = "https://github.com/adam01110/noCheatSheet.nvim"; + license = licenses.gpl3Only; + platforms = platforms.all; + }; + }; + }; +} diff --git a/modules/pkgs/neovim/telescope-all-recent-nvim.nix b/modules/pkgs/neovim/telescope-all-recent-nvim.nix new file mode 100644 index 0000000..9f78dea --- /dev/null +++ b/modules/pkgs/neovim/telescope-all-recent-nvim.nix @@ -0,0 +1,43 @@ +{ + perSystem = { + # keep-sorted start + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (builtins) attrValues; + inherit (pkgs) fetchFromGitHub; + inherit (pkgs.vimUtils) buildVimPlugin; + in { + packages.telescope-all-recent-nvim = buildVimPlugin { + pname = "telescope-all-recent.nvim"; + version = "0-unstable-2025-03-19"; + + src = fetchFromGitHub { + owner = "prochri"; + repo = "telescope-all-recent.nvim"; + rev = "e437f60ea468268e49ffdc0c9ed7c5ba384e63cf"; + hash = "sha256-Dq0eahhshmgdrof9eG3rBNm06UCCsjMYNkBs4KE/cEQ="; + }; + + dependencies = attrValues { + inherit + (pkgs.vimPlugins) + # keep-sorted start + plenary-nvim + sqlite-lua + telescope-nvim + # keep-sorted end + ; + }; + + meta = with lib; { + description = "Frecency sorting for all Telescope pickers"; + homepage = "https://github.com/prochri/telescope-all-recent.nvim"; + license = licenses.mit; + platforms = platforms.all; + }; + }; + }; +} diff --git a/modules/programs/cli/starship/build.nix b/modules/programs/cli/starship/build.nix new file mode 100644 index 0000000..a4246c4 --- /dev/null +++ b/modules/programs/cli/starship/build.nix @@ -0,0 +1,27 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit (lib.self) starshipBase01Segment; + in { + programs.starship.settings = { + # keep-sorted start block=yes newline_separated=yes + cmake = + starshipBase01Segment "$symbol($version)" "red bold" + // { + symbol = "󰔶 "; + }; + + gradle = + starshipBase01Segment "$symbol($version)" "cyan bold" + // { + symbol = " "; + }; + + meson = + starshipBase01Segment "$symbol$project" "blue bold" + // { + symbol = "⬢ "; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/cli/starship/container.nix b/modules/programs/cli/starship/container.nix new file mode 100644 index 0000000..d683e09 --- /dev/null +++ b/modules/programs/cli/starship/container.nix @@ -0,0 +1,21 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit (lib.self) starshipBase01Segment; + in { + programs.starship.settings = { + # keep-sorted start block=yes newline_separated=yes + container = + starshipBase01Segment "$symbol \\[$name\\]" "blue bold" + // { + symbol = " "; + }; + + docker_context = + starshipBase01Segment "$symbol$context" "blue bold" + // { + symbol = "󰡨 "; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/cli/starship/default.nix b/modules/programs/cli/starship/default.nix new file mode 100644 index 0000000..53f1054 --- /dev/null +++ b/modules/programs/cli/starship/default.nix @@ -0,0 +1,5 @@ +{ + flake.modules.homeManager.starship = _: { + programs.starship.enable = true; + }; +} diff --git a/modules/programs/cli/starship/disabled.nix b/modules/programs/cli/starship/disabled.nix new file mode 100644 index 0000000..5379355 --- /dev/null +++ b/modules/programs/cli/starship/disabled.nix @@ -0,0 +1,54 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit (lib.self) starshipDisabledModules; + in { + programs.starship.settings = starshipDisabledModules [ + # keep-sorted start + "aws" + "battery" + "buf" + "cobol" + "conda" + "crystal" + "daml" + "elixir" + "elm" + "env_var" + "erlang" + "fennel" + "fill" + "fossil_metrics" + "gcloud" + "gleam" + "guix_shell" + "helm" + "julia" + "kubernetes" + "mojo" + "nats" + "netns" + "nim" + "ocaml" + "odin" + "opa" + "openstack" + "pixi" + "pulumi" + "purescript" + "quarto" + "raku" + "red" + "rlang" + "scala" + "singularity" + "solidity" + "spack" + "terraform" + "typst" + "vagrant" + "vcsh" + "vlang" + # keep-sorted end + ]; + }; +} diff --git a/modules/programs/cli/starship/format.nix b/modules/programs/cli/starship/format.nix new file mode 100644 index 0000000..c88da86 --- /dev/null +++ b/modules/programs/cli/starship/format.nix @@ -0,0 +1,74 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit (lib) concatStrings; + in { + # Layout and coloring for the full starship prompt. + programs.starship.settings.format = concatStrings [ + "[ ](bg:base01)" + "$hostname" + "[/](fg:base05 bg:base01)" + "$username" + "[ ](bg:base01)" + + "[ ](#00000000)" + "[ ](bg:base01)" + "$os" + "[ ](bg:base01)" + "$shell" + "[ ](bg:base01)" + + "$nix_shell" + + "$container" + "$docker_context" + + "$directory" + "$direnv" + "$sudo" + "$jobs" + + "$fossil_branch" + "[ ](#00000000)" + "[ ](bg:base01)" + "$git_branch" + "[ ](bg:base01)" + "$git_commit" + "$git_state" + "$git_status" + "[ ](bg:base01)" + + "$package" + + "$bun" + "$deno" + "$nodejs" + + "$cmake" + "$gradle" + "$meson" + + "$c" + "$cpp" + "$dart" + "$dotnet" + "$golang" + "$haskell" + "$haxe" + "$java" + "$kotlin" + "$lua" + "$perl" + "$php" + "$python" + "$ruby" + "$rust" + "$swift" + "$zig" + + "$cmd_duration" + + "$line_break" + "$character" + ]; + }; +} diff --git a/modules/programs/cli/starship/general.nix b/modules/programs/cli/starship/general.nix new file mode 100644 index 0000000..b5de177 --- /dev/null +++ b/modules/programs/cli/starship/general.nix @@ -0,0 +1,170 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + starshipBase01Segment + starshipBase01Style + # keep-sorted end + ; + in { + programs.starship.settings = { + # keep-sorted start block=yes newline_separated=yes + character = { + format = "$symbol "; + # keep-sorted start + error_symbol = "[➜](fg:red bold)"; + success_symbol = "[➜](fg:green bold)"; + vimcmd_replace_one_symbol = "[](fg:magenta bold)"; + vimcmd_replace_symbol = "[](fg:magenta bold)"; + vimcmd_symbol = "[](fg:green bold)"; + vimcmd_visual_symbol = "[](fg:yellow bold)"; + # keep-sorted end + }; + + cmd_duration = + starshipBase01Segment "$duration" "yellow bold" + // { + min_time = 500; + }; + + directory = + starshipBase01Segment "$path" "blue bold" + // { + format = "[ ](#00000000)[ ](bg:base01)[$path]($style)[$read_only]($read_only_style)[ ](bg:base01)"; + read_only = " "; + read_only_style = starshipBase01Style "red bold"; + truncate_to_repo = true; + truncation_length = 2; + truncation_symbol = "…/"; + }; + + direnv = + starshipBase01Segment "$symbol$loaded( \\($allowed\\))" "base09 bold" + // { + disabled = false; + symbol = " "; + }; + + hostname = { + format = "[$ssh_symbol$hostname]($style)"; + style = "bg:base01 fg:green bold"; + ssh_symbol = "󰖟 "; + ssh_only = false; + }; + + jobs = + starshipBase01Segment "$symbol$number" "blue bold" + // { + symbol = "󱜯 "; + }; + + nix_shell = + starshipBase01Segment "$symbol$state( \\($name\\))" "blue bold" + // { + symbol = "󱄅 "; + heuristic = true; + }; + + os = { + disabled = false; + format = "[$symbol]($style)"; + style = "bg:base01 fg:base04 bold"; + + symbols = let + # keep-sorted start + apple = ""; + linux = ""; + redhat = "󱄛"; + # keep-sorted end + in { + # keep-sorted start + Android = ""; + Linux = linux; + NixOS = "󱄅"; + Unknown = ""; + Windows = ""; + # keep-sorted end + + # keep-sorted start + AlmaLinux = ""; + Alpine = ""; + Arch = ""; + Artix = ""; + CachyOS = linux; + CentOS = ""; + Debian = ""; + Elementary = ""; + EndeavourOS = ""; + Fedora = ""; + Garuda = ""; + Gentoo = ""; + Kali = ""; + Manjaro = ""; + Mint = "󰣭"; + Nobara = ""; + PikaOS = linux; + Pop = ""; + Raspbian = ""; + RedHatEnterprise = redhat; + Redhat = redhat; + RockyLinux = ""; + SUSE = ""; + Solus = ""; + Ubuntu = ""; + Void = ""; + Zorin = ""; + openSUSE = ""; + # keep-sorted end + + # keep-sorted start + Ios = apple; + Macos = apple; + # keep-sorted end + + # keep-sorted start + FreeBSD = ""; + OpenBSD = ""; + # keep-sorted end + }; + }; + + package = + starshipBase01Segment "$symbol$version" "base09 bold" + // { + symbol = "󰏗 "; + display_private = true; + }; + + shell = { + disabled = false; + format = "[$indicator]($style)"; + # keep-sorted start + bash_indicator = ""; + cmd_indicator = ""; + fish_indicator = ""; + nu_indicator = "󰟆 "; + powershell_indicator = "󰨊"; + unknown_indicator = ""; + zsh_indicator = ""; + # keep-sorted end + style = "bg:base01 fg:base04 bold"; + }; + + sudo = + starshipBase01Segment "$symbol" "yellow bold" + // { + disabled = false; + symbol = ""; + }; + + username = { + format = "[$user]($style)"; + style_root = "bg:base01 fg:base08 bold"; + style_user = "bg:base01 fg:blue bold"; + show_always = true; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/cli/starship/git.nix b/modules/programs/cli/starship/git.nix new file mode 100644 index 0000000..e245bc4 --- /dev/null +++ b/modules/programs/cli/starship/git.nix @@ -0,0 +1,47 @@ +{ + flake.modules.homeManager.starship = _: { + programs.starship.settings = { + # keep-sorted start block=yes newline_separated=yes + fossil_branch = { + format = "[$symbol$branch]($style) "; + symbol = " "; + style = "fg:magenta bold"; + }; + + git_branch = { + format = "[$symbol$branch(:$remote_branch)]($style)"; + symbol = " "; + style = "bg:base01 fg:magenta bold"; + }; + + git_commit = { + format = "[\\($hash$tag\\)]($style)"; + style = "bg:base01 fg:green bold"; + tag_disabled = false; + only_detached = false; + tag_symbol = "  "; + }; + + git_state = { + format = "[ $state( $progress_current/$progress_total)]($style)"; + style = "bg:base01 fg:yellow bold"; + # keep-sorted start + am = "am"; + am_or_rebase = "am/rebase"; + bisect = "bisecting"; + cherry_pick = "cherry-picking"; + merge = "merging"; + rebase = "rebasing"; + revert = "reverting"; + # keep-sorted end + }; + + git_status = { + format = "([ \\[$all_status$ahead_behind\\]]($style))"; + style = "bg:base01 fg:base08 bold"; + deleted = ""; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/cli/starship/languages.nix b/modules/programs/cli/starship/languages.nix new file mode 100644 index 0000000..0d77837 --- /dev/null +++ b/modules/programs/cli/starship/languages.nix @@ -0,0 +1,123 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + starshipBase01Segment + starshipBase01Style + # keep-sorted end + ; + in { + programs.starship.settings = { + # keep-sorted start block=yes newline_separated=yes + c = + starshipBase01Segment "$symbol($version(-$name))" "blue bold" + // { + symbol = " "; + }; + + cpp = + starshipBase01Segment "$symbol($version(-$name))" "green bold" + // { + disabled = false; + symbol = " "; + }; + + dart = + starshipBase01Segment "$symbol($version)" "blue bold" + // { + symbol = " "; + }; + + dotnet = + starshipBase01Segment "$symbol($version)( $tfm)" "blue bold" + // { + symbol = "󰪮 "; + }; + + golang = + starshipBase01Segment "$symbol($version)" "cyan bold" + // { + symbol = " "; + not_capable_style = starshipBase01Style "base08 bold"; + }; + + haskell = + starshipBase01Segment "$symbol($version)" "magenta bold" + // { + symbol = "󰲒 "; + }; + + haxe = + starshipBase01Segment "$symbol($version)" "base08 bold" + // { + symbol = " "; + }; + + java = + starshipBase01Segment "$symbol($version)" "base08 bold" + // { + symbol = "󰅶 "; + }; + + kotlin = + starshipBase01Segment "$symbol($version)" "base09 bold" + // { + symbol = "󱈙 "; + }; + + lua = + starshipBase01Segment "$symbol($version)" "blue bold" + // { + symbol = "󰢱 "; + }; + + perl = + starshipBase01Segment "$symbol($version)" "base09 bold" + // { + symbol = " "; + }; + + php = + starshipBase01Segment "$symbol($version)" "blue bold" + // { + symbol = " "; + }; + + python = + starshipBase01Segment "$symbol$pyenv_prefix($version)( \\($virtualenv\\))" "yellow bold" + // { + symbol = "󰌠 "; + + # Show the pyenv name with an uv prefix. + pyenv_version_name = true; + pyenv_prefix = "uv"; + }; + + ruby = + starshipBase01Segment "$symbol($version)" "base08 bold" + // { + symbol = " "; + }; + + rust = + starshipBase01Segment "$symbol($version)" "base09 bold" + // { + symbol = "󱘗 "; + }; + + swift = + starshipBase01Segment "$symbol($version)" "base09 bold" + // { + symbol = "󰛥 "; + }; + + zig = + starshipBase01Segment "$symbol($version)" "yellow bold" + // { + symbol = " "; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/cli/starship/runtimes.nix b/modules/programs/cli/starship/runtimes.nix new file mode 100644 index 0000000..4ed74c2 --- /dev/null +++ b/modules/programs/cli/starship/runtimes.nix @@ -0,0 +1,34 @@ +{ + flake.modules.homeManager.starship = {lib, ...}: let + inherit + (lib.self) + # keep-sorted start + starshipBase01Segment + starshipBase01Style + # keep-sorted end + ; + in { + programs.starship.settings = { + # keep-sorted start block=yes newline_separated=yes + bun = + starshipBase01Segment "$symbol($version)" "yellow bold" + // { + symbol = " "; + }; + + deno = + starshipBase01Segment "$symbol($version)" "green bold" + // { + symbol = " "; + }; + + nodejs = + starshipBase01Segment "$symbol($version)" "green bold" + // { + symbol = "󰎙 "; + not_capable_style = starshipBase01Style "base08 bold"; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/beeper.nix b/modules/programs/gui/beeper.nix index b8a65e3..a2f9326 100644 --- a/modules/programs/gui/beeper.nix +++ b/modules/programs/gui/beeper.nix @@ -1,5 +1,53 @@ { - flake.modules.homeManager.beeper = {pkgs, ...}: { - home.packages = [pkgs.nur.repos.forkprince.beeper-nightly]; + flake.modules.homeManager.beeper = { + # keep-sorted start + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit + (lib) + # keep-sorted start + getExe + getExe' + # keep-sorted end + ; + inherit + (pkgs) + # keep-sorted start + makeDesktopItem + writeShellScriptBin + # keep-sorted end + ; + + pkg = pkgs.nur.repos.forkprince.beeper-nightly; + + beeperAutostart = makeDesktopItem { + name = "beeper-autostart"; + desktopName = "Beeper"; + icon = "beepertexts"; + startupWMClass = "Beeper"; + + exec = getExe (writeShellScriptBin "beeper-autostart" '' + sleep 4 + exec ${getExe' pkg "beeper"} --no-sandbox + ''); + + categories = [ + # keep-sorted start + "Chat" + "InstantMessaging" + "Network" + # keep-sorted end + ]; + }; + in { + home.packages = [pkg]; + + xdg.autostart = { + enable = true; + entries = ["${beeperAutostart}/share/applications/beeper-autostart.desktop"]; + }; }; } diff --git a/modules/programs/gui/discord/autostart.nix b/modules/programs/gui/discord/autostart.nix new file mode 100644 index 0000000..c0821b4 --- /dev/null +++ b/modules/programs/gui/discord/autostart.nix @@ -0,0 +1,37 @@ +{ + flake.modules.homeManager.discord = { + # keep-sorted start + config, + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (lib) getExe; + inherit (pkgs) makeDesktopItem; + + pkg = config.programs.nixcord.equibop.package; + + equibopAutostart = makeDesktopItem { + name = "equibop-autostart"; + desktopName = "Equibop"; + icon = "equibop"; + startupWMClass = "Equibop"; + + exec = "${getExe pkg} --start-minimized"; + + categories = [ + # keep-sorted start + "Chat" + "InstantMessaging" + "Network" + # keep-sorted end + ]; + }; + in { + xdg.autostart = { + enable = true; + entries = ["${equibopAutostart}/share/applications/equibop-autostart.desktop"]; + }; + }; +} diff --git a/modules/programs/gui/discord/default.nix b/modules/programs/gui/discord/default.nix new file mode 100644 index 0000000..004b150 --- /dev/null +++ b/modules/programs/gui/discord/default.nix @@ -0,0 +1,34 @@ +{ + inputs, + self, + ... +}: { + flake-file.inputs.nixcord = { + url = "github:kaylorben/nixcord"; + inputs = { + flake-parts.follows = "flake-parts"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + flake.modules.homeManager.discord = {config, ...}: { + imports = [ + # keep-sorted start + inputs.nixcord.homeModules.nixcord + self.modules.generic.vars + # keep-sorted end + ]; + + # keep-sorted start block=yes newline_separated=yes + programs.nixcord = { + enable = true; + discord.enable = false; + + equibop = { + enable = true; + configDir = "${config.xdg.configHome}/equibop"; + }; + }; + # keep-sorted end + }; +} diff --git a/modules/programs/gui/discord/plugins/communication.nix b/modules/programs/gui/discord/plugins/communication.nix new file mode 100644 index 0000000..f75fbed --- /dev/null +++ b/modules/programs/gui/discord/plugins/communication.nix @@ -0,0 +1,210 @@ +{ + flake.modules.homeManager.discord = { + # keep-sorted start + equibopStylix, + vars, + # keep-sorted end + ... + }: { + programs.nixcord.config.plugins = { + # keep-sorted start block=yes newline_separated=yes + ClearURLs.enable = true; + + CopyUserURLs.enable = true; + + autoZipper.enable = true; + + betterCommands.enable = true; + + betterGifAltText.enable = true; + + cleanChannelName.enable = true; + + copyEmojiMarkdown.enable = true; + + copyFileContents.enable = true; + + copyStickerLinks.enable = true; + + copyUserMention.enable = true; + + customTimestamps = { + enable = true; + }; + + dontRoundMyTimestamps.enable = true; + + expressionCloner.enable = true; + + favoriteGifSearch.enable = true; + + fixCodeblockGap.enable = true; + + fixFileExtensions.enable = true; + + fixImagesQuality.enable = true; + + fixSpotifyEmbeds.enable = true; + + fixYoutubeEmbeds.enable = true; + + forwardAnywhere.enable = true; + + gifPaste.enable = true; + + iLoveSpam.enable = true; + + imageFilename.enable = true; + + messageBurst = { + enable = true; + timePeriod = 3; + }; + + messageFetchTimer = { + enable = true; + iconColor = equibopStylix.messageFetchTimerIcon; + }; + + messageLatency = { + enable = true; + showMillis = true; + latency = 1; + }; + + messageLinkEmbeds = { + enable = true; + messageBackgroundColor = true; + }; + + moreQuickReactions.enable = true; + + noPushToTalk.enable = true; + + normalizeMessageLinks.enable = true; + + polishWording = { + enable = true; + # keep-sorted start + fixCapitalization = true; + fixPunctuation = true; + # keep-sorted end + }; + + replyTimestamp.enable = true; + + searchFix.enable = true; + + selfForward.enable = true; + + sendTimestamps.enable = true; + + songLink = { + enable = true; + userCountry = vars.countryCode; + }; + + splitLargeMessages.enable = true; + + stickerPaste.enable = true; + + textReplace = { + enable = true; + regexRules = [ + # keep-sorted start block=yes newline_separated=yes + # Minecraft bugs regex. + { + find = "(?:^|(?<=[\\s,:;/&]))MC-\\d+(?:(?=[\\s.?!,:;/&])|$) replace: [$&]"; + replace = "()"; + } + + # XKCD regex. + { + find = "(?:^|(?<=[\s,:;/&]))xkcd\/(\d+)(?:(?=[\s.?!,:;/&])|$)"; + replace = "[$&](https://xkcd.com/$1)"; + } + + # Tenor regex. + { + find = "(https:\/\/media1\.tenor\.com\/m\/[^\/]+\/[^\/]+\.gif)(?!\?)"; + replace = "$1?download=1"; + } + + { + find = "(https:\\/\\/github\\.com\\/[^\\/]+\\/[^\\/]+\\/commit\\/([0-9a-f]{7})([0-9a-f]{0,33}))"; + replace = "[`$2`]($1)"; + } + + { + find = "(https:\\/\\/github\\.com\\/[^\\/]+\\/[^\\/]+\\/issues\\/([0-9a-f]{1,}))"; + replace = "[`Issue #$2`]($1)"; + } + + { + find = "(https:\\/\\/github\\.com\\/[^\\/]+\\/[^\\/]+\\/pull\\/([0-9a-f]{1,}))"; + replace = "[`Pull Request #$2`]($1)"; + } + + # Github regex. + { + find = "gh:([a-zA-Z0-9_-]+)/([a-zA-Z0-9_.-]+)"; + replace = "[$1/$2](https://github.com/$1/$2)"; + } + + # Reddit regex. + { + find = "http(?:s)?:\/\/(?:redd\.it\/|(?:[\w-]+\.)?r[ex]ddit\.com\/)(?:(r\/[\w-]+\/s\/\w+)|(u)(?:ser)?(\/)([\w-]+)(.*?)?|(?:(?:r\/[\w-]+\/)?((c)omments\/))(\w+)(?:\/[^\/\s\)\]\}]+)+(\/)(\w+)|(?:(?:(?:r\/[\w-]+\/)?comments\/)|(?<=redd\.it\/))(\w+)(?:\/.*?)?|(?!r\/|u\/|user\/|comments\/)(.*?)?)\/?(?:[?#][^\s\)\]\}]*)?(?=[\s\)\]\}]|$)"; + replace = "https://rxddit.com/$1$2$3$4$5$6$8$9$7$9$10$11"; + } + + # Steam regex. + { + find = "https?://store\.steampowered\.com/app/(\d+)(?:/[^\s]*)?"; + replace = "https://s.team/a/$1"; + } + + { + find = "tenor\.com\/..-..\/view "; + replace = "tenor.com/view"; + } + # keep-sorted end + ]; + }; + + timezones = { + enable = true; + _24hTime = true; + }; + + translate.enable = true; + + typingIndicator = { + enable = true; + # keep-sorted start + includeBlockedUsers = true; + includeIgnoredUsers = true; + includeMutedChannels = true; + # keep-sorted end + }; + + unsuppressEmbeds.enable = true; + + userMessagesPronouns.enable = true; + + validReply.enable = true; + + voiceRejoin.enable = true; + + waitForSlot.enable = true; + + webScreenShareFixes.enable = true; + + writeUpperCase = { + enable = true; + blockedWords = "http, https"; + }; + + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/discord/plugins/core.nix b/modules/programs/gui/discord/plugins/core.nix new file mode 100644 index 0000000..1a47744 --- /dev/null +++ b/modules/programs/gui/discord/plugins/core.nix @@ -0,0 +1,57 @@ +{ + flake.modules.homeManager.discord = _: { + programs.nixcord.config.plugins = { + # keep-sorted start block=yes newline_separated=yes + alwaysTrust.enable = true; + + betterSessions = { + enable = true; + backgroundCheck = true; + checkInterval = 30; + }; + + consoleJanitor = { + enable = true; + + # keep-sorted start + disableLoggers = true; + disableSpotifyLogger = true; + # keep-sorted end + + allowLevel = { + # keep-sorted start + debug = false; + error = false; + info = false; + log = false; + trace = false; + warn = false; + # keep-sorted end + }; + }; + + crashHandler.attemptToNavigateToHome = true; + + declutter.enable = true; + + equibopStreamFixes.enable = true; + + newPluginsManager.enable = true; + + noDevtoolsWarning.enable = true; + + noF1.enable = true; + + noOnboardingDelay.enable = true; + + permissionFreeWill.enable = true; + + startupTimings.enable = true; + + unlimitedAccounts.enable = true; + + validUser.enable = true; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/discord/plugins/integrations.nix b/modules/programs/gui/discord/plugins/integrations.nix new file mode 100644 index 0000000..b6d4e15 --- /dev/null +++ b/modules/programs/gui/discord/plugins/integrations.nix @@ -0,0 +1,29 @@ +{ + flake.modules.homeManager.discord = _: { + programs.nixcord.config.plugins = { + # keep-sorted start block=yes newline_separated=yes + dragify.enable = true; + + gitHubRepos.enable = true; + + openInApp = { + enable = true; + }; + + replaceGoogleSearch = { + customEngineName = "Brave"; + customEngineURL = "https://search.brave.com/search?q="; + enable = true; + replacementEngine = "custom"; + }; + + reverseImageSearch.enable = true; + + unitConverter = { + enable = true; + myUnits = "metric"; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/discord/plugins/interface.nix b/modules/programs/gui/discord/plugins/interface.nix new file mode 100644 index 0000000..2dc288c --- /dev/null +++ b/modules/programs/gui/discord/plugins/interface.nix @@ -0,0 +1,159 @@ +{ + flake.modules.homeManager.discord = { + # keep-sorted start + equibopStylix, + lib, + # keep-sorted end + ... + }: let + inherit (lib) mkEnableOption; + in + # Interface and layout related plugins. + { + # User option to toggle camera features in plugins. + options.programs.nixcord.equibop.camera.enable = mkEnableOption "Enable camera functionality for Equibop plugins."; + + config.programs.nixcord.config.plugins = { + # keep-sorted start block=yes newline_separated=yes + FullVCPFP.enable = true; + + USRBG = { + enable = true; + voiceBackground = true; + }; + + UserPFP.enable = true; + + accountPanelServerProfile = { + enable = true; + prioritizeServerProfile = true; + }; + + betterActivities = { + enable = true; + # keep-sorted start + memberList = false; + removeGameActivityStatus = true; + renderGifs = false; + # keep-sorted end + }; + + betterSettings = { + enable = true; + disableFade = false; + }; + + betterUploadButton.enable = true; + + cleanerChannelGroups.enable = true; + + clickableRoles.enable = true; + + customIdle = { + enable = true; + remainInIdle = false; + idleTimeout = 5.0; + }; + + dearrow = { + enable = true; + replaceElements = 1; + }; + + decor.enable = true; + + fakeNitro = { + enable = true; + transformCompoundSentence = true; + }; + + fakeProfileThemes.enable = true; + + fullSearchContext.enable = true; + + fullUserInChatbox.enable = true; + + gameActivityToggle.enable = true; + + globalBadges.enable = true; + + greetStickerPicker.enable = true; + + mentionAvatars.enable = true; + + micLoopbackTester.enable = true; + + neverPausePreviews.enable = true; + + noNitroUpsell.enable = true; + + noPendingCount = { + enable = true; + # keep-sorted start + hideFriendRequestsCount = false; + hideMessageRequestsCount = false; + # keep-sorted end + }; + + noUnblockToJump.enable = true; + + platformIndicators.enable = true; + + platformSpoofer.enable = true; + + previewMessage.enable = true; + + questify = { + enable = true; + # keep-sorted start + completeGameQuestsInBackground = true; + completeVideoQuestsInBackground = true; + # keep-sorted end + fetchingQuestsInterval = 0; + # keep-sorted start + questButtonDisplay = "never"; + questButtonUnclaimed = "none"; + # keep-sorted end + # keep-sorted start + restyleQuestsClaimed = equibopStylix.questify.claimed; + restyleQuestsExpired = equibopStylix.questify.expired; + restyleQuestsIgnored = equibopStylix.questify.ignored; + restyleQuestsUnclaimed = equibopStylix.questify.unclaimed; + # keep-sorted end + }; + + readAllNotificationsButton.enable = true; + + roleColorEverywhere.enable = true; + + serverListIndicators = { + enable = true; + mode = 3; + }; + + showAllMessageButtons.enable = true; + + showBadgesInChat.enable = true; + + showHiddenThings = { + enable = true; + }; + + statusWhileActive.enable = true; + + themeAttributes.enable = true; + + title = { + enable = true; + title = "Equibop"; + }; + + unlockedAvatarZoom.enable = true; + + userVoiceShow.enable = true; + + viewIcons.enable = true; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/discord/plugins/media.nix b/modules/programs/gui/discord/plugins/media.nix new file mode 100644 index 0000000..6a36cf8 --- /dev/null +++ b/modules/programs/gui/discord/plugins/media.nix @@ -0,0 +1,57 @@ +{ + flake.modules.homeManager.discord = {config, ...}: let + cfgCamera = config.programs.nixcord.equibop.camera.enable; + in { + programs.nixcord.config.plugins = { + # keep-sorted start block=yes newline_separated=yes + VCPanelSettings = { + enable = true; + camera = cfgCamera; + + # keep-sorted start + showInputDeviceHeader = true; + showOutputDeviceHeader = true; + showVideoDeviceHeader = true; + # keep-sorted end + }; + + biggerStreamPreview.enable = true; + + callTimer = { + enable = true; + allCallTimers = true; + }; + + disableCallIdle.enable = true; + + disableCameras.enable = !cfgCamera; + + mediaPlaybackSpeed.enable = true; + + musicControls = { + enable = true; + # keep-sorted start + showSpotifyControls = true; + useSpotifyUris = true; + # keep-sorted end + lyricsConversion = "Romanized"; + }; + + pictureInPicture.enable = true; + + voiceButtons = { + enable = true; + whichNameToShow = "username"; + }; + + voiceChatDoubleClick.enable = true; + + voiceDownload.enable = true; + + voiceMessages.enable = true; + + volumeBooster.enable = true; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/discord/plugins/social.nix b/modules/programs/gui/discord/plugins/social.nix new file mode 100644 index 0000000..14bfff2 --- /dev/null +++ b/modules/programs/gui/discord/plugins/social.nix @@ -0,0 +1,61 @@ +{ + flake.modules.homeManager.discord = _: { + programs.nixcord.config.plugins = { + # keep-sorted start block=yes newline_separated=yes + MutualGroupDMs.enable = true; + + ReviewDB = { + enable = true; + hideBlockedUsers = false; + }; + + betterBlockedUsers.enable = true; + + betterInvites.enable = true; + + forceOwnerCrown.enable = true; + + friendsSince.enable = true; + + friendshipRanks.enable = true; + + implicitRelationships.enable = true; + + inviteDefaults.enable = true; + + memberCount.enable = true; + + moreUserTags.enable = true; + + newGuildSettings = { + enable = true; + messages = 1; + }; + + pauseInvitesForever.enable = true; + + relationshipNotifier.enable = true; + + serverInfo.enable = true; + + showConnections = { + enable = true; + iconSpacing = 0; + }; + + showHiddenChannels.enable = true; + + showTimeoutDuration.enable = true; + + songSpotlight.enable = true; + + sortFriends = { + enable = true; + showDates = true; + }; + + whoReacted.enable = true; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/gui/discord/settings.nix b/modules/programs/gui/discord/settings.nix new file mode 100644 index 0000000..1e1bcf8 --- /dev/null +++ b/modules/programs/gui/discord/settings.nix @@ -0,0 +1,28 @@ +{ + flake.modules.homeManager.discord = _: { + programs.nixcord = { + # Write main equibop configuration. + config = { + # keep-sorted start + autoUpdate = true; + frameless = true; + transparent = true; + # keep-sorted end + }; + + equibop.settings = { + discordBranch = "stable"; + + # keep-sorted start + arRPC = true; + autoStartMinimized = true; + clickTrayToShowHide = true; + customTitleBar = false; + hardwareVideoAcceleration = true; + minimizeToTray = true; + splashProgress = true; + # keep-sorted end + }; + }; + }; +} diff --git a/modules/programs/gui/discord/themes/default.nix b/modules/programs/gui/discord/themes/default.nix new file mode 100644 index 0000000..3692daa --- /dev/null +++ b/modules/programs/gui/discord/themes/default.nix @@ -0,0 +1,78 @@ +{ + flake.modules.homeManager.discord = { + # keep-sorted start + config, + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit + (builtins) + # keep-sorted start + attrNames + readFile + replaceStrings + # keep-sorted end + ; + inherit (lib) fromHexString; + inherit (config.lib.stylix) colors; + + equibopStylix = { + # keep-sorted start block=yes newline_separated=yes + # Icon color for message fetch timer. + messageFetchTimerIcon = colors.base0B; + + # Reuse the full Stylix palette in local Equibop theming. + palette = colors.withHashtag; + + # Questify color assignments. + questify = { + # keep-sorted start + claimed = fromHexString colors.base0E; + expired = fromHexString colors.base00; + ignored = fromHexString colors.base08; + unclaimed = fromHexString colors.base0D; + # keep-sorted end + }; + # keep-sorted end + }; + + themeVars = equibopStylix.palette // {monospaceFont = config.stylix.fonts.monospace.name;}; + + system24Theme = pkgs.writeText "system24.css" ( + replaceStrings + (map (name: "__${name}__") (attrNames themeVars)) + (map (name: themeVars.${name}) (attrNames themeVars)) + (readFile ./system24.css) + ); + in { + # Pass themed values to Equibop plugins through module arguments. + _module.args.equibopStylix = equibopStylix; + + programs.nixcord.config = { + # Enable custom css themes. + enabledThemes = [ + # keep-sorted start + "snippets.css" + "system24.css" + # keep-sorted end + ]; + + # Load external theme links for enhanced styling. + themeLinks = [ + # keep-sorted start + "https://raw.githubusercontent.com/Augenbl1ck/Discord-Styles/refs/heads/main/expProfile.css" + "https://raw.githubusercontent.com/mudrhiod/discord-iconpacks/refs/heads/master/vencord/solar/solar.css" + "https://raw.githubusercontent.com/yiruzu/vencord-snippets/refs/heads/main/snippets/BubbleUsernames/import.css" + # keep-sorted end + ]; + }; + + # Add snippets stylesheet for additional styling. + xdg.configFile."equibop/themes/snippets.css".source = ./snippets.css; + + # Install themed css with fonts and palette from Stylix. + xdg.configFile."equibop/themes/system24.css".source = system24Theme; + }; +} diff --git a/modules/programs/gui/discord/themes/snippets.css b/modules/programs/gui/discord/themes/snippets.css new file mode 100644 index 0000000..4a8ca93 --- /dev/null +++ b/modules/programs/gui/discord/themes/snippets.css @@ -0,0 +1,111 @@ +/* Additional snippets for discord ui tweaks. */ +/* Readd timestamp tooltips. */ +time[datetime]:hover::after { + content: "(" attr(datetime) ")"; + width: max-content; + position: absolute; + z-index: 9999; + backdrop-filter: blur(4px); + padding: 0 4px; + border-radius: 6px; +} + +/* Swap clan tag and role icons. */ +[class^="headerText"] { + display: inline-flex; + & > span:has([class^="roleicon"]) { + order: 2; + } + & > span:has([class^="chipletContainerInner"]) { + order: 3; + } + & > span[style="display: none;"] { + order: 4; + } + & > .vc-message-decorations-wrapper { + order: 5; + } + & > span:has(> [class^="newMemberBadge"]) { + order: 6; + } + & > span:is([class^="botTag"]) { + order: 7; + } +} + +/* Replace all display name fonts with a single one. */ +[class*="zillaSlab"] /* Tempo */, +[class*="cherryBomb"] /* Sakura */, +[class*="chicle"] /* Jellybean */, +[class*="museoModerno"] /* Modern */, +[class*="neoCastel"] /* Medieval */, +[class*="pixelify"] /* 8Bit */, +[class*="sinistre"] { + /* Vampyre */ + font-family: var(--font-primary) !important; +} + +/* Read all notifications button version. */ +.vc-ranb-button { + margin: 0 var(--space-md); + display: flex; + position: relative; + width: var(--guildbar-avatar-size); + height: var(--guildbar-avatar-size); + min-height: 40px; + max-height: 40px !important; + max-width: 40px !important; + padding: 2px; + background-color: var(--background-mod-subtle); + border: none; + border-radius: 12px; + text-decoration: none !important; + font-size: 0px; + left: -5px; + transition: + scale 0.15s ease-out, + background-color 0.15s ease-out, + color 0.15s ease-out; + + &:hover { + background: var(--brand-500); + + &::after { + background-image: url("data:image/svg+xml,%3Csvg%20x%3D%220%22%20y%3D%220%22%20class%3D%22icon__9293f%22%20aria-hidden%3D%22true%22%20role%3D%22img%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20fill%3D%22%23FFFFFF%22%20d%3D%22M9.7%202.89c.18-.07.32-.24.37-.43a2%202%200%200%201%203.86%200c.05.2.19.36.38.43A7%207%200%200%201%2019%209.5v2.09c0%20.12.05.24.13.33l1.1%201.22a3%203%200%200%201%20.77%202.01v.28c0%20.67-.34%201.29-.95%201.56-1.31.6-4%201.51-8.05%201.51-4.05%200-6.74-.91-8.05-1.5-.61-.28-.95-.9-.95-1.57v-.28a3%203%200%200%201%20.77-2l1.1-1.23a.5.5%200%200%200%20.13-.33V9.5a7%207%200%200%201%204.7-6.61ZM9.18%2019.84A.16.16%200%200%200%209%2020a3%203%200%201%200%206%200c0-.1-.09-.17-.18-.16a24.86%2024.86%200%200%201-5.64%200Z%22%20class%3D%22%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E"); + } + } + + &::after { + content: "asdfg"; + font-size: 18px; + position: relative; + color: transparent; + left: 12px; + background-image: url("data:image/svg+xml,%3Csvg%20x%3D%220%22%20y%3D%220%22%20class%3D%22icon__9293f%22%20aria-hidden%3D%22true%22%20role%3D%22img%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20fill%3D%22%23D8D8DB%22%20d%3D%22M9.7%202.89c.18-.07.32-.24.37-.43a2%202%200%200%201%203.86%200c.05.2.19.36.38.43A7%207%200%200%201%2019%209.5v2.09c0%20.12.05.24.13.33l1.1%201.22a3%203%200%200%201%20.77%202.01v.28c0%20.67-.34%201.29-.95%201.56-1.31.6-4%201.51-8.05%201.51-4.05%200-6.74-.91-8.05-1.5-.61-.28-.95-.9-.95-1.57v-.28a3%203%200%200%201%20.77-2l1.1-1.23a.5.5%200%200%200%20.13-.33V9.5a7%207%200%200%201%204.7-6.61ZM9.18%2019.84A.16.16%200%200%200%209%2020a3%203%200%201%200%206%200c0-.1-.09-.17-.18-.16a24.86%2024.86%200%200%201-5.64%200Z%22%20class%3D%22%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E"); + background-repeat: no-repeat; + } +} + +/* Remove discords checkpoint. */ +.button__85643:nth-of-type(1) { + display: none; +} + +/* Fix overlap */ +[class*="toolbar_"] [class*="clickable_"]:has(path[d^="M4 5a3 3 0 0"]), +#message-save-for-later-upsell, +[class*="buttonContainer_"] [class*="button_"]:has(path[d^="M17 4H7a1 1 0 0 0-1"]), +[class*="tabBar_"] div:nth-child(4) { + display: none; +} +.alignedRightContent__80679, +[class^="search__"], +[class^="inviteToolbar__"] { + margin-right: 40px; +} +.navBarContent__131a1 > .button_a22cb0 { + margin-right: 195px; +} +.headerBarContent__1a9ce { + margin-right: calc(var(--top-bar-right-margin) / 2); +} diff --git a/modules/programs/gui/discord/themes/system24.css b/modules/programs/gui/discord/themes/system24.css new file mode 100644 index 0000000..72539d5 --- /dev/null +++ b/modules/programs/gui/discord/themes/system24.css @@ -0,0 +1,231 @@ +/** + * @name system24 + * @description a tui-style discord theme. + * @author refact0r + * @version 2.0.0 + * @invite nz87hXyvcy + * @website https://github.com/refact0r/system24 + * @source https://github.com/refact0r/system24/blob/master/theme/system24.theme.css + * @authorId 508863359777505290 + * @authorLink https://www.refact0r.dev + */ + +/* Import theme modules */ +@import url("https://refact0r.github.io/system24/build/system24.css"); + +body { + /* Font, change to '' for default discord font */ + --font: "__monospaceFont__"; /* Change to "" for default discord font */ + --code-font: "__monospaceFont__"; /* Change to "" for default discord font */ + font-weight: 300; /* Text font weight. 300 is light, 400 is normal. DOES NOT AFFECT BOLD TEXT */ + letter-spacing: -0.05ch; /* Decreases letter spacing for better readability. recommended on monospace fonts.*/ + + /* Sizes */ + --gap: 8px; /* Spacing between panels */ + --divider-thickness: 4px; /* Thickness of unread messages divider and highlighted message borders */ + --border-thickness: 1px; /* Thickness of borders around main panels. DOES NOT AFFECT OTHER BORDERS */ + --border-hover-transition: none; /* Transition for borders when hovered */ + + /* Animation/transition options */ + --animations: off; /* Off: disable animations/transitions, on: enable animations/transitions */ + --list-item-transition: none; /* Transition for list items */ + --dms-icon-svg-transition: none; /* Transition for the dms icon */ + + /* Top bar options */ + --top-bar-height: var( + --gap + ); /* Height of the top bar (discord default is 36px, old discord style is 24px, var(--gap) recommended if button position is set to titlebar) */ + --top-bar-button-position: titlebar; /* Off: default position, hide: hide buttons completely, serverlist: move inbox button to server list, titlebar: move inbox button to channel titlebar (will hide title) */ + --top-bar-title-position: off; /* Off: default centered position, hide: hide title completely, left: left align title (like old discord) */ + --subtle-top-bar-title: off; /* Off: default, on: hide the icon and use subtle text color (like old discord) */ + + /* Window controls */ + --custom-window-controls: off; /* Off: default window controls, on: custom window controls */ + --window-control-size: 14px; /* Size of custom window controls */ + + /* Dms button options */ + --custom-dms-icon: off; /* Off: use default discord icon, hide: remove icon entirely, custom: use custom icon */ + --dms-icon-svg-url: url(""); /* Icon svg url. MUST BE A SVG. */ + --dms-icon-svg-size: 90%; /* Size of the svg (css mask-size property) */ + --dms-icon-color-before: var(--icon-subtle); /* Normal icon color */ + --dms-icon-color-after: var(--white); /* Icon color when button is hovered/selected */ + --custom-dms-background: off; /* Off to disable, image to use a background image (must set url variable below), color to use a custom color/gradient */ + --dms-background-image-url: url(""); /* Url of the background image */ + --dms-background-image-size: cover; /* Size of the background image (css background-size property) */ + --dms-background-color: linear-gradient( + 70deg, + var(--blue-2), + var(--purple-2), + var(--red-2) + ); /* Fixed color/gradient (css background property) */ + + /* Background image options */ + --background-image: off; /* Off: no background image, on: enable background image (must set url variable below) */ + --background-image-url: url(""); /* Url of the background image */ + + /* Transparency/blur options */ + /* NOTE: TO USE TRANSPARENCY/BLUR, YOU MUST HAVE TRANSPARENT BG COLORS. FOR EXAMPLE: --bg-4: hsla(220, 15%, 10%, 0.7); */ + --transparency-tweaks: on; /* Off: no changes, on: remove some elements for better transparency */ + --remove-bg-layer: off; /* Off: no changes, on: remove the base --bg-3 layer for use with window transparency (WILL OVERRIDE BACKGROUND IMAGE) */ + --panel-blur: on; /* Off: no changes, on: blur the background of panels */ + --blur-amount: 8px; /* Amount of blur */ + --bg-floating: var( + --bg-3 + ); /* Set this to a more opaque color if floating panels look too transparent. only applies if panel blur is on */ + + /* Other options */ + --small-user-panel: off; /* Off: default user panel, on: smaller user panel like in old discord */ + + /* Unrounding options */ + --unrounding: on; /* Off: default, on: remove rounded corners from panels */ + + /* Styling options */ + --custom-spotify-bar: on; /* Off: default, on: custom text-like spotify progress bar */ + --ascii-titles: on; /* Off: default, on: use ascii font for titles at the start of a channel */ + --ascii-loader: system24; /* Off: default, system24: use system24 ascii loader, cats: use cats loader */ + + /* Panel labels */ + --panel-labels: on; /* Off: default, on: add labels to panels */ + --label-color: var(--text-muted); /* Color of labels */ + --label-font-weight: 500; /* Font weight of labels */ +} + +/* Color options */ +:root { + --base00: __base00__; + --base01: __base01__; + --base02: __base02__; + --base03: __base03__; + --base04: __base04__; + --base05: __base05__; + --base06: __base06__; + --base07: __base07__; + --base08: __base08__; + --base09: __base09__; + --base0A: __base0A__; + --base0B: __base0B__; + --base0C: __base0C__; + --base0D: __base0D__; + --base0E: __base0E__; + --base0F: __base0F__; + + --colors: on; /* Off: discord default colors, on: midnight custom colors */ + + /* Text colors */ + --text-0: var(--bg-4); /* Text on colored elements */ + --text-1: var(--base06); /* Other normally white text */ + --text-2: var(--base05); /* Headings and important text */ + --text-3: var(--base04); /* Normal text */ + --text-4: var(--base03); /* Icon buttons and channels */ + --text-5: color-mix( + in oklab, + var(--base03) 65%, + var(--base01) + ); /* Muted channels/chats and timestamps */ + + /* Background and dark colors */ + --bg-1: color-mix(in oklab, var(--base02) 70%, var(--base01)); /* Dark buttons when clicked */ + --bg-2: var(--base01); /* Dark buttons */ + --bg-3: color-mix(in oklab, var(--base00) 78%, var(--base01)); /* Spacing, secondary elements */ + --bg-4: color-mix(in srgb, var(--base00) 92%, transparent); /* Main background color */ + --hover: color-mix( + in srgb, + var(--base05) 10%, + transparent + ); /* Channels and buttons when hovered */ + --active: color-mix( + in srgb, + var(--base05) 18%, + transparent + ); /* Channels and buttons when clicked or selected */ + --active-2: color-mix( + in srgb, + var(--base05) 28%, + transparent + ); /* Extra state for transparent buttons */ + --message-hover: color-mix(in srgb, var(--base05) 6%, transparent); /* Messages when hovered */ + + /* Accent colors */ + --accent-1: var(--base0B); /* Links and other accent text */ + --accent-2: var(--base0D); /* Small accent elements */ + --accent-3: var(--base0B); /* Accent buttons */ + --accent-4: color-mix( + in oklab, + var(--base0B) 78%, + var(--base00) + ); /* Accent buttons when hovered */ + --accent-5: color-mix( + in oklab, + var(--base0B) 60%, + var(--base00) + ); /* Accent buttons when clicked */ + --accent-new: var(--base08); /* Stuff that's normally red like mute/deafen buttons */ + --mention: linear-gradient( + to right, + color-mix(in hsl, var(--accent-2), transparent 90%) 40%, + transparent + ); /* Background of messages that mention you */ + --mention-hover: linear-gradient( + to right, + color-mix(in hsl, var(--accent-2), transparent 95%) 40%, + transparent + ); /* Background of messages that mention you when hovered */ + --reply: linear-gradient( + to right, + color-mix(in hsl, var(--text-3), transparent 90%) 40%, + transparent + ); /* Background of messages that reply to you */ + --reply-hover: linear-gradient( + to right, + color-mix(in hsl, var(--text-3), transparent 95%) 40%, + transparent + ); /* Background of messages that reply to you when hovered */ + + /* Status indicator colors */ + --online: var(--base0B); /* Change to #40a258 for default */ + --dnd: var(--base08); /* Change to #d83a41 for default */ + --idle: var(--base0A); /* Change to #cc954c for default */ + --streaming: var(--base0E); /* Change to ##9147ff for default */ + --offline: var(--text-4); /* Change to #82838b for default offline color */ + + /* Border colors */ + --border-light: var(--hover); /* General light border color */ + --border: var(--active); /* General normal border color */ + --border-hover: var(--accent-2); /* Border color of panels when hovered */ + --button-border: color-mix( + in srgb, + var(--text-1) 10%, + transparent + ); /* Neutral border color of buttons */ + + /* Base colors */ + --red-1: color-mix(in oklab, var(--base08) 82%, var(--base06)); + --red-2: var(--base08); + --red-3: color-mix(in oklab, var(--base08) 78%, var(--base00)); + --red-4: color-mix(in oklab, var(--base08) 62%, var(--base00)); + --red-5: color-mix(in oklab, var(--base08) 46%, var(--base00)); + + --green-1: color-mix(in oklab, var(--base0B) 82%, var(--base06)); + --green-2: color-mix(in oklab, var(--base0B) 92%, var(--base06)); + --green-3: var(--base0B); + --green-4: color-mix(in oklab, var(--base0B) 78%, var(--base00)); + --green-5: color-mix(in oklab, var(--base0B) 60%, var(--base00)); + + --blue-1: color-mix(in oklab, var(--base0D) 82%, var(--base06)); + --blue-2: color-mix(in oklab, var(--base0C) 88%, var(--base06)); + --blue-3: color-mix(in oklab, var(--base0D) 88%, var(--base00)); + --blue-4: color-mix(in oklab, var(--base0D) 70%, var(--base00)); + --blue-5: color-mix(in oklab, var(--base0D) 54%, var(--base00)); + + --yellow-1: color-mix(in oklab, var(--base0A) 86%, var(--base06)); + --yellow-2: color-mix(in oklab, var(--base0A) 94%, var(--base06)); + --yellow-3: var(--base0A); + --yellow-4: color-mix(in oklab, var(--base0A) 76%, var(--base00)); + --yellow-5: color-mix(in oklab, var(--base0A) 58%, var(--base00)); + + --purple-1: color-mix(in oklab, var(--base0E) 84%, var(--base06)); + --purple-2: var(--base0E); + --purple-3: color-mix(in oklab, var(--base0E) 82%, var(--base00)); + --purple-4: color-mix(in oklab, var(--base0E) 64%, var(--base00)); + --purple-5: color-mix(in oklab, var(--base0E) 48%, var(--base00)); +} diff --git a/modules/programs/gui/ghostty/cursor.glsl b/modules/programs/gui/ghostty/cursor.glsl new file mode 100644 index 0000000..a04421a --- /dev/null +++ b/modules/programs/gui/ghostty/cursor.glsl @@ -0,0 +1,216 @@ +// -- CONFIGURATION --- +vec4 TRAIL_COLOR = iCurrentCursorColor; // can change to eg: vec4(0.2, 0.6, 1.0, 0.5); +const float DURATION = 0.2; // in seconds +const float TRAIL_LENGTH = 0.5; +const float BLUR = 2.0; // blur size in pixels (for antialiasing) + +// --- CONSTANTS for easing functions --- +const float PI = 3.14159265359; +const float C1_BACK = 1.70158; +const float C2_BACK = C1_BACK * 1.525; +const float C3_BACK = C1_BACK + 1.0; +const float C4_ELASTIC = (2.0 * PI) / 3.0; +const float C5_ELASTIC = (2.0 * PI) / 4.5; +const float SPRING_STIFFNESS = 9.0; +const float SPRING_DAMPING = 0.9; + +// --- EASING FUNCTIONS --- + +// // Linear +// float ease(float x) { +// return x; +// } + +// // EaseOutQuad +// float ease(float x) { +// return 1.0 - (1.0 - x) * (1.0 - x); +// } + +// EaseOutCubic +float ease(float x) { + return 1.0 - pow(1.0 - x, 3.0); +} + +// // EaseOutQuart +// float ease(float x) { +// return 1.0 - pow(1.0 - x, 4.0); +// } + +// // EaseOutQuint +// float ease(float x) { +// return 1.0 - pow(1.0 - x, 5.0); +// } + +// EaseOutSine +// float ease(float x) { +// return sin((x * PI) / 2.0); +// } + +// // EaseOutExpo +// float ease(float x) { +// return x == 1.0 ? 1.0 : 1.0 - pow(2.0, -10.0 * x); +// } + +// // EaseOutCirc +// float ease(float x) { +// return sqrt(1.0 - pow(x - 1.0, 2.0)); +// } + +// // EaseOutBack +// float ease(float x) { +// return 1.0 + C3_BACK * pow(x - 1.0, 3.0) + C1_BACK * pow(x - 1.0, 2.0); +// } + +// // EaseOutElastic +// float ease(float x) { +// return x == 0.0 ? 0.0 +// : x == 1.0 ? 1.0 +// : pow(2.0, -10.0 * x) * sin((x * 10.0 - 0.75) * C4_ELASTIC) + 1.0; +// } + +// Parametric Spring +// float ease(float x) { +// x = clamp(x, 0.0, 1.0); +// float decay = exp(-SPRING_DAMPING * SPRING_STIFFNESS * x); +// float freq = sqrt(SPRING_STIFFNESS * (1.0 - SPRING_DAMPING * SPRING_DAMPING)); +// float osc = cos(freq * 6.283185 * x) + (SPRING_DAMPING * sqrt(SPRING_STIFFNESS) / freq) * sin(freq * 6.283185 * x); +// return 1.0 - decay * osc; +// } + +float getSdfRectangle(in vec2 point, in vec2 center, in vec2 halfSize) +{ + vec2 d = abs(point - center) - halfSize; + return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); +} + +// Based on Inigo Quilez's 2D distance functions article: https://iquilezles.org/articles/distfunctions2d/ +// Potencially optimized by eliminating conditionals and loops to enhance performance and reduce branching + +float seg(in vec2 p, in vec2 a, in vec2 b, inout float s, float d) { + vec2 e = b - a; + vec2 w = p - a; + vec2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0); + float segd = dot(p - proj, p - proj); + d = min(d, segd); + + float c0 = step(0.0, p.y - a.y); + float c1 = 1.0 - step(0.0, p.y - b.y); + float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x); + float allCond = c0 * c1 * c2; + float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2); + float flip = mix(1.0, -1.0, step(0.5, allCond + noneCond)); + s *= flip; + return d; +} + +float getSdfParallelogram(in vec2 p, in vec2 v0, in vec2 v1, in vec2 v2, in vec2 v3) { + float s = 1.0; + float d = dot(p - v0, p - v0); + + d = seg(p, v0, v3, s, d); + d = seg(p, v1, v0, s, d); + d = seg(p, v2, v1, s, d); + d = seg(p, v3, v2, s, d); + + return s * sqrt(d); +} + +vec2 normalize(vec2 value, float isPosition) { + return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y; +} + +float antialising(float distance) { + return 1. - smoothstep(0., normalize(vec2(BLUR, BLUR), 0.).x, distance); +} + +float getTopVertexFlag(vec2 a, vec2 b) { + float condition1 = step(b.x, a.x) * step(a.y, b.y); // a.x < b.x && a.y > b.y + float condition2 = step(a.x, b.x) * step(b.y, a.y); // a.x > b.x && a.y < b.y + + // if neither condition is met, return 1 (else case) + return 1.0 - max(condition1, condition2); +} + +vec2 getRectangleCenter(vec4 rectangle) { + return vec2(rectangle.x + (rectangle.z / 2.), rectangle.y - (rectangle.w / 2.)); +} + + +void mainImage(out vec4 fragColor, in vec2 fragCoord){ + #if !defined(WEB) + fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy); + #endif + + // normalization & setup(-1, 1 coords) + vec2 vu = normalize(fragCoord, 1.); + vec2 offsetFactor = vec2(-.5, 0.5); + + vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.)); + vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.)); + + vec2 centerCC = currentCursor.xy - (currentCursor.zw * offsetFactor); + vec2 centerCP = previousCursor.xy - (previousCursor.zw * offsetFactor); + + float sdfCurrentCursor = getSdfRectangle(vu, centerCC, currentCursor.zw * 0.5); + + float lineLength = distance(centerCC, centerCP); + + vec4 newColor = vec4(fragColor); + + float minDist = 0.0; + float progress = clamp((iTime - iTimeCursorChange) / DURATION, 0.0, 1.0); + if (lineLength > minDist) { + // --- Animation Logic --- + float shrinkFactor = ease(progress); + + // detect straight moves + vec2 delta = abs(centerCC - centerCP); + float threshold = 0.001; + float isHorizontal = step(delta.y, threshold); + float isVertical = step(delta.x, threshold); + float isStraightMove = max(isHorizontal, isVertical); + + // -- Making parallelogram sdf (diagonal moves) --- + float topVertexFlag = getTopVertexFlag(currentCursor.xy, previousCursor.xy); + float bottomVertexFlag = 1.0 - topVertexFlag; + vec2 v0 = vec2(currentCursor.x + currentCursor.z * topVertexFlag, currentCursor.y - currentCursor.w); + vec2 v1 = vec2(currentCursor.x + currentCursor.z * bottomVertexFlag, currentCursor.y); + vec2 v2_full = vec2(previousCursor.x + currentCursor.z * bottomVertexFlag, previousCursor.y); + vec2 v3_full = vec2(previousCursor.x + currentCursor.z * topVertexFlag, previousCursor.y - previousCursor.w); + + vec2 v2_start = mix(v1, v2_full, TRAIL_LENGTH); + vec2 v3_start = mix(v0, v3_full, TRAIL_LENGTH); + vec2 v2_anim = mix(v2_start, v1, shrinkFactor); + vec2 v3_anim = mix(v3_start, v0, shrinkFactor); + + float sdfTrail_diag = getSdfParallelogram(vu, v0, v1, v2_anim, v3_anim); + + // --- Making rectangle sdf (straight moves) --- + vec2 min_center = min(centerCP, centerCC); + vec2 max_center = max(centerCP, centerCC); + + vec2 bBoxSize_full = (max_center - min_center) + currentCursor.zw; + vec2 bBoxCenter_full = (min_center + max_center) * 0.5; + + vec2 bBoxSize_start = mix(currentCursor.zw, bBoxSize_full, TRAIL_LENGTH); + vec2 bBoxCenter_start = mix(centerCC, bBoxCenter_full, TRAIL_LENGTH); + + vec2 animSize = mix(bBoxSize_start, currentCursor.zw, shrinkFactor); + vec2 animCenter = mix(bBoxCenter_start, centerCC, shrinkFactor); + + float sdfTrail_rect = getSdfRectangle(vu, animCenter, animSize * 0.5); + + // -- Selecting and drawing the trail sdf -- + float sdfTrail = mix(sdfTrail_diag, sdfTrail_rect, isStraightMove); + + vec4 trail = TRAIL_COLOR; + float trailAlpha = antialising(sdfTrail); + newColor = mix(newColor, trail, trailAlpha); + + // Punch hole + newColor = mix(newColor, fragColor, step(sdfCurrentCursor, 0.)); + } + + + fragColor = newColor; +} diff --git a/modules/programs/gui/ghostty/default.nix b/modules/programs/gui/ghostty/default.nix new file mode 100644 index 0000000..95b39fe --- /dev/null +++ b/modules/programs/gui/ghostty/default.nix @@ -0,0 +1,32 @@ +{ + flake.modules.homeManager.ghostty = { + config, + lib, + ... + }: let + inherit (lib) getExe; + in { + # keep-sorted start block=yes newline_separated=yes + programs.ghostty = { + enable = true; + + # Add the cursor shader to to ghostty config. + settings.custom-shader = "${config.xdg.configHome}/ghostty/cursor.glsl"; + }; + + # Start Ghostty with a systemd service. + systemd.user.services.ghostty = { + Unit = { + After = ["graphical-session.target"]; + PartOf = ["graphical-session.target"]; + }; + + Service.ExecStart = "${getExe config.programs.ghostty.package} --initial-window=false"; + Install.WantedBy = ["graphical-session.target"]; + }; + + # Put the shader file into the ghostty config dir. + xdg.configFile."ghostty/cursor.glsl".source = ./cursor.glsl; + # keep-sorted end + }; +} diff --git a/modules/programs/gui/ghostty/keybinds.nix b/modules/programs/gui/ghostty/keybinds.nix new file mode 100644 index 0000000..1f80f41 --- /dev/null +++ b/modules/programs/gui/ghostty/keybinds.nix @@ -0,0 +1,32 @@ +{ + flake.modules.homeManager.ghostty = _: { + programs.ghostty.settings.keybind = [ + # keep-sorted start + "alt+f4=unbind" + "ctrl+down=unbind" + "ctrl+enter=unbind" + "ctrl+shift+0=unbind" + "ctrl+shift+e=unbind" + "ctrl+shift+enter=unbind" + "ctrl+shift+q=unbind" + "super+alt+down=unbind" + "super+alt+left=unbind" + "super+alt+right=unbind" + "super+alt+up=unbind" + "super+ctrl+O=unbind" + "super+ctrl+T=unbind" + "super+ctrl+W=unbind" + "super+ctrl+down=unbind" + "super+ctrl+equal=unbind" + "super+ctrl+left=unbind" + "super+ctrl+right=unbind" + "super+ctrl+up=unbind" + "super+d=unbind" + "super+left_bracket=unbind" + "super+right_bracket=unbind" + "super+shift+d=unbind" + "super+shift+enter=unbind" + # keep-sorted end + ]; + }; +} diff --git a/modules/programs/gui/ghostty/settings.nix b/modules/programs/gui/ghostty/settings.nix new file mode 100644 index 0000000..61d647b --- /dev/null +++ b/modules/programs/gui/ghostty/settings.nix @@ -0,0 +1,40 @@ +{ + flake.modules.homeManager.ghostty = _: { + programs.ghostty.settings = { + # Font settings. + font-feature = "-calt, -liga, -dlig"; + + # Cursor settings. + cursor-style = "block"; + cursor-style-blink = true; + shell-integration-features = "no-cursor,ssh-terminfo,ssh-env"; + + # Appearance settings. + window-theme = "system"; + window-decoration = "none"; + gtk-titlebar = false; + window-padding-x = 2; + window-padding-y = 2; + resize-overlay = "never"; + + # Misc settings. + copy-on-select = false; + auto-update = "off"; + confirm-close-surface = false; + right-click-action = "ignore"; + + # Improve startup time. + quit-after-last-window-closed = false; + gtk-single-instance = true; + + # Disable directory inherit. + working-directory = "home"; + window-inherit-working-directory = false; + tab-inherit-working-directory = false; + split-inherit-working-directory = false; + + # Enable cursor shader animation. + custom-shader-animation = true; + }; + }; +} diff --git a/modules/programs/gui/steam.nix b/modules/programs/gui/steam.nix index 23c29f4..79600e4 100644 --- a/modules/programs/gui/steam.nix +++ b/modules/programs/gui/steam.nix @@ -1,5 +1,18 @@ { + inputs, + self, + ... +}: { + flake-file.inputs.millennium = { + url = "github:SteamClientHomebrew/Millennium?dir=packages/nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake.overlays.millennium = inputs.millennium.overlays.default; + flake.modules.nixos.steam = {pkgs, ...}: { + nixpkgs.overlays = [self.overlays.millennium]; + programs.steam = { enable = true; diff --git a/modules/programs/tui/neovim/keymap.nix b/modules/programs/tui/neovim/keymap.nix index 58516b4..5fa8aa1 100644 --- a/modules/programs/tui/neovim/keymap.nix +++ b/modules/programs/tui/neovim/keymap.nix @@ -7,21 +7,21 @@ key = "q"; mode = "n"; action = "q"; - desc = " Quit window"; + desc = "Quit window"; } { key = "w"; mode = "n"; action = "w"; - desc = "󰆓 Write file"; + desc = "Write file"; } { key = "x"; mode = "n"; action = "x"; - desc = "󰸧 Write and quit"; + desc = "Write and quit"; } # keep-sorted end ]; diff --git a/modules/programs/tui/opencode/default.nix b/modules/programs/tui/opencode/default.nix new file mode 100644 index 0000000..bc9897c --- /dev/null +++ b/modules/programs/tui/opencode/default.nix @@ -0,0 +1,106 @@ +{self, ...}: { + flake.modules.homeManager.opencode = { + # keep-sorted start + config, + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (builtins) attrValues; + inherit + (lib) + # keep-sorted start + concatStringsSep + escapeShellArg + getExe + getExe' + makeBinPath + mapAttrsToList + # keep-sorted end + ; + inherit (config.lib.file) mkOutOfStoreSymlink; + inherit + (pkgs) + # keep-sorted start + symlinkJoin + makeWrapper + # keep-sorted end + ; + + inherit + (config.programs.opencode) + # keep-sorted start + env + mcpServers + # keep-sorted end + ; + + home = config.home.homeDirectory; + wrapEnvArgs = concatStringsSep " \\" ( + mapAttrsToList + (name: value: "--set ${escapeShellArg name} ${escapeShellArg (toString value)}") + env + ); + in { + imports = [self.modules.homeManager.xdgTerminal]; + + # keep-sorted start block=yes newline_separated=yes + programs.opencode = { + enable = true; + + # Wrap opencode with mcp's, and other dependencies. + package = symlinkJoin { + name = "opencode-wrapped"; + paths = [pkgs.nur.repos.adam0.opencode]; + nativeBuildInputs = [makeWrapper]; + + # Set feature flags and prepend runtime tools before launching opencode. + postBuild = '' + wrapProgram $out/bin/opencode \ + ${wrapEnvArgs} \ + --prefix PATH : ${makeBinPath (attrValues { + inherit (pkgs.nur.repos.adam0) modular-mcp; + inherit + (pkgs) + # keep-sorted start + libnotify + wl-clipboard + # keep-sorted end + ; + } + // mcpServers)} + ''; + }; + + # Set agent rules. + context = ./instructions.md; + }; + + xdg = { + # Point opencode at the shared agent skills directory. + configFile."opencode/skills".source = mkOutOfStoreSymlink "${home}/.agents/skills"; + + # Create desktop entry to allow launching via launcher. + desktopEntries.opencode = { + name = "Opencode"; + genericName = "AI Coding Assistant"; + + exec = let + # keep-sorted start + opencode = getExe' config.programs.opencode.package "opencode"; + terminalCommand = getExe config.xdg.terminal-exec.package; + # keep-sorted end + in "${terminalCommand} --title=Opencode ${opencode}"; + + categories = [ + # keep-sorted start + "ConsoleOnly" + "Development" + # keep-sorted end + ]; + }; + }; + # keep-sorted end + }; +} diff --git a/modules/programs/tui/opencode/env.nix b/modules/programs/tui/opencode/env.nix new file mode 100644 index 0000000..a413f46 --- /dev/null +++ b/modules/programs/tui/opencode/env.nix @@ -0,0 +1,35 @@ +{ + flake.modules.homeManager.opencode = {lib, ...}: let + inherit (lib) mkOption types; + inherit (lib.self) envFlags; + in { + options.programs.opencode.env = mkOption { + type = types.attrsOf types.anything; + default = {}; + description = "Environment variables set by the wrapped opencode launcher."; + }; + + config.programs.opencode.env = let + experimentalFeatures = [ + # keep-sorted start + "FILEWATCHER" + "ICON_DISCOVERY" + "LSP_TOOL" + "LSP_TY" + # keep-sorted end + ]; + + # Keep builtin lsp downloads disabled and rely on nix instead. + disabledFeatures = [ + # keep-sorted start + "AUTOCOMPACT" + "LSP_DOWNLOAD" + # keep-sorted end + ]; + enabledFeatures = ["EXA"]; + in + envFlags "OPENCODE_EXPERIMENTAL" experimentalFeatures + // envFlags "OPENCODE_DISABLE" disabledFeatures + // envFlags "OPENCODE_ENABLE" enabledFeatures; + }; +} diff --git a/modules/programs/tui/opencode/formatter.nix b/modules/programs/tui/opencode/formatter.nix new file mode 100644 index 0000000..d5c47d7 --- /dev/null +++ b/modules/programs/tui/opencode/formatter.nix @@ -0,0 +1,162 @@ +{ + flake.modules.homeManager.opencode = { + # keep-sorted start + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit + (lib) + # keep-sorted start + getExe + getExe' + # keep-sorted end + ; + in { + programs.opencode.settings.formatter = { + # keep-sorted start block=yes newline_separated=yes + alejandra = { + command = [(getExe pkgs.alejandra) "$FILE"]; + extensions = [".nix"]; + }; + + fish_indent = { + command = [ + (getExe' pkgs.fish "fish_indent") + "--write" + "$FILE" + ]; + extensions = [".fish"]; + }; + + ktlint = { + command = [ + (getExe pkgs.ktlint) + "-F" + "$FILE" + ]; + extensions = [ + # keep-sorted start + ".kt" + ".kts" + # keep-sorted end + ]; + }; + + oxfmt = { + command = [ + (getExe pkgs.oxfmt) + "$FILE" + ]; + extensions = [ + # keep-sorted start + ".cjs" + ".css" + ".hbs" + ".html" + ".js" + ".json" + ".json5" + ".jsonc" + ".jsx" + ".less" + ".mjs" + ".scss" + ".toml" + ".ts" + ".tsx" + ".yaml" + ".yml" + # keep-sorted end + ]; + }; + + oxlint = { + command = [ + (getExe pkgs.oxlint) + "--fix" + "$FILE" + ]; + extensions = [ + # keep-sorted start + ".cjs" + ".js" + ".jsx" + ".mjs" + ".ts" + ".tsx" + # keep-sorted end + ]; + }; + + ruff = { + command = [ + (getExe pkgs.ruff) + "format" + "$FILE" + ]; + extensions = [ + # keep-sorted start + ".py" + ".pyi" + # keep-sorted end + ]; + }; + + rustfmt = { + command = [(getExe pkgs.rustfmt) "$FILE"]; + extensions = [".rs"]; + }; + + shfmt = { + command = [ + (getExe pkgs.shfmt) + "--write" + "$FILE" + ]; + extensions = [ + # keep-sorted start + ".bash" + ".sh" + # keep-sorted end + ]; + }; + + stylua = { + command = [ + (getExe pkgs.stylua) + "--respect-ignores" + "$FILE" + ]; + extensions = [".lua"]; + }; + + rumdl = { + command = [ + (getExe pkgs.rumdl) + "fmt" + "--fix" + "$FILE" + ]; + extensions = [ + # keep-sorted start + ".markdown" + ".md" + # keep-sorted end + ]; + }; + + selene = { + command = [ + (getExe pkgs.selene) + "--display-style" + "quiet" + "$FILE" + ]; + extensions = [".lua"]; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/tui/opencode/instructions.md b/modules/programs/tui/opencode/instructions.md new file mode 100644 index 0000000..7a1b51f --- /dev/null +++ b/modules/programs/tui/opencode/instructions.md @@ -0,0 +1,67 @@ +# Agent Instructions + +## Agent behavior profile + +- Highly efficient, task-focused, and precise. +- Use direct, low-emotion, clinical language. +- Avoid warmth, enthusiasm, and expressive tone. +- Do not include greetings, pleasantries, or sign-offs. +- Do not add opinions or subjective commentary. +- Do not add unsolicited tips or digressions. + +### Response construction + +- Prefer structured markdown (headers, lists, tables). +- Keep answers concise but complete. +- Optimize for clarity and scanability. +- Avoid verbosity unless required for correctness. + +### Interaction rules + +- Ask follow-up questions only when necessary to proceed. +- Do not mirror the user's tone unless explicitly required. +- Stay strictly aligned with the requested task. + +### Writing artifacts + +- Match the tone and format to the requested artifact type + instead of enforcing assistant voice. + +## Tools + +### Use skills aggressively + +Default to loading a relevant skill before doing substantive work whenever a +skill plausibly applies. Treat skills as the first-line workflow, not an +optional enhancement. + +- Proactively check whether an available skill matches the task before using + ad-hoc shell commands, web research, or custom reasoning. +- If a task clearly matches a known skill, load it immediately without waiting + for the user to ask. +- If multiple skills may apply, load the most specific one first, then load an + additional skill if the task expands. +- Do not skip a relevant skill just because the task looks small; still load it + when it changes the workflow, tool choice, or quality bar. + +Prefer a false positive over missing a useful skill. Only skip loading a skill +when you are confident none of the available skills materially help. + +### Always use the TODO tool + +Any time the user is planning, tracking progress, breaking down work, or +managing ongoing tasks, automatically use the TODO tool to create, update, or +maintain the task list without me having to explicitly ask. +When you need to ask the user a question, use the question tool and skip the +TODO tool for that interaction. +When you are only explaining, skip the TODO tool. + +### Always use the question tool when asking questions + +When you need to ask the user a question, use the question tool and skip the +TODO tool for that interaction. + +### Use shell fallbacks when needed + +If a required tool is unavailable, use shell helpers such as `nix run nixpkgs#` or +`comma ` to access the missing command. diff --git a/modules/programs/tui/opencode/lsp.nix b/modules/programs/tui/opencode/lsp.nix new file mode 100644 index 0000000..761831a --- /dev/null +++ b/modules/programs/tui/opencode/lsp.nix @@ -0,0 +1,150 @@ +{ + flake.modules.homeManager.opencode = { + # keep-sorted start + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit + (lib) + # keep-sorted start + getExe + getExe' + # keep-sorted end + ; + in { + programs.opencode.settings.lsp = { + # keep-sorted start block=yes newline_separated=yes + bash = { + command = [(getExe pkgs.bash-language-server) "start"]; + extensions = [ + # keep-sorted start + ".bash" + ".sh" + # keep-sorted end + ]; + }; + + cssls = { + command = [(getExe' pkgs.vscode-langservers-extracted "vscode-css-language-server")]; + extensions = ["css"]; + }; + + csharp = { + command = [(getExe pkgs.csharp-ls)]; + extensions = [ + # keep-sorted start + ".cs" + ".csx" + # keep-sorted end + ]; + }; + + json = { + command = [(getExe pkgs.vscode-json-languageserver) "--stdio"]; + extensions = [ + # keep-sorted start + ".json" + ".jsonc" + # keep-sorted end + ]; + }; + + jdtls = { + command = [(getExe pkgs.jdt-language-server)]; + extensions = [".java"]; + }; + + lemminx = { + command = [(getExe pkgs.lemminx)]; + extensions = [".xml"]; + }; + + kotlin-ls = { + command = [(getExe pkgs.kotlin-language-server)]; + extensions = [".kt" ".kts"]; + }; + + lua = { + command = [(getExe pkgs.lua-language-server)]; + extensions = [".lua"]; + }; + + nixd = { + command = [(getExe pkgs.nixd)]; + extensions = [".nix"]; + }; + + oxlint = { + command = [(getExe pkgs.oxlint)]; + extensions = [ + # keep-sorted start + ".js" + ".jsx" + ".ts" + ".tsx" + # keep-sorted end + ]; + }; + + rumdl = { + command = [(getExe pkgs.rumdl) "server"]; + extensions = [ + # keep-sorted start + ".markdown" + ".md" + # keep-sorted end + ]; + }; + + rust = { + command = [(getExe pkgs.rust-analyzer)]; + extensions = [".rs"]; + }; + + superhtml = { + command = [(getExe pkgs.superhtml) "lsp"]; + extensions = [".html"]; + }; + + tombi = { + command = [(getExe pkgs.tombi) "lsp"]; + extensions = [".toml"]; + }; + + ty = { + command = [(getExe pkgs.ty)]; + extensions = [ + # keep-sorted start + ".py" + ".pyi" + # keep-sorted end + ]; + }; + + typescript = { + command = [(getExe pkgs.typescript-language-server) "--stdio"]; + extensions = [ + # keep-sorted start + ".js" + ".jsx" + ".ts" + ".tsx" + # keep-sorted end + ]; + }; + + yaml-ls = { + command = [(getExe pkgs.yaml-language-server) "--stdio"]; + extensions = [ + # keep-sorted start + ".yaml" + ".yml" + # keep-sorted end + ]; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/tui/opencode/mcp-wrappers.nix b/modules/programs/tui/opencode/mcp-wrappers.nix new file mode 100644 index 0000000..bc92746 --- /dev/null +++ b/modules/programs/tui/opencode/mcp-wrappers.nix @@ -0,0 +1,59 @@ +{self, ...}: { + flake.modules.homeManager.opencode = { + # keep-sorted start + config, + lib, + pkgs, + # keep-sorted end + ... + }: let + inherit (lib) getExe mkOption types; + inherit (pkgs) writeShellApplication; + + # keep-sorted start block=yes newline_separated=yes + context7McpWrapper = writeShellApplication { + name = "context7-mcp-wrapper"; + runtimeInputs = [pkgs.coreutils]; + text = '' + CONTEXT7_API_KEY="$(cat "${config.sops.secrets."ai/context7_key".path}")" + export CONTEXT7_API_KEY + exec "${getExe pkgs.context7-mcp}" "$@" + ''; + }; + + githubMcpServerWrapper = writeShellApplication { + name = "github-mcp-server-wrapper"; + runtimeInputs = [pkgs.coreutils]; + text = '' + GITHUB_PERSONAL_ACCESS_TOKEN="$(cat "${config.sops.secrets."ai/github_access_token".path}")" + export GITHUB_PERSONAL_ACCESS_TOKEN + exec "${getExe pkgs.github-mcp-server}" stdio --read-only "$@" + ''; + }; + # keep-sorted end + in { + imports = [self.modules.homeManager.sops]; + + options.programs.opencode.mcpServers = mkOption { + type = types.attrsOf types.package; + default = {}; + description = "MCP server packages added to the wrapped opencode launcher PATH."; + }; + + config.programs.opencode.mcpServers = { + inherit + # keep-sorted start + context7McpWrapper + githubMcpServerWrapper + # keep-sorted end + ; + }; + + sops.secrets = { + # keep-sorted start + "ai/context7_key" = {}; + "ai/github_access_token" = {}; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/tui/opencode/plugins.nix b/modules/programs/tui/opencode/plugins.nix new file mode 100644 index 0000000..fd6ad1b --- /dev/null +++ b/modules/programs/tui/opencode/plugins.nix @@ -0,0 +1,35 @@ +{ + flake.modules.homeManager.opencode = {pkgs, ...}: { + imports = [pkgs.nur.repos.adam0.hmModules.opencode-plugins]; + + programs.opencode.plugins = { + # keep-sorted start + cc-safety-net.enable = true; + changelog.enable = true; + dynamic-context-pruning.enable = true; + ignore.enable = true; + lazy-mcp.enable = true; + unmoji.enable = true; + # keep-sorted end + + # keep-sorted start block=yes newline_separated=yes + notifier = { + enable = true; + settings.sound = false; + }; + + quota = { + enable = true; + sidebar.enable = true; + + settings = { + # keep-sorted start + enableToast = false; + formatStyle = "allWindows"; + # keep-sorted end + }; + }; + # keep-sorted end + }; + }; +} diff --git a/modules/programs/tui/opencode/settings.nix b/modules/programs/tui/opencode/settings.nix new file mode 100644 index 0000000..1b275fe --- /dev/null +++ b/modules/programs/tui/opencode/settings.nix @@ -0,0 +1,43 @@ +{ + flake.modules.homeManager.opencode = {config, ...}: let + home = config.home.homeDirectory; + in { + programs.opencode = { + settings = { + # keep-sorted start block=yes newline_separated=yes + autoupdate = false; + + permission.external_directory = { + # keep-sorted start + "${home}/Infra/**" = "allow"; + "/nix/store/**" = "allow"; + # keep-sorted end + }; + + watcher.ignore = [ + # keep-sorted start + ".direnv/**" + ".git/**" + ".rumdl_cache/**" + "dist/**" + "node_modules/**" + "target/**" + # keep-sorted end + ]; + # keep-sorted end + + # keep-sorted start + agent.explore.reasoningEffort = "medium"; + agent.general.reasoningEffort = "medium"; + # keep-sorted end + }; + + tui = { + # keep-sorted start + diff_style = "stacked"; + scroll_acceleration.enabled = true; + # keep-sorted end + }; + }; + }; +} diff --git a/modules/programs/tui/opencode/skills/create-readme.md b/modules/programs/tui/opencode/skills/create-readme.md new file mode 100644 index 0000000..ccc6562 --- /dev/null +++ b/modules/programs/tui/opencode/skills/create-readme.md @@ -0,0 +1,28 @@ +--- +name: create-readme +description: Use this skill to create a concise, well-structured `README.md` file for the project. Review the repo first, then write clear GitHub-flavored documentation without unnecessary sections or filler. +--- + +## Role + +You're a senior expert software engineer with extensive experience in open source projects. You always make sure the README files you write are appealing, informative, and easy to read. + +## Task + +1. Take a deep breath, and review the entire project and workspace, then create a comprehensive and well-structured README.md file for the project. +2. Use my README style as the primary reference for structure, tone, formatting, and level of detail. The final README should read predominantly like my existing READMEs, while still taking selective inspiration from the other repos where it materially improves clarity or presentation: + +- Mine: + - + - + - +- Others: + - + - + - + - + +3. Do not use emojis, and keep the readme concise and to the point. +4. Do not include sections like "LICENSE", "CONTRIBUTING", "CHANGELOG", etc. There are dedicated files for those sections. +5. Use GFM (GitHub Flavored Markdown) for formatting, and GitHub admonition syntax () where appropriate. +6. If you find a logo or icon for the project, use it in the readme's header. diff --git a/modules/programs/tui/opencode/skills/default.nix b/modules/programs/tui/opencode/skills/default.nix new file mode 100644 index 0000000..26e5c73 --- /dev/null +++ b/modules/programs/tui/opencode/skills/default.nix @@ -0,0 +1,39 @@ +{ + flake.modules.homeManager.opencode = {lib, ...}: let + inherit + (builtins) + # keep-sorted start + attrNames + foldl' + readDir + # keep-sorted end + ; + inherit + (lib) + # keep-sorted start + filterAttrs + hasSuffix + mapAttrs' + nameValuePair + removeSuffix + # keep-sorted end + ; + + mkSkills = prefix: dir: + mapAttrs' ( + name: _: + nameValuePair "${prefix}${removeSuffix ".md" name}" { + source = dir + "/${name}"; + } + ) (filterAttrs (name: type: type == "regular" && hasSuffix ".md" name) (readDir dir)); + + rootEntries = readDir ./.; + skillDirectories = attrNames (filterAttrs (_: type: type == "directory") rootEntries); + rootSkills = mkSkills "" ./.; + prefixedSkills = foldl' (acc: dir: acc // mkSkills "${dir}-" (./. + "/${dir}")) {} skillDirectories; + in { + home.file = mapAttrs' ( + name: value: nameValuePair ".agents/skills/${name}/SKILL.md" value + ) (rootSkills // prefixedSkills); + }; +} diff --git a/modules/programs/tui/opencode/skills/find-skills.md b/modules/programs/tui/opencode/skills/find-skills.md new file mode 100644 index 0000000..2815e66 --- /dev/null +++ b/modules/programs/tui/opencode/skills/find-skills.md @@ -0,0 +1,143 @@ +--- +name: find-skills +description: Use this skill when the user is looking for functionality that might exist as an installable skill. Search for relevant skills, verify their quality, and present the best options with install commands. +--- + +# Find Skills + +This skill helps you discover and install skills from the open agent skills ecosystem. + +## When to Use This Skill + +Use this skill when the user: + +- Asks "how do I do X" where X might be a common task with an existing skill +- Says "find a skill for X" or "is there a skill for X" +- Asks "can you do X" where X is a specialized capability +- Expresses interest in extending agent capabilities +- Wants to search for tools, templates, or workflows +- Mentions they wish they had help with a specific domain (design, testing, deployment, etc.) + +## What is the Skills CLI? + +The Skills CLI (`comma bunx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools. + +**Key commands:** + +- `comma bunx skills find [query]` - Search for skills interactively or by keyword +- `comma bunx skills add ` - Install a skill from GitHub or other sources +- `comma bunx skills check` - Check for skill updates +- `comma bunx skills update` - Update all installed skills + +**Browse skills at:** + +## How to Help Users Find Skills + +### Step 1: Understand What They Need + +When a user asks for help with something, identify: + +1. The domain (e.g., React, testing, design, deployment) +2. The specific task (e.g., writing tests, creating animations, reviewing PRs) +3. Whether this is a common enough task that a skill likely exists + +### Step 2: Check the Leaderboard First + +Before running a CLI search, check the [skills.sh leaderboard](https://skills.sh/) to see if a well-known skill already exists for the domain. The leaderboard ranks skills by total installs, surfacing the most popular and battle-tested options. + +For example, top skills for web development include: + +- `vercel-labs/agent-skills` — React, Next.js, web design (100K+ installs each) +- `anthropics/skills` — Frontend design, document processing (100K+ installs) + +### Step 3: Search for Skills + +If the leaderboard doesn't cover the user's need, run the find command: + +```bash +comma bunx skills find [query] +``` + +For example: + +- User asks "how do I make my React app faster?" → `comma bunx skills find react performance` +- User asks "can you help me with PR reviews?" → `comma bunx skills find pr review` +- User asks "I need to create a changelog" → `comma bunx skills find changelog` + +### Step 4: Verify Quality Before Recommending + +**Do not recommend a skill based solely on search results.** Always verify: + +1. **Install count** — Prefer skills with 1K+ installs. Be cautious with anything under 100. +2. **Source reputation** — Official sources (`vercel-labs`, `anthropics`, `microsoft`) are more trustworthy than unknown authors. +3. **GitHub stars** — Check the source repository. A skill from a repo with <100 stars should be treated with skepticism. + +### Step 5: Present Options to the User + +When you find relevant skills, present them to the user with: + +1. The skill name and what it does +2. The install count and source +3. The install command they can run +4. A link to learn more at skills.sh + +Example response: + +```text +I found a skill that might help! The "react-best-practices" skill provides +React and Next.js performance optimization guidelines from Vercel Engineering. +(185K installs) + +To install it: +comma bunx skills add vercel-labs/agent-skills@react-best-practices + +Learn more: https://skills.sh/vercel-labs/agent-skills/react-best-practices +``` + +### Step 6: Offer to Install + +If the user wants to proceed, you can install the skill for them: + +```bash +comma bunx skills add -g -y +``` + +The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts. + +## Common Skill Categories + +When searching, consider these common categories: + +| Category | Example Queries | +| --------------- | ---------------------------------------- | +| Web Development | react, nextjs, typescript, css, tailwind | +| Testing | testing, jest, playwright, e2e | +| DevOps | deploy, docker, kubernetes, ci-cd | +| Documentation | docs, readme, changelog, api-docs | +| Code Quality | review, lint, refactor, best-practices | +| Design | ui, ux, design-system, accessibility | +| Productivity | workflow, automation, git | + +## Tips for Effective Searches + +1. **Use specific keywords**: "react testing" is better than just "testing" +2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd" +3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills` + +## When No Skills Are Found + +If no relevant skills exist: + +1. Acknowledge that no existing skill was found +2. Offer to help with the task directly using your general capabilities +3. Suggest the user could create their own skill with `comma bunx skills init` + +Example: + +```text +I searched for skills related to "xyz" but didn't find any matches. +I can still help you with this task directly! Would you like me to proceed? + +If this is something you do often, you could create your own skill: +comma bunx skills init my-xyz-skill +``` diff --git a/modules/programs/tui/opencode/skills/lazy-mcp/context7.md b/modules/programs/tui/opencode/skills/lazy-mcp/context7.md new file mode 100644 index 0000000..0ac6aa1 --- /dev/null +++ b/modules/programs/tui/opencode/skills/lazy-mcp/context7.md @@ -0,0 +1,35 @@ +--- +name: lazy-mcp-context7 +description: Use this skill when you need up-to-date library or framework documentation. Resolve the correct Context7 library ID, then query the docs for APIs, configuration, setup steps, or code generation. +mcp: + context7: + command: ["context7-mcp-wrapper"] +--- + +# Context7 Documentation Search + +Use this skill to search current, library-specific documentation through Context7. + +## When to Use This Skill + +Use this skill when the user needs: + +- Library or framework API documentation. +- Setup or configuration steps. +- Version-specific behavior. +- Code generation grounded in current docs. +- Accurate examples for a specific package. + +## Workflow + +1. Resolve the library ID with `resolve-library-id` when you do not already know it. +2. Query the documentation with `query-docs` using the resolved ID. + +If you already know the exact Context7 library ID in `/org/project` format, skip the resolution step. + +## Usage Rules + +- Prefer Context7 over memory when the answer depends on current library behavior. +- Include the library name, feature, and version in the query when possible. +- Keep queries specific enough to retrieve code and configuration details. +- Use the resolved library ID directly for follow-up lookups in the same task. diff --git a/modules/programs/tui/opencode/skills/lazy-mcp/github.md b/modules/programs/tui/opencode/skills/lazy-mcp/github.md new file mode 100644 index 0000000..4f1de71 --- /dev/null +++ b/modules/programs/tui/opencode/skills/lazy-mcp/github.md @@ -0,0 +1,34 @@ +--- +name: lazy-mcp-github +description: Use this skill when you need GitHub repository data. Query repositories, code, issues, pull requests, commits, branches, releases, workflows, Actions, CI/CD state, collaborators, permissions, or repository metadata. +mcp: + github: + command: ["github-mcp-server-wrapper"] +--- + +# GitHub MCP + +Use this skill to query and inspect GitHub data through the GitHub MCP server. + +## When to Use This Skill + +Use this skill when the user needs: + +- Repository metadata or configuration details. +- Source code, trees, files, or commit history. +- Issues, pull requests, reviews, comments, or discussions. +- Branch, release, workflow, or Actions state. +- Collaborator, permission, or repository access information. + +## Workflow + +1. Identify the owner and repository first. +2. Query the GitHub MCP tool that matches the requested GitHub object. +3. Reuse repository context across follow-up lookups in the same task. + +## Usage Rules + +- Prefer the GitHub MCP server over memory for repository state and metadata. +- Be explicit about `owner`, `repo`, branch, PR number, issue number, or commit SHA. +- Use read-only inspection workflows unless the user explicitly asks for a write action and the available tools support it. +- Keep follow-up queries scoped to the same repository until the user switches context. diff --git a/modules/programs/tui/opencode/skills/lazy-mcp/grep-app.md b/modules/programs/tui/opencode/skills/lazy-mcp/grep-app.md new file mode 100644 index 0000000..bfea9e0 --- /dev/null +++ b/modules/programs/tui/opencode/skills/lazy-mcp/grep-app.md @@ -0,0 +1,34 @@ +--- +name: lazy-mcp-grep-app +description: Use this skill when you need real-world code examples from public GitHub repositories. Search implementation patterns, API usage, error strings, or language-specific examples through grep.app. +mcp: + grep-app: + type: remote + url: https://mcp.grep.app +--- + +# grep.app Code Search + +Use this skill to search real code examples from public GitHub repositories through grep.app. + +## When to Use This Skill + +Use this skill when the user needs: + +- Real-world implementation patterns. +- API usage examples from open-source code. +- Error strings or syntax found in actual repositories. +- Language-specific or repository-specific code examples. + +## Workflow + +1. Search for a concrete code pattern instead of a generic topic. +2. Narrow the query by language or repository when needed. +3. Reuse the strongest matching pattern for follow-up searches in the same task. + +## Usage Rules + +- Prefer code-shaped queries such as function names, imports, types, or error strings. +- Use language filters when the user needs examples in a specific ecosystem. +- Use repository filters when the user wants patterns from a known project. +- Treat grep.app as a source of examples, not as authoritative documentation. diff --git a/modules/programs/tui/opencode/skills/nix/debug.md b/modules/programs/tui/opencode/skills/nix/debug.md new file mode 100644 index 0000000..02b1df9 --- /dev/null +++ b/modules/programs/tui/opencode/skills/nix/debug.md @@ -0,0 +1,103 @@ +--- +name: nix-debug +description: Use this skill to debug Nix expressions, flake outputs, modules, and option values with `nix repl`. Prefer it when investigating evaluation errors, missing attributes, unexpected option values, derivation inputs, or output structure. Use `nix repl` instead of `nix eval` for inspection because the interactive REPL is faster for repeated queries. +--- + +## Role + +Debug Nix interactively. Prefer one `nix repl` session with repeated probes over many separate `nix eval` calls. + +## Workflow + +1. Start from the narrowest useful target. +2. Load the flake or expression into `nix repl`. +3. Inspect attribute names before drilling into deeper paths. +4. Evaluate subexpressions incrementally to isolate the failing edge. +5. Report the exact attr path, value, or error that explains the behavior. + +## Core Commands + +Load the current flake: + +```bash +nix repl +:lf . +``` + +Load a specific Nix file when flake outputs are not the target: + +```bash +nix repl ./path/to/file.nix +``` + +Inspect available top-level attrs before guessing paths: + +```nix +builtins.attrNames inputs +builtins.attrNames packages +builtins.attrNames nixosConfigurations +builtins.attrNames homeConfigurations +``` + +Inspect a concrete value: + +```nix +nixosConfigurations..config.networking.hostName +packages.${builtins.currentSystem}.default +``` + +Pretty-print larger values: + +```nix +:p nixosConfigurations..config.environment.systemPackages +``` + +## Debug Patterns + +Missing attribute: + +1. Check each parent attrset with `builtins.attrNames`. +2. Walk down one segment at a time instead of evaluating the full path immediately. + +Unexpected option value: + +1. Inspect `config.` for the final value. +2. Inspect `options.` when type or declaration details matter. + +Example: + +```nix +nixosConfigurations..config.services.pipewire.enable +nixosConfigurations..options.services.pipewire.enable.type.name +``` + +Derivation or package debugging: + +```nix +packages.${builtins.currentSystem}.foo +:p packages.${builtins.currentSystem}.foo.drvAttrs +``` + +Input or flake output debugging: + +```nix +builtins.attrNames inputs +inputs.nixpkgs.outPath +builtins.attrNames overlays +``` + +## Heuristics + +- Prefer `nix repl` over `nix eval` for repeated inspection. +- Inspect attr names before assuming an output shape. +- Keep expressions small while narrowing down an error. +- Reuse the same REPL session until the failing boundary is clear. +- If a path contains special characters, use quoted attrs such as `foo."bar-baz"`. + +If a module value is surprising, inspect the realized config first and only inspect `options` when declaration metadata is needed. + +## Restrictions + +- Do not default to `nix eval` for interactive debugging. +- Do not brute-force deep attr paths without checking parent attrsets first. +- Do not guess host or user keys when `builtins.attrNames` can confirm them. diff --git a/modules/programs/tui/opencode/skills/nix/index.md b/modules/programs/tui/opencode/skills/nix/index.md new file mode 100644 index 0000000..ab79bb0 --- /dev/null +++ b/modules/programs/tui/opencode/skills/nix/index.md @@ -0,0 +1,103 @@ +--- +name: nix-index +description: Use this skill to locate which Nix package provides a command or file with `nix-locate` and the existing local database. Do not use it to build, refresh, inspect, or manage the `nix-index` database. +--- + +# Nix Index + +Use this skill to map files to packages. + +## Core Commands + +Find the package that provides a command: + +```bash +nix-locate --top-level --whole-name --type x --type s '/bin/rg' +``` + +Find the package that provides any file path: + +```bash +nix-locate --top-level --whole-name '/share/applications/firefox.desktop' +nix-locate --top-level --whole-name '/lib/libssl.so' +``` + +Search more loosely when the exact path is unknown: + +```bash +nix-locate --top-level --type x 'bin/(rg|ripgrep)' +nix-locate --top-level 'python.*/site-packages/pandas' +``` + +Inspect all outputs instead of only top-level packages when necessary: + +```bash +nix-locate --whole-name '/bin/hello' +``` + +## Command-Not-Found Workflow + +If the user asks which package provides a missing command, prefer `nix-locate` over generic `nix search`. + +For a missing executable `foo`, try: + +```bash +nix-locate --top-level --whole-name --type x --type s "/bin/foo" +``` + +If nothing matches, broaden the query: + +```bash +nix-locate --top-level --type x 'bin/foo' +nix-locate --top-level --type x 'bin/.+foo.+' +``` + +## Restrictions + +- Do not run `nix-index`. +- Do not explain how to build, refresh, delete, or inspect the database. +- Do not tell the user to manage cache paths. +- If lookup fails, broaden the `nix-locate` query or fall back to `nix-search-tv`. + +## Result Handling + +- Prefer `--top-level` when the user wants an installable package attribute. +- Prefer `--whole-name` when the full file path is known. +- Add `--type x --type s` for executable lookup. +- Treat multiple matches as normal; report the most likely package first, then mention close alternatives. +- If the user wants package metadata, follow up with `nix-search-tv preview --offline --indexes nixpkgs `. +- If `nix-locate` does not find a match, stay in query mode rather than switching to database maintenance steps. + +## Common Patterns + +Resolve a binary name to a package: + +```bash +nix-locate --top-level --whole-name --type x --type s '/bin/git' +``` + +Resolve a desktop file to a package: + +```bash +nix-locate --top-level --whole-name '/share/applications/org.gnome.Calculator.desktop' +``` + +Resolve a shared library to a package: + +```bash +nix-locate --top-level --whole-name '/lib/libzstd.so' +``` + +## Troubleshooting + +No results: + +- Remove `--whole-name` and search with a broader regex. +- Remove `--top-level` if the file may live in a split output. +- Fall back to `nix-search-tv` if a broader `nix-locate` query still does not help. + +Too many results: + +- Add `--whole-name`. +- Restrict the path, such as `/bin/foo` instead of `foo`. +- Add `--type` filters. diff --git a/modules/programs/tui/opencode/skills/nix/search.md b/modules/programs/tui/opencode/skills/nix/search.md new file mode 100644 index 0000000..95be4ca --- /dev/null +++ b/modules/programs/tui/opencode/skills/nix/search.md @@ -0,0 +1,146 @@ +--- +name: nix-search +description: Use this skill to search NixOS packages and options with `nix-search-tv`. Query package details, source locations, and configured NixOS, Home Manager, and flake option indexes. +--- + +# Nix Search + +This skill helps you search NixOS packages and options using `nix-search-tv`, a tool that indexes packages and options from builtin upstream indexes plus this repo's filtered custom option sources. + +## Available Sources + +Common builtins: + +- `nixpkgs` for packages. +- `home-manager` for Home Manager options. +- `nixos` for NixOS options. +- `nur` for NUR packages. +- `noogle` for function and library docs. + +Configured custom option sources: + +- `disko` for Disko NixOS options. +- `home-manager-nixos` for the Home Manager NixOS module. +- `lanzaboote` for Lanzaboote NixOS options. +- `nix-flatpak` for the nix-flatpak Home Manager module. +- `nix-index-database` for the nix-index-database Home Manager module. +- `nixcord` for nixcord options. +- `noctalia` for the Noctalia Home Manager module. +- `nvf` for NVF options. +- `overzicht` for the Overzicht Home Manager module. +- `sops-nix` for SOPS NixOS options. +- `sops-nix-home-manager` for SOPS Home Manager options. +- `spicetify-nix` for Spicetify Home Manager options. +- `stylix` for Stylix NixOS options. +- `stylix-home-manager` for Stylix Home Manager options. +- `zen-browser` for the Zen Browser Home Manager module. + +The repo wires builtin upstream indexes through `settings.indexes` and the custom sources above through `settings.experimental.options_file`. +They are still selected with the same `--indexes ` CLI flag. + +## Commands + +### 1. List Available Packages or Options + +Use `print` to list entries from one or more builtin or custom sources: + +```bash +nix-search-tv print --indexes nixpkgs +nix-search-tv print --indexes nixos +nix-search-tv print --indexes nixpkgs,home-manager +nix-search-tv print --indexes stylix,stylix-home-manager +``` + +Use cached data without refetching: + +```bash +nix-search-tv print --indexes nixpkgs +``` + +Search through package or option names: + +```bash +nix-search-tv print --indexes nixpkgs | grep -i firefox +nix-search-tv print --indexes noctalia | grep -i panel +``` + +The first run downloads and indexes data, so it can take a few minutes. + +### 2. Get Package or Option Details + +Use `preview` to inspect a package or option from a specific source: + +```bash +nix-search-tv preview --indexes nixpkgs firefox +nix-search-tv preview --indexes nixos boot.loader.systemd-boot.enable +nix-search-tv preview --indexes stylix-home-manager stylix.targets.gtk.enable +``` + +Use JSON output for automation: + +```bash +nix-search-tv preview --indexes nixpkgs --json firefox +``` + +The `--json` flag returns structured output and includes the selected key in `_key`. + +### 3. Get Source Code Location + +Use `source` to get the source location for a package or option: + +```bash +nix-search-tv source --indexes nixpkgs firefox +nix-search-tv source --indexes noctalia programs.noctalia-shell.settings.bar +``` + +### 4. Get Homepage + +Use `homepage` to get a package homepage URL: + +```bash +nix-search-tv homepage --indexes nixpkgs firefox +``` + +## Tips + +- First run downloads and indexes data. Later runs reuse the cache. +- `print` prefixes entries with the source name, for example `stylix/ stylix.image`. +- Some custom sources intentionally share namespaces, such as `stylix` and `stylix-home-manager`, so duplicate-looking option names can appear across sources. +- Set `NO_COLOR=1` to disable ANSI colors in command output. +- Use `--json` with `preview` when the result will be parsed by tools. + +## Agent Usage Patterns + +### Efficient cached lookup + +```bash +nix-search-tv preview --indexes nixpkgs --json firefox | jq -r '.description' +``` + +### Search and get source in one pipeline + +```bash +PACKAGE=$(nix-search-tv print --indexes nixpkgs | grep -i neovim | head -1) +nix-search-tv source --indexes nixpkgs "$PACKAGE" +``` + +### Inspect configured flake indexes + +```bash +nix-search-tv print --indexes stylix +nix-search-tv print --indexes stylix-home-manager +nix-search-tv preview --indexes zen-browser enable +``` + +## Troubleshooting + +`cache.txt` not found: run `nix-search-tv print --indexes ` to trigger initial indexing. + +Stale data: remove the relevant cache directory and rerun a query: + +```bash +rm -rf ~/.cache/nix-search-tv/ +nix-search-tv print --indexes +``` + +Multiple sources causing ambiguity: use an explicit `--indexes` list. diff --git a/modules/programs/tui/opencode/skills/nvf-config.md b/modules/programs/tui/opencode/skills/nvf-config.md new file mode 100644 index 0000000..610b70b --- /dev/null +++ b/modules/programs/tui/opencode/skills/nvf-config.md @@ -0,0 +1,327 @@ +--- +name: nvf-config +description: Generate, edit, or review Neovim configuration in NVF and Nix form using verified docs instead of guessed APIs. Use for `programs.nvf.settings.*`, `setupOpts`, `keymaps`, language modules, plugin options, `mkLuaInline`, `luaConfigPre` snippets, and translating upstream plugin docs into NVF modules. +--- + +# NVF Config Skill + +Work NVF-first. + +Treat the repo as an NVF and Nix configuration, not a plain Lua config. + +When the user asks for a Neovim change, prefer editing `programs.nvf.settings.*` and nearby Nix modules. Only fall back to raw Lua when NVF does not expose the needed option. + +## Core rule + +Do not guess NVF options, Neovim APIs, or plugin setup keys. + +Prefer a small verified Nix snippet over a large plausible Lua snippet. + +## Source lookup order + +Before writing or changing config, use this order: + +1. Existing project config +2. Local NVF option docs or repo-specific option search tools +3. NVF manual and NVF options reference +4. NVF module source for the exact option path +5. Installed Neovim help docs for built-in APIs +6. Plugin repository docs for plugin-specific setup +7. Context7 MCP, if available +8. Web or GitHub fallback +9. Inference only when clearly marked as uncertain + +Do not skip directly to writing code from memory. + +## Existing config first + +Inspect nearby config before adding new code. + +Look for: + +* NVF option layout +* existing helper functions +* keymap conventions +* autocmd conventions +* custom Nix helpers +* `mkLuaInline` usage +* `luaConfigPre` usage +* language module layout +* repo-specific option search tools + +Do not introduce a new style unless necessary. + +In this repo, expect patterns like: + +* `programs.nvf.settings.vim = { ... };` +* focused modules under `modules/home/tui/neovim/` +* `keymaps = [ { key = ...; mode = ...; action = ...; } ];` +* `setupOpts` for plugin option tables +* `lib.generators.mkLuaInline` for inline Lua functions inside Nix attrsets +* `neovim.luaConfigPreSnippets` for raw Lua that must run before plugin setup + +## NVF docs first + +Prefer local option search over web examples. + +If the repo exposes NVF docs through a local search tool, use that before reading upstream docs. + +Good targets to confirm: + +* exact `programs.nvf.settings.*` path +* option type +* whether the option is a boolean, attrset, list, or raw Lua string +* whether the plugin already has an NVF wrapper + +If a local generated options source exists for NVF, it wins over memory. + +## Use NVF docs and module source + +For NVF-specific configuration: + +1. Find the exact NVF option path. +2. Confirm whether the plugin or feature is already exposed by NVF. +3. Check the option type and example shape. +4. Read the module source when the docs are unclear. +5. Only inject raw Lua for the parts NVF does not model. + +Do not assume lazy.nvim examples map directly onto NVF options. + +## Use local Neovim docs for core APIs + +For built-in Neovim APIs, prefer the installed docs over memory. + +Useful help pages: + +```vim +:help lua +:help api +:help deprecated +:help autocmd-events +:help vim.keymap.set() +:help vim.diagnostic +:help lsp +:help treesitter +``` + +If local docs disagree with memory, local docs win. + +## Use plugin docs for plugin APIs + +For plugin-specific config, identify the exact plugin repository and only do this after checking whether NVF already exposes the needed option. + +Prefer sources in this order: + +1. `doc/*.txt` +2. README setup section +3. examples shipped in the repo +4. plugin Lua source, especially `setup()` defaults +5. issues or discussions only when official docs are missing + +Do not invent setup keys. + +If a field is not in the README, help docs, defaults table, or source code, do not use it. + +## NVF rules + +When working with NVF: + +* Prefer NVF options over raw Lua whenever an option exists. +* Use the exact `programs.nvf.settings.*` path used by the repo. +* Match surrounding Nix style before inventing a new layout. +* Keep plugin configuration in `setupOpts` when the module already exposes a plugin block. +* Use `mkLuaInline` for Lua functions embedded inside Nix attrsets. +* Use `neovim.luaConfigPreSnippets` for early startup logic, autocmds, globals, or larger raw Lua blocks that do not belong in a single option value. +* Preserve list-vs-attrset style already used by the repo. +* Keep Lua valid inside Nix strings and escape `${...}` correctly. + +Use raw Lua only when at least one of these is true: + +* NVF has no option for the feature. +* The option exists but does not expose the needed callback or function body. +* The repo already handles that category of behavior through `luaConfigPreSnippets`. + +## Repo patterns + +### Simple NVF options + +```nix +_: { + programs.nvf.settings.vim = { + visuals.nvim-web-devicons.enable = true; + }; +} +``` + +### Keymaps + +Prefer NVF keymaps over raw `vim.keymap.set()` when the mapping is static. + +```nix +_: { + programs.nvf.settings.vim.keymaps = [ + { + key = "w"; + mode = "n"; + action = "w"; + } + ]; +} +``` + +### Plugin setup tables + +Prefer `setupOpts` when NVF exposes the plugin and the config is a table. + +```nix +{lib, ...}: let + inherit (lib.generators) mkLuaInline; +in { + programs.nvf.settings.vim.statusline.lualine.setupOpts = { + sections.lualine_c = [ + { + "@1" = "filename"; + fmt = mkLuaInline '' + function(str) + return vim.fs.basename(str) + end + ''; + } + ]; + }; +} +``` + +### Early raw Lua snippets + +```nix +_: { + neovim.luaConfigPreSnippets = [ + '' + vim.api.nvim_create_autocmd("FileType", { + pattern = "snacks_dashboard", + callback = function() + vim.opt_local.scrolloff = 0 + end, + }) + '' + ]; +} +``` + +## Nix rules + +For Nix snippets: + +* Preserve attribute naming style. +* Prefer explicit nested attributes when matching surrounding code. +* Use `let` bindings only when they actually simplify the module. +* Preserve helper imports already used nearby, such as `mkLuaInline`. +* Preserve `keep-sorted` comments and existing grouping structure. +* Keep comments short, local, and only for non-obvious intent. + +## Plugin API rules + +When configuring a plugin: + +1. Identify the exact repository. +2. Check whether NVF already exposes the plugin or feature. +3. Check whether the plugin has a help file. +4. Find its current `setup()` signature. +5. Check default options if present. +6. Only use documented or source-confirmed fields. +7. Translate the result into NVF and Nix instead of copying upstream Lua directly. + +Bad: + +```nix +_: { + programs.nvf.settings.vim.somePlugin.setupOpts = { + random_option = true; + }; +} +``` + +Better: + +```text +Unverified: I could not find this NVF option or plugin field in the docs/source. +``` + +Do not make up options because they sound consistent with other plugins. + +## Output format + +When giving code, use this format: + +```text +Assumption: NVF via Home Manager or NixOS module, Nix config, Neovim >= target version if relevant. + +Put this in: modules/home/tui/neovim/.nix +``` + +Then provide the code: + +```nix +_: { + programs.nvf.settings.vim = { + # verified NVF options only + }; +} +``` + +Then provide validation: + +```text +Checked against: +- local repo config +- local NVF docs / NVF manual / NVF module source +- plugin docs or Neovim help when needed + +Validate with: +home-manager build --flake .# +``` + +If something is uncertain, say exactly what is uncertain. + +## Validation commands + +For Nix syntax and evaluation, prefer the repo's normal build command. + +```sh +nix flake check +home-manager build --flake .# +nixos-rebuild build --flake .# +``` + +Use the repo's actual target names when known. + +## Hard stop conditions + +Stop and state uncertainty when: + +* the correct NVF option path cannot be found +* NVF appears to expose the feature, but the option shape is unclear +* the plugin API cannot be found +* the plugin version is unknown and docs differ between versions +* a config field appears only in old dotfiles +* the requested option is not in docs or source +* the snippet depends on a plugin that is not installed +* the change would require restructuring unrelated config + +Do not hide uncertainty behind plausible Nix or Lua. + +## Final behavior + +When asked for an NVF snippet: + +1. Inspect existing style. +2. Look for an NVF option before writing raw Lua. +3. Verify APIs and option paths using docs or source. +4. Generate the smallest working Nix snippet. +5. Use raw Lua only where NVF cannot express the change. +6. State assumptions. +7. Provide a validation command. +8. Mention any uncertainty. + +Accuracy is more important than completeness. diff --git a/modules/programs/tui/opencode/skills/uncodixfy.md b/modules/programs/tui/opencode/skills/uncodixfy.md new file mode 100644 index 0000000..b88888c --- /dev/null +++ b/modules/programs/tui/opencode/skills/uncodixfy.md @@ -0,0 +1,157 @@ +--- +name: uncodixfy +description: Use this skill whenever generating frontend UI code. Avoid generic AI UI patterns and enforce clean, human-designed HTML, CSS, React, Vue, or Svelte interfaces. +license: MIT +--- + +# Uncodixify + +This document exists to teach you how to act as non-Codex as possible when building UI. + +Codex UI is the default AI aesthetic: soft gradients, floating panels, eyebrow labels, decorative copy, hero sections in dashboards, oversized rounded corners, transform animations, dramatic shadows, and layouts that try too hard to look premium. It's the visual language that screams "an AI made this" because it follows the path of least resistance. + +This file is your guide to break that pattern. Everything listed below is what Codex UI does by default. Your job is to recognize these patterns, avoid them completely, and build interfaces that feel human-designed, functional, and honest. + +When you read this document, you're learning what NOT to do. The banned patterns are your red flags. The normal implementations are your blueprint. Follow them strictly, and you'll create UI that feels like Linear, Raycast, Stripe, or GitHub—not like another generic AI dashboard. + +This is how you Uncodixify. + +## Keep It Normal (Uncodexy-UI Standard) + +- Sidebars: normal (240-260px fixed width, solid background, simple border-right, no floating shells, no rounded outer corners) +- Headers: normal (simple text, no eyebrows, no uppercase labels, no gradient text, just h1/h2 with proper hierarchy) +- Sections: normal (standard padding 20-30px, no hero blocks inside dashboards, no decorative copy) +- Navigation: normal (simple links, subtle hover states, no transform animations, no badges unless functional) +- Buttons: normal (solid fills or simple borders, 8-10px radius max, no pill shapes, no gradient backgrounds) +- Cards: normal (simple containers, 8-12px radius max, subtle borders, no shadows over 8px blur, no floating effect) +- Forms: normal (standard inputs, clear labels above fields, no fancy floating labels, simple focus states) +- Inputs: normal (solid borders, simple focus ring, no animated underlines, no morphing shapes) +- Modals: normal (centered overlay, simple backdrop, no slide-in animations, straightforward close button) +- Dropdowns: normal (simple list, subtle shadow, no fancy animations, clear selected state) +- Tables: normal (clean rows, simple borders, subtle hover, no zebra stripes unless needed, left-aligned text) +- Lists: normal (simple items, consistent spacing, no decorative bullets, clear hierarchy) +- Tabs: normal (simple underline or border indicator, no pill backgrounds, no sliding animations) +- Badges: normal (small text, simple border or background, 6-8px radius, no glows, only when needed) +- Avatars: normal (simple circle or rounded square, no decorative borders, no status rings unless functional) +- Icons: normal (simple shapes, consistent size 16-20px, no decorative icon backgrounds, monochrome or subtle color) +- Typography: normal (system fonts or simple sans-serif, clear hierarchy, no mixed serif/sans combos, readable sizes 14-16px body) +- Spacing: normal (consistent scale 4/8/12/16/24/32px, no random gaps, no excessive padding) +- Borders: normal (1px solid, subtle colors, no thick decorative borders, no gradient borders) +- Shadows: normal (subtle 0 2px 8px rgba(0,0,0,0.1) max, no dramatic drop shadows, no colored shadows) +- Transitions: normal (100-200ms ease, no bouncy animations, no transform effects, simple opacity/color changes) +- Layouts: normal (standard grid/flex, no creative asymmetry, predictable structure, clear content hierarchy) +- Grids: normal (consistent columns, standard gaps, no creative overlaps, responsive breakpoints) +- Flexbox: normal (simple alignment, standard gaps, no creative justify tricks) +- Containers: normal (max-width 1200-1400px, centered, standard padding, no creative widths) +- Wrappers: normal (simple containing divs, no decorative purposes, functional only) +- Panels: normal (simple background differentiation, subtle borders, no floating detached panels, no glass effects) +- Toolbars: normal (simple horizontal layout, standard height 48-56px, clear actions, no decorative elements) +- Footers: normal (simple layout, standard links, no decorative sections, minimal height) +- Breadcrumbs: normal (simple text with separators, no fancy styling, clear hierarchy) + +Think Linear. Think Raycast. Think Stripe. Think GitHub. They don't try to grab attention. They just work. Stop playing hard to get. Make normal UI. + +- A landing page needs its sections. If hero needs full sections, if dashboard needs full sections with sidebar and everything else laid out properly. DO NOT invent a new layout. +- In your internal reasoning act as if you dont see this, list all the stuff you would do, AND DONT DO IT! +- Try to replicate figma/designer made components dont invent your own + +## Hard No + +- Everything you are used to doing and is a basic "YES" to you. +- No oversized rounded corners. +- No pill overload. +- No floating glassmorphism shells as the default visual language. +- No soft corporate gradients used to fake taste. +- No generic dark SaaS UI composition. +- No decorative sidebar blobs. +- No "control room" cosplay unless explicitly requested. +- No serif headline + system sans fallback combo as a shortcut to "premium." +- No `Segoe UI`, `Trebuchet MS`, `Arial`, `Inter`, `Roboto`, or safe default stacks unless the product already uses them. +- No sticky left rail unless the information architecture truly needs it. +- No metric-card grid as the first instinct. +- No fake charts that exist only to fill space. +- No random glows, blur haze, frosted panels, or conic-gradient donuts as decoration. +- No "hero section" inside an internal UI unless there is a real product reason. +- No alignment that creates dead space just to look expensive. +- No overpadded layouts. +- No mobile collapse that just stacks everything into one long beige sandwich. +- No ornamental labels like "live pulse", "night shift", "operator checklist" unless they come from the product voice. +- No generic startup copy. +- No style decisions made because they are easy to generate. +- No Headlines of any sort + +```html +
+ Team Command +

One place to track what matters today.

+

+ The layout stays strict and readable: live project health, + team activity, and near-term priorities without the usual + dashboard filler. +

+
+``` + +This is not allowed. + +- `` headers are NOT allowed +- Big no to rounded `span`s +- Colors going towards blue — **NOPE, bad.** Dark muted colors are best. +- Anything in the structure of this card is a **BIG no**. + +```html +
+ Focus + + Keep updates brief, blockers visible, and next actions easy to spot. + +
+``` + +This one is **THE BIGGEST NO**. + +## Specifically Banned (Based on Mistakes) + +- Border radii in the 20px to 32px range across everything ( uses 12px everywhere - too much) +- Repeating the same rounded rectangle on sidebar, cards, buttons, and panels +- Sidebar width around 280px with a brand block on top and nav links below (: 248px with brand block) +- Floating detached sidebar with rounded outer shell +- Canvas chart placed in a glass card with no product-specific reason +- Donut chart paired with hand-wavy percentages +- UI cards using glows instead of hierarchy +- Mixed alignment logic where some content hugs the left edge and some content floats in center-ish blocks +- Overuse of muted gray-blue text that weakens contrast and clarity +- "Premium dark mode" that really means blue-black gradients plus cyan accents ( has radial gradients in background) +- UI typography that feels like a template instead of a brand +- Eyebrow labels (: "MARCH SNAPSHOT" uppercase with letter-spacing) +- Hero sections inside dashboards ( has full hero-strip with decorative copy) +- Decorative copy like "Operational clarity without the clutter" as page headers +- Section notes and mini-notes everywhere explaining what the UI does +- Transform animations on hover (: translateX(2px) on nav links) +- Dramatic box shadows (: 0 24px 60px rgba(0,0,0,0.35)) +- Status indicators with ::before pseudo-elements creating colored dots +- Muted labels with uppercase + letter-spacing ( overuses this pattern) +- Pipeline bars with gradient fills (: linear-gradient(90deg, var(--primary), #4fe0c0)) +- KPI cards in a grid as the default dashboard layout ( has 3-column kpi-grid) +- "Team focus" or "Recent activity" panels with decorative internal copy +- Tables with tag badges for every status ( overuses .tag class) +- Workspace blocks in sidebar with call-to-action buttons +- Brand marks with gradient backgrounds (: linear-gradient(135deg, #2a2a2a, #171717)) +- Nav badges showing counts or "Live" status ( has nav-badge class) +- Quota/usage panels with progress bars ( has three quota sections) +- Footer lines with meta information (: "Northstar dashboard • dark mode • single-file HTML") +- Trend indicators with colored text (: trend-up, trend-flat classes) +- Rail panels on the right side with "Today" schedule ( has full right rail) +- Multiple nested panel types (panel, panel-2, rail-panel, table-panel) + +## Rule + +If a UI choice feels like a default AI UI move, ban it and pick the harder, cleaner option. + +- Colors should stay calm, not fight. + +- You are bad at picking colors follow this priority order when selecting colors: + +1. **Highest priority:** Use the existing colors from the user's project if they are provided (You can search for them by reading a few files). +2. If the project does not provide a palette, **get inspired from one of the predefined palettes below**. +3. Do **not invent random color combinations** unless explicitly requested. diff --git a/modules/programs/tui/wiremix.nix b/modules/programs/tui/wiremix.nix index e3a45e1..b81bbab 100644 --- a/modules/programs/tui/wiremix.nix +++ b/modules/programs/tui/wiremix.nix @@ -1,4 +1,4 @@ -{ +{self, ...}: { flake.modules.homeManager.wiremix = { # keep-sorted start config, @@ -11,6 +11,8 @@ pkg = pkgs.wiremix; in { + imports = [self.modules.homeManager.xdgTerminal]; + # keep-sorted start block=yes newline_separated=yes home.packages = [pkg]; diff --git a/modules/programs/tui/yazi/plugins/recycle-bin.nix b/modules/programs/tui/yazi/plugins/recycle-bin.nix index bff2d37..a93ad90 100644 --- a/modules/programs/tui/yazi/plugins/recycle-bin.nix +++ b/modules/programs/tui/yazi/plugins/recycle-bin.nix @@ -11,9 +11,9 @@ } ]; - plugins.recycle-bin = pkgs.yaziPlugins.recycle-bin; - pluginSetupOpts.recycle-bin = {}; + + plugins.recycle-bin = pkgs.yaziPlugins.recycle-bin; # keep-sorted end }; }; diff --git a/modules/programs/tui/yazi/plugins/relative-motions.nix b/modules/programs/tui/yazi/plugins/relative-motions.nix index d61afeb..077c13c 100644 --- a/modules/programs/tui/yazi/plugins/relative-motions.nix +++ b/modules/programs/tui/yazi/plugins/relative-motions.nix @@ -61,9 +61,9 @@ # keep-sorted end ]; - plugins.relative-motions = pkgs.yaziPlugins.relative-motions; - pluginSetupOpts.relative-motions.show_numbers = "relative"; + + plugins.relative-motions = pkgs.yaziPlugins.relative-motions; # keep-sorted end }; }; diff --git a/modules/services/envfs.nix b/modules/services/envfs.nix index 912257b..f293788 100644 --- a/modules/services/envfs.nix +++ b/modules/services/envfs.nix @@ -1,5 +1,26 @@ -{ +{self, ...}: { + flake.overlays.envfs = _final: prev: let + src = prev.fetchFromGitHub { + owner = "Mic92"; + repo = "envfs"; + rev = "1.2.0"; + hash = "sha256-hj/6zS9ebF0IDqgc1Dne59nWx80nk6jn2gj8BzQUFIQ="; + }; + in { + # Pin envfs until nixpkgs picks up the upstream mount helper fix. + envfs = prev.envfs.overrideAttrs (_oldAttrs: { + version = "1.2.0"; + inherit src; + cargoDeps = prev.rustPlatform.fetchCargoVendor { + inherit src; + hash = "sha256-dz3gpE464jnmSDsAsmJHcxUsEKeUURNoUjgGU2214Xg="; + }; + }); + }; + flake.modules.nixos.envfs = { + nixpkgs.overlays = [self.overlays.envfs]; + # Provide fhs-style paths for compatibility with legacy applications. services.envfs.enable = true; }; diff --git a/todo-stats.sh b/todo-stats.sh index f4bc951..46eeb24 100755 --- a/todo-stats.sh +++ b/todo-stats.sh @@ -10,21 +10,100 @@ if [ ! -f "$todo_file" ]; then fi awk ' - /^- \[ \]/ { todo++ } - /^- \[[xX]\]/ { done++ } - /^- \[-\]/ { removed++ } + function pct(part, whole) { + return whole == 0 ? 0 : part * 100 / whole + } + + function bar(part, whole, width, filled, i, out) { + filled = whole == 0 ? 0 : int((part * width / whole) + 0.5) + out = "" + + for (i = 1; i <= width; i++) { + out = out (i <= filled ? "#" : "-") + } + + return out + } + + function print_stat(label, count, whole) { + printf "%-11s %4d %6.1f%% [%s]\n", label ":", count, pct(count, whole), bar(count, whole, 28) + } + + function section_total(i) { + return section_todo[i] + section_done[i] + section_removed[i] + } + + function trim_heading(line) { + sub(/^#+[[:space:]]+/, "", line) + return line + } + + function fit(text, width) { + return length(text) <= width ? text : substr(text, 1, width - 3) "..." + } + + /^##[[:space:]]+/ { + section = trim_heading($0) + if (!(section in seen)) { + seen[section] = 1 + sections[++section_count] = section + } + next + } + + /^- \[ \]/ { + todo++ + section_todo[section]++ + } + + /^- \[[xX]\]/ { + done++ + section_done[section]++ + } + + /^- \[-\]/ { + removed++ + section_removed[section]++ + } + END { total = todo + done + removed + active = todo + done if (total == 0) { print "No todo entries found." exit } - printf "Todo summary for %s\n", FILENAME - printf "Still to do: %d (%.1f%%)\n", todo, todo * 100 / total - printf "Done: %d (%.1f%%)\n", done, done * 100 / total - printf "Removed: %d (%.1f%%)\n", removed, removed * 100 / total - printf "Total: %d\n", total + print "==============================================" + printf "Todo summary: %s\n", FILENAME + print "==============================================" + printf "Progress %6.1f%% [%s]\n", pct(done, active), bar(done, active, 28) + printf "Remaining %6.1f%% [%s]\n", pct(todo, active), bar(todo, active, 28) + print "----------------------------------------------" + print_stat("To do", todo, total) + print_stat("Done", done, total) + print_stat("Removed", removed, total) + print "----------------------------------------------" + printf "Active: %4d\n", active + printf "Total: %4d\n", total + + if (section_count > 0) { + print "" + print "Section progress" + print "----------------------------------------------" + + for (i = 1; i <= section_count; i++) { + name = sections[i] + subtotal = section_total(name) + + if (subtotal == 0) { + continue + } + + section_active = section_todo[name] + section_done[name] + printf "%-28s %6.1f%% [%s] %3d/%-3d\n", fit(name, 28), pct(section_done[name], section_active), bar(section_done[name], section_active, 12), section_done[name], section_active + } + } } ' "$todo_file" diff --git a/todo.md b/todo.md index 11256c8..57f7bea 100644 --- a/todo.md +++ b/todo.md @@ -31,7 +31,7 @@ ## keys/users/ -- [x] adam0.asc (moved to keys/) +- [x] adam0.asc ## libs/ @@ -120,15 +120,15 @@ ## modules/home/cli/starship/ -- [ ] build.nix -- [ ] container.nix -- [ ] default.nix -- [ ] disabled.nix -- [ ] format.nix -- [ ] general.nix -- [ ] git.nix -- [ ] languages.nix -- [ ] runtimes.nix +- [x] build.nix +- [x] container.nix +- [x] default.nix +- [x] disabled.nix +- [x] format.nix +- [x] general.nix +- [x] git.nix +- [x] languages.nix +- [x] runtimes.nix ## modules/home/desktop/ @@ -168,7 +168,7 @@ - [ ] sessionmenu.nix - [ ] systemmonitor.nix - [ ] ui.nix -- [ ] wallpaper.nix +x [ ] wallpaper.nix ## modules/home/desktop/noctalia/patches/ @@ -194,23 +194,23 @@ ## modules/home/desktop/xdg/ -- [ ] applications.nix -- [ ] autostart.nix -- [ ] cleanup.nix -- [ ] default.nix -- [ ] portal.nix -- [ ] terminal.nix +- [x] applications.nix +- [x] autostart.nix (moved into the specific application modules) +- [x] cleanup.nix +- [x] default.nix (split into multiple files) +- [x] portal.nix +- [x] terminal.nix ## modules/home/desktop/xdg/applications/ -- [ ] audio.nix -- [ ] code.nix -- [ ] document-viewers.nix -- [ ] files.nix -- [ ] image-editors.nix -- [ ] image-viewers.nix -- [ ] protocols.nix -- [ ] video.nix +- [x] audio.nix +- [x] code.nix +- [x] document-viewers.nix +- [x] files.nix +- [x] image-editors.nix +- [x] image-viewers.nix +- [x] protocols.nix +- [x] video.nix ## modules/home/gui/ @@ -225,21 +225,21 @@ ## modules/home/gui/discord/ -- [ ] default.nix +- [x] default.nix ## modules/home/gui/discord/plugins/ -- [ ] communication.nix -- [ ] core.nix -- [ ] integrations.nix -- [ ] interface.nix -- [ ] media.nix -- [ ] social.nix +- [x] communication.nix +- [x] core.nix +- [x] integrations.nix +- [x] interface.nix +- [x] media.nix +- [x] social.nix ## modules/home/gui/discord/themes/ -- [ ] snippets.css -- [ ] system24.css +- [x] snippets.css +- [x] system24.css ## modules/home/gui/ghostty/ @@ -280,7 +280,7 @@ - [ ] other.nix - [ ] oxicord.nix - [ ] spotify-player.nix -- [ ] wiremix.nix (need to import terminal-exec before finished) +- [x] wiremix.nix ## modules/home/tui/neovim/ @@ -327,34 +327,34 @@ ## modules/home/tui/opencode/ -- [ ] default.nix -- [ ] env.nix -- [ ] formatter.nix -- [ ] instructions.md -- [ ] lsp.nix -- [ ] mcp-wrappers.nix -- [ ] plugins.nix -- [ ] settings.nix +- [x] default.nix +- [x] env.nix +- [x] formatter.nix +- [x] instructions.md +- [x] lsp.nix +- [x] mcp-wrappers.nix +- [x] plugins.nix +- [x] settings.nix ## modules/home/tui/opencode/skills/ -- [ ] create-readme.md -- [ ] default.nix -- [ ] find-skills.md -- [ ] nvf-config.md -- [ ] uncodixfy.md +- [x] create-readme.md +- [x] default.nix +- [x] find-skills.md +- [x] nvf-config.md +- [x] uncodixfy.md ## modules/home/tui/opencode/skills/lazy-mcp/ -- [ ] context7.md -- [ ] github.md -- [ ] grep-app.md +- [x] context7.md +- [x] github.md +- [x] grep-app.md ## modules/home/tui/opencode/skills/nix/ -- [ ] debug.md -- [ ] index.md -- [ ] search.md +- [x] debug.md +- [x] index.md +- [x] search.md ## modules/home/tui/television/ @@ -448,7 +448,7 @@ - [ ] stylix.nix - [ ] tablet.nix - [x] tuigreet.nix -- [ ] xdg-portal.nix +- [x] xdg-portal.nix ## modules/system/gui/ @@ -479,27 +479,27 @@ - [x] scx.nix - [-] ssh.nix - [x] timesyncd.nix -- [ ] timezone.nix +- [x] timezone.nix (moved to modules/profiles/common/timezone.nix) - [x] tlp.nix - [x] zram.nix ## overlays/ -- [ ] _inputs.nix -- [ ] default.nix -- [ ] envfs.nix -- [ ] external-pkgs.nix +- [x] _inputs.nix +- [-] default.nix +- [x] envfs.nix (moved into the envfs module) +- [x] external-pkgs.nix - [ ] hyprland-plugins.nix -- [ ] pkgs.nix +- [-] pkgs.nix - [ ] superhtml.nix - [-] zaread.nix ## pkgs/ -- [ ] default.nix +- [x] default.nix - [x] lutris.nix -- [ ] nocheatsheet-nvim.nix -- [ ] telescope-all-recent-nvim.nix +- [x] nocheatsheet-nvim.nix +- [x] telescope-all-recent-nvim.nix - [x] zaread.nix ## pkgs/scripts/ -- 2.51.2