diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 0000000..218f9a1 --- /dev/null +++ b/ansible/README.md @@ -0,0 +1,30 @@ +# hypr — Ansible deployment + +Installs the runtime packages this Hyprland config shells out to and links the +config into place. Mirrors the sibling [`quickshell`](https://github.com/quantumfate/quickshell) +role — deploy both for the full desktop. + +## Deploy locally + +```sh +ansible-galaxy collection install -r ansible/requirements.yml +ansible-playbook ansible/playbook.yml --ask-become-pass +``` + +## What it does + +- Installs `hypr_packages` (compositor + `hypr*` ecosystem, terminals, menu, + and the tools binds/scripts call — see `roles/hypr/defaults/main.yml`). +- Symlinks the checkout to `~/.config/hypr` (skipped when you edit in place). +- Seeds the shared Dofus state dir (`$XDG_STATE_HOME/dofus`). + +## Not installed here + +AUR / proprietary / per-host bits the config touches but can't come from the +official repos — install yourself: + +- `ankama-launcher` (Dofus), the NVIDIA driver, `hyprqt6engine` (Qt theming). +- User scripts on `PATH` (`,hyprshot.sh`, `,brightness.sh`, `dofus_swap.py`). + +Set `hypr_install_extra_packages: true` (with an AUR helper configured) to let +the role attempt `hypr_extra_packages`. diff --git a/ansible/playbook.yml b/ansible/playbook.yml new file mode 100644 index 0000000..b4c6053 --- /dev/null +++ b/ansible/playbook.yml @@ -0,0 +1,15 @@ +--- +# Standalone entrypoint — deploy this Hyprland config to the local machine: +# +# ansible-playbook ansible/playbook.yml --ask-become-pass +# +# From a separate controller, import instead of running directly (see README): +# - import_playbook: path/to/hypr/ansible/playbook.yml +# or include just the role: +# roles: [{ role: hypr }] # with ansible/roles on your roles_path +- name: Deploy quantumfate Hyprland config + hosts: localhost + connection: local + gather_facts: true + roles: + - role: hypr diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100644 index 0000000..602afb2 --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,18 @@ +--- +# Collections this role needs (install with: ansible-galaxy collection install -r requirements.yml). +collections: + - name: community.general # pacman module + +# --- Consuming this from a controller ------------------------------------------ +# This repo's role lives at ansible/roles/hypr. To import it into another +# controller/playbook, either add it as a git submodule and put ansible/roles on +# your `roles_path`, or vendor it. Then in your playbook: +# +# - hosts: workstation +# roles: +# - role: hypr +# vars: +# hypr_repo_path: /path/to/checkout +# +# Pairs with the sibling `quickshell` role (the bar); deploy both for the full +# desktop. This mirrors how a Nix flake exposes a module for another to import. diff --git a/ansible/roles/hypr/defaults/main.yml b/ansible/roles/hypr/defaults/main.yml new file mode 100644 index 0000000..b135430 --- /dev/null +++ b/ansible/roles/hypr/defaults/main.yml @@ -0,0 +1,48 @@ +--- +# Path to this repo checkout on the target. Defaults to the repo this role ships +# in; override when importing from a controller that clones it elsewhere. +hypr_repo_path: "{{ playbook_dir }}/.." + +# Where the live config lives. The role symlinks it to hypr_repo_path unless they +# already resolve to the same path (i.e. you edit the config in place). +hypr_config_dir: "{{ ansible_user_dir }}/.config/hypr" + +# Runtime packages (Arch). Set false to manage them yourself. +hypr_install_packages: true +hypr_packages: + # --- compositor + hypr* ecosystem (each has its own .conf in this repo) --- + - hyprland # the compositor + - hypridle # hypridle.conf (idle → lock/dpms) + - hyprlock # hyprlock.conf / hyprlock-laptop.conf + - hyprpaper # hyprpaper.conf (wallpaper) + - hyprsunset # hyprsunset.conf (night light) + - hyprpicker # color-pick bind + - hyprshot # screenshot binds + - xdg-desktop-portal-hyprland # portal backend + - xdg-desktop-portal-gtk # portal (file pickers / settings) + # --- shell / launcher / session --- + - uwsm # session-scoped app launches (autostart qs) + - rofi-wayland # menus + - kitty # terminal + - foot # terminal (alttab preview) + # --- tools invoked from binds / lua / scripts --- + - xdotool # window poke / click-repeat helpers + - brightnessctl # brightness binds + - gamemode # gamemoderun (Dofus launch) + - satty # screenshot annotate + - grim # screenshot capture + - jq # JSON parsing in lua/scripts + - libnotify # notify-send (fallback path) + - procps-ng # pgrep / pkill + - xorg-setxkbmap # setxkbmap dvorak-custom + - inetutils # hostname (per-host config selection) + # The bar itself (`qs -c quantumfate`) is deployed by the sibling quickshell + # repo's role; its package is pulled in there. Listed as a reminder, not here. + +# AUR / proprietary bits the config touches but can't be installed from the +# official repos — install these yourself (paru/yay). Enable to have the role +# attempt them via community.general.pacman with an AUR helper you configure. +hypr_install_extra_packages: false +hypr_extra_packages: + - hyprqt6engine # hyprqt6engine.conf (Qt theming) — AUR + # ankama-launcher (Dofus) is proprietary; install manually. diff --git a/ansible/roles/hypr/meta/main.yml b/ansible/roles/hypr/meta/main.yml new file mode 100644 index 0000000..2c9ee29 --- /dev/null +++ b/ansible/roles/hypr/meta/main.yml @@ -0,0 +1,18 @@ +--- +galaxy_info: + role_name: hypr + namespace: quantumfate + author: quantumfate + description: Deploy the quantumfate Hyprland (Lua) config + its runtime deps. + license: MIT + min_ansible_version: "2.14" + platforms: + - name: Archlinux + versions: + - all + galaxy_tags: + - hyprland + - wayland + - desktop + - dotfiles +dependencies: [] diff --git a/ansible/roles/hypr/tasks/main.yml b/ansible/roles/hypr/tasks/main.yml new file mode 100644 index 0000000..266f87e --- /dev/null +++ b/ansible/roles/hypr/tasks/main.yml @@ -0,0 +1,40 @@ +--- +- name: Install Hyprland runtime packages + become: true + community.general.pacman: + name: "{{ hypr_packages }}" + state: present + when: hypr_install_packages | bool + +- name: Install extra (AUR) packages + become: true + community.general.pacman: + name: "{{ hypr_extra_packages }}" + state: present + when: hypr_install_extra_packages | bool + +- name: Ensure the ~/.config directory exists + ansible.builtin.file: + path: "{{ ansible_user_dir }}/.config" + state: directory + mode: "0755" + +# Deploy the config by symlinking it to the checkout — unless the checkout IS +# the config dir already (editing in place), in which case there's nothing to do. +- name: Symlink the config into place + ansible.builtin.file: + src: "{{ hypr_repo_path | realpath }}" + dest: "{{ hypr_config_dir }}" + state: link + force: true + when: (hypr_repo_path | realpath) != (hypr_config_dir | realpath) + +# Shared Dofus state (team.json) lives in XDG_STATE_HOME and is written by both +# this config (hypr/lib/store.lua) and the quickshell UI. Seed the directory. +- name: Ensure the XDG state directory exists + ansible.builtin.file: + path: >- + {{ (ansible_env.XDG_STATE_HOME | default(ansible_user_dir + '/.local/state')) + + '/dofus' }} + state: directory + mode: "0755" diff --git a/flake.nix b/flake.nix index 2de8bdd..9b70208 100644 --- a/flake.nix +++ b/flake.nix @@ -8,24 +8,68 @@ outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: - let pkgs = import nixpkgs { inherit system; }; - in { + let + pkgs = import nixpkgs { inherit system; }; + + # Repo tooling (formatters, hooks, linters) for editing the Lua config. + devTools = with pkgs; [ + git + just + pre-commit + stylua + luajit + lua-language-server + luarocks + luaPackages.luacheck + shfmt + shellcheck + yamllint + prettier + nixpkgs-fmt + ]; + + # Runtime dependencies the config binds/scripts shell out to, so a + # `nix develop` shell can drive it. Mirrors the ansible role + # (ansible/roles/hypr). Package names track the binaries scanned from + # hypr/**.lua, *.conf and scripts/*.sh. + runtimeDeps = with pkgs; [ + # --- compositor + hypr* ecosystem (own .conf files in this repo) --- + hyprland # the compositor + hypridle # hypridle.conf + hyprlock # hyprlock.conf / hyprlock-laptop.conf + hyprpaper # hyprpaper.conf + hyprsunset # hyprsunset.conf + hyprpicker # color pick binds + hyprshot # screenshot binds (,hyprshot.sh) + xdg-desktop-portal-hyprland # portal backend + xdg-desktop-portal-gtk # portal (file pickers) + # --- shell / launcher / session --- + quickshell # the bar (autostarted: `uwsm app -- qs -c quantumfate`) + uwsm # session-scoped app launches + rofi-wayland # menus + kitty # terminal + foot # terminal (alttab preview) + # --- tools invoked from binds / lua / scripts --- + xdotool # window poke / click-repeat helpers + brightnessctl # brightness binds + gamemode # gamemoderun (Dofus launch) + satty # screenshot annotate + grim # screenshot capture + jq # JSON parsing in lua/scripts + libnotify # notify-send (fallback path) + procps # pgrep / pkill + xorg.setxkbmap # keyboard layout (setxkbmap dvorak-custom) + inetutils # hostname (per-host config selection) + # Not declared (host/proprietary, install per-host): + # ankama-launcher (AUR) — `gamemoderun ankama-launcher` + # nvidia driver, hyprqt6engine (hyprqt6engine.conf) — host Qt theming + # dofus_swap.py — provided by the quickshell repo's scripts/bin on PATH + # ~/.config/waybar/scripts/*, ,brightness.sh, ,hyprshot.sh — user scripts + ]; + in + { devShells.default = pkgs.mkShell { - packages = with pkgs; [ - git - just - pre-commit - stylua - luajit - lua-language-server - luarocks - luaPackages.luacheck - shfmt - shellcheck - yamllint - prettier - nixpkgs-fmt - ]; + packages = devTools ++ runtimeDeps; # Wire the repo into git on shell entry. shellHook = '' command -v pre-commit >/dev/null && \ diff --git a/hypr/binds.lua b/hypr/binds.lua index 237dd31..eb0d084 100644 --- a/hypr/binds.lua +++ b/hypr/binds.lua @@ -277,5 +277,27 @@ submap.tree({ qs.notify("Quickshell IPC", "help", "all") end, }, + { + key = "m", + desc = "Toggle system monitor", + action = function() + qs.call("sysmon", "toggle") + end, + }, + { + key = "b", + desc = "Toggle notifications", + action = function() + qs.call("notifications", "toggle") + end, + }, + { + key = "d", + desc = "Toggle do-not-disturb", + stay = true, + action = function() + qs.call("notify", "dnd") + end, + }, }, }) diff --git a/hypr/lib/notify.lua b/hypr/lib/notify.lua index d398f86..7ec6d58 100644 --- a/hypr/lib/notify.lua +++ b/hypr/lib/notify.lua @@ -1,8 +1,10 @@ +-- NotifyWrapper — routes WM feedback (swap on/off, launch enable, layout hints) +-- through the Quickshell notification daemon instead of Hyprland's internal +-- overlay. These are all transient: shown once, never kept in history. +-- Public API is unchanged (text, duration, level) so call sites need no edits. ---@class NotifyWrapper local M = {} -local colors = require("hypr.themes.macchiato") - ---@enum NotifyLevel M.level = { INFO = "INFO", @@ -10,21 +12,20 @@ M.level = { ERROR = "ERROR", } -M.colors = { - [M.level.INFO] = colors.mauve, - [M.level.WARNING] = colors.peach, - [M.level.ERROR] = colors.red, +-- WM levels → the shell's toast levels (it has no distinct "warning"). +local qs_level = { + INFO = "info", + WARNING = "info", + ERROR = "error", } ---@param text string ----@param duration integer +---@param _duration integer unused — the daemon owns toast lifetime (kept for API compat) ---@param level NotifyLevel -function M:notify(text, duration, level) - hl.notification.create({ - text = text, - duration = duration, - color = self.colors[level], - }) +function M:notify(text, _duration, level) + local lvl = qs_level[level] or "info" + -- Transient `notify toast`: feedback-only, excluded from history. + hl.exec_cmd(("qs -c quantumfate ipc call notify toast %q %q"):format(text, lvl)) end M.__index = M