refactor: implement component-based architecture for enhanced maintainability

- Reorganize directory structure into core/, features/, and themes/
- Colocate application Nix logic, configs, scripts, and theme overrides
- Implement 'Inversion of Control' for theming: apps now pull theme-specific layouts
- Update flake.nix and shared library paths to match the new structure
- Document the new Feature-Centric architecture in README.md
This commit is contained in:
Bernardo Magri
2026-04-12 14:51:15 +01:00
parent a9ee79a5ce
commit bbdf34ced8
535 changed files with 119 additions and 127 deletions

View File

@@ -0,0 +1,71 @@
# paths to stylesheets on the filesystem which should be applied to the application
#
# relative paths are resolved relative to the location of the config file
stylesheets: ["../nomarchy/current/theme/hyprland-preview-share-picker.css"]
# default page selected when the picker is opened
default_page: outputs
window:
# height of the application window
height: 500
# width of the application window
width: 1000
image:
# size to which the images should be internally resized to reduce the memory footprint
resize_size: 500
# target size of the longer side of the image widget
widget_size: 150
classes:
# css classname of the window
window: window
# css classname of the card containing an image and a label
image_card: card
# css classname of the card containing an image and a label when the image is still being loaded
image_card_loading: card-loading
# css classname of the image inside the card
image: image
# css classname of the label inside the card
image_label: image-label
# css classname of the notebook containing all pages
notebook: notebook
# css classname of a label of the notebook
tab_label: tab-label
# css classname of a notebook page (e.g. windows container)
notebook_page: page
# css classname of the region selection button
region_button: region-button
# css classname of the button containing the session restore checkbox and label
restore_button: restore-button
windows:
# minimum amount of image cards per row on the windows page
min_per_row: 3
# maximum amount of image cards per row on the windows page
max_per_row: 999
# number of clicks needed to select a window
clicks: 1
# spacing in pixels between the window cards
spacing: 12
outputs:
# number of clicks needed to select an output
clicks: 1
# spacing in pixels between the outputs in the layout
# note: the spacing is applied from both sides (the gap is `spacing * 2`)
spacing: 6
# show the label with the output name
show_label: false
# size the output cards respectively to their scaling
respect_output_scaling: true
region:
# command to run for region selection
# the output needs to be in the <output>@<x>,<y>,<w>,<h> (e.g. DP-3@2789,436,756,576) format
command: slurp -f '%o@%x,%y,%w,%h'
# hide the token restore checkbox and use the default value instead
hide_token_restore: true
# enable debug logs by default
debug: false

View File

@@ -0,0 +1,2 @@
# Extra autostart processes
# exec-once = uwsm-app -- my-service

View File

@@ -0,0 +1,34 @@
# Application bindings
bindd = SUPER, RETURN, Terminal, exec, uwsm-app -- xdg-terminal-exec --dir="$(nomarchy-cmd-terminal-cwd)"
bindd = SUPER ALT, RETURN, Tmux, exec, uwsm-app -- xdg-terminal-exec --dir="$(nomarchy-cmd-terminal-cwd)" bash -c "tmux attach || tmux new -s Work"
bindd = SUPER SHIFT, RETURN, Browser, exec, nomarchy-launch-browser
bindd = SUPER SHIFT, F, File manager, exec, uwsm-app -- nautilus --new-window
bindd = SUPER ALT SHIFT, F, File manager (cwd), exec, uwsm-app -- nautilus --new-window "$(nomarchy-cmd-terminal-cwd)"
bindd = SUPER SHIFT, B, Browser, exec, nomarchy-launch-browser
bindd = SUPER SHIFT ALT, B, Browser (private), exec, nomarchy-launch-browser --private
bindd = SUPER SHIFT, M, Music, exec, nomarchy-launch-or-focus spotify
bindd = SUPER SHIFT, N, Editor, exec, nomarchy-launch-editor
bindd = SUPER SHIFT, D, Docker, exec, nomarchy-launch-tui lazydocker
bindd = SUPER SHIFT, G, Signal, exec, nomarchy-launch-or-focus ^signal$ "uwsm-app -- signal-desktop"
bindd = SUPER SHIFT, O, Obsidian, exec, nomarchy-launch-or-focus ^obsidian$ "uwsm-app -- obsidian -disable-gpu --enable-wayland-ime"
bindd = SUPER SHIFT, W, Typora, exec, uwsm-app -- typora --enable-wayland-ime
bindd = SUPER SHIFT, SLASH, Passwords, exec, uwsm-app -- 1password
# If your web app url contains #, type it as ## to prevent hyprland treating it as a comment
bindd = SUPER SHIFT, A, ChatGPT, exec, nomarchy-launch-webapp "https://chatgpt.com"
bindd = SUPER SHIFT ALT, A, Grok, exec, nomarchy-launch-webapp "https://grok.com"
bindd = SUPER SHIFT, C, Calendar, exec, nomarchy-launch-webapp "https://app.hey.com/calendar/weeks/"
bindd = SUPER SHIFT, E, Email, exec, nomarchy-launch-webapp "https://app.hey.com"
bindd = SUPER SHIFT, Y, YouTube, exec, nomarchy-launch-webapp "https://youtube.com/"
bindd = SUPER SHIFT ALT, G, WhatsApp, exec, nomarchy-launch-or-focus-webapp WhatsApp "https://web.whatsapp.com/"
bindd = SUPER SHIFT CTRL, G, Google Messages, exec, nomarchy-launch-or-focus-webapp "Google Messages" "https://messages.google.com/web/conversations"
bindd = SUPER SHIFT, P, Google Photos, exec, nomarchy-launch-or-focus-webapp "Google Photos" "https://photos.google.com/"
bindd = SUPER SHIFT, X, X, exec, nomarchy-launch-webapp "https://x.com/"
bindd = SUPER SHIFT ALT, X, X Post, exec, nomarchy-launch-webapp "https://x.com/compose/post"
# Add extra bindings
# bind = SUPER SHIFT, R, exec, alacritty -e ssh your-server
# Overwrite existing bindings, like putting Nomarchy Menu on Super + Space
# unbind = SUPER, SPACE
# bindd = SUPER, SPACE, Nomarchy menu, exec, nomarchy-menu

