dots-hyprland Documentation

QML dotfiles & hyprland repository by @end-4. Model Context Protocol (MCP) server & tools with 16k stars.

RAW Doc

Technical Documentation: end-4/dots-hyprland

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`.github/README.md`)

[!WARNING]
Hyprland 0.55 update:
If your distro has not shipped Hyprland 0.55 and/or you're not ready for it, you should switch to the Pre-Hyprland Luaification release (or not update yet, if you're going to do that). See the wiki for more info: Install | Update

What this is/isn't

  • Technically, configuration files
  • Realistically, mostly the custom graphical shell
  • NOT a system setup script: no graphic drivers, no zram setup, etc.

Notable features

  • Overview: Shows open apps with live previews
  • AI: Gemini, Ollama, and more
  • QoL: screen translation, anti-flashbang, Google Lens
  • Material themes: Choose your wallpaper, done, enjoy
  • Transparent installation: Every command is shown before it's run

Installation

  • IMPORTANT: Hyprland 0.55 Update: If your distro has not shipped Hyprland 0.55 and/or you're not ready for it, you should switch to the Pre-Hyprland Luaification release. See the wiki for more info
  • Just run bash <(curl -s https://ii.clsty.link/get)
    • Or, clone this repo and run ./setup install
    • See the wiki for more details
  • Keybinds: Should be somewhat familiar to Windows or GNOME users. Important ones:
    • Super+/ = keybind list
    • Super+Enter = terminal

Software overview

Software Purpose
Hyprland The compositor (manages and renders windows)
Quickshell A QtQuick-based widget system, used for the status bar, sidebars, etc.
Others See deps-info.md
text
Discord
    [ Server link](https://discord.gg/GtdRBXgMwq) | I hope this provides a friendlier environment for support without needing me to personally accept every friend request/DM. For real issues, prefer GitHub

Widget system: Quickshell | Support: Yes

Showcase video

AI, settings app Some widgets
Window management wow look its orange


  • Unsupported!
  • Source: illogical-impulse AGS in ii-ags branch, others in archive branch.
  • List is in reverse chronological order

illogical-impulse (AGS)

Widget system: AGS | Support: No

AI Common widgets
Window management Weeb power

m3ww

Widget system: EWW | Support: No

NovelKnock

Widget system: EWW | Support: No

Hybrid

Widget system: EWW | Support: No

Windoes

Widget system: EWW | Support: No

  • Inspiration: osu!lazer (Hybrid), Windows 11 (Windoes), AvdanOS (NovelKnock), Material Design 3 (m3ww & later)
  • Copying: Absolutely, feel free. Just follow the license and it's all good

Overview (`licenses/README.md`)

Licenses

This repository contains code from other repositories. Files containing such code should include a license notice, and a copy should be stored in this folder.


Overview (`sdata/README.md`)

This folder mainly contains data for the script setup.

  • TODO: output the logs to a temp file, then show the path of the file so users will be able to read it again or upload it to issue.
  • TODO: unify the message output via functions (for example log_error, log_warning).

Overview (`dots-extra/via-nix/README.md`)

This folder contains tweakd configs when --via-nix is specified.


Overview (`sdata/dist-arch/README.md`)

Install scripts for Arch Linux

Old Dependency Installation Method

The old deps install method mainly involved ./sdata/dependencies.conf (which has been removed now).

Current Dependency Installation

Local PKGBUILDs under ./sdata/dist-arch/ are used to install dependencies.

The mechanism is introduced by Makrennel in PR#570.

Why is this awesome?

  • It makes it possible to control version since some packages may involve breaking changes from time to time.
  • It makes the dependency trackable for package manager, so that you always know why you have installed some package.
  • As a result, it enables a workable uninstall process.

The PKGBUILDs contains two forms of dependencies:

  • Package name written in dependencies, like a "meta" package.
  • Normal PKGBUILD content to build dependencies, e.g. AGS, which is often for version controlling.

Note

  • pkgver() should be removed from PKGBUILD cuz it will modify the PKGBUILD which is tracked by Git and should not be modified during building.

Overview (`sdata/dist-fedora/README.md`)

Install scripts for Fedora Linux

Note:

Contributors

Tested

  • It has been tested on Fedora Everything 44 on the x86_64 platform.

Post installation

  • Fix the issue of the right column crashing when clicking the Details button in Wi-Fi mode. Edit this file: ~/.config/illogical-impulse/config.json
  • Replace pacman with dnf in update app.
diff
@@ 44,3 44,3 @@
-  "apps": {
-    "bluetooth": "kcmshell6 kcm_bluetooth",
-    "network": "kitty -1 fish -c nmtui",
-    "update": "kitty -1 --hold=yes fish -i -c 'pkexec pacman -Syu'",

+  "apps": {
+    "bluetooth": "kcmshell6 kcm_bluetooth",
+    "network": "plasmawindowed org.kde.plasma.networkmanagement",
+    "update": "kitty -1 --hold=yes fish -i -c 'sudo dnf update -y'",

Overview (`sdata/dist-gentoo/README.md`)

Install scripts for Gentoo

Note:

Contributors

This directory contains the Gentoo dependency definitions used by the main
illogical-impulse installer. It mirrors the package groups maintained in
sdata/dist-arch with Gentoo metapackages and a local Portage
overlay.

The complete package set is currently intended for amd64. Some individual
ebuilds also carry arm64 or x86 keywords, but not every illogical-impulse
metapackage supports those architectures yet.

How installation works

The dependency installer:

  1. Installs eselect-repository, rsync, and smart-live-rebuild.
  2. Enables the GURU repository when necessary.
  3. Copies overlay/ to /var/db/repos/ii-dots and installs its
    repository configuration.
  4. Generates /etc/portage/package.accept_keywords/illogical-impulse for the
    current architecture from keywords.
  5. Installs useflags and additional-useflags
    as /etc/portage/package.use/illogical-impulse.
  6. Syncs configured repositories, updates @world, and rebuilds
    installed live packages.
  7. Emerges every metapackage listed in metapkgs.sh.

Note: The installer also ensures Python 3.12 is installed.

The ii-dots repository has auto-sync = no. A normal emerge --sync does
not update it; rerun the illogical-impulse installer to copy the current
overlay into Portage.

Repository contents

Path Purpose
overlay/ Local ebuild repository containing the II metapackages and packages not provided in the required form elsewhere.
ii-dots.conf Portage repository configuration installed under /etc/portage/repos.conf.
keywords Packages that must accept testing or unkeyworded ebuilds. The installer appends the current testing keyword.
useflags Direct feature requirements for the II package set.
additional-useflags Transitive feature requirements appended to the same Portage package.use file.
metapkgs.sh Package groups installed and removed by the dependency scripts.

MicroTeX and Material Symbols use live ebuilds. Quickshell keeps the -git
package name for compatibility but is pinned to a tested upstream commit.
Versioned theme and font ebuilds may also use pinned commits as their source.

System requirements

Choose either elogind or systemd globally so all installed packages use
the same session manager. On OpenRC, elogind is recommended and
must be running so the user session receives XDG_RUNTIME_DIR.

Uninstalling

The uninstall path removes the II metapackages, local overlay, repository
configuration, keyword file, and USE file. It does not automatically remove
dependencies that have become unused; review emerge --depclean --pretend
before deciding whether to depclean them.


Overview (`sdata/dist-nix/README.md`)

Install scripts using Nix to achieve cross-distros

NOTE: The dist-nix is not for NixOS but every distro, using Nix and home-manager.

As we all know Nix and Home-manager has two major functionalities:

  • Handling dependencies (i.e. package installation)
  • Handling dotfiles

They are discussed in following sections.

Handling dependencies

Status

Partially works. See Discussion #2382.

plan

Note that this script must be idempotent.

TODO:

  • Fix all TODOs inside dist-nix. (search online)
  • Since Nix uses a large number of inodes, need to warn user if inode-limited filesystem (typically ext4) is used.
  • Deal with error when running systemctl --user enable ydotool --now:
    plain
    Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=<user>@.host --user to connect to bus of other user)
  • Handle problem that pkill qs and pkill hyprland does not work (should be .quickshell-wra and .Hyprland-wrapp when installed via Nix).

Attentions

PAM

On non-NixOS distros, programs using PAM (typically screen locker) will not work if installed via Nix, so user has to use their own distro's package for the screen lock.

  • One problem is that Debian(-based) distros use modified version of PAM which supports @include directive in /etc/pam.d config files but the PAM from Nix does not support it, see this comment.
  • Another problem is the location of a suid helper binary that is necessary, see this comment.

As commented by @Cu3PO42 , both the problem could be solved by using the system-provided libpam instead.

See also caelestia-dots/shell#668.

GPU

On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch.

nixGL should be used to address the problem.

Since home-manager 25.11, for non-NixOS just set the following:

nix
targets.genericLinux.enable = true;

Then during building, home-manager will show a message to tell you running a command manually to configure GPU, like:

bash
sudo /nix/store/<HASH>-non-nixos-gpu/bin/non-nixos-gpu-setup

It runs a bash script with following content:

text
#!/nix/store/<HASH>-bash-<VERSION>/bin/bash

set -e

# Install the systemd service file and ensure that the store path won't be
# garbage-collected as long as it's installed.
unit_path=/etc/systemd/system/non-nixos-gpu.service
ln -sf /nix/store/<HASH>-non-nixos-gpu/resources/non-nixos-gpu.service "$unit_path"
ln -sf "$unit_path" "/nix/var/nix"/gcroots/non-nixos-gpu.service

systemctl daemon-reload
systemctl enable non-nixos-gpu.service
systemctl restart non-nixos-gpu.service

Note: it uses systemctl, maybe won't work for OpenRC...

See gpu-non-nixos.

Handling dot files

Status

Paused, until some suitable method has been confirmed to meet the requirements below.

Requirements

About handling the dotfiles, i.e. dots/, if we are doing this using Nix then the following requirements must be fulfilled.

1. Allow modifications over the existing dotfiles.

Current state of ./setup install:

  • After finishing running ./setup install, users can modify any dotfiles in a traditional way, and if they run ./setup install again to update then they need to skip the steps which overwrite the targets that they have modified and later sync the upgrade manually for such targets by themselves.
    • For Hyprland, specially we have a custom folder along with ~/.config/hypr/hyprland.conf which will only get overwritten the first time but not the later times running ./setup install.
  • This works but is not elegant. An experimental solution is using yaml config to store the selected behavior for each target, see #2137.

If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation.

2. Allow choosing targets.

This is similar to the above. For example user may want to use their own ~/.config/foot instead of the files under dots/.config/foot entirely.

3. Easy developing dotfiles or at least not worse than current state.

About the current state:

  • @clsty: "If I were the one who develops the dotfiles, I will make changes to the local Git repo dots-hyprland and rerun ./setup install-files -f to apply the changes to observe the outcome."
  • @end-4 (who develops the dots; see comment): "I modify my local copy of stuff, copy the relevant parts over, optionally selectively pick changes then commit. It's.... the most obvious way but I guess not necessarily the cleanest"

If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation.

4. Others

Find out a good method to avoid what @end-4 mentioned:

About home-manager, from my limited understanding of and experience with it, any change to the config files require a rebuild right? If this is indeed the case, switching entirely to this is not okay. Having to wait 20 seconds for each change is absurd.

Some information may help, e.g. @darsh032 commented:

I mean thats not really needed you can use mkOutOfStoreSymlink or use hjem-impure to change the configs without rebuilding

And also the "hmrice" mentioned by @Markus328 , and the flake.nix (for quickshell only) mentioned by @darsh032 .


Overview (`sdata/uv/README.md`)

Why is this important?

Instead of installing python packages via system package manager, we should install them into virtual environment.

This is important because there has been so many complaints about the failure installing/updating python packages via system package manager, see #1017.

How to add/remove python package?

  1. Edit requirements.in. You may refer to PyPI for possible package names.
  • If PyPI does not have the needed package, we probably need to build it manually inside the venv. In such case we need to edit the install scripts.
  1. Run uv pip compile requirements.in -o requirements.txt in this folder.

Notes:

  • For reference see uv doc.
  • requirements.txt is included in Git. It's for locking package versions to enhance stability and reproducibility.[^1]

[^1]: In fact, including package version lock file in Git is also the most common way for similar situations, for example the package-lock.json of Node.js projects (see also this stackoverflow question). Although there are some situations when it's not suitable to include the lock file, for example the poetry document recommend application developers to include package version lock file in Git, but library developers should consider more, such as not including the lock file or including it but refreshing regularly.

How will the python packages get installed?

For summary:

  • They will be installed to the virtual environment $ILLOGICAL_IMPULSE_VIRTUAL_ENV.
  • The default value of $ILLOGICAL_IMPULSE_VIRTUAL_ENV is $XDG_STATE_HOME/quickshell/.venv.
    • The default value of $XDG_STATE_HOME is $HOME/.local/state.
  • Currently we use env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/quickshell/.venv in ~/.config/hypr/hyprland/env.conf to set this environment variable.[^2]

For details: see the function install-python-packages() defined in /sdata/lib/package-installers.sh.

[^2]: Hyprland seems to have weird problem dealing with recursive variable, so we can not use $XDG_STATE_HOME/quickshell/.venv even if we had set $XDG_STATE_HOME to ~/.local/state explicitly, else $XDG_STATE_HOME will possibly not get expanded but get recognised as literally $XDG_STATE_HOME. This problem never happens for some users, but according to some issues when we were using recursive variable setting in the past, it's possible to happen for other users. Reason unknown.

How to use the python packages installed through here?

Basically you'll need to activate the virtual environment first:

bash
source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate

It will add the python executable located in the venv to $PATH and give it the highest priority.
Run which python and you'll understand.

This python executable will also search and use the python package inside the venv,
which enables running any python script or running command provided via python package using the venv.

After that you probably need to deactivate it:

bash
deactivate

Situation 1: As a single command

Description: At someplace which accept a single command,

  • run a python script,
  • or run a command provided by python package.

Example: In ~/โ€Ž.config/quickshell/ii/screenshot.qml:

qml
Process {
id: imageDetectionProcess
                command: ["bash", "-c", `${Directories.scriptPath}/images/find_regions.py ` 
+ `--hyprctl ` 
+ `--image '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}' ` 
+ `--max-width ${Math.round(panelWindow.screen.width * root.falsePositivePreventionRatio)} `

In this example, python script find_regions.py is called and receives some arguments.

Solution A: shebang

Add the shebang below to the beginning of python script:

python
#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""

And that's it!

Note: This is the simplest solution as it only modifies the shebang of python script.
However:

  • It's only for python script, not the command provided by python package.
  • It can not deal with complex argument (e.g. filename containing spaces) passed to the python script.
    • If we apply this solution to the example above, it may cause problem, considering that --image '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}' could be a rather complex argument passed to find_regions.py.
  • This solution rely on shebang to activate the correct python venv, but the shebang will be ignored if the script is directly passed to the interpreter, e.g. python3 foo.py.

Solution B: bash script as wrapper

First make sure the python script is using the shebang #!/usr/bin/env python3, instead of #!/usr/bin/python3 or something else.

Then write a wrapper script in bash.
Let's continue the screenshot.qml example, in the same directory as find_regions.py, write a find-regions-venv.sh:

bash
#!/usr/bin/env bash

# Specify the path of the python script.
# The example below only applies when `find_regions.py` and this wrapper script are under the same folder.
PY_SCRIPT="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)/find_regions.py"

source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate
"$PY_SCRIPT" "$@"
deactivate

Not done yet! Do not forget to update the code calling the original python script.
In this example, in ~/โ€Ž.config/quickshell/ii/screenshot.qml we should modify find_regions.py to the wrapper script find-regions-venv.sh:

qml
Process {
id: imageDetectionProcess
                command: ["bash", "-c", `${Directories.scriptPath}/images/find-regions-venv.sh ` 
+ `--hyprctl ` 
+ `--image '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}' ` 
+ `--max-width ${Math.round(panelWindow.screen.width * root.falsePositivePreventionRatio)} `

Situation 2: Inside a bash script

Note: the solutions for Situation 1: As a single command also apply here; but not vice versa.

Description:
Inside a bash script,

  • run a python script,
  • or run a command provided by python package.

Solution:

  • Add "activation command" before the target line,
  • Also add "deactivation command" after the target line.

Example:

For running a python script,
take generate_colors_material.py as example:

bash
source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate"
python3 "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \
  > "$STATE_DIR"/user/generated/material_colors.scss
"$SCRIPT_DIR"/applycolor.sh

For running a python script provided by python package,
take kde-material-you-colors as example:

bash
source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate"
kde-material-you-colors "$mode_flag" --color "$color" -sv "$sv_num"
deactivate

Overview (`dots/.config/quickshell/ii/defaults/ai/README.md`)

A note about sources of the prompts

  • ii- prefixed ones are from illogical impulse
  • The Acchan one is from Nyarch Assistant (GPLv3). I know there's already the Imouto one but this one's very ๐Ÿ˜ญ๐Ÿ’ข
  • w- prefixed ones... I don't remember what w stands for but these prompts are [cough cough] inspired by certain apps

Overview (`dots/.config/quickshell/ii/modules/waffle/README.md`)

Waffle

A recreation of Windoes. It's WIP!

  • If you install illogical-impulse fully, you can press Super+Alt+W to switch to this style.
  • If you're just copying the Quickshell config, run the config as usual (qs -c ii) then run qs -c ii ipc call panelFamily cycle

From EWW version to Quickshell

Just a reflection, in case anyone's interested. My blog is probably a better place for this, but it does not exist. Besides, this is going to change as I do more stuff. Currently there's just the bar.

Improvements

  • QtQuick's Button has the {top/bottom/left/right}Inset properties, so we can have clickable regions expanding beyond the button background for free. With EWW it was annoying to wrap the button content with an eventbox that has some padding, then somehow use CSS selectors to make sure hovering effects work. I have to admit, (a large) part of that annoyance was with how bad my copy-pasting coding practice was at the time, but still...

  • Fancy effects: Gtk3 CSS does not support transformations. In QtQuick we can smack rotation and scale almost everywhere, so it's simple to make bouncy icons and rotating chevrons

  • Quickshell provides a system tray service (EWW does now but didn't at the time I created the EWW Windoes version), so now there's no Waybar needed for the tray.

  • QtQuick has Loaders, so we can have this live-switchable from the main style without killing the widget system, moving styles to the correct folder, and relaunching.

  • This time my computer is powerful enough to run a VM, so I don't have to occasionally reboot to take quick screenshots for reference. I try to make everything pixel-perfect so this is necessary. Speaking about pixel-perfectness, in the EWW version I hardcoded sizes, but this time I'm still doing that (lol), BUT that's normal and not a problem because Qt has the QT_SCALE_FACTOR env var for scaling. (Please feel free to prove me wrong in saying Gtk3 doesn't have that magic)

Challenges

  • Qt is not Gtk and definitely not React

    • We don't get directional border on QtQuick Rectangles like in CSS. I was able to get around this with manual drawing, but it was a bit more work
  • Fluent Icons is difficult to use, compared to Material Symbols

    • No React, so no clean use via a library.
    • If we use the font, there's no proper, searchable codepoint cheatsheet like Nerd Fonts, and there's no ligatures
    • I resorted to downloading individual SVGs. Not that nice, but it's better than scanning the whole table of icons every time I want one. For this we have fluenticon.com and fluenticons.co, but icons are awkwardly named and there's no alias. Why is the reload/refresh icon called "arrow-sync"? Well, the name is not misleading, but arguably reload/refresh are more common actions. From Fluent Design's page on Iconography:

    Fluent system icons are literal metaphors and are named for the shape or object they represent, not the functionality they provide

    "sync" is functionality.


Overview (`dots/.config/quickshell/ii/assets/icons/fluent/README.md`)

The "start-here", "search" and "task view" icons are from here, with modifications

Windows 11 by Joshua Oghenekaro Okwe - Figma

License: CC BY 4.0


--- METRICS ---