Personal Nix setup

Update theme config and hyprland look

Changed files
+580 -43
home
lib
modules
desktop
+1
home/desktop/default.nix
···
imports = [
./hyprland.nix
+
./theme.nix
];
}
+41 -42
home/desktop/hyprland.nix
···
let
cfg = config.modules.desktop;
+
hyprpanel = pkgs.callPackage ./hyprpanel {};
+
wpctl = "${pkgs.wireplumber}/bin/wpctl";
brightnessctl = "${pkgs.brightnessctl}/bin/brightnessctl";
playerctl = "${pkgs.playerctl}/bin/playerctl";
+
hyprshot = "${pkgs.hyprshot}/bin/hyprshot";
in {
options.modules.desktop.hyprland = {
enable = mkOption {
···
settings = {
general = {
-
gaps_out = 10;
+
gaps_out = 9;
+
gaps_in = 4;
resize_on_border = true;
hover_icon_on_border = false;
+
no_border_on_floating = true;
extend_border_grab_area = 10;
+
border_size = 1;
+
"col.active_border" = "0xB35A5A5A";
+
"col.inactive_border" = "0x8C3A3A3A";
+
};
+
+
decoration = {
+
rounding = 9;
+
dim_inactive = true;
+
dim_strength = 0.12;
+
+
blur = {
+
enabled = true;
+
size = 16;
+
passes = 4;
+
contrast = 0.8;
+
brightness = 0.45;
+
vibrancy = 0.15;
+
vibrancy_darkness = 0.1;
+
ignore_opacity = true;
+
noise = 0.012;
+
};
+
+
shadow = {
+
color = "0x81000000";
+
range = 40;
+
render_power = 1;
+
offset = "5, 5";
+
};
};
input = {
···
gestures = {
workspace_swipe = true;
workspace_swipe_invert = false;
-
workspace_swipe_distance = 450;
+
workspace_swipe_distance = 600;
};
debug.error_position = 1;
···
"SUPER, T, exec, uwsm-app ghostty"
"SUPER, B, exec, uwsm-app zen-beta"
"SUPER, W, killactive"
+
+
"SUPERSHIFT, F, fullscreen, 1"
+
+
"SUPERSHIFT, 2, exec, ${hyprshot} -z -m window -m active"
+
"SUPERSHIFT, 3, exec, ${hyprshot} -z -m output -m active"
+
"SUPERSHIFT, 4, exec, ${hyprshot} -z -m region"
];
windowrule = [
···
shadow_passes = 2;
}
];
-
};
-
};
-
-
home.pointerCursor = {
-
gtk.enable = true;
-
hyprcursor.enable = true;
-
x11.enable = true;
-
package = pkgs.apple-cursor;
-
name = "macOS";
-
size = 28;
-
};
-
-
gtk = {
-
enable = true;
-
gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc";
-
theme = {
-
package = pkgs.flat-remix-gtk;
-
name = "Flat-Remix-GTK-Grey-Darkest";
-
};
-
iconTheme = {
-
name = "WhiteSur";
-
package = pkgs.whitesur-icon-theme.override {
-
boldPanelIcons = true;
-
alternativeIcons = true;
-
};
-
};
-
font = {
-
name = "SF Pro";
-
package = pkgs.sf-pro;
-
size = 11;
-
};
-
};
-
-
fonts.fontconfig = {
-
enable = true;
-
defaultFonts = {
-
serif = [ "Noto Serif" "Noto Color Emoji" ];
-
sansSerif = [ "Inter" "Noto Color Emoji" ];
-
monospace = [ "Dank Mono" "Roboto Mono" "Noto Color Emoji" ];
-
emoji = [ "Noto Color Emoji" ];
};
};
};
+56
home/desktop/hyprpanel/default.nix
···
+
{
+
stdenv,
+
astal,
+
meson,
+
ninja,
+
pkg-config,
+
gobject-introspection,
+
blueprint-compiler,
+
gtk4-layer-shell,
+
gtk4,
+
json-glib,
+
networkmanager,
+
dart-sass,
+
vala,
+
wrapGAppsHook4,
+
...
+
}: let
+
astalPackages = [
+
astal.astal4
+
astal.io
+
astal.gjs
+
astal.auth
+
astal.cava
+
astal.apps
+
astal.greet
+
astal.mpris
+
astal.notifd
+
astal.network
+
astal.battery
+
astal.hyprland
+
astal.bluetooth
+
astal.wireplumber
+
astal.powerprofiles
+
];
+
in stdenv.mkDerivation {
+
name = "hyprpanel";
+
src = ./.;
+
depsBuildBuild = [
+
pkg-config
+
];
+
nativeBuildInputs = [
+
meson
+
ninja
+
gobject-introspection
+
wrapGAppsHook4
+
blueprint-compiler
+
dart-sass
+
vala
+
];
+
buildInputs = [
+
gtk4
+
gtk4-layer-shell
+
networkmanager
+
json-glib
+
] ++ astalPackages;
+
}
+2
home/desktop/hyprpanel/meson.build
···
+
project('hyprpanel', 'vala')
+
subdir('src')
+7
home/desktop/hyprpanel/src/gresource.xml
···
+
<?xml version="1.0" encoding="UTF-8"?>
+
<gresources>
+
<gresource prefix="/">
+
<file>main.css</file>
+
<file>ui/Bar.ui</file>
+
</gresource>
+
</gresources>
+1
home/desktop/hyprpanel/src/main.scss
···
+
@use "./scss/Bar.scss";
+66
home/desktop/hyprpanel/src/meson.build
···
+
pkgdatadir = get_option('prefix') / get_option('datadir')
+
bindir = get_option('prefix') / get_option('bindir')
+
blp = find_program('blueprint-compiler', required: true)
+
sass = find_program('sass', required: true)
+
+
dependencies = [
+
dependency('glib-2.0'),
+
dependency('libnm'),
+
dependency('gtk4-layer-shell-0'),
+
dependency('astal-io-0.1'),
+
dependency('astal-4-4.0'),
+
dependency('astal-battery-0.1'),
+
dependency('astal-wireplumber-0.1'),
+
dependency('astal-network-0.1'),
+
dependency('astal-mpris-0.1'),
+
dependency('astal-power-profiles-0.1'),
+
dependency('astal-bluetooth-0.1'),
+
]
+
+
blueprint_sources = files(
+
'ui/Bar.blp',
+
)
+
+
vala_sources = files(
+
'vala/Bar.vala',
+
'vala/App.vala',
+
)
+
+
# transplie blueprints
+
ui = custom_target(
+
'blueprint',
+
input: blueprint_sources,
+
output: '.',
+
command: [
+
blp,
+
'batch-compile',
+
'@OUTPUT@',
+
'@CURRENT_SOURCE_DIR@',
+
'@INPUT@',
+
],
+
)
+
+
# bundle scss files
+
css = custom_target(
+
'scss',
+
input: files('main.scss'),
+
command: [sass, '@INPUT@', '@OUTPUT@'],
+
output: ['main.css'],
+
)
+
+
# compiling data files into a binary
+
resource = import('gnome').compile_resources(
+
'data',
+
files('gresource.xml'),
+
dependencies: [ui, css],
+
source_dir: meson.current_build_dir(),
+
)
+
+
executable(
+
meson.project_name(),
+
dependencies: dependencies,
+
sources: [vala_sources, resource],
+
link_args: ['-lm'], # Link math library
+
install: true,
+
install_dir: bindir,
+
)
+20
home/desktop/hyprpanel/src/scss/Bar.scss
···
+
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gtk/theme/Default/_colors-public.scss
+
$fg-color: #{"@theme_fg_color"};
+
$bg-color: #{"@theme_bg_color"};
+
+
window.Bar {
+
> box {
+
background: $bg-color;
+
color: $fg-color;
+
font-weight: bold;
+
}
+
+
button {
+
min-height: 0;
+
min-width: 0;
+
border-radius: 8px;
+
margin: 4px;
+
padding: 4px 8px;
+
}
+
}
+
+81
home/desktop/hyprpanel/src/ui/Bar.blp
···
+
using Gtk 4.0;
+
using Astal 4.0;
+
+
template $Bar: Astal.Window {
+
CenterBox centerbox {
+
start-widget: Box {
+
MenuButton {
+
Label {
+
label: bind template.clock;
+
}
+
+
popover: Popover popover {
+
Calendar calendar {
+
show-day-names: true;
+
show-heading: true;
+
show-week-numbers: true;
+
}
+
};
+
}
+
};
+
+
center-widget: Box {
+
Box {
+
visible: bind template.mpris-visible;
+
+
Image {
+
file: bind template.mpris-art;
+
}
+
+
Label {
+
label: bind template.mpris-label;
+
}
+
}
+
};
+
+
end-widget: Box {
+
spacing: 4;
+
+
Image {
+
visible: bind template.bluetooth-visible;
+
icon-name: "bluetooth-symbolic";
+
}
+
+
Image {
+
icon-name: bind template.power-profile-icon;
+
}
+
+
Image {
+
icon-name: bind template.network-icon;
+
}
+
+
Box {
+
Image {
+
icon-name: bind template.volume-icon;
+
}
+
+
Scale {
+
width-request: 100;
+
change-value => $change_volume();
+
+
adjustment: Adjustment {
+
value: bind template.volume;
+
lower: 0;
+
upper: 1;
+
};
+
}
+
}
+
+
Box {
+
Image {
+
icon-name: bind template.battery-icon;
+
}
+
+
Label {
+
label: bind template.battery-label;
+
}
+
}
+
};
+
}
+
}
+
+46
home/desktop/hyprpanel/src/vala/App.vala
···
+
class App : Astal.Application {
+
static App instance;
+
+
// this is where request handlers can be implemented
+
// that will be used to handle `astal` cli invocations
+
public override void request(string request, GLib.SocketConnection conn) {
+
print(@"incoming request: $request\n");
+
+
AstalIO.write_sock.begin(conn, "response", null);
+
}
+
+
// this is the method that will be invoked on `app.run()`
+
// this is where everything should be initialized and instantiated
+
public override void activate() {
+
this.apply_css("resource:///main.css", false);
+
this.add_window(new Bar());
+
}
+
+
// entry point of our app
+
static int main(string[] argv) {
+
App.instance = new App() { instance_name = "hyprpanel" };
+
+
try {
+
// `app.acquire_socket()` needed for the request API to work
+
App.instance.acquire_socket();
+
+
// if it succeeds we can run the app
+
return App.instance.run(null);
+
} catch (Error _) {
+
// if it throws an error it means there is already an instance
+
// with `instance_name` running, so we just send a request instead
+
try {
+
var response = AstalIO.send_request(
+
"hyprpanel",
+
string.joinv(" ", argv[1:])
+
);
+
print(@"$response\n");
+
return 0;
+
} catch (Error err) {
+
printerr(err.message);
+
return 1;
+
}
+
}
+
}
+
}
+
+128
home/desktop/hyprpanel/src/vala/Bar.vala
···
+
[GtkTemplate(ui="/ui/Bar.ui")]
+
class Bar : Astal.Window {
+
public string clock { get; set; }
+
public string volume_icon { get; set; }
+
public string battery_visible { get; set; }
+
public string battery_label { get; set; }
+
public string battery_icon { get; set; }
+
public double volume { get; set; }
+
public string network_icon { get; set; }
+
public bool mpris_visible { get; set; }
+
public string mpris_label { get; set; }
+
public string mpris_art { get; set; }
+
public string power_profile_icon { get; set; }
+
public bool bluetooth_visible { get; set; }
+
+
AstalIO.Time timer;
+
AstalMpris.Player player;
+
+
[GtkChild] unowned Gtk.Popover popover;
+
[GtkChild] unowned Gtk.Calendar calendar;
+
+
public Bar() {
+
anchor = TOP | LEFT | RIGHT;
+
exclusivity = EXCLUSIVE;
+
add_css_class("Bar");
+
present();
+
+
// clock
+
timer = AstalIO.Time.interval(1000, null);
+
timer.now.connect(() => {
+
clock = new DateTime.now_local().format("%H:%M:%S");
+
});
+
+
// everytime popover is opened, select current day
+
popover.notify["visible"].connect(() => {
+
if (popover.visible) {
+
calendar.select_day(new DateTime.now_local());
+
}
+
});
+
+
// network
+
var nw = AstalNetwork.get_default();
+
Binding networkBinding = null;
+
+
nw.bind_property(
+
"primary",
+
this,
+
"network-icon",
+
BindingFlags.SYNC_CREATE,
+
(_, primary) => {
+
if (networkBinding != null) networkBinding.unbind();
+
+
switch (primary.get_enum()) {
+
case AstalNetwork.Primary.WIRED:
+
networkBinding = nw.wired.bind_property(
+
"icon-name",
+
this,
+
"network-icon",
+
BindingFlags.SYNC_CREATE
+
);
+
return false;
+
+
case AstalNetwork.Primary.WIFI:
+
networkBinding = nw.wifi.bind_property(
+
"icon-name",
+
this,
+
"network-icon",
+
BindingFlags.SYNC_CREATE
+
);
+
return false;
+
+
default:
+
network_icon = "network-idle-symbolic";
+
return false;
+
}
+
},
+
null
+
);
+
+
// battery
+
var bat = AstalBattery.get_default();
+
bat.bind_property("is-present", this, "battery-visible", BindingFlags.SYNC_CREATE);
+
bat.bind_property("icon-name", this, "battery-icon", BindingFlags.SYNC_CREATE);
+
bat.bind_property("percentage", this, "battery-label", BindingFlags.SYNC_CREATE, (_, src, ref target) => {
+
target.set_string(@"$(Math.floor(bat.percentage * 100))%");
+
return true;
+
}, null);
+
+
// volume
+
var speaker = AstalWp.get_default().get_default_speaker();
+
speaker.bind_property("volume-icon", this, "volume-icon", BindingFlags.SYNC_CREATE);
+
speaker.bind_property("volume", this, "volume", BindingFlags.SYNC_CREATE);
+
+
// mpris
+
player = new AstalMpris.Player("spotify");
+
player.bind_property("available", this, "mpris-visible", BindingFlags.SYNC_CREATE);
+
player.bind_property("cover-art", this, "mpris-art", BindingFlags.SYNC_CREATE);
+
player.bind_property("metadata", this, "mpris-label", BindingFlags.SYNC_CREATE, (_, src, ref target) => {
+
if (player.title == null || player.artist == null) {
+
return false;
+
}
+
target.set_string(@"$(player.artist) - $(player.title)");
+
return true;
+
}, null);
+
+
// powerprofiles
+
var powerprofile = AstalPowerProfiles.get_default();
+
powerprofile.bind_property("icon-name", this, "power-profile-icon", BindingFlags.SYNC_CREATE);
+
+
// bluetooth
+
var bt = AstalBluetooth.get_default();
+
bt.bind_property("is-connected", this, "bluetooth-visible", BindingFlags.SYNC_CREATE);
+
}
+
+
[GtkCallback]
+
bool change_volume(Gtk.Range scale, Gtk.ScrollType type, double value) {
+
AstalWp.get_default().get_default_speaker().set_volume(value);
+
return true;
+
}
+
+
public override void dispose() {
+
timer.cancel();
+
timer.dispose();
+
player.dispose();
+
base.dispose();
+
}
+
}
+
+119
home/desktop/theme.nix
···
+
{ lib, config, pkgs, ... }:
+
+
with lib;
+
let
+
cfg = config.modules.desktop;
+
+
cursorTheme = {
+
name = "macOS";
+
package = pkgs.apple-cursor;
+
};
+
+
iconTheme = {
+
name = "WhiteSur";
+
package = pkgs.whitesur-icon-theme.override {
+
boldPanelIcons = true;
+
alternativeIcons = true;
+
};
+
};
+
+
gtkTheme = {
+
name = "WhiteSur-Dark-solid";
+
package = pkgs.whitesur-gtk-theme;
+
};
+
+
kvantumTheme = rec {
+
name = "WhiteSur-opaqueDark";
+
package = pkgs.stdenv.mkDerivation {
+
pname = "whitesur-kde";
+
version = pkgs.whitesur-kde.version;
+
src = pkgs.whitesur-kde.src;
+
installPhase = /*sh*/''
+
mkdir -p "$out/share/Kvantum/${name}"
+
cp -R Kvantum/**/* "$out/share/Kvantum/${name}"
+
'';
+
};
+
};
+
in {
+
options.modules.desktop.theme = {
+
enable = mkOption {
+
default = cfg.enable;
+
example = true;
+
description = "Whether to enable theming configuration.";
+
type = types.bool;
+
};
+
};
+
+
config = mkIf cfg.theme.enable {
+
home.pointerCursor = {
+
inherit (cursorTheme) package name;
+
gtk.enable = true;
+
hyprcursor.enable = true;
+
x11.enable = true;
+
size = 28;
+
};
+
+
home.packages = with pkgs; [
+
catppuccin-kvantum
+
libsForQt5.qtstyleplugin-kvantum
+
libsForQt5.qt5ct
+
];
+
+
qt = {
+
enable = true;
+
platformTheme.name = "gtk";
+
style.name = "kvantum";
+
};
+
+
xdg.configFile = {
+
"Kvantum/${kvantumTheme.name}".source = "${kvantumTheme.package}/share/Kvantum/${kvantumTheme.name}";
+
"Kvantum/kvantum.kvconfig".text = ''
+
[General]
+
theme=${kvantumTheme.name}
+
'';
+
"qt5ct/qt5ct.conf".text = ''
+
[Appearance]
+
icon_theme=${iconTheme.name}
+
'';
+
"qt6ct/qt6ct.conf".text = ''
+
[Appearance]
+
icon_theme=${iconTheme.name}
+
'';
+
};
+
+
gtk = {
+
enable = true;
+
inherit iconTheme;
+
theme = gtkTheme;
+
font = {
+
name = "SF Pro Display";
+
package = pkgs.sf-pro;
+
size = 11;
+
};
+
gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc";
+
gtk3.extraConfig.gtk-application-prefer-dark-theme = 1;
+
gtk4.extraConfig.gtk-application-prefer-dark-theme = 1;
+
};
+
+
dconf = {
+
enable = true;
+
settings = {
+
"org/gnome/desktop/interface" = {
+
color-scheme = "prefer-dark";
+
gtk-theme = gtkTheme.name;
+
icon-theme = iconTheme.name;
+
};
+
};
+
};
+
+
fonts.fontconfig = {
+
enable = true;
+
defaultFonts = {
+
serif = [ "Noto Serif" "Noto Color Emoji" ];
+
sansSerif = [ "Inter" "Noto Color Emoji" ];
+
monospace = [ "Dank Mono" "Roboto Mono" "Noto Color Emoji" ];
+
emoji = [ "Noto Color Emoji" ];
+
};
+
};
+
};
+
}
+1
lib/pkgs/default.nix
···
pkgs: {
sf-pro = import ./sf-pro.nix pkgs;
sf-pro-mono = import ./sf-pro-mono.nix pkgs;
+
hyprpanel = import ../../home/desktop/hyprpanel pkgs;
}
+11 -1
modules/desktop/session.nix
···
consoleLogLevel = 0;
};
+
environment.sessionVariables = {
+
GSK_RENDERER = mkDefault "ngl";
+
QT_QPA_PLATFORM = mkDefault "wayland";
+
GDK_BACKEND = mkDefault "wayland";
+
NIXOS_OZONE_WL = mkDefault "1";
+
};
+
services = {
-
greetd.enable = true;
+
greetd = {
+
enable = true;
+
settings.terminal.vt = 1;
+
};
hypridle.enable = true;
logind = {
powerKey = "suspend";