View File

@@ -0,0 +1,43 @@
source = ~/.config/nomarchy/current/theme/hyprlock.conf
general {
ignore_empty_input = true
}
background {
monitor =
color = $color
path = ~/.config/nomarchy/current/background
blur_passes = 3
}
animations {
enabled = false
}
input-field {
monitor =
size = 650, 100
position = 0, 0
halign = center
valign = center
inner_color = $inner_color
outer_color = $outer_color
outline_thickness = 4
font_family = JetBrainsMono Nerd Font
font_color = $font_color
placeholder_text = Enter Password
check_color = $check_color
fail_text = <i>$FAIL ($ATTEMPTS)</i>
rounding = 0
shadow_passes = 0
fade_on_empty = false
}
auth {
fingerprint:enabled = false
}

View File

@@ -0,0 +1,14 @@
# Makes hyprsunset do nothing to the screen by default
# Without this, the default applies some tint to the monitor
profile {
time = 07:00
identity = true
}
# To enable auto switch to nightlight, set in your .config/hypr/autostart:
# exec-once = uwsm app -- hyprsunset
# and use the following:
# profile {
# time = 20:00
# temperature = 4000
# }

View File

@@ -0,0 +1,53 @@
# Control your input devices
# See https://wiki.hypr.land/Configuring/Variables/#input
input {
# Use multiple keyboard layouts and switch between them with Left Alt + Right Alt
# kb_layout = us,dk,eu
# Use a specific keyboard variant if needed (e.g. intl for international keyboards)
# kb_variant = intl
kb_options = compose:caps # ,grp:alts_toggle
# Change speed of keyboard repeat
repeat_rate = 40
repeat_delay = 600
# Start with numlock on by default
numlock_by_default = true
# Increase sensitivity for mouse/trackpad (default: 0)
# sensitivity = 0.35
# Turn off mouse acceleration (default: false)
# force_no_accel = true
touchpad {
# Use natural (inverse) scrolling
# natural_scroll = true
# Use two-finger clicks for right-click instead of lower-right corner
# clickfinger_behavior = true
# Control the speed of your scrolling
scroll_factor = 0.4
# Enable the touchpad while typing
# disable_while_typing = false
# Left-click-and-drag with three fingers
# drag_3fg = 1
}
}
# Scroll nicely in the terminal
#windowrulev2 = match:class (Alacritty|kitty), scroll_touchpad 1.5
#windowrulev2 = match:class com.mitchellh.ghostty, scroll_touchpad 0.2
# Enable touchpad gestures for changing workspaces
# See https://wiki.hyprland.org/Configuring/Gestures/
# gesture = 3, horizontal, workspace
# Enable touchpad gestures for moving focus (helpful on scrolling layout)
# gesture = 3, left, dispatcher, movefocus, l
# gesture = 3, right, dispatcher, movefocus, r

View File

@@ -0,0 +1,34 @@
# Change the default Nomarchy look'n'feel
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
# No gaps between windows or borders
# gaps_in = 0
# gaps_out = 0
# border_size = 0
# Change to niri-like side-scrolling layout
# layout = scrolling
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
# Use round window corners
# rounding = 8
# Dim unfocused windows (0.0 = no dim, 1.0 = fully dimmed)
# dim_inactive = true
# dim_strength = 0.15
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
# Disable all animations
# enabled = no
}
# https://wiki.hypr.land/Configuring/Variables/#layout
layout {
# Avoid overly wide single-window layouts on wide screens
# single_window_aspect_ratio = 1 1
}

View File

@@ -0,0 +1,23 @@
# See https://wiki.hyprland.org/Configuring/Monitors/
# List current monitors and resolutions possible: hyprctl monitors
# Format: monitor = [port], resolution, position, scale
# Optimized for retina-class 2x displays, like 13" 2.8K, 27" 5K, 32" 6K.
env = GDK_SCALE,2
monitor=,preferred,auto,auto
# Good compromise for 27" or 32" 4K monitors (but fractional!)
# env = GDK_SCALE,1.75
# monitor=,preferred,auto,1.6
# Straight 1x setup for low-resolution displays like 1080p or 1440p
# Or for ultrawide monitors like 34" 3440x1440 or 49" 5120x1440
# env = GDK_SCALE,1
# monitor=,preferred,auto,1
# Portrait/rotated secondary monitor (transform: 1 = 90°, 3 = 270°)
# monitor = DP-2, preferred, auto, 1, transform, 1
# Example for Framework 13 w/ 6K XDR Apple display
# monitor = DP-5, 6016x3384@60, auto, 2
# monitor = eDP-1, 2880x1920@120, auto, 2

View File

@@ -0,0 +1,47 @@
# Learn how to configure Hyprland: https://wiki.hyprland.org/Configuring/
# ============================================================================
# BEHAVIOR CONFIGS (keybindings, input, rules - non-visual)
# ============================================================================
# These are the Nomarchy defaults - don't edit these directly!
# Environment variables
source = ~/.config/nomarchy/default/hypr/envs.conf
# Keybindings
source = ~/.config/nomarchy/default/hypr/bindings/media.conf
source = ~/.config/nomarchy/default/hypr/bindings/clipboard.conf
source = ~/.config/nomarchy/default/hypr/bindings/tiling-v2.conf
source = ~/.config/nomarchy/default/hypr/bindings/utilities.conf
# Input settings
source = ~/.config/nomarchy/default/hypr/input.conf
# Window rules and layout
source = ~/.config/nomarchy/default/hypr/windows.conf
# Autostart applications
source = ~/.config/nomarchy/default/hypr/autostart.conf
# Look and feel (animations, layout behavior)
source = ~/.config/nomarchy/default/hypr/looknfeel.conf
# ============================================================================
# VISUAL CONFIGS (colors, theming)
# ============================================================================
# Theme colors - loaded from the currently active theme
source = ~/.config/nomarchy/current/theme/hyprland.conf
# ============================================================================
# USER OVERRIDES
# ============================================================================
# Your personal overrides - edit these files to customize Nomarchy!
source = ~/.config/hypr/monitors.conf
source = ~/.config/hypr/input.conf
source = ~/.config/hypr/bindings.conf
source = ~/.config/hypr/looknfeel.conf
source = ~/.config/hypr/autostart.conf
# Add any other personal Hyprland configuration below
# windowrule = workspace 5, match:class qemu

View File

@@ -0,0 +1,4 @@
screencopy {
allow_token_by_default = true
custom_picker_binary = hyprland-preview-share-picker
}

View File

@@ -0,0 +1,44 @@
{ config, pkgs, lib, ... }:
let
nomarchyLib = import ../lib { inherit lib; };
assetsPath = ../../assets/themes;
# Use shared wallpaper resolver
activeWallpaper = nomarchyLib.resolveWallpaper {
wallpaperPath = config.nomarchy.wallpaper;
themeName = config.nomarchy.theme;
inherit assetsPath;
};
hyprlandState = config.nomarchy.hyprland;
in
{
home.sessionVariables = lib.mkDefault {
WLR_NO_HARDWARE_CURSORS = "1";
HYPRLAND_LOG_WLR = "1";
};
wayland.windowManager.hyprland = {
enable = lib.mkDefault true;
settings = lib.mkDefault {
"debug" = {
"disable_logs" = false;
"enable_stdout_logs" = true;
};
"cursor" = {
"no_hardware_cursors" = true;
};
"general" = {
"gaps_in" = hyprlandState.gaps_in;
"gaps_out" = hyprlandState.gaps_out;
"border_size" = hyprlandState.border_size;
"col.active_border" = "rgb(${config.colorScheme.palette.base0E})";
"col.inactive_border" = "rgb(${config.colorScheme.palette.base03})";
};
};
extraConfig = lib.mkDefault ''
source = ~/.config/hypr/nomarchy.conf
'';
};
}

View File

@@ -0,0 +1,12 @@
$activeBorderColor = rgb(dcd7ba)
general {
col.active_border = $activeBorderColor
}
group {
col.border_active = $activeBorderColor
}
# Kanagawa backdrop is too strong for detault opacity
windowrule = opacity 0.98 0.95, match:tag terminal

View File

@@ -0,0 +1,26 @@
$activeBorderColor = rgb(f2fcff)
$activeShadowColor = rgb(6fb8e3)
$inactiveBorderColor = rgba(30486099)
$inactiveShadowColor = rgba(30486077)
general {
col.active_border = $activeBorderColor
col.inactive_border = $inactiveBorderColor
gaps_in = 8
gaps_out = 16
}
group {
col.border_active = $activeBorderColor
col.border_inactive = $inactiveBorderColor
}
decoration {
shadow {
enabled = true
range = 16
render_power = 4
color = $activeShadowColor
color_inactive = $inactiveShadowColor
}
}

View File

@@ -0,0 +1,9 @@
$activeBorderColor = rgb(81a1c1)
general {
col.active_border = $activeBorderColor
}
group {
col.border_active = $activeBorderColor
}

View File

@@ -0,0 +1,9 @@
$activeBorderColor = rgb(faa968)
general {
col.active_border = $activeBorderColor
}
group {
col.border_active = $activeBorderColor
}

View File

@@ -0,0 +1,61 @@
# --- Everforest Color Palette ---
$bg0 = rgba(2d353bee)
$bg1 = rgba(343f44ee)
$bg2 = rgba(3d484dee)
$bg3 = rgba(475258ee)
$bg4 = rgba(4f585eee)
$bg5 = rgba(56635fee)
$fg = rgba(d3c6aaee)
$red = rgba(e67e80ee)
$orange = rgba(e69875ee)
$yellow = rgba(dbbc7fee)
$green = rgba(a7c080ee)
$aqua = rgba(83c092ee)
$blue = rgba(7fbbb3ee)
$purple = rgba(d699b6ee)
$grey0 = rgba(7a8478ee)
$grey1 = rgba(859289ee)
$grey2 = rgba(9da9a0ee)
general {
gaps_in = 6
gaps_out = 12
border_size = 3
col.active_border = $fg
col.inactive_border = $bg5
layout = dwindle
resize_on_border = true
}
decoration {
rounding = 10
blur {
enabled = true
size = 5
passes = 3
new_optimizations = true
ignore_opacity = true
}
shadow {
enabled = true
range = 20
render_power = 3
color = rgba(00000044)
}
}
animations {
enabled = true
bezier = overshot, 0.05, 0.9, 0.1, 1.05
bezier = smoothOut, 0.36, 0, 0.66, -0.56
bezier = smoothIn, 0.25, 1, 0.5, 1
animation = windows, 1, 3, overshot, slide
animation = windowsOut, 1, 3, smoothOut, slide
animation = windowsMove, 1, 3, default
animation = border, 1, 3, default
animation = fade, 1, 3, smoothIn
animation = workspaces, 1, 3, smoothIn, slide
}

36
features/desktop/idle.nix Normal file
View File

@@ -0,0 +1,36 @@
{ config, lib, ... }:
{
services.hypridle = {
enable = lib.mkDefault config.nomarchy.toggles.idle;
settings = lib.mkDefault {
general = {
lock_cmd = "nomarchy-lock-screen";
before_sleep_cmd = "loginctl lock-session";
after_sleep_cmd = "sleep 1 && hyprctl dispatch dpms on";
inhibit_sleep = 3;
};
listener = [
{
timeout = 150;
on-timeout = "pidof hyprlock || nomarchy-launch-screensaver";
}
{
timeout = 151;
on-timeout = "loginctl lock-session";
}
{
timeout = 330;
on-timeout = "brightnessctl -sd '*::kbd_backlight' set 0";
on-resume = "brightnessctl -rd '*::kbd_backlight'";
}
{
timeout = 330;
on-timeout = "hyprctl dispatch dpms off";
on-resume = "hyprctl dispatch dpms on && brightnessctl -r";
}
];
};
};
}

View File

@@ -0,0 +1,11 @@
{ config, lib, ... }:
let
temp = toString (if config.nomarchy.toggles.nightlight then config.nomarchy.nightlightTemperature else 6500);
in
{
services.hyprsunset = {
enable = lib.mkDefault true; # Always enabled, we control via IPC and state
extraArgs = lib.mkDefault [ "--temperature" temp ];
};
}

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Toggles transparency for the currently focused window.
hyprctl dispatch setprop "address:$(hyprctl activewindow -j | jq -r '.address')" opaque toggle

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Get the active monitor (the one with the cursor)
MONITOR_INFO=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)')
ACTIVE_MONITOR=$(echo "$MONITOR_INFO" | jq -r '.name')
CURRENT_SCALE=$(echo "$MONITOR_INFO" | jq -r '.scale')
WIDTH=$(echo "$MONITOR_INFO" | jq -r '.width')
HEIGHT=$(echo "$MONITOR_INFO" | jq -r '.height')
REFRESH_RATE=$(echo "$MONITOR_INFO" | jq -r '.refreshRate')
# Cycle through scales: 1 → 1.6 → 2 → 3 → 1
CURRENT_INT=$(awk -v s="$CURRENT_SCALE" 'BEGIN { printf "%.0f", s * 10 }')
case "$CURRENT_INT" in
10) NEW_SCALE=1.6 ;;
16) NEW_SCALE=2 ;;
20) NEW_SCALE=3 ;;
*) NEW_SCALE=1 ;;
esac
hyprctl keyword misc:disable_scale_notification true
hyprctl keyword monitor "$ACTIVE_MONITOR,${WIDTH}x${HEIGHT}@${REFRESH_RATE},auto,$NEW_SCALE"
hyprctl keyword misc:disable_scale_notification false
notify-send -u low "󰍹 Display scaling set to ${NEW_SCALE}x"

View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Close all open windows
hyprctl clients -j | \
jq -r ".[].address" | \
xargs -I{} hyprctl dispatch closewindow address:{}
# Move to first workspace
hyprctl dispatch workspace 1

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Toggles the window gaps globally between no gaps and the default 10/5/2, declaratively and instantly.
STATE_DIR="$HOME/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
mkdir -p "$STATE_DIR"
if [ ! -f "$STATE_FILE" ]; then
echo "{}" > "$STATE_FILE"
fi
gaps=$(jq -r '.hyprland.gaps_out // 10' "$STATE_FILE")
if [[ $gaps == "0" ]]; then
NEW_STATE='{"gaps_out": 10, "gaps_in": 5, "border_size": 2}'
hyprctl keyword general:gaps_out 10
hyprctl keyword general:gaps_in 5
hyprctl keyword general:border_size 2
else
NEW_STATE='{"gaps_out": 0, "gaps_in": 0, "border_size": 0}'
hyprctl keyword general:gaps_out 0
hyprctl keyword general:gaps_in 0
hyprctl keyword general:border_size 0
fi
TMP_JSON=$(mktemp)
jq --argjson state "$NEW_STATE" '.hyprland = $state' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Toggled gaps to $NEW_STATE declaratively."

View File

@@ -0,0 +1,46 @@
#!/bin/bash
# Toggle to pop-out a tile to stay fixed on a display basis.
# Usage:
# nomarchy-hyprland-window-pop [width height [x y]]
#
# Arguments:
# width Optional. Width of the floating window. Default: 1300
# height Optional. Height of the floating window. Default: 900
# x Optional. X position of the window. Must provide both X and Y to take effect.
# y Optional. Y position of the window. Must provide both X and Y to take effect.
#
# Behavior:
# - If the window is already pinned, it will be unpinned and removed from the pop layer.
# - If the window is not pinned, it will be floated, resized, moved/centered, pinned, brought to top, and popped.
width=${1:-1300}
height=${2:-900}
x=${3:-}
y=${4:-}
active=$(hyprctl activewindow -j)
pinned=$(echo "$active" | jq ".pinned")
addr=$(echo "$active" | jq -r ".address")
if [[ $pinned == "true" ]]; then
hyprctl -q --batch \
"dispatch pin address:$addr;" \
"dispatch togglefloating address:$addr;" \
"dispatch tagwindow -pop address:$addr;"
elif [[ -n $addr ]]; then
hyprctl dispatch togglefloating address:$addr
hyprctl dispatch resizeactive exact $width $height address:$addr
if [[ -n $x && -n $y ]]; then
hyprctl dispatch moveactive $x $y address:$addr
else
hyprctl dispatch centerwindow address:$addr
fi
hyprctl -q --batch \
"dispatch pin address:$addr;" \
"dispatch alterzorder top address:$addr;" \
"dispatch tagwindow +pop address:$addr;"
fi

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Check current single_window_aspect_ratio setting
CURRENT_VALUE=$(hyprctl getoption "layout:single_window_aspect_ratio" 2>/dev/null | head -1)
# Parse vec2 output: "vec2: [1, 1]" or "vec2: [0, 0]"
if [[ $CURRENT_VALUE == *"[1, 1]"* ]]; then
hyprctl keyword layout:single_window_aspect_ratio "0 0"
notify-send -u low " Disable single-window square aspect ratio"
else
hyprctl keyword layout:single_window_aspect_ratio "1 1"
notify-send -u low " Enable single-window square aspect"
fi

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Toggle the layout on the current active workspace between dwindle and scrolling
ACTIVE_WORKSPACE=$(hyprctl activeworkspace -j | jq -r '.id')
CURRENT_LAYOUT=$(hyprctl activeworkspace -j | jq -r '.tiledLayout')
case "$CURRENT_LAYOUT" in
dwindle) NEW_LAYOUT=scrolling ;;
*) NEW_LAYOUT=dwindle ;;
esac
hyprctl keyword workspace $ACTIVE_WORKSPACE, layout:$NEW_LAYOUT
notify-send -u low "󱂬 Workspace layout set to $NEW_LAYOUT"

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Reload hyprland configuration (used by the Nomarchy theme switching).
hyprctl reload >/dev/null

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Restart the hypridle service (used for idle detection and auto-lock).
nomarchy-restart-app hypridle

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Restart the hyprsunset service (used for blue light filtering/night light).
nomarchy-restart-app hyprsunset

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Reload mako configuration (used by the Nomarchy theme switching).
makoctl reload

View File

@@ -0,0 +1,3 @@
#!/bin/bash
nomarchy-restart-app swayosd-server

View File

@@ -0,0 +1,22 @@
#!/bin/bash
restart_services() {
if systemctl --user is-enabled elephant.service &>/dev/null; then
systemctl --user restart elephant.service
fi
if systemctl --user is-enabled app-walker@autostart.service &>/dev/null; then
systemctl --user restart app-walker@autostart.service
else
echo -e "\e[31mUnable to restart Walker -- RESTART MANUALLY\e[0m"
fi
}
if (( EUID == 0 )); then
SCRIPT_OWNER=$(stat -c '%U' "$0")
USER_UID=$(id -u "$SCRIPT_OWNER")
systemd-run --uid="$SCRIPT_OWNER" --setenv=XDG_RUNTIME_DIR="/run/user/$USER_UID" \
bash -c "$(declare -f restart_services); restart_services"
else
restart_services
fi

View File

@@ -0,0 +1,3 @@
#!/bin/bash
nomarchy-restart-app waybar

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Display brightness level using SwayOSD on the current monitor.
# Usage: nomarchy-swayosd-brightness <percent>
percent="$1"
progress="$(awk -v p="$percent" 'BEGIN{printf "%.2f", p/100}')"
[[ $progress == "0.00" ]] && progress="0.01"
swayosd-client \
--monitor "$(hyprctl monitors -j | jq -r '.[]|select(.focused==true).name')" \
--custom-icon display-brightness \
--custom-progress "$progress" \
--custom-progress-text "${percent}%"

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Display keyboard brightness level using SwayOSD on the current monitor.
# Usage: nomarchy-swayosd-kbd-brightness <percent>
percent="$1"
progress="$(awk -v p="$percent" 'BEGIN{printf "%.2f", p/100}')"
[[ $progress == "0.00" ]] && progress="0.01"
swayosd-client \
--monitor "$(hyprctl monitors -j | jq -r '.[]|select(.focused==true).name')" \
--custom-icon keyboard-brightness \
--custom-progress "$progress" \
--custom-progress-text "${percent}%"

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Toggles the screensaver availability.
# Hybrid: updates state.json and runs env-update for persistence.
STATE_DIR="$HOME/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
mkdir -p "$STATE_DIR"
# Initialize if doesn't exist
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
if [[ $NOMARCHY_TOGGLE_SCREENSAVER == "false" ]]; then
NEW_VALUE="true"
notify-send -u low " Screensaver enabled"
else
NEW_VALUE="false"
notify-send -u low " Screensaver disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.screensaver = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Screensaver state set to $NEW_VALUE. Updating environment..."
env-update

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Toggles the waybar top bar.
# Hybrid: updates state.json and provides instant feedback.
STATE_DIR="$HOME/.config/nomarchy"
STATE_FILE="$STATE_DIR/state.json"
mkdir -p "$STATE_DIR"
# Initialize if doesn't exist
[[ ! -f $STATE_FILE ]] && echo "{}" > "$STATE_FILE"
if [[ $NOMARCHY_TOGGLE_WAYBAR == "false" ]]; then
NEW_VALUE="true"
uwsm-app -- waybar >/dev/null 2>&1 &
notify-send -u low " Top bar enabled"
else
NEW_VALUE="false"
pkill -x waybar
notify-send -u low " Top bar disabled"
fi
TMP_JSON=$(mktemp)
jq --argjson val "$NEW_VALUE" '.waybar = $val' "$STATE_FILE" > "$TMP_JSON" && mv "$TMP_JSON" "$STATE_FILE"
echo "Waybar state set to $NEW_VALUE. Environment will be fully updated on next rebuild."

View File

@@ -0,0 +1,174 @@
{
"reload_style_on_change": true,
"layer": "top",
"position": "top",
"spacing": 0,
"height": 26,
"modules-left": ["custom/nomarchy", "hyprland/workspaces"],
"modules-center": ["clock", "custom/update", "custom/voxtype", "custom/screenrecording-indicator", "custom/idle-indicator", "custom/notification-silencing-indicator"],
"modules-right": [
"group/tray-expander",
"bluetooth",
"network",
"pulseaudio",
"cpu",
"battery"
],
"hyprland/workspaces": {
"on-click": "activate",
"format": "{icon}",
"format-icons": {
"default": "",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"10": "0",
"active": "󱓻"
},
"persistent-workspaces": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": []
}
},
"custom/nomarchy": {
"format": "<span font='nomarchy'>a</span>",
"on-click": "nomarchy-menu",
"on-click-right": "xdg-terminal-exec",
"tooltip-format": "Nomarchy Menu\n\nSuper + Alt + Space"
},
"custom/update": {
"format": "",
"exec": "nomarchy-update-available",
"on-click": "nomarchy-launch-floating-terminal-with-presentation nomarchy-update",
"tooltip-format": "Nomarchy update available",
"signal": 7,
"interval": 21600
},
"cpu": {
"interval": 5,
"format": "󰍛",
"on-click": "nomarchy-launch-or-focus-tui btop",
"on-click-right": "alacritty"
},
"clock": {
"format": "{:L%A %H:%M}",
"format-alt": "{:L%d %B W%V %Y}",
"tooltip": false,
"on-click-right": "nomarchy-launch-floating-terminal-with-presentation nomarchy-tz-select"
},
"network": {
"format-icons": ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"],
"format": "{icon}",
"format-wifi": "{icon}",
"format-ethernet": "󰀂",
"format-disconnected": "󰤮",
"tooltip-format-wifi": "{essid} ({frequency} GHz)\n⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}",
"tooltip-format-ethernet": "⇣{bandwidthDownBytes} ⇡{bandwidthUpBytes}",
"tooltip-format-disconnected": "Disconnected",
"interval": 3,
"spacing": 1,
"on-click": "nomarchy-launch-wifi"
},
"battery": {
"format": "{capacity}% {icon}",
"format-discharging": "{icon}",
"format-charging": "{icon}",
"format-plugged": "",
"format-icons": {
"charging": ["󰢜", "󰂆", "󰂇", "󰂈", "󰢝", "󰂉", "󰢞", "󰂊", "󰂋", "󰂅"],
"default": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"]
},
"format-full": "󰂅",
"tooltip-format-discharging": "{power:>1.0f}W↓ {capacity}%",
"tooltip-format-charging": "{power:>1.0f}W↑ {capacity}%",
"interval": 5,
"on-click": "nomarchy-menu power",
"states": {
"warning": 20,
"critical": 10
}
},
"bluetooth": {
"format": "",
"format-off": "󰂲",
"format-disabled": "󰂲",
"format-connected": "󰂱",
"format-no-controller": "",
"tooltip-format": "Devices connected: {num_connections}",
"on-click": "nomarchy-launch-bluetooth"
},
"pulseaudio": {
"format": "{icon}",
"on-click": "nomarchy-launch-audio",
"on-click-right": "pamixer -t",
"tooltip-format": "Playing at {volume}%",
"scroll-step": 5,
"format-muted": "",
"format-icons": {
"headphone": "",
"headset": "",
"default": ["", "", ""]
}
},
"group/tray-expander": {
"orientation": "inherit",
"drawer": {
"transition-duration": 600,
"children-class": "tray-group-item"
},
"modules": ["custom/expand-icon", "tray"]
},
"custom/expand-icon": {
"format": "",
"tooltip": false,
"on-scroll-up": "",
"on-scroll-down": "",
"on-scroll-left": "",
"on-scroll-right": ""
},
"custom/screenrecording-indicator": {
"on-click": "nomarchy-cmd-screenrecord",
"exec": "$OMARCHY_PATH/default/waybar/indicators/screen-recording.sh",
"signal": 8,
"return-type": "json"
},
"custom/idle-indicator": {
"on-click": "nomarchy-toggle-idle",
"exec": "$OMARCHY_PATH/default/waybar/indicators/idle.sh",
"signal": 9,
"return-type": "json"
},
"custom/notification-silencing-indicator": {
"on-click": "nomarchy-toggle-notification-silencing",
"exec": "~/.config/nomarchy/default/waybar/indicators/notification-silencing.sh",
"signal": 10,
"return-type": "json"
},
"custom/voxtype": {
"exec": "nomarchy-voxtype-status",
"return-type": "json",
"format": "{icon}",
"format-icons": {
"idle": "",
"recording": "󰍬",
"transcribing": "󰔟"
},
"tooltip": true,
"on-click-right": "nomarchy-voxtype-config",
"on-click": "nomarchy-voxtype-model"
},
"tray": {
"icon-size": 12,
"spacing": 17
}
}

View File

@@ -0,0 +1,100 @@
@import "../nomarchy/current/theme/waybar.css";
* {
background-color: @background;
color: @foreground;
border: none;
border-radius: 0;
min-height: 0;
font-family: 'JetBrainsMono Nerd Font';
font-size: 12px;
}
.modules-left {
margin-left: 8px;
}
.modules-right {
margin-right: 8px;
}
#workspaces button {
all: initial;
padding: 0 6px;
margin: 0 1.5px;
min-width: 9px;
}
#workspaces button.empty {
opacity: 0.5;
}
#cpu,
#battery,
#pulseaudio,
#custom-nomarchy,
#custom-update {
min-width: 12px;
margin: 0 7.5px;
}
#tray {
margin-right: 16px;
}
#bluetooth {
margin-right: 17px;
}
#network {
margin-right: 13px;
}
#custom-expand-icon {
margin-right: 18px;
}
tooltip {
padding: 2px;
}
#custom-update {
font-size: 10px;
}
#clock {
margin-left: 8.75px;
}
.hidden {
opacity: 0;
}
#custom-screenrecording-indicator,
#custom-idle-indicator,
#custom-notification-silencing-indicator {
min-width: 12px;
margin-left: 5px;
margin-right: 0;
font-size: 10px;
padding-bottom: 1px;
}
#custom-screenrecording-indicator.active {
color: #a55555;
}
#custom-idle-indicator.active,
#custom-notification-silencing-indicator.active {
color: #a55555;
}
#custom-voxtype {
min-width: 12px;
margin: 0 0 0 7.5px;
}
#custom-voxtype.recording {
color: #a55555;
}

View File

@@ -0,0 +1,32 @@
{ config, pkgs, lib, ... }:
let
activeTheme = config.nomarchy.theme;
# Path to local theme files
themeDir = ./themes + "/${activeTheme}";
hasThemeConfig = builtins.pathExists (themeDir + "/config.jsonc");
hasThemeStyle = builtins.pathExists (themeDir + "/style.css");
# Default fallback files
defaultConfig = ./config/config.jsonc;
defaultStyle = ./config/style.css;
# Selected files
configFile = if hasThemeConfig then (themeDir + "/config.jsonc") else defaultConfig;
styleFile = if hasThemeStyle then (themeDir + "/style.css") else defaultStyle;
in
{
programs.waybar = {
enable = lib.mkDefault true;
systemd.enable = lib.mkDefault true;
settings = lib.mkDefault [ (builtins.fromJSON (builtins.readFile configFile)) ];
style = lib.mkDefault (builtins.readFile styleFile);
};
home.packages = lib.mkDefault (with pkgs; [
font-awesome
]);
}

View File

@@ -0,0 +1,2 @@
@define-color foreground #cdd6f4;
@define-color background #181824;

View File

@@ -0,0 +1,2 @@
@define-color foreground #d6e2ee;
@define-color background #213442;

View File

@@ -0,0 +1,3 @@
@define-color bg #00172e;
@define-color foreground #f6dcac;
@define-color background alpha(@bg, 0.8);

View File

@@ -0,0 +1,122 @@
{
"margin-top": 0,
"margin-left": 120,
"margin-bottom": 0,
"margin-right": 120,
"height": 61,
"layer": "top",
"position": "top",
"spacing": 15,
"modules-left": ["custom/launcher", "clock", "clock#date"],
"modules-center": ["hyprland/workspaces"],
"modules-right": ["idle_inhibitor", "pulseaudio", "custom/battery", "backlight", "tray", "custom/powermenu"],
"hyprland/workspaces": {
"disable-scroll": true,
"all-outputs": true,
"on-click": "activate",
"on-scroll-up": "hyprctl dispatch workspace r+1",
"on-scroll-down": "hyprctl dispatch workspace r-1",
"persistent_workspaces": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": []
}
},
"tray": {
"icon-size": 20,
"spacing": 5
},
"custom/launcher": {
"interval": "once",
"format": "",
"on-click": "pkill rofi || rofi -show drun",
"tooltip-format": "Application Launcher"
},
"backlight": {
"max-length": "4",
"format": "{icon} {percent}%",
"tooltip-format": "{percent}%",
"format-icons": ["󱩎","󱩏","󱩐","󱩑", "󱩓", "󱩔", "󰛨"],
"on-click": "",
"on-scroll-up": "brightnessctl set 10%-",
"on-scroll-down": "brightnessctl set +10%"
},
"memory": {
"interval": 30,
"format": " {}%",
"format-alt":" {used:0.1f}G",
"max-length": 10
},
"idle_inhibitor": {
"format": "{icon}",
"format-icons": {
"activated": " 󰅶 ",
"deactivated": " 󰾪 "
}
},
"pulseaudio": {
"scroll-step": 5,
"format": "{icon} {volume}%",
"format-bluetooth": "{icon} {volume}%",
"format-bluetooth-muted": " {icon}",
"format-muted": " muted",
"format-icons": {
"headphone": "",
"hands-free": "",
"headset": "",
"phone": "",
"portable": "",
"car": "",
"default": ["", "", ""]
},
"on-click": "mute-vol",
"on-click-middle": "audio-menu",
"on-click-right": "pavucontrol",
"tooltip-format": "{icon} {desc} | {volume}%"
},
"network": {
"format-wifi": " {signalStrength}%",
"format-ethernet": " {signalStrength}%",
"format-disconnected": "󰤭",
"on-click": ""
},
"custom/battery": {
"interval": 30,
"format": "{}",
"exec": "waybar-battery-status",
"on-click": "power-menu"
},
"clock": {
"format": " {:%H:%M}",
"tooltip": false
},
"clock#date": {
"format": " {:%A, %B %d, %Y}",
"on-click": "kitty calcurse",
"tooltip-format": "Open calendar"
},
"custom/powermenu": {
"format": "",
"on-click": "wlogout",
"tooltip": false
}
}

View File

@@ -0,0 +1,122 @@
@define-color bg_dim #232a2e;
@define-color bg0 #2d353b;
@define-color bg1 #343f44;
@define-color bg2 #3d484d;
@define-color bg3 #475258;
@define-color bg4 #4f585e;
@define-color bg5 #56635f;
@define-color bg_visual #543a48;
@define-color bg_red #514045;
@define-color bg_green #425047;
@define-color bg_blue #3a515d;
@define-color bg_yellow #4d4c43;
@define-color fg #d3c6aa;
@define-color red #e67e80;
@define-color orange #e69875;
@define-color yellow #dbbc7f;
@define-color green #a7c080;
@define-color aqua #83c092;
@define-color blue #7fbbb3;
@define-color purple #d699b6;
@define-color grey0 #7a8478;
@define-color grey1 #859289;
@define-color grey2 #9da9a0;
/* margin: top right bottom left */
/* Spacing outside the element */
/* padding: top right bottom left */
/* Spacing inside the element */
* {
font-family: JetBrainsMono Nerd Font, FontAwesome;
font-size: 13px;
font-weight: bold;
}
window#waybar {
background-color: @fg;
color: @bg0;
transition-property: background-color;
transition-duration: 0.5s;
border-radius: 0px 0px 15px 15px;
transition-duration: .5s;
border-bottom-width: 5px;
border-bottom-color: #7d6a40;
border-bottom-style: solid;
}
#backlight,
#tray,
#custom-launcher,
#clock,
#clock-date,
#workspaces,
#pulseaudio,
#idle_inhibitor,
#custom-battery,
#custom-powermenu {
background-color: @bg0;
color: @fg;
padding-left: 10px;
padding-right: 10px;
margin-top: 7px;
margin-bottom: 12px;
border-radius: 10px;
border-bottom-width: 5px;
border-bottom-color: #161a1d;
border-bottom-style: solid;
}
#tray > .active {
color: @fg;
}
#tray > .needs-attention {
color: @green;
}
#workspaces {
padding: 0px;
}
#workspaces button.active {
background-color: @blue;
color: @bg0;
border-radius: 10px;
margin-bottom: -5px;
border-bottom-width: 5px;
border-bottom-color: #366660;
border-bottom-style: solid;
}
#custom-launcher {
background-color: @green;
color: @bg0;
border-bottom-color: #556a35;
margin-left: 15px;
padding-left: 20px;
padding-right: 21px;
font-size: 20px;
}
#custom-powermenu {
background-color: @red;
color: @bg0;
border-bottom-color: #951c1f;
margin-right: 15px;
padding-left: 20px;
padding-right: 23px;
}
#custom-battery.preservation {
color: @green;
}
#idle_inhibitor {
padding-left: 10px;
padding-right: 10px;
font-size: 20px;
